mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-08-31 12:46:38 +00:00
feat(optimization): model battery LCOS and probabilistic bypass
This commit is contained in:
@@ -32,6 +32,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
resolution, so both the hourly and the 15-minute optimizer are fed the correct
|
resolution, so both the hourly and the 15-minute optimizer are fed the correct
|
||||||
grid. The seasonal price extrapolation is resolution-agnostic and stays identical
|
grid. The seasonal price extrapolation is resolution-agnostic and stays identical
|
||||||
at the default hourly resolution.
|
at the default hourly resolution.
|
||||||
|
- Separate battery economics into two independent settings: battery
|
||||||
|
`levelized_cost_of_storage_kwh` is now charged once on delivered DC energy, while
|
||||||
|
`optimization.terminal_value_euro_per_kwh` values usable battery energy left at the end of the
|
||||||
|
optimization horizon. LCOS applies to both local battery supply and battery-to-grid export and is
|
||||||
|
included in hourly and total costs.
|
||||||
|
- Model direct PV-to-load consumption probabilistically from the bundled conditional minute-load
|
||||||
|
table. The expected direct flow is used consistently for PV bypass, residual load, battery
|
||||||
|
charging, and grid export on hourly and 15-minute optimization grids.
|
||||||
|
|
||||||
## 0.3.0 (2026-03-17)
|
## 0.3.0 (2026-03-17)
|
||||||
|
|
||||||
|
|||||||
@@ -456,7 +456,7 @@ as a cohesive unit for scheduling and availability checking.
|
|||||||
| charging_efficiency | `float` | `rw` | `0.88` | Charging efficiency [0.01 ... 1.00]. |
|
| charging_efficiency | `float` | `rw` | `0.88` | Charging efficiency [0.01 ... 1.00]. |
|
||||||
| device_id | `str` | `rw` | `<unknown>` | ID of device |
|
| device_id | `str` | `rw` | `<unknown>` | ID of device |
|
||||||
| discharging_efficiency | `float` | `rw` | `0.88` | Discharge efficiency [0.01 ... 1.00]. |
|
| discharging_efficiency | `float` | `rw` | `0.88` | Discharge efficiency [0.01 ... 1.00]. |
|
||||||
| levelized_cost_of_storage_kwh | `float` | `rw` | `0.0` | Levelized cost of storage (LCOS), the average lifetime cost of delivering one kWh [€/kWh]. |
|
| levelized_cost_of_storage_kwh | `float` | `rw` | `0.0` | Levelized cost of storage (LCOS), applied once to each kWh delivered by the battery [€/kWh]. |
|
||||||
| max_charge_power_w | `Optional[float]` | `rw` | `5000` | Maximum charging power [W]. |
|
| max_charge_power_w | `Optional[float]` | `rw` | `5000` | Maximum charging power [W]. |
|
||||||
| max_soc_percentage | `int` | `rw` | `100` | Maximum state of charge (SOC) as percentage of capacity [%]. |
|
| max_soc_percentage | `int` | `rw` | `100` | Maximum state of charge (SOC) as percentage of capacity [%]. |
|
||||||
| measurement_key_power_3_phase_sym_w | `str` | `ro` | `N/A` | Measurement key for the symmetric 3 phase power the battery is charged or discharged with [W]. |
|
| measurement_key_power_3_phase_sym_w | `str` | `ro` | `N/A` | Measurement key for the symmetric 3 phase power the battery is charged or discharged with [W]. |
|
||||||
|
|||||||
@@ -99,7 +99,7 @@
|
|||||||
| Name | Type | Read-Only | Default | Description |
|
| Name | Type | Read-Only | Default | Description |
|
||||||
| ---- | ---- | --------- | ------- | ----------- |
|
| ---- | ---- | --------- | ------- | ----------- |
|
||||||
| access_token | `Optional[str]` | `rw` | `None` | Tibber API access token. |
|
| access_token | `Optional[str]` | `rw` | `None` | Tibber API access token. |
|
||||||
| home_id | `Optional[str]` | `rw` | `None` | Tibber home id to read prices from. |
|
| home_id | `Optional[str]` | `rw` | `None` | Optional Tibber home id. If omitted, the first home with a subscription is used. |
|
||||||
:::
|
:::
|
||||||
<!-- pyml enable line-length -->
|
<!-- pyml enable line-length -->
|
||||||
|
|
||||||
|
|||||||
@@ -176,6 +176,8 @@
|
|||||||
"horizon_hours": 24,
|
"horizon_hours": 24,
|
||||||
"interval": 3600,
|
"interval": 3600,
|
||||||
"algorithm": "GENETIC",
|
"algorithm": "GENETIC",
|
||||||
|
"visualize_pdf": true,
|
||||||
|
"terminal_value_euro_per_kwh": 0.0,
|
||||||
"genetic": {
|
"genetic": {
|
||||||
"individuals": 400,
|
"individuals": 400,
|
||||||
"generations": 400,
|
"generations": 400,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
| 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. |
|
| 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 (slot length) [sec]. The genetic optimizer supports 3600 (1 hour) and 900 (15 min); other values fall back to 3600. Defaults to 3600 seconds (1 hour). |
|
| interval | `EOS_OPTIMIZATION__INTERVAL` | `int` | `rw` | `3600` | The optimization interval (slot length) [sec]. The genetic optimizer supports 3600 (1 hour) and 900 (15 min); other values fall back to 3600. Defaults to 3600 seconds (1 hour). |
|
||||||
| keys | | `list[str]` | `ro` | `N/A` | The keys of the solution. |
|
| keys | | `list[str]` | `ro` | `N/A` | The keys of the solution. |
|
||||||
|
| terminal_value_euro_per_kwh | `EOS_OPTIMIZATION__TERMINAL_VALUE_EURO_PER_KWH` | `float` | `rw` | `0.0` | Value assigned to usable battery energy remaining at the end of the optimization horizon [EUR/kWh]. This terminal value is independent of the battery LCOS. Defaults to 0 EUR/kWh. |
|
||||||
|
| visualize_pdf | `EOS_OPTIMIZATION__VISUALIZE_PDF` | `bool` | `rw` | `True` | Generate the PDF visualization after each optimization run. Disable for headless setups (e.g. Node-RED integration) to save several seconds per run. Defaults to True. |
|
||||||
:::
|
:::
|
||||||
<!-- pyml enable line-length -->
|
<!-- pyml enable line-length -->
|
||||||
|
|
||||||
@@ -27,6 +29,8 @@
|
|||||||
"horizon_hours": 24,
|
"horizon_hours": 24,
|
||||||
"interval": 3600,
|
"interval": 3600,
|
||||||
"algorithm": "GENETIC",
|
"algorithm": "GENETIC",
|
||||||
|
"visualize_pdf": true,
|
||||||
|
"terminal_value_euro_per_kwh": 0.0,
|
||||||
"genetic": {
|
"genetic": {
|
||||||
"individuals": 400,
|
"individuals": 400,
|
||||||
"generations": 400,
|
"generations": 400,
|
||||||
@@ -51,6 +55,8 @@
|
|||||||
"horizon_hours": 24,
|
"horizon_hours": 24,
|
||||||
"interval": 3600,
|
"interval": 3600,
|
||||||
"algorithm": "GENETIC",
|
"algorithm": "GENETIC",
|
||||||
|
"visualize_pdf": true,
|
||||||
|
"terminal_value_euro_per_kwh": 0.0,
|
||||||
"genetic": {
|
"genetic": {
|
||||||
"individuals": 400,
|
"individuals": 400,
|
||||||
"generations": 400,
|
"generations": 400,
|
||||||
|
|||||||
@@ -145,6 +145,11 @@ The energy management can be run in three modes:
|
|||||||
`prediction.hours * (3600 / interval)`, and device power caps as well as the solution
|
`prediction.hours * (3600 / interval)`, and device power caps as well as the solution
|
||||||
and energy-management-plan serializers are slot-aware.
|
and energy-management-plan serializers are slot-aware.
|
||||||
|
|
||||||
|
- **terminal_value_euro_per_kwh** (`float`, default: `0.0`): Monetary value assigned to usable
|
||||||
|
battery energy remaining at the end of the optimization horizon. This terminal value influences
|
||||||
|
whether the optimizer preserves or depletes the battery near the horizon. It is independent of
|
||||||
|
the battery's `levelized_cost_of_storage_kwh`, which prices actual discharge throughput.
|
||||||
|
|
||||||
:::{note}
|
:::{note}
|
||||||
Use `900` together with a 15-minute electricity price source (for example a dynamic or
|
Use `900` together with a 15-minute electricity price source (for example a dynamic or
|
||||||
exchange-priced tariff) to let the optimizer schedule on a quarter-hour grid. Keeping the
|
exchange-priced tariff) to let the optimizer schedule on a quarter-hour grid. Keeping the
|
||||||
@@ -224,8 +229,9 @@ The behavior of the genetic algorithm can be customized using the following conf
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"optimization": {
|
"optimization": {
|
||||||
"hours": 24,
|
"horizon_hours": 24,
|
||||||
"interval": 3600,
|
"interval": 3600,
|
||||||
|
"terminal_value_euro_per_kwh": 0.20,
|
||||||
"genetic" : {
|
"genetic" : {
|
||||||
"individuals": 300,
|
"individuals": 300,
|
||||||
"generations": 400,
|
"generations": 400,
|
||||||
@@ -342,6 +348,19 @@ The inverter supports separate AC↔DC conversion efficiencies:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`levelized_cost_of_storage_kwh` is an optional variable cost in EUR/kWh (default `0.0`). EOS applies
|
||||||
|
it exactly once to the DC energy actually delivered by the battery, whether that energy supplies
|
||||||
|
the local load or is exported to the grid:
|
||||||
|
|
||||||
|
```{math}
|
||||||
|
C_{LCOS} = \frac{E_{bat,out,Wh}}{1000}\,c_{LCOS,EUR/kWh}
|
||||||
|
```
|
||||||
|
|
||||||
|
Charging energy, battery-internal discharge losses, and the subsequent DC-to-AC inverter loss do
|
||||||
|
not receive another LCOS charge. LCOS is included in the hourly and total simulation costs and is
|
||||||
|
separate from `optimization.terminal_value_euro_per_kwh`, which values only the usable energy left
|
||||||
|
at the end of the optimization horizon.
|
||||||
|
|
||||||
#### Home appliance simulation configuration
|
#### Home appliance simulation configuration
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ to `DISABLED` in the configuration.
|
|||||||
"pv_akku": {
|
"pv_akku": {
|
||||||
"device_id": "battery1",
|
"device_id": "battery1",
|
||||||
"capacity_wh": 26400,
|
"capacity_wh": 26400,
|
||||||
|
"levelized_cost_of_storage_kwh": 0.12,
|
||||||
"max_charge_power_w": 5000,
|
"max_charge_power_w": 5000,
|
||||||
"initial_soc_percentage": 80,
|
"initial_soc_percentage": 80,
|
||||||
"min_soc_percentage": 15
|
"min_soc_percentage": 15
|
||||||
@@ -112,12 +113,15 @@ to `DISABLED` in the configuration.
|
|||||||
|
|
||||||
### Energy Management System (EMS)
|
### Energy Management System (EMS)
|
||||||
|
|
||||||
#### Battery Cost (`preis_euro_pro_wh_akku`)
|
#### Battery Terminal Value (`preis_euro_pro_wh_akku`)
|
||||||
|
|
||||||
- Unit: €/Wh
|
- Unit: €/Wh
|
||||||
- Purpose: Represents the residual value of energy stored in the battery
|
- Purpose: Represents the residual value of energy stored in the battery
|
||||||
- Impact: Lower values encourage battery depletion, higher values preserve charge at the end of the
|
- Impact: Lower values encourage battery depletion, higher values preserve charge at the end of the
|
||||||
simulation.
|
simulation.
|
||||||
|
- Separation from LCOS: This value is only applied to usable battery energy remaining at the end of
|
||||||
|
the optimization horizon. Battery discharge throughput is priced separately with
|
||||||
|
`pv_akku.levelized_cost_of_storage_kwh`.
|
||||||
|
|
||||||
#### Feed-in Tariff (`einspeiseverguetung_euro_pro_wh`)
|
#### Feed-in Tariff (`einspeiseverguetung_euro_pro_wh`)
|
||||||
|
|
||||||
@@ -145,6 +149,51 @@ to `DISABLED` in the configuration.
|
|||||||
- Format: Array of hourly values
|
- Format: Array of hourly values
|
||||||
- Data Source: `GET /v1/prediction/series?key=pvforecast_ac_power`
|
- Data Source: `GET /v1/prediction/series?key=pvforecast_ac_power`
|
||||||
|
|
||||||
|
#### Probabilistic Direct PV Consumption and Bypass
|
||||||
|
|
||||||
|
Hourly or 15-minute mean values alone would optimistically assume that the smaller of mean PV
|
||||||
|
generation and mean load is consumed directly. Real household load varies within the interval. EOS
|
||||||
|
therefore uses a conditional probability table derived from one-minute load samples. For a forecast
|
||||||
|
mean load \(\mu_L\), the table contains load-bin powers \(L_i\) and their conditional probabilities
|
||||||
|
\(p_i = P(L=L_i\mid\mu_L)\), with \(\sum_i p_i=1\).
|
||||||
|
|
||||||
|
Because the finite 50 W table grid can deviate slightly from the requested forecast mean, the load
|
||||||
|
bins are first normalized without changing the shape of the distribution:
|
||||||
|
|
||||||
|
```{math}
|
||||||
|
\widetilde{L}_i = L_i \frac{\mu_L}{\sum_j p_j L_j}
|
||||||
|
```
|
||||||
|
|
||||||
|
For mean PV power \(P_{PV}\), the expected power flowing directly from PV to the load is:
|
||||||
|
|
||||||
|
```{math}
|
||||||
|
P_{direct} = \sum_i p_i \min\left(\widetilde{L}_i, P_{PV}\right)
|
||||||
|
```
|
||||||
|
|
||||||
|
For a slot of duration \(\Delta t\), EOS converts this power into energy and derives both residual
|
||||||
|
flows from the same direct-consumption value:
|
||||||
|
|
||||||
|
```{math}
|
||||||
|
\begin{aligned}
|
||||||
|
E_{direct} &= \Delta t\,P_{direct} \\
|
||||||
|
E_{load,residual} &= E_{load}-E_{direct} \\
|
||||||
|
E_{PV,surplus} &= E_{PV}-E_{direct}
|
||||||
|
\end{aligned}
|
||||||
|
```
|
||||||
|
|
||||||
|
The residual load is supplied by the battery and then the grid. The PV surplus charges the battery;
|
||||||
|
any remainder bypasses the battery and is exported. Both residual load and PV surplus may be
|
||||||
|
positive in the same coarse slot because they occur during different sub-intervals. This is expected
|
||||||
|
and preserves the energy balances
|
||||||
|
\(E_{direct}+E_{load,residual}=E_{load}\) and
|
||||||
|
\(E_{direct}+E_{PV,surplus}=E_{PV}\).
|
||||||
|
|
||||||
|
The bundled table is conditioned on a one-hour mean load and models load variation only; mean PV is
|
||||||
|
treated as constant inside the slot. For a 15-minute grid produced by splitting hourly energy, the
|
||||||
|
power lookup retains the original hourly mean. A native 15-minute load forecast uses the same table
|
||||||
|
as an approximation until a separately calibrated 15-minute distribution is available. Fast PV
|
||||||
|
variability, for example from clouds, is not represented by this table.
|
||||||
|
|
||||||
#### Electricity Price Forecast (`strompreis_euro_pro_wh`)
|
#### Electricity Price Forecast (`strompreis_euro_pro_wh`)
|
||||||
|
|
||||||
- Unit: €/Wh
|
- Unit: €/Wh
|
||||||
@@ -162,8 +211,27 @@ Verify prices against your local tariffs.
|
|||||||
- `capacity_wh`: Total battery capacity in Wh
|
- `capacity_wh`: Total battery capacity in Wh
|
||||||
- `charging_efficiency`: Charging efficiency (0-1)
|
- `charging_efficiency`: Charging efficiency (0-1)
|
||||||
- `discharging_efficiency`: Discharging efficiency (0-1)
|
- `discharging_efficiency`: Discharging efficiency (0-1)
|
||||||
|
- `levelized_cost_of_storage_kwh`: LCOS in EUR/kWh, charged once for every kWh of DC energy
|
||||||
|
delivered by the battery. Default: `0.0`.
|
||||||
- `max_charge_power_w`: Maximum charging power in W
|
- `max_charge_power_w`: Maximum charging power in W
|
||||||
|
|
||||||
|
#### Battery LCOS (`levelized_cost_of_storage_kwh`)
|
||||||
|
|
||||||
|
LCOS and terminal value have different purposes. LCOS is a variable battery-use cost and is added
|
||||||
|
once when the battery delivers energy, both for local load coverage and battery-to-grid export. It
|
||||||
|
is not charged when the battery is charged and is not charged again on battery-internal or
|
||||||
|
DC-to-AC inverter losses.
|
||||||
|
|
||||||
|
For battery-delivered DC energy `E_bat,out` in one slot:
|
||||||
|
|
||||||
|
```{math}
|
||||||
|
C_{LCOS} = \frac{E_{bat,out}}{1000}\,c_{LCOS}
|
||||||
|
```
|
||||||
|
|
||||||
|
where `E_bat,out` is in Wh and `c_LCOS` is in EUR/kWh. This cost is included in
|
||||||
|
`Kosten_Euro_pro_Stunde`, `Gesamtkosten_Euro`, and therefore `Gesamtbilanz_Euro`. The terminal value
|
||||||
|
`preis_euro_pro_wh_akku`, by contrast, applies only to usable energy remaining after the last slot.
|
||||||
|
|
||||||
#### State of Charge (SoC)
|
#### State of Charge (SoC)
|
||||||
|
|
||||||
- `initial_soc_percentage`: Current battery level (%)
|
- `initial_soc_percentage`: Current battery level (%)
|
||||||
@@ -198,7 +266,7 @@ Round-trip efficiency for AC charging and discharging:
|
|||||||
`η_round_trip = ac_to_dc_efficiency × charging_efficiency × discharging_efficiency × dc_to_ac_efficiency`
|
`η_round_trip = ac_to_dc_efficiency × charging_efficiency × discharging_efficiency × dc_to_ac_efficiency`
|
||||||
|
|
||||||
For profitability, the discharge electricity price must exceed:
|
For profitability, the discharge electricity price must exceed:
|
||||||
`buy_price / η_round_trip`
|
`buy_price / η_round_trip + LCOS / dc_to_ac_efficiency`
|
||||||
|
|
||||||
**Backward compatibility**: With default values (`ac_to_dc_efficiency=1.0`,
|
**Backward compatibility**: With default values (`ac_to_dc_efficiency=1.0`,
|
||||||
`dc_to_ac_efficiency=1.0`, `max_ac_charge_power_w=null`), existing configurations work identically.
|
`dc_to_ac_efficiency=1.0`, `max_ac_charge_power_w=null`), existing configurations work identically.
|
||||||
@@ -223,7 +291,7 @@ penalty = ac_wh_charged × (break_even_price − best_uncovered_price) × factor
|
|||||||
```
|
```
|
||||||
|
|
||||||
where:
|
where:
|
||||||
- `break_even_price = charge_price / η_round_trip`
|
- `break_even_price = charge_price / η_round_trip + LCOS / dc_to_ac_efficiency`
|
||||||
- `best_uncovered_price` = highest future price not already covered by free PV battery energy
|
- `best_uncovered_price` = highest future price not already covered by free PV battery energy
|
||||||
- `factor` = `optimization.genetic.penalties.ac_charge_break_even` (default `1.0`)
|
- `factor` = `optimization.genetic.penalties.ac_charge_break_even` (default `1.0`)
|
||||||
|
|
||||||
|
|||||||
+65
-5
@@ -2213,8 +2213,9 @@
|
|||||||
},
|
},
|
||||||
"levelized_cost_of_storage_kwh": {
|
"levelized_cost_of_storage_kwh": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
"minimum": 0.0,
|
||||||
"title": "Levelized Cost Of Storage Kwh",
|
"title": "Levelized Cost Of Storage Kwh",
|
||||||
"description": "Levelized cost of storage (LCOS), the average lifetime cost of delivering one kWh [\u20ac/kWh].",
|
"description": "Levelized cost of storage (LCOS), applied once to each kWh delivered by the battery [\u20ac/kWh].",
|
||||||
"default": 0.0,
|
"default": 0.0,
|
||||||
"examples": [
|
"examples": [
|
||||||
0.12
|
0.12
|
||||||
@@ -2367,8 +2368,9 @@
|
|||||||
},
|
},
|
||||||
"levelized_cost_of_storage_kwh": {
|
"levelized_cost_of_storage_kwh": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
"minimum": 0.0,
|
||||||
"title": "Levelized Cost Of Storage Kwh",
|
"title": "Levelized Cost Of Storage Kwh",
|
||||||
"description": "Levelized cost of storage (LCOS), the average lifetime cost of delivering one kWh [\u20ac/kWh].",
|
"description": "Levelized cost of storage (LCOS), applied once to each kWh delivered by the battery [\u20ac/kWh].",
|
||||||
"default": 0.0,
|
"default": 0.0,
|
||||||
"examples": [
|
"examples": [
|
||||||
0.12
|
0.12
|
||||||
@@ -4886,7 +4888,7 @@
|
|||||||
"preis_euro_pro_wh_akku": {
|
"preis_euro_pro_wh_akku": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"title": "Preis Euro Pro Wh Akku",
|
"title": "Preis Euro Pro Wh Akku",
|
||||||
"description": "A float representing the cost of battery energy per watt-hour."
|
"description": "Terminal value of usable battery energy remaining at the end of the optimization horizon [EUR/Wh]. This is not the battery LCOS."
|
||||||
},
|
},
|
||||||
"gesamtlast": {
|
"gesamtlast": {
|
||||||
"items": {
|
"items": {
|
||||||
@@ -5111,6 +5113,14 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"title": "Electricity Price",
|
"title": "Electricity Price",
|
||||||
"description": "Used Electricity Price, including predictions"
|
"description": "Used Electricity Price, including predictions"
|
||||||
|
},
|
||||||
|
"Feed_in_tariff": {
|
||||||
|
"items": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
|
"title": "Feed In Tariff",
|
||||||
|
"description": "Used feed-in tariff in \u20ac/Wh per hour, including predictions"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@@ -6860,7 +6870,7 @@
|
|||||||
"maximum": 3600.0,
|
"maximum": 3600.0,
|
||||||
"minimum": 900.0,
|
"minimum": 900.0,
|
||||||
"title": "Interval",
|
"title": "Interval",
|
||||||
"description": "The optimization interval [sec]. Defaults to 3600 seconds (1 hour)",
|
"description": "The optimization interval (slot length) [sec]. The genetic optimizer supports 3600 (1 hour) and 900 (15 min); other values fall back to 3600. Defaults to 3600 seconds (1 hour).",
|
||||||
"default": 3600,
|
"default": 3600,
|
||||||
"examples": [
|
"examples": [
|
||||||
3600,
|
3600,
|
||||||
@@ -6876,6 +6886,26 @@
|
|||||||
"GENETIC"
|
"GENETIC"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"visualize_pdf": {
|
||||||
|
"type": "boolean",
|
||||||
|
"title": "Visualize Pdf",
|
||||||
|
"description": "Generate the PDF visualization after each optimization run. Disable for headless setups (e.g. Node-RED integration) to save several seconds per run. Defaults to True.",
|
||||||
|
"default": true,
|
||||||
|
"examples": [
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"terminal_value_euro_per_kwh": {
|
||||||
|
"type": "number",
|
||||||
|
"title": "Terminal Value Euro Per Kwh",
|
||||||
|
"description": "Value assigned to usable battery energy remaining at the end of the optimization horizon [EUR/kWh]. This terminal value is independent of the battery LCOS. Defaults to 0 EUR/kWh.",
|
||||||
|
"default": 0.0,
|
||||||
|
"examples": [
|
||||||
|
0.0,
|
||||||
|
0.2
|
||||||
|
]
|
||||||
|
},
|
||||||
"genetic": {
|
"genetic": {
|
||||||
"$ref": "#/components/schemas/GeneticCommonSettings",
|
"$ref": "#/components/schemas/GeneticCommonSettings",
|
||||||
"description": "Genetic optimization algorithm configuration.",
|
"description": "Genetic optimization algorithm configuration.",
|
||||||
@@ -6910,7 +6940,7 @@
|
|||||||
"maximum": 3600.0,
|
"maximum": 3600.0,
|
||||||
"minimum": 900.0,
|
"minimum": 900.0,
|
||||||
"title": "Interval",
|
"title": "Interval",
|
||||||
"description": "The optimization interval [sec]. Defaults to 3600 seconds (1 hour)",
|
"description": "The optimization interval (slot length) [sec]. The genetic optimizer supports 3600 (1 hour) and 900 (15 min); other values fall back to 3600. Defaults to 3600 seconds (1 hour).",
|
||||||
"default": 3600,
|
"default": 3600,
|
||||||
"examples": [
|
"examples": [
|
||||||
3600,
|
3600,
|
||||||
@@ -6926,6 +6956,26 @@
|
|||||||
"GENETIC"
|
"GENETIC"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"visualize_pdf": {
|
||||||
|
"type": "boolean",
|
||||||
|
"title": "Visualize Pdf",
|
||||||
|
"description": "Generate the PDF visualization after each optimization run. Disable for headless setups (e.g. Node-RED integration) to save several seconds per run. Defaults to True.",
|
||||||
|
"default": true,
|
||||||
|
"examples": [
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"terminal_value_euro_per_kwh": {
|
||||||
|
"type": "number",
|
||||||
|
"title": "Terminal Value Euro Per Kwh",
|
||||||
|
"description": "Value assigned to usable battery energy remaining at the end of the optimization horizon [EUR/kWh]. This terminal value is independent of the battery LCOS. Defaults to 0 EUR/kWh.",
|
||||||
|
"default": 0.0,
|
||||||
|
"examples": [
|
||||||
|
0.0,
|
||||||
|
0.2
|
||||||
|
]
|
||||||
|
},
|
||||||
"genetic": {
|
"genetic": {
|
||||||
"$ref": "#/components/schemas/GeneticCommonSettings",
|
"$ref": "#/components/schemas/GeneticCommonSettings",
|
||||||
"description": "Genetic optimization algorithm configuration.",
|
"description": "Genetic optimization algorithm configuration.",
|
||||||
@@ -8877,6 +8927,16 @@
|
|||||||
],
|
],
|
||||||
null
|
null
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"levelized_cost_of_storage_kwh": {
|
||||||
|
"type": "number",
|
||||||
|
"minimum": 0.0,
|
||||||
|
"title": "Levelized Cost Of Storage Kwh",
|
||||||
|
"description": "Levelized cost of storage applied once to each kWh delivered by the battery [EUR/kWh].",
|
||||||
|
"default": 0.0,
|
||||||
|
"examples": [
|
||||||
|
0.12
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@@ -50,8 +50,12 @@ class BatteriesCommonSettings(DevicesBaseSettings):
|
|||||||
|
|
||||||
levelized_cost_of_storage_kwh: float = Field(
|
levelized_cost_of_storage_kwh: float = Field(
|
||||||
default=0.0,
|
default=0.0,
|
||||||
|
ge=0.0,
|
||||||
json_schema_extra={
|
json_schema_extra={
|
||||||
"description": "Levelized cost of storage (LCOS), the average lifetime cost of delivering one kWh [€/kWh].",
|
"description": (
|
||||||
|
"Levelized cost of storage (LCOS), applied once to each kWh delivered "
|
||||||
|
"by the battery [€/kWh]."
|
||||||
|
),
|
||||||
"examples": [0.12],
|
"examples": [0.12],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ class Battery:
|
|||||||
self.initial_soc_percentage = self.parameters.initial_soc_percentage
|
self.initial_soc_percentage = self.parameters.initial_soc_percentage
|
||||||
self.charging_efficiency = self.parameters.charging_efficiency
|
self.charging_efficiency = self.parameters.charging_efficiency
|
||||||
self.discharging_efficiency = self.parameters.discharging_efficiency
|
self.discharging_efficiency = self.parameters.discharging_efficiency
|
||||||
|
self.levelized_cost_of_storage_kwh = (
|
||||||
|
self.parameters.levelized_cost_of_storage_kwh
|
||||||
|
if isinstance(self.parameters, SolarPanelBatteryParameters)
|
||||||
|
else 0.0
|
||||||
|
)
|
||||||
|
|
||||||
# Charge rates, in case of None use default
|
# Charge rates, in case of None use default
|
||||||
self.charge_rates = np.array(BATTERY_DEFAULT_CHARGE_RATES, dtype=float)
|
self.charge_rates = np.array(BATTERY_DEFAULT_CHARGE_RATES, dtype=float)
|
||||||
@@ -115,6 +120,10 @@ class Battery:
|
|||||||
raw_soc_available_wh = max(self.soc_wh - self.min_soc_wh, 0.0)
|
raw_soc_available_wh = max(self.soc_wh - self.min_soc_wh, 0.0)
|
||||||
return min(raw_power_remaining_wh, raw_soc_available_wh) * self.discharging_efficiency
|
return min(raw_power_remaining_wh, raw_soc_available_wh) * self.discharging_efficiency
|
||||||
|
|
||||||
|
def discharged_energy_wh(self, hour: int) -> float:
|
||||||
|
"""Return DC energy delivered by the battery in one optimization slot."""
|
||||||
|
return self._discharged_raw_wh_per_slot[hour] * self.discharging_efficiency
|
||||||
|
|
||||||
def set_discharge_per_hour(self, discharge_array: np.ndarray) -> None:
|
def set_discharge_per_hour(self, discharge_array: np.ndarray) -> None:
|
||||||
"""Sets the discharge values for each hour."""
|
"""Sets the discharge values for each hour."""
|
||||||
if len(discharge_array) != self.prediction_hours:
|
if len(discharge_array) != self.prediction_hours:
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ class Inverter:
|
|||||||
battery: Optional[Battery] = None,
|
battery: Optional[Battery] = None,
|
||||||
slot_duration_h: float = 1.0,
|
slot_duration_h: float = 1.0,
|
||||||
):
|
):
|
||||||
# slot_duration_h scales the per-slot energy cap (max_power_wh). It
|
|
||||||
# defaults to 1.0, which keeps the hourly behaviour for the default
|
|
||||||
# optimization interval of 3600 s.
|
|
||||||
self.parameters: InverterParameters = parameters
|
self.parameters: InverterParameters = parameters
|
||||||
self.battery: Optional[Battery] = battery
|
self.battery: Optional[Battery] = battery
|
||||||
self.slot_duration_h: float = slot_duration_h
|
self.slot_duration_h: float = slot_duration_h
|
||||||
@@ -28,16 +25,13 @@ class Inverter:
|
|||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
raise ValueError(error_msg)
|
raise ValueError(error_msg)
|
||||||
self.self_consumption_predictor = get_eos_load_interpolator()
|
self.self_consumption_predictor = get_eos_load_interpolator()
|
||||||
# max_power_wh is supplied as a power [W] that the legacy hourly code
|
# max_power_wh is supplied as power [W] but used as the maximum energy
|
||||||
# treats as Wh-per-hour. Scale it to the actual slot length so a 15-min
|
# the inverter can move during one optimization slot.
|
||||||
# slot can move at most a quarter of that energy.
|
self.max_power_wh = self.parameters.max_power_wh * self.slot_duration_h
|
||||||
self.max_power_wh = (
|
|
||||||
self.parameters.max_power_wh * self.slot_duration_h
|
|
||||||
) # Maximum energy the inverter can move in one optimization slot
|
|
||||||
self.dc_to_ac_efficiency = self.parameters.dc_to_ac_efficiency
|
self.dc_to_ac_efficiency = self.parameters.dc_to_ac_efficiency
|
||||||
self.ac_to_dc_efficiency = self.parameters.ac_to_dc_efficiency
|
self.ac_to_dc_efficiency = self.parameters.ac_to_dc_efficiency
|
||||||
# max_ac_charge_power_w stays in Watts. It feeds a dimensionless,
|
# This value remains a power [W]. GeneticSimulation converts it into a
|
||||||
# slot-agnostic power-ratio cap in genetic.py simulate().
|
# slot-independent charge-factor limit.
|
||||||
self.max_ac_charge_power_w = self.parameters.max_ac_charge_power_w
|
self.max_ac_charge_power_w = self.parameters.max_ac_charge_power_w
|
||||||
|
|
||||||
def _discharge_battery_to_ac(self, requested_ac_wh: float, hour: int) -> tuple[float, float]:
|
def _discharge_battery_to_ac(self, requested_ac_wh: float, hour: int) -> tuple[float, float]:
|
||||||
@@ -58,128 +52,86 @@ class Inverter:
|
|||||||
hour: int,
|
hour: int,
|
||||||
allow_battery_grid_export: bool = False,
|
allow_battery_grid_export: bool = False,
|
||||||
) -> tuple[float, float, float, float]:
|
) -> tuple[float, float, float, float]:
|
||||||
|
"""Process one slot using probabilistic direct PV-to-load overlap.
|
||||||
|
|
||||||
|
``generation`` and ``consumption`` are interval energies. The load
|
||||||
|
probability table is evaluated in watts and yields the expected direct
|
||||||
|
PV-to-load power. The remaining load and PV surplus are then handled
|
||||||
|
independently, because both can occur during different sub-intervals of
|
||||||
|
the same hourly or 15-minute slot.
|
||||||
|
"""
|
||||||
losses = 0.0
|
losses = 0.0
|
||||||
grid_export = 0.0
|
grid_export = 0.0
|
||||||
grid_import = 0.0
|
generation = max(float(generation), 0.0)
|
||||||
self_consumption = 0.0
|
consumption = max(float(consumption), 0.0)
|
||||||
|
|
||||||
# Cache inverter DC→AC efficiency for discharge path
|
# Convert interval energy [Wh] to mean power [W] for the probability
|
||||||
dc_to_ac_eff = self.dc_to_ac_efficiency
|
# lookup, then convert its expected direct power back to slot energy.
|
||||||
|
if generation > 0.0 and consumption > 0.0:
|
||||||
if generation >= consumption:
|
expected_direct_power_w = (
|
||||||
if consumption > self.max_power_wh:
|
self.self_consumption_predictor.calculate_expected_direct_consumption(
|
||||||
# If consumption exceeds maximum inverter power
|
consumption / self.slot_duration_h,
|
||||||
losses += generation - self.max_power_wh
|
generation / self.slot_duration_h,
|
||||||
remaining_power = self.max_power_wh - consumption
|
|
||||||
grid_import = -remaining_power # Negative indicates feeding into the grid
|
|
||||||
self_consumption = self.max_power_wh
|
|
||||||
else:
|
|
||||||
# Calculate scr using cached results per energy management/optimization run.
|
|
||||||
# The interpolator expects power levels [W]; consumption/generation are
|
|
||||||
# energy per slot [Wh], so convert via the slot duration (identical at
|
|
||||||
# the hourly default, ×4 on the 15-minute grid).
|
|
||||||
scr = self.self_consumption_predictor.calculate_self_consumption(
|
|
||||||
consumption / self.slot_duration_h, generation / self.slot_duration_h
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
# Remaining power after consumption
|
direct_pv_energy = expected_direct_power_w * self.slot_duration_h
|
||||||
remaining_power = (generation - consumption) * scr # EVQ
|
|
||||||
# Remaining load Self Consumption not perfect
|
|
||||||
remaining_load_evq = (generation - consumption) * (1.0 - scr)
|
|
||||||
|
|
||||||
from_battery_dc = 0.0
|
|
||||||
if remaining_load_evq > 0:
|
|
||||||
# Akku muss den Restverbrauch decken
|
|
||||||
if self.battery:
|
|
||||||
# Request more DC from battery to account for DC→AC conversion loss
|
|
||||||
dc_request = remaining_load_evq / dc_to_ac_eff
|
|
||||||
from_battery_dc, discharge_losses = self.battery.discharge_energy(
|
|
||||||
dc_request, hour
|
|
||||||
)
|
|
||||||
# Convert DC output to AC
|
|
||||||
from_battery_ac = from_battery_dc * dc_to_ac_eff
|
|
||||||
inverter_discharge_losses = from_battery_dc - from_battery_ac
|
|
||||||
remaining_load_evq -= from_battery_ac
|
|
||||||
losses += discharge_losses + inverter_discharge_losses
|
|
||||||
else:
|
|
||||||
from_battery_ac = 0.0
|
|
||||||
|
|
||||||
# Wenn der Akku den Restverbrauch nicht vollständig decken kann, wird der Rest ins Netz gezogen
|
|
||||||
if remaining_load_evq > 0:
|
|
||||||
grid_import += remaining_load_evq
|
|
||||||
remaining_load_evq = 0
|
|
||||||
else:
|
|
||||||
from_battery_ac = 0.0
|
|
||||||
|
|
||||||
if remaining_power > 0:
|
|
||||||
# Load battery with excess energy (DC path, no inverter conversion needed)
|
|
||||||
charge_losses = 0.0
|
|
||||||
if self.battery:
|
|
||||||
charged_energie, charge_losses = self.battery.charge_energy(
|
|
||||||
remaining_power, hour
|
|
||||||
)
|
|
||||||
remaining_surplus = remaining_power - (charged_energie + charge_losses)
|
|
||||||
else:
|
|
||||||
remaining_surplus = remaining_power
|
|
||||||
|
|
||||||
# Feed-in to the grid based on remaining capacity
|
|
||||||
if remaining_surplus > self.max_power_wh - consumption:
|
|
||||||
grid_export = self.max_power_wh - consumption
|
|
||||||
losses += remaining_surplus - grid_export
|
|
||||||
else:
|
|
||||||
grid_export = remaining_surplus
|
|
||||||
|
|
||||||
losses += charge_losses
|
|
||||||
self_consumption = (
|
|
||||||
consumption + from_battery_ac
|
|
||||||
) # Self-consumption is equal to the load
|
|
||||||
|
|
||||||
if allow_battery_grid_export and self.battery:
|
|
||||||
export_capacity = max(self.max_power_wh - consumption - grid_export, 0.0)
|
|
||||||
remaining_battery_ac = (
|
|
||||||
self.battery.remaining_discharge_energy_wh(hour) * dc_to_ac_eff
|
|
||||||
)
|
|
||||||
export_capacity = min(export_capacity, remaining_battery_ac)
|
|
||||||
battery_export_ac, battery_export_losses = self._discharge_battery_to_ac(
|
|
||||||
export_capacity, hour
|
|
||||||
)
|
|
||||||
grid_export += battery_export_ac
|
|
||||||
losses += battery_export_losses
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Case 2: Insufficient generation, cover shortfall
|
direct_pv_energy = 0.0
|
||||||
shortfall = consumption - generation
|
|
||||||
available_ac_power = max(self.max_power_wh - generation, 0)
|
|
||||||
|
|
||||||
# Discharge battery to cover shortfall, if possible
|
# Direct PV is bounded by both input energies and by the AC energy the
|
||||||
if self.battery:
|
# inverter can move during this slot.
|
||||||
# Need shortfall in AC, request more DC from battery for DC→AC conversion
|
direct_pv_energy = min(
|
||||||
ac_needed = min(shortfall, available_ac_power)
|
max(direct_pv_energy, 0.0),
|
||||||
dc_request = ac_needed / dc_to_ac_eff
|
generation,
|
||||||
battery_discharge_dc, discharge_losses = self.battery.discharge_energy(
|
consumption,
|
||||||
dc_request, hour
|
self.max_power_wh,
|
||||||
)
|
)
|
||||||
# Convert DC output to AC
|
remaining_load = max(consumption - direct_pv_energy, 0.0)
|
||||||
battery_discharge_ac = battery_discharge_dc * dc_to_ac_eff
|
pv_surplus = max(generation - direct_pv_energy, 0.0)
|
||||||
inverter_discharge_losses = battery_discharge_dc - battery_discharge_ac
|
remaining_inverter_ac_capacity = max(self.max_power_wh - direct_pv_energy, 0.0)
|
||||||
losses += discharge_losses + inverter_discharge_losses
|
|
||||||
else:
|
|
||||||
battery_discharge_ac = 0
|
|
||||||
|
|
||||||
# Draw remaining required power from the grid (discharge_losses are already subtracted in the battery)
|
# Load gaps and PV surplus may both occur within the same coarse slot.
|
||||||
grid_import = shortfall - battery_discharge_ac
|
# Cover the load gap first; this preserves the existing chronological
|
||||||
self_consumption = generation + battery_discharge_ac
|
# approximation and can create headroom for later PV charging.
|
||||||
|
battery_discharge_ac = 0.0
|
||||||
|
if remaining_load > 0.0 and self.battery and remaining_inverter_ac_capacity > 0.0:
|
||||||
|
requested_ac_wh = min(remaining_load, remaining_inverter_ac_capacity)
|
||||||
|
battery_discharge_ac, battery_discharge_losses = self._discharge_battery_to_ac(
|
||||||
|
requested_ac_wh, hour
|
||||||
|
)
|
||||||
|
remaining_load = max(remaining_load - battery_discharge_ac, 0.0)
|
||||||
|
remaining_inverter_ac_capacity = max(
|
||||||
|
remaining_inverter_ac_capacity - battery_discharge_ac, 0.0
|
||||||
|
)
|
||||||
|
losses += battery_discharge_losses
|
||||||
|
|
||||||
if allow_battery_grid_export and self.battery and grid_import <= 0.0:
|
grid_import = remaining_load
|
||||||
export_capacity = max(self.max_power_wh - consumption, 0.0)
|
|
||||||
remaining_battery_ac = (
|
|
||||||
self.battery.remaining_discharge_energy_wh(hour) * dc_to_ac_eff
|
|
||||||
)
|
|
||||||
export_capacity = min(export_capacity, remaining_battery_ac)
|
|
||||||
battery_export_ac, battery_export_losses = self._discharge_battery_to_ac(
|
|
||||||
export_capacity, hour
|
|
||||||
)
|
|
||||||
grid_export += battery_export_ac
|
|
||||||
losses += battery_export_losses
|
|
||||||
|
|
||||||
|
# Charge from the probabilistic PV surplus on the DC path. Stored energy
|
||||||
|
# plus charge losses equals the PV energy accepted by the battery.
|
||||||
|
remaining_surplus = pv_surplus
|
||||||
|
if remaining_surplus > 0.0 and self.battery:
|
||||||
|
charged_energy, charge_losses = self.battery.charge_energy(remaining_surplus, hour)
|
||||||
|
remaining_surplus = max(remaining_surplus - charged_energy - charge_losses, 0.0)
|
||||||
|
losses += charge_losses
|
||||||
|
|
||||||
|
pv_grid_export = min(remaining_surplus, remaining_inverter_ac_capacity)
|
||||||
|
grid_export += pv_grid_export
|
||||||
|
remaining_inverter_ac_capacity = max(remaining_inverter_ac_capacity - pv_grid_export, 0.0)
|
||||||
|
# PV which can neither charge the battery nor pass through the inverter
|
||||||
|
# is curtailed and reported as a loss.
|
||||||
|
losses += max(remaining_surplus - pv_grid_export, 0.0)
|
||||||
|
|
||||||
|
if allow_battery_grid_export and self.battery and remaining_inverter_ac_capacity > 0.0:
|
||||||
|
remaining_battery_ac = (
|
||||||
|
self.battery.remaining_discharge_energy_wh(hour) * self.dc_to_ac_efficiency
|
||||||
|
)
|
||||||
|
export_capacity = min(remaining_inverter_ac_capacity, remaining_battery_ac)
|
||||||
|
battery_export_ac, battery_export_losses = self._discharge_battery_to_ac(
|
||||||
|
export_capacity, hour
|
||||||
|
)
|
||||||
|
grid_export += battery_export_ac
|
||||||
|
losses += battery_export_losses
|
||||||
|
|
||||||
|
self_consumption = direct_pv_energy + battery_discharge_ac
|
||||||
return grid_export, grid_import, losses, self_consumption
|
return grid_export, grid_import, losses, self_consumption
|
||||||
|
|||||||
@@ -450,7 +450,18 @@ class GeneticSimulation(PydanticBaseModel):
|
|||||||
feed_in_tariff_per_hour[hour_idx] = hourly_feed_in_tariff
|
feed_in_tariff_per_hour[hour_idx] = hourly_feed_in_tariff
|
||||||
|
|
||||||
# Financial calculations
|
# Financial calculations
|
||||||
costs_per_hour[hour_idx] = energy_consumption_grid_actual * hourly_electricity_price
|
grid_cost = energy_consumption_grid_actual * hourly_electricity_price
|
||||||
|
# LCOS is charged exactly once on battery-delivered DC energy. It is
|
||||||
|
# not charged on input energy, internal discharge losses, or the
|
||||||
|
# downstream DC-to-AC inverter loss.
|
||||||
|
battery_lcos_cost = 0.0
|
||||||
|
if battery_fast:
|
||||||
|
battery_lcos_cost = (
|
||||||
|
battery_fast.discharged_energy_wh(hour)
|
||||||
|
* battery_fast.levelized_cost_of_storage_kwh
|
||||||
|
/ 1000.0
|
||||||
|
)
|
||||||
|
costs_per_hour[hour_idx] = grid_cost + battery_lcos_cost
|
||||||
revenue_per_hour[hour_idx] = energy_feedin_grid_actual * hourly_feed_in_tariff
|
revenue_per_hour[hour_idx] = energy_feedin_grid_actual * hourly_feed_in_tariff
|
||||||
|
|
||||||
total_cost = np.nansum(costs_per_hour)
|
total_cost = np.nansum(costs_per_hour)
|
||||||
@@ -1244,8 +1255,14 @@ class GeneticOptimization(OptimizationBase):
|
|||||||
if charge_price <= 0:
|
if charge_price <= 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Price that a future discharge hour must reach to break even
|
# Price that a future AC discharge hour must reach to break
|
||||||
break_even_price = charge_price / round_trip_eff
|
# even. LCOS is defined per DC Wh delivered by the battery;
|
||||||
|
# dividing it by DC-to-AC efficiency converts it to the
|
||||||
|
# corresponding cost per useful/exported AC Wh.
|
||||||
|
lcos_per_wh_dc = getattr(bat, "levelized_cost_of_storage_kwh", 0.0) / 1000.0
|
||||||
|
break_even_price = (
|
||||||
|
charge_price / round_trip_eff + lcos_per_wh_dc / inv.dc_to_ac_efficiency
|
||||||
|
)
|
||||||
|
|
||||||
best_uncovered_price = best_prices[hour]
|
best_uncovered_price = best_prices[hour]
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,17 @@ class BaseBatteryParameters(DeviceParameters):
|
|||||||
class SolarPanelBatteryParameters(BaseBatteryParameters):
|
class SolarPanelBatteryParameters(BaseBatteryParameters):
|
||||||
"""PV battery device simulation configuration."""
|
"""PV battery device simulation configuration."""
|
||||||
|
|
||||||
|
levelized_cost_of_storage_kwh: float = Field(
|
||||||
|
default=0.0,
|
||||||
|
ge=0.0,
|
||||||
|
json_schema_extra={
|
||||||
|
"description": (
|
||||||
|
"Levelized cost of storage applied once to each kWh delivered "
|
||||||
|
"by the battery [EUR/kWh]."
|
||||||
|
),
|
||||||
|
"examples": [0.12],
|
||||||
|
},
|
||||||
|
)
|
||||||
max_charge_power_w: Optional[float] = max_charging_power_field()
|
max_charge_power_w: Optional[float] = max_charging_power_field()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,10 @@ class GeneticEnergyManagementParameters(GeneticParametersBaseModel):
|
|||||||
)
|
)
|
||||||
preis_euro_pro_wh_akku: float = Field(
|
preis_euro_pro_wh_akku: float = Field(
|
||||||
json_schema_extra={
|
json_schema_extra={
|
||||||
"description": "A float representing the cost of battery energy per watt-hour."
|
"description": (
|
||||||
|
"Terminal value of usable battery energy remaining at the end of the "
|
||||||
|
"optimization horizon [EUR/Wh]. This is not the battery LCOS."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
gesamtlast: list[float] = Field(
|
gesamtlast: list[float] = Field(
|
||||||
@@ -416,7 +419,6 @@ class GeneticOptimizationParameters(
|
|||||||
cls.config.devices.max_batteries = 1
|
cls.config.devices.max_batteries = 1
|
||||||
if cls.config.devices.max_batteries == 0:
|
if cls.config.devices.max_batteries == 0:
|
||||||
battery_params = None
|
battery_params = None
|
||||||
battery_lcos_kwh = 0
|
|
||||||
else:
|
else:
|
||||||
if cls.config.devices.batteries is None:
|
if cls.config.devices.batteries is None:
|
||||||
logger.info("No battery device data available - defaulting to demo data.")
|
logger.info("No battery device data available - defaulting to demo data.")
|
||||||
@@ -428,6 +430,9 @@ class GeneticOptimizationParameters(
|
|||||||
capacity_wh=battery_config.capacity_wh,
|
capacity_wh=battery_config.capacity_wh,
|
||||||
charging_efficiency=battery_config.charging_efficiency,
|
charging_efficiency=battery_config.charging_efficiency,
|
||||||
discharging_efficiency=battery_config.discharging_efficiency,
|
discharging_efficiency=battery_config.discharging_efficiency,
|
||||||
|
levelized_cost_of_storage_kwh=(
|
||||||
|
battery_config.levelized_cost_of_storage_kwh
|
||||||
|
),
|
||||||
max_charge_power_w=battery_config.max_charge_power_w,
|
max_charge_power_w=battery_config.max_charge_power_w,
|
||||||
min_soc_percentage=battery_config.min_soc_percentage,
|
min_soc_percentage=battery_config.min_soc_percentage,
|
||||||
max_soc_percentage=battery_config.max_soc_percentage,
|
max_soc_percentage=battery_config.max_soc_percentage,
|
||||||
@@ -441,14 +446,6 @@ class GeneticOptimizationParameters(
|
|||||||
cls.config.devices.batteries = [{"device_id": "battery1", "capacity_wh": 8000}]
|
cls.config.devices.batteries = [{"device_id": "battery1", "capacity_wh": 8000}]
|
||||||
# Retry
|
# Retry
|
||||||
continue
|
continue
|
||||||
# Levelized cost of ownership
|
|
||||||
if battery_config.levelized_cost_of_storage_kwh is None:
|
|
||||||
logger.info(
|
|
||||||
"No battery device LCOS data available - defaulting to 0 €/kWh. Parameter preparation attempt {}.",
|
|
||||||
attempt,
|
|
||||||
)
|
|
||||||
battery_config.levelized_cost_of_storage_kwh = 0
|
|
||||||
battery_lcos_kwh = battery_config.levelized_cost_of_storage_kwh
|
|
||||||
# Initial SOC
|
# Initial SOC
|
||||||
try:
|
try:
|
||||||
initial_soc_factor = cls.measurement.key_to_value(
|
initial_soc_factor = cls.measurement.key_to_value(
|
||||||
@@ -654,7 +651,9 @@ class GeneticOptimizationParameters(
|
|||||||
strompreis_euro_pro_wh=elecprice_marketprice_wh,
|
strompreis_euro_pro_wh=elecprice_marketprice_wh,
|
||||||
einspeiseverguetung_euro_pro_wh=feed_in_tariff_wh,
|
einspeiseverguetung_euro_pro_wh=feed_in_tariff_wh,
|
||||||
gesamtlast=loadforecast_power_w,
|
gesamtlast=loadforecast_power_w,
|
||||||
preis_euro_pro_wh_akku=battery_lcos_kwh / 1000,
|
preis_euro_pro_wh_akku=(
|
||||||
|
cls.config.optimization.terminal_value_euro_per_kwh / 1000
|
||||||
|
),
|
||||||
),
|
),
|
||||||
temperature_forecast=weather_temp_air,
|
temperature_forecast=weather_temp_air,
|
||||||
pv_akku=battery_params,
|
pv_akku=battery_params,
|
||||||
|
|||||||
@@ -101,6 +101,18 @@ class OptimizationCommonSettings(SettingsBaseModel):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
terminal_value_euro_per_kwh: float = Field(
|
||||||
|
default=0.0,
|
||||||
|
json_schema_extra={
|
||||||
|
"description": (
|
||||||
|
"Value assigned to usable battery energy remaining at the end of the "
|
||||||
|
"optimization horizon [EUR/kWh]. This terminal value is independent "
|
||||||
|
"of the battery LCOS. Defaults to 0 EUR/kWh."
|
||||||
|
),
|
||||||
|
"examples": [0.0, 0.20],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
genetic: GeneticCommonSettings = Field(
|
genetic: GeneticCommonSettings = Field(
|
||||||
default_factory=GeneticCommonSettings,
|
default_factory=GeneticCommonSettings,
|
||||||
json_schema_extra={
|
json_schema_extra={
|
||||||
|
|||||||
@@ -17,8 +17,32 @@ class SelfConsumptionProbabilityInterpolator:
|
|||||||
self.interpolator: RegularGridInterpolator = pickle.load(file) # noqa: S301
|
self.interpolator: RegularGridInterpolator = pickle.load(file) # noqa: S301
|
||||||
self.load_power_min_w = float(self.interpolator.grid[0][0])
|
self.load_power_min_w = float(self.interpolator.grid[0][0])
|
||||||
self.load_power_max_w = float(self.interpolator.grid[0][-1])
|
self.load_power_max_w = float(self.interpolator.grid[0][-1])
|
||||||
|
self.minute_load_levels_w = np.asarray(self.interpolator.grid[1], dtype=float)
|
||||||
self.minute_load_max_w = float(self.interpolator.grid[1][-1])
|
self.minute_load_max_w = float(self.interpolator.grid[1][-1])
|
||||||
|
|
||||||
|
def _load_distribution(self, mean_load_power_w: float) -> tuple[np.ndarray, np.ndarray]:
|
||||||
|
"""Return the conditional minute-load distribution for a mean load.
|
||||||
|
|
||||||
|
The table stores one probability mass for each 50 W minute-load bin.
|
||||||
|
Linear interpolation between its mean-load rows can introduce very small
|
||||||
|
numerical deviations, so negative masses are removed and the result is
|
||||||
|
normalized explicitly.
|
||||||
|
"""
|
||||||
|
bounded_mean_load_w = float(
|
||||||
|
np.clip(mean_load_power_w, self.load_power_min_w, self.load_power_max_w)
|
||||||
|
)
|
||||||
|
points = np.column_stack(
|
||||||
|
(
|
||||||
|
np.full(self.minute_load_levels_w.shape, bounded_mean_load_w),
|
||||||
|
self.minute_load_levels_w,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
probabilities = np.maximum(np.asarray(self.interpolator(points), dtype=float), 0.0)
|
||||||
|
probability_sum = float(probabilities.sum())
|
||||||
|
if probability_sum <= 0.0:
|
||||||
|
return self.minute_load_levels_w, probabilities
|
||||||
|
return self.minute_load_levels_w, probabilities / probability_sum
|
||||||
|
|
||||||
def _generate_points(
|
def _generate_points(
|
||||||
self, mean_load_power_w: float, pv_power_w: float
|
self, mean_load_power_w: float, pv_power_w: float
|
||||||
) -> tuple[np.ndarray, np.ndarray]:
|
) -> tuple[np.ndarray, np.ndarray]:
|
||||||
@@ -39,7 +63,12 @@ class SelfConsumptionProbabilityInterpolator:
|
|||||||
|
|
||||||
@cache_energy_management
|
@cache_energy_management
|
||||||
def calculate_self_consumption(self, mean_load_power_w: float, pv_power_w: float) -> float:
|
def calculate_self_consumption(self, mean_load_power_w: float, pv_power_w: float) -> float:
|
||||||
"""Calculate the PV self-consumption rate using RegularGridInterpolator.
|
"""Return the legacy cumulative minute-load probability.
|
||||||
|
|
||||||
|
This method is retained for API compatibility. Its result is the
|
||||||
|
probability that the minute load is no greater than ``pv_power_w``;
|
||||||
|
it is not an energy self-consumption ratio. New energy-flow code must
|
||||||
|
use :meth:`calculate_expected_direct_consumption`.
|
||||||
|
|
||||||
The results are cached until the start of the next energy management run/ optimization.
|
The results are cached until the start of the next energy management run/ optimization.
|
||||||
|
|
||||||
@@ -54,6 +83,46 @@ class SelfConsumptionProbabilityInterpolator:
|
|||||||
probabilities = self.interpolator(points)
|
probabilities = self.interpolator(points)
|
||||||
return float(np.clip(probabilities.sum(), 0.0, 1.0))
|
return float(np.clip(probabilities.sum(), 0.0, 1.0))
|
||||||
|
|
||||||
|
@cache_energy_management
|
||||||
|
def calculate_expected_direct_consumption(
|
||||||
|
self, mean_load_power_w: float, pv_power_w: float
|
||||||
|
) -> float:
|
||||||
|
"""Calculate expected direct PV-to-load power in watts.
|
||||||
|
|
||||||
|
For conditional minute-load probabilities ``p_i`` and load-bin powers
|
||||||
|
``L_i``, the expected direct consumption is
|
||||||
|
|
||||||
|
``sum(p_i * min(L_i, pv_power_w))``.
|
||||||
|
|
||||||
|
The tabulated load-bin powers are rescaled to preserve the supplied
|
||||||
|
forecast mean exactly. This compensates for discretization and the
|
||||||
|
finite upper table boundary while retaining the distribution shape.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mean_load_power_w: Mean load power of the forecast interval [W].
|
||||||
|
pv_power_w: Mean PV power of the forecast interval [W].
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Expected direct PV-to-load power [W].
|
||||||
|
"""
|
||||||
|
mean_load_power_w = max(float(mean_load_power_w), 0.0)
|
||||||
|
pv_power_w = max(float(pv_power_w), 0.0)
|
||||||
|
if mean_load_power_w == 0.0 or pv_power_w == 0.0:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
load_levels_w, probabilities = self._load_distribution(mean_load_power_w)
|
||||||
|
modeled_mean_load_w = float(np.dot(probabilities, load_levels_w))
|
||||||
|
if modeled_mean_load_w <= 0.0:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
# Preserve the requested mean load while keeping the conditional shape
|
||||||
|
# from the probability table.
|
||||||
|
normalized_load_levels_w = load_levels_w * (mean_load_power_w / modeled_mean_load_w)
|
||||||
|
expected_direct_power_w = float(
|
||||||
|
np.dot(probabilities, np.minimum(normalized_load_levels_w, pv_power_w))
|
||||||
|
)
|
||||||
|
return float(np.clip(expected_direct_power_w, 0.0, min(mean_load_power_w, pv_power_w)))
|
||||||
|
|
||||||
# def calculate_self_consumption(self, load_1h_power: float, pv_power: float) -> float:
|
# def calculate_self_consumption(self, load_1h_power: float, pv_power: float) -> float:
|
||||||
# """Calculate the PV self-consumption rate using RegularGridInterpolator.
|
# """Calculate the PV self-consumption rate using RegularGridInterpolator.
|
||||||
|
|
||||||
|
|||||||
@@ -337,4 +337,9 @@ def test_quarter_hour_discharge_calls_share_one_power_budget():
|
|||||||
|
|
||||||
assert first_delivered == pytest.approx(200.0)
|
assert first_delivered == pytest.approx(200.0)
|
||||||
assert second_delivered == pytest.approx(50.0)
|
assert second_delivered == pytest.approx(50.0)
|
||||||
|
assert battery.discharged_energy_wh(0) == pytest.approx(250.0)
|
||||||
assert battery.soc_wh == pytest.approx(9_750.0)
|
assert battery.soc_wh == pytest.approx(9_750.0)
|
||||||
|
|
||||||
|
battery.reset()
|
||||||
|
|
||||||
|
assert battery.discharged_energy_wh(0) == 0.0
|
||||||
|
|||||||
@@ -338,15 +338,15 @@ def test_simulation(genetic_simulation):
|
|||||||
|
|
||||||
# Verify the total balance
|
# Verify the total balance
|
||||||
assert (
|
assert (
|
||||||
abs(result["Gesamtbilanz_Euro"] - 6.62818441758576) < 1e-5
|
abs(result["Gesamtbilanz_Euro"] - 7.025236588371921) < 1e-5
|
||||||
), "Total balance should reflect the shared per-slot battery power limit."
|
), "Total balance should reflect the shared per-slot battery power limit."
|
||||||
|
|
||||||
# Check total revenue and total costs
|
# Check total revenue and total costs
|
||||||
assert (
|
assert (
|
||||||
abs(result["Gesamteinnahmen_Euro"] - 1.9606946615517515) < 1e-5
|
abs(result["Gesamteinnahmen_Euro"] - 2.3247787887715) < 1e-5
|
||||||
), "Total revenue should respect the shared per-slot battery power limit."
|
), "Total revenue should respect the shared per-slot battery power limit."
|
||||||
assert (
|
assert (
|
||||||
abs(result["Gesamtkosten_Euro"] - 8.588879079137512) < 1e-5
|
abs(result["Gesamtkosten_Euro"] - 9.350015377143421) < 1e-5
|
||||||
), "Total costs should respect the shared per-slot battery power limit."
|
), "Total costs should respect the shared per-slot battery power limit."
|
||||||
|
|
||||||
# Check the losses
|
# Check the losses
|
||||||
@@ -387,8 +387,8 @@ def test_direct_marketing_curtails_negative_feed_in(config_eos, monkeypatch):
|
|||||||
inverter = Inverter(InverterParameters(device_id="inverter1", max_power_wh=1000.0))
|
inverter = Inverter(InverterParameters(device_id="inverter1", max_power_wh=1000.0))
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
inverter.self_consumption_predictor,
|
inverter.self_consumption_predictor,
|
||||||
"calculate_self_consumption",
|
"calculate_expected_direct_consumption",
|
||||||
Mock(return_value=1.0),
|
Mock(side_effect=min),
|
||||||
)
|
)
|
||||||
|
|
||||||
simulation = GeneticSimulation()
|
simulation = GeneticSimulation()
|
||||||
@@ -413,7 +413,11 @@ def test_direct_marketing_curtails_negative_feed_in(config_eos, monkeypatch):
|
|||||||
assert result["Verluste_Pro_Stunde"][0] == pytest.approx(500.0)
|
assert result["Verluste_Pro_Stunde"][0] == pytest.approx(500.0)
|
||||||
|
|
||||||
|
|
||||||
def _direct_marketing_battery_export_simulation(config_eos) -> GeneticSimulation:
|
def _direct_marketing_battery_export_simulation(
|
||||||
|
config_eos,
|
||||||
|
levelized_cost_of_storage_kwh: float = 0.0,
|
||||||
|
dc_to_ac_efficiency: float = 1.0,
|
||||||
|
) -> GeneticSimulation:
|
||||||
config_eos.merge_settings_from_dict(
|
config_eos.merge_settings_from_dict(
|
||||||
{"prediction": {"hours": 2}, "optimization": {"horizon_hours": 2}}
|
{"prediction": {"hours": 2}, "optimization": {"horizon_hours": 2}}
|
||||||
)
|
)
|
||||||
@@ -426,6 +430,7 @@ def _direct_marketing_battery_export_simulation(config_eos) -> GeneticSimulation
|
|||||||
min_soc_percentage=0,
|
min_soc_percentage=0,
|
||||||
charging_efficiency=1.0,
|
charging_efficiency=1.0,
|
||||||
discharging_efficiency=1.0,
|
discharging_efficiency=1.0,
|
||||||
|
levelized_cost_of_storage_kwh=levelized_cost_of_storage_kwh,
|
||||||
max_charge_power_w=500,
|
max_charge_power_w=500,
|
||||||
),
|
),
|
||||||
prediction_hours=config_eos.prediction.hours,
|
prediction_hours=config_eos.prediction.hours,
|
||||||
@@ -435,6 +440,7 @@ def _direct_marketing_battery_export_simulation(config_eos) -> GeneticSimulation
|
|||||||
device_id="inverter1",
|
device_id="inverter1",
|
||||||
max_power_wh=500.0,
|
max_power_wh=500.0,
|
||||||
battery_id=battery.parameters.device_id,
|
battery_id=battery.parameters.device_id,
|
||||||
|
dc_to_ac_efficiency=dc_to_ac_efficiency,
|
||||||
),
|
),
|
||||||
battery=battery,
|
battery=battery,
|
||||||
)
|
)
|
||||||
@@ -479,3 +485,23 @@ def test_direct_marketing_battery_grid_export_uses_separate_signal(config_eos):
|
|||||||
assert result["Einnahmen_Euro_pro_Stunde"][0] == pytest.approx(0.1)
|
assert result["Einnahmen_Euro_pro_Stunde"][0] == pytest.approx(0.1)
|
||||||
assert simulation.battery is not None
|
assert simulation.battery is not None
|
||||||
assert simulation.battery.current_soc_percentage() == 50.0
|
assert simulation.battery.current_soc_percentage() == 50.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_battery_lcos_is_charged_once_on_delivered_energy(config_eos):
|
||||||
|
simulation = _direct_marketing_battery_export_simulation(
|
||||||
|
config_eos,
|
||||||
|
levelized_cost_of_storage_kwh=0.12,
|
||||||
|
dc_to_ac_efficiency=0.8,
|
||||||
|
)
|
||||||
|
assert simulation.bat_grid_export_hours is not None
|
||||||
|
simulation.bat_grid_export_hours[0] = 1
|
||||||
|
|
||||||
|
result = simulation.simulate(start_hour=0)
|
||||||
|
|
||||||
|
# The battery delivers 500 Wh DC, so LCOS is 0.5 kWh * 0.12 EUR/kWh
|
||||||
|
# = 0.06 EUR exactly once. After the 80% inverter, 400 Wh AC reaches
|
||||||
|
# the grid and earns 400 Wh * 0.0002 EUR/Wh = 0.08 EUR.
|
||||||
|
assert result["Kosten_Euro_pro_Stunde"][0] == pytest.approx(0.06)
|
||||||
|
assert result["Gesamtkosten_Euro"] == pytest.approx(0.06)
|
||||||
|
assert result["Einnahmen_Euro_pro_Stunde"][0] == pytest.approx(0.08)
|
||||||
|
assert result["Gesamtbilanz_Euro"] == pytest.approx(-0.02)
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ def test_quarter_hour_energy_is_converted_back_to_same_mean_power():
|
|||||||
hourly_pv_wh = 1200.0
|
hourly_pv_wh = 1200.0
|
||||||
slot_duration_h = 0.25
|
slot_duration_h = 0.25
|
||||||
|
|
||||||
hourly = interpolator.calculate_self_consumption(hourly_load_wh, hourly_pv_wh)
|
hourly = interpolator.calculate_expected_direct_consumption(hourly_load_wh, hourly_pv_wh)
|
||||||
quarter_hour = interpolator.calculate_self_consumption(
|
quarter_hour = interpolator.calculate_expected_direct_consumption(
|
||||||
(hourly_load_wh / 4) / slot_duration_h,
|
(hourly_load_wh / 4) / slot_duration_h,
|
||||||
(hourly_pv_wh / 4) / slot_duration_h,
|
(hourly_pv_wh / 4) / slot_duration_h,
|
||||||
)
|
)
|
||||||
@@ -28,3 +28,43 @@ def test_load_above_probability_grid_uses_highest_supported_distribution():
|
|||||||
|
|
||||||
assert above_boundary == pytest.approx(at_boundary)
|
assert above_boundary == pytest.approx(at_boundary)
|
||||||
assert above_boundary > 0.99
|
assert above_boundary > 0.99
|
||||||
|
|
||||||
|
|
||||||
|
def test_expected_direct_consumption_accounts_for_subhourly_load_variation():
|
||||||
|
"""Expected overlap must be below the optimistic overlap of interval means."""
|
||||||
|
interpolator = get_eos_load_interpolator()
|
||||||
|
|
||||||
|
direct_power_w = interpolator.calculate_expected_direct_consumption(800.0, 1200.0)
|
||||||
|
|
||||||
|
assert direct_power_w == pytest.approx(621.0, abs=2.0)
|
||||||
|
assert 0.0 < direct_power_w < 800.0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("mean_load_power_w", "pv_power_w"),
|
||||||
|
[(800.0, 1200.0), (1000.0, 500.0), (1500.0, 1500.0)],
|
||||||
|
)
|
||||||
|
def test_expected_direct_consumption_produces_conservative_energy_balance(
|
||||||
|
mean_load_power_w, pv_power_w
|
||||||
|
):
|
||||||
|
"""Direct use, residual load and surplus must conserve both mean powers."""
|
||||||
|
interpolator = get_eos_load_interpolator()
|
||||||
|
|
||||||
|
direct_power_w = interpolator.calculate_expected_direct_consumption(
|
||||||
|
mean_load_power_w, pv_power_w
|
||||||
|
)
|
||||||
|
residual_load_w = mean_load_power_w - direct_power_w
|
||||||
|
pv_surplus_w = pv_power_w - direct_power_w
|
||||||
|
|
||||||
|
assert 0.0 <= direct_power_w <= min(mean_load_power_w, pv_power_w)
|
||||||
|
assert direct_power_w + residual_load_w == pytest.approx(mean_load_power_w)
|
||||||
|
assert direct_power_w + pv_surplus_w == pytest.approx(pv_power_w)
|
||||||
|
|
||||||
|
|
||||||
|
def test_expected_direct_consumption_preserves_forecast_mean_at_high_pv():
|
||||||
|
"""A PV level above every normalized load bin covers the complete mean load."""
|
||||||
|
interpolator = get_eos_load_interpolator()
|
||||||
|
|
||||||
|
direct_power_w = interpolator.calculate_expected_direct_consumption(3000.0, 10000.0)
|
||||||
|
|
||||||
|
assert direct_power_w == pytest.approx(3000.0)
|
||||||
|
|||||||
+91
-28
@@ -5,7 +5,9 @@ import pytest
|
|||||||
|
|
||||||
from akkudoktoreos.devices.genetic.battery import Battery
|
from akkudoktoreos.devices.genetic.battery import Battery
|
||||||
from akkudoktoreos.devices.genetic.inverter import Inverter, InverterParameters
|
from akkudoktoreos.devices.genetic.inverter import Inverter, InverterParameters
|
||||||
from akkudoktoreos.optimization.genetic.geneticdevices import SolarPanelBatteryParameters
|
from akkudoktoreos.optimization.genetic.geneticdevices import (
|
||||||
|
SolarPanelBatteryParameters,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@@ -20,7 +22,7 @@ def mock_battery() -> Mock:
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def inverter(mock_battery) -> Inverter:
|
def inverter(mock_battery) -> Inverter:
|
||||||
mock_self_consumption_predictor = Mock()
|
mock_self_consumption_predictor = Mock()
|
||||||
mock_self_consumption_predictor.calculate_self_consumption.return_value = 1.0
|
mock_self_consumption_predictor.calculate_expected_direct_consumption.side_effect = min
|
||||||
with patch(
|
with patch(
|
||||||
"akkudoktoreos.devices.genetic.inverter.get_eos_load_interpolator",
|
"akkudoktoreos.devices.genetic.inverter.get_eos_load_interpolator",
|
||||||
return_value=mock_self_consumption_predictor,
|
return_value=mock_self_consumption_predictor,
|
||||||
@@ -91,7 +93,7 @@ def test_process_energy_excess_generation(inverter, mock_battery):
|
|||||||
assert self_consumption == 200.0 # All consumption is met
|
assert self_consumption == 200.0 # All consumption is met
|
||||||
mock_battery.charge_energy.assert_called_once_with(400.0, hour)
|
mock_battery.charge_energy.assert_called_once_with(400.0, hour)
|
||||||
mock_battery.discharge_energy.assert_not_called()
|
mock_battery.discharge_energy.assert_not_called()
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||||
consumption, generation
|
consumption, generation
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -100,7 +102,8 @@ def test_process_energy_excess_generation_interpolator(inverter, mock_battery):
|
|||||||
# Battery charges 100 Wh with 10 Wh loss
|
# Battery charges 100 Wh with 10 Wh loss
|
||||||
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
||||||
mock_battery.discharge_energy.return_value = (20.0, 2.0)
|
mock_battery.discharge_energy.return_value = (20.0, 2.0)
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.return_value = 0.95
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.side_effect = None
|
||||||
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.return_value = 180.0
|
||||||
|
|
||||||
generation = 600.0
|
generation = 600.0
|
||||||
consumption = 200.0
|
consumption = 200.0
|
||||||
@@ -110,19 +113,71 @@ def test_process_energy_excess_generation_interpolator(inverter, mock_battery):
|
|||||||
generation, consumption, hour
|
generation, consumption, hour
|
||||||
)
|
)
|
||||||
|
|
||||||
assert grid_export == pytest.approx(
|
assert grid_export == pytest.approx(300.0, rel=1e-2)
|
||||||
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 grid_import == pytest.approx(0.0, rel=1e-2) # No grid draw
|
||||||
assert losses == 12.0 # Battery charging losses
|
assert losses == 22.0 # Battery/inverter losses plus curtailed PV
|
||||||
assert self_consumption == 220.0 # All consumption is met
|
assert self_consumption == 200.0 # 180 Wh direct PV + 20 Wh battery
|
||||||
mock_battery.charge_energy.assert_called_once_with(pytest.approx(380.0, rel=1e-2), hour)
|
mock_battery.charge_energy.assert_called_once_with(pytest.approx(420.0, rel=1e-2), hour)
|
||||||
mock_battery.discharge_energy.assert_called_once_with(pytest.approx(20.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(
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||||
consumption, generation
|
consumption, generation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_probabilistic_bypass_conserves_energy_without_battery():
|
||||||
|
predictor = Mock()
|
||||||
|
predictor.calculate_expected_direct_consumption.return_value = 150.0
|
||||||
|
with patch(
|
||||||
|
"akkudoktoreos.devices.genetic.inverter.get_eos_load_interpolator",
|
||||||
|
return_value=predictor,
|
||||||
|
):
|
||||||
|
inverter_without_battery = Inverter(
|
||||||
|
InverterParameters(device_id="inverter", max_power_wh=1000.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
generation = 600.0
|
||||||
|
consumption = 200.0
|
||||||
|
grid_export, grid_import, losses, self_consumption = (
|
||||||
|
inverter_without_battery.process_energy(generation, consumption, hour=0)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert self_consumption == pytest.approx(150.0)
|
||||||
|
assert grid_import == pytest.approx(50.0)
|
||||||
|
assert grid_export == pytest.approx(450.0)
|
||||||
|
assert losses == 0.0
|
||||||
|
assert generation + grid_import == pytest.approx(
|
||||||
|
consumption + grid_export + losses
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_probabilistic_bypass_conserves_energy_on_quarter_hour_grid():
|
||||||
|
predictor = Mock()
|
||||||
|
predictor.calculate_expected_direct_consumption.return_value = 600.0
|
||||||
|
with patch(
|
||||||
|
"akkudoktoreos.devices.genetic.inverter.get_eos_load_interpolator",
|
||||||
|
return_value=predictor,
|
||||||
|
):
|
||||||
|
inverter_without_battery = Inverter(
|
||||||
|
InverterParameters(device_id="inverter", max_power_wh=2000.0),
|
||||||
|
slot_duration_h=0.25,
|
||||||
|
)
|
||||||
|
|
||||||
|
generation = 300.0 # 1200 W over 15 minutes
|
||||||
|
consumption = 200.0 # 800 W over 15 minutes
|
||||||
|
grid_export, grid_import, losses, self_consumption = (
|
||||||
|
inverter_without_battery.process_energy(generation, consumption, hour=0)
|
||||||
|
)
|
||||||
|
|
||||||
|
predictor.calculate_expected_direct_consumption.assert_called_once_with(800.0, 1200.0)
|
||||||
|
assert self_consumption == pytest.approx(150.0)
|
||||||
|
assert grid_import == pytest.approx(50.0)
|
||||||
|
assert grid_export == pytest.approx(150.0)
|
||||||
|
assert losses == 0.0
|
||||||
|
assert generation + grid_import == pytest.approx(
|
||||||
|
consumption + grid_export + losses
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_generation_equals_consumption(inverter, mock_battery):
|
def test_process_energy_generation_equals_consumption(inverter, mock_battery):
|
||||||
generation = 300.0
|
generation = 300.0
|
||||||
consumption = 300.0
|
consumption = 300.0
|
||||||
@@ -139,7 +194,7 @@ def test_process_energy_generation_equals_consumption(inverter, mock_battery):
|
|||||||
|
|
||||||
mock_battery.charge_energy.assert_not_called()
|
mock_battery.charge_energy.assert_not_called()
|
||||||
mock_battery.discharge_energy.assert_not_called()
|
mock_battery.discharge_energy.assert_not_called()
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||||
consumption, generation
|
consumption, generation
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -163,7 +218,9 @@ def test_process_energy_battery_discharges(inverter, mock_battery):
|
|||||||
assert self_consumption == 200.0 # Generation + battery discharge
|
assert self_consumption == 200.0 # Generation + battery discharge
|
||||||
mock_battery.charge_energy.assert_not_called()
|
mock_battery.charge_energy.assert_not_called()
|
||||||
mock_battery.discharge_energy.assert_called_once_with(150.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(150.0, hour)
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||||
|
consumption, generation
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_allows_battery_grid_export(inverter, mock_battery):
|
def test_process_energy_allows_battery_grid_export(inverter, mock_battery):
|
||||||
@@ -183,7 +240,7 @@ def test_process_energy_allows_battery_grid_export(inverter, mock_battery):
|
|||||||
assert losses == 0.0
|
assert losses == 0.0
|
||||||
assert self_consumption == 100.0
|
assert self_consumption == 100.0
|
||||||
mock_battery.discharge_energy.assert_has_calls([call(100.0, 12), call(200.0, 12)])
|
mock_battery.discharge_energy.assert_has_calls([call(100.0, 12), call(200.0, 12)])
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_battery_empty(inverter, mock_battery):
|
def test_process_energy_battery_empty(inverter, mock_battery):
|
||||||
@@ -203,7 +260,9 @@ def test_process_energy_battery_empty(inverter, mock_battery):
|
|||||||
assert self_consumption == 100.0 # Only generation is consumed
|
assert self_consumption == 100.0 # Only generation is consumed
|
||||||
mock_battery.charge_energy.assert_not_called()
|
mock_battery.charge_energy.assert_not_called()
|
||||||
mock_battery.discharge_energy.assert_called_once_with(200.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(200.0, hour)
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||||
|
consumption, generation
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_battery_full_at_start(inverter, mock_battery):
|
def test_process_energy_battery_full_at_start(inverter, mock_battery):
|
||||||
@@ -225,7 +284,7 @@ def test_process_energy_battery_full_at_start(inverter, mock_battery):
|
|||||||
assert self_consumption == 200.0 # Only consumption is met
|
assert self_consumption == 200.0 # Only consumption is met
|
||||||
mock_battery.charge_energy.assert_called_once_with(300.0, hour)
|
mock_battery.charge_energy.assert_called_once_with(300.0, hour)
|
||||||
mock_battery.discharge_energy.assert_not_called()
|
mock_battery.discharge_energy.assert_not_called()
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||||
consumption, generation
|
consumption, generation
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -247,7 +306,9 @@ def test_process_energy_insufficient_generation_no_battery(inverter, mock_batter
|
|||||||
assert self_consumption == 100.0 # Only generation is consumed
|
assert self_consumption == 100.0 # Only generation is consumed
|
||||||
mock_battery.charge_energy.assert_not_called()
|
mock_battery.charge_energy.assert_not_called()
|
||||||
mock_battery.discharge_energy.assert_called_once_with(400.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(400.0, hour)
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||||
|
consumption, generation
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_insufficient_generation_battery_assists(inverter, mock_battery):
|
def test_process_energy_insufficient_generation_battery_assists(inverter, mock_battery):
|
||||||
@@ -272,7 +333,9 @@ def test_process_energy_insufficient_generation_battery_assists(inverter, mock_b
|
|||||||
assert self_consumption == 250.0 # Generation + battery discharge
|
assert self_consumption == 250.0 # Generation + battery discharge
|
||||||
mock_battery.charge_energy.assert_not_called()
|
mock_battery.charge_energy.assert_not_called()
|
||||||
mock_battery.discharge_energy.assert_called_once_with(200.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(200.0, hour)
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||||
|
consumption, generation
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_zero_generation(inverter, mock_battery):
|
def test_process_energy_zero_generation(inverter, mock_battery):
|
||||||
@@ -295,7 +358,7 @@ def test_process_energy_zero_generation(inverter, mock_battery):
|
|||||||
assert self_consumption == 100.0 # Only battery discharge is consumed
|
assert self_consumption == 100.0 # Only battery discharge is consumed
|
||||||
mock_battery.charge_energy.assert_not_called()
|
mock_battery.charge_energy.assert_not_called()
|
||||||
mock_battery.discharge_energy.assert_called_once_with(300.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(300.0, hour)
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_zero_consumption(inverter, mock_battery):
|
def test_process_energy_zero_consumption(inverter, mock_battery):
|
||||||
@@ -315,9 +378,7 @@ def test_process_energy_zero_consumption(inverter, mock_battery):
|
|||||||
assert self_consumption == 0.0 # Zero consumption
|
assert self_consumption == 0.0 # Zero consumption
|
||||||
mock_battery.charge_energy.assert_called_once_with(500.0, hour)
|
mock_battery.charge_energy.assert_called_once_with(500.0, hour)
|
||||||
mock_battery.discharge_energy.assert_not_called()
|
mock_battery.discharge_energy.assert_not_called()
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_not_called()
|
||||||
consumption, generation
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_zero_generation_zero_consumption(inverter, mock_battery):
|
def test_process_energy_zero_generation_zero_consumption(inverter, mock_battery):
|
||||||
@@ -335,9 +396,7 @@ def test_process_energy_zero_generation_zero_consumption(inverter, mock_battery)
|
|||||||
assert self_consumption == 0.0 # No consumption
|
assert self_consumption == 0.0 # No consumption
|
||||||
mock_battery.charge_energy.assert_not_called()
|
mock_battery.charge_energy.assert_not_called()
|
||||||
mock_battery.discharge_energy.assert_not_called()
|
mock_battery.discharge_energy.assert_not_called()
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_not_called()
|
||||||
consumption, generation
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_partial_battery_discharge(inverter, mock_battery):
|
def test_process_energy_partial_battery_discharge(inverter, mock_battery):
|
||||||
@@ -358,7 +417,9 @@ def test_process_energy_partial_battery_discharge(inverter, mock_battery):
|
|||||||
assert self_consumption == 250.0 # Generation + battery discharge
|
assert self_consumption == 250.0 # Generation + battery discharge
|
||||||
mock_battery.charge_energy.assert_not_called()
|
mock_battery.charge_energy.assert_not_called()
|
||||||
mock_battery.discharge_energy.assert_called_once_with(200.0, 12)
|
mock_battery.discharge_energy.assert_called_once_with(200.0, 12)
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||||
|
consumption, generation
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_consumption_exceeds_max_no_battery(inverter, mock_battery):
|
def test_process_energy_consumption_exceeds_max_no_battery(inverter, mock_battery):
|
||||||
@@ -378,7 +439,9 @@ def test_process_energy_consumption_exceeds_max_no_battery(inverter, mock_batter
|
|||||||
assert self_consumption == 100.0 # Only the generation is consumed, maxing out the inverter
|
assert self_consumption == 100.0 # Only the generation is consumed, maxing out the inverter
|
||||||
mock_battery.charge_energy.assert_not_called()
|
mock_battery.charge_energy.assert_not_called()
|
||||||
mock_battery.discharge_energy.assert_called_once_with(400.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(400.0, hour)
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||||
|
consumption, generation
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_zero_generation_full_battery_high_consumption(inverter, mock_battery):
|
def test_process_energy_zero_generation_full_battery_high_consumption(inverter, mock_battery):
|
||||||
@@ -400,4 +463,4 @@ def test_process_energy_zero_generation_full_battery_high_consumption(inverter,
|
|||||||
assert self_consumption == 500.0 # Battery fully discharges to meet consumption
|
assert self_consumption == 500.0 # Battery fully discharges to meet consumption
|
||||||
mock_battery.charge_energy.assert_not_called()
|
mock_battery.charge_energy.assert_not_called()
|
||||||
mock_battery.discharge_energy.assert_called_once_with(500.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(500.0, hour)
|
||||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_not_called()
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ def _make_inverter(
|
|||||||
) -> Inverter:
|
) -> Inverter:
|
||||||
"""Create an Inverter with custom efficiency parameters and a mock battery."""
|
"""Create an Inverter with custom efficiency parameters and a mock battery."""
|
||||||
mock_self_consumption_predictor = Mock()
|
mock_self_consumption_predictor = Mock()
|
||||||
mock_self_consumption_predictor.calculate_self_consumption.return_value = 1.0
|
mock_self_consumption_predictor.calculate_expected_direct_consumption.side_effect = min
|
||||||
|
|
||||||
params = InverterParameters(
|
params = InverterParameters(
|
||||||
device_id="inv1",
|
device_id="inv1",
|
||||||
@@ -165,12 +165,14 @@ class TestDcToAcEfficiency:
|
|||||||
assert losses == pytest.approx(10.0, rel=1e-5) # Only battery losses
|
assert losses == pytest.approx(10.0, rel=1e-5) # Only battery losses
|
||||||
|
|
||||||
def test_discharge_surplus_path_with_efficiency(self, mock_battery):
|
def test_discharge_surplus_path_with_efficiency(self, mock_battery):
|
||||||
"""When generation > consumption but SCR < 1, discharge goes through inverter."""
|
"""A probabilistic load gap discharges through the inverter."""
|
||||||
mock_battery.discharge_energy.return_value = (50.0, 5.0)
|
mock_battery.discharge_energy.return_value = (30.0 / 0.90, 5.0)
|
||||||
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
||||||
|
|
||||||
inv = _make_inverter(dc_to_ac_efficiency=0.90, mock_battery=mock_battery)
|
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
|
predictor = cast(Mock, inv.self_consumption_predictor)
|
||||||
|
predictor.calculate_expected_direct_consumption.side_effect = None
|
||||||
|
predictor.calculate_expected_direct_consumption.return_value = 170.0
|
||||||
|
|
||||||
generation = 500.0
|
generation = 500.0
|
||||||
consumption = 200.0
|
consumption = 200.0
|
||||||
@@ -180,18 +182,23 @@ class TestDcToAcEfficiency:
|
|||||||
generation, consumption, hour
|
generation, consumption, hour
|
||||||
)
|
)
|
||||||
|
|
||||||
# surplus = 300, remaining_power = 300*0.9 = 270, remaining_load_evq = 300*0.1 = 30
|
# Expected direct PV is 170 Wh, leaving 30 Wh of load gap and
|
||||||
# DC request for discharge = 30 / 0.90 = 33.333
|
# 330 Wh of PV surplus within different sub-periods of the slot.
|
||||||
|
# DC request for discharge = 30 / 0.90 = 33.333 Wh.
|
||||||
expected_dc_request = 30.0 / 0.90
|
expected_dc_request = 30.0 / 0.90
|
||||||
mock_battery.discharge_energy.assert_called_once_with(
|
mock_battery.discharge_energy.assert_called_once_with(
|
||||||
pytest.approx(expected_dc_request, rel=1e-3), hour
|
pytest.approx(expected_dc_request, rel=1e-3), hour
|
||||||
)
|
)
|
||||||
|
|
||||||
# Battery delivers 50 Wh DC → 45 Wh AC
|
# Battery delivers 33.333 Wh DC -> 30 Wh AC.
|
||||||
from_battery_ac = 50.0 * 0.90 # 45 Wh
|
from_battery_dc = 30.0 / 0.90
|
||||||
inverter_discharge_loss = 50.0 - from_battery_ac # 5 Wh
|
from_battery_ac = from_battery_dc * 0.90
|
||||||
|
inverter_discharge_loss = from_battery_dc - from_battery_ac
|
||||||
|
|
||||||
assert self_consumption == pytest.approx(consumption + from_battery_ac, rel=1e-5)
|
assert self_consumption == pytest.approx(170.0 + from_battery_ac, rel=1e-5)
|
||||||
|
assert grid_import == pytest.approx(0.0)
|
||||||
|
assert grid_export == pytest.approx(220.0)
|
||||||
|
assert losses == pytest.approx(5.0 + inverter_discharge_loss + 10.0)
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
@@ -492,6 +499,7 @@ def _make_mock_simulation(
|
|||||||
initial_soc_percentage: float = 0.0, # fraction of capacity already stored (0 = empty)
|
initial_soc_percentage: float = 0.0, # fraction of capacity already stored (0 = empty)
|
||||||
min_soc_wh: float = 0.0,
|
min_soc_wh: float = 0.0,
|
||||||
max_charge_power_w: float = 5_000.0,
|
max_charge_power_w: float = 5_000.0,
|
||||||
|
levelized_cost_of_storage_kwh: float = 0.0,
|
||||||
# Arrays (must be same length)
|
# Arrays (must be same length)
|
||||||
ac_charge_hours: list | None = None,
|
ac_charge_hours: list | None = None,
|
||||||
elect_price_hourly: list | None = None,
|
elect_price_hourly: list | None = None,
|
||||||
@@ -517,6 +525,7 @@ def _make_mock_simulation(
|
|||||||
initial_soc_percentage=initial_soc_percentage,
|
initial_soc_percentage=initial_soc_percentage,
|
||||||
min_soc_wh=min_soc_wh,
|
min_soc_wh=min_soc_wh,
|
||||||
max_charge_power_w=max_charge_power_w,
|
max_charge_power_w=max_charge_power_w,
|
||||||
|
levelized_cost_of_storage_kwh=levelized_cost_of_storage_kwh,
|
||||||
current_energy_content=Mock(return_value=0.0),
|
current_energy_content=Mock(return_value=0.0),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -744,6 +753,28 @@ class TestAcChargeBreakEvenPenalty:
|
|||||||
# Fitness must be worse (higher) than base
|
# Fitness must be worse (higher) than base
|
||||||
assert fitness > base + 1e-6
|
assert fitness > base + 1e-6
|
||||||
|
|
||||||
|
def test_lcos_is_included_in_ac_charge_break_even_price(self, config_eos):
|
||||||
|
"""LCOS can make an otherwise profitable price spread unprofitable."""
|
||||||
|
n = 24
|
||||||
|
prices = [0.0001] + [0.00015] * (n - 1)
|
||||||
|
sim = _make_mock_simulation(
|
||||||
|
ac_to_dc_efficiency=1.0,
|
||||||
|
dc_to_ac_efficiency=1.0,
|
||||||
|
charging_efficiency=1.0,
|
||||||
|
discharging_efficiency=1.0,
|
||||||
|
levelized_cost_of_storage_kwh=0.10,
|
||||||
|
ac_charge_hours=[1.0] + [0.0] * (n - 1),
|
||||||
|
elect_price_hourly=prices,
|
||||||
|
load_energy_array=[1000.0] * n,
|
||||||
|
initial_soc_percentage=0.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
fitness = _run_evaluate_with_mocked_sim(config_eos, sim)
|
||||||
|
|
||||||
|
# Break-even is 0.0001 + 0.0001 = 0.0002 EUR/Wh.
|
||||||
|
# The 0.00005 EUR/Wh gap on a 5000 Wh charge adds 0.25 EUR.
|
||||||
|
assert fitness == pytest.approx(0.25)
|
||||||
|
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
# 5d. Free PV energy covers expensive hours → penalty reduced/eliminated
|
# 5d. Free PV energy covers expensive hours → penalty reduced/eliminated
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
from akkudoktoreos.devices.devices import BatteriesCommonSettings
|
||||||
|
from akkudoktoreos.optimization.genetic.geneticdevices import (
|
||||||
|
SolarPanelBatteryParameters,
|
||||||
|
)
|
||||||
|
from akkudoktoreos.optimization.optimization import OptimizationCommonSettings
|
||||||
|
|
||||||
|
|
||||||
|
def test_terminal_value_is_independent_from_battery_lcos():
|
||||||
|
battery = BatteriesCommonSettings(
|
||||||
|
device_id="battery1",
|
||||||
|
levelized_cost_of_storage_kwh=0.12,
|
||||||
|
)
|
||||||
|
optimization = OptimizationCommonSettings(terminal_value_euro_per_kwh=0.20)
|
||||||
|
|
||||||
|
assert battery.levelized_cost_of_storage_kwh == 0.12
|
||||||
|
assert optimization.terminal_value_euro_per_kwh == 0.20
|
||||||
|
|
||||||
|
|
||||||
|
def test_terminal_value_defaults_to_zero():
|
||||||
|
assert OptimizationCommonSettings().terminal_value_euro_per_kwh == 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_genetic_battery_lcos_is_independent_from_terminal_value():
|
||||||
|
battery = SolarPanelBatteryParameters(
|
||||||
|
device_id="battery1",
|
||||||
|
capacity_wh=8000,
|
||||||
|
levelized_cost_of_storage_kwh=0.12,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert battery.levelized_cost_of_storage_kwh == 0.12
|
||||||
+97
-97
@@ -157,7 +157,7 @@
|
|||||||
1063.91,
|
1063.91,
|
||||||
1320.56,
|
1320.56,
|
||||||
1132.03,
|
1132.03,
|
||||||
1308.5200000002487,
|
1308.5200000000004,
|
||||||
1176.82,
|
1176.82,
|
||||||
1216.22,
|
1216.22,
|
||||||
1103.78,
|
1103.78,
|
||||||
@@ -238,10 +238,12 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.20303854854278033,
|
0.21077535122459587,
|
||||||
0.1899652543898917,
|
0.19320652266312205,
|
||||||
0.12833851757957424,
|
0.1358062627100041,
|
||||||
0.04233866391809883,
|
0.0692592282596561,
|
||||||
|
0.023370695807696434,
|
||||||
|
0.0019327051509204403,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -260,22 +262,20 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.18814608875566813,
|
||||||
0.0,
|
0.15236390731688437,
|
||||||
0.16357655037574115,
|
0.10316291465819699,
|
||||||
0.14900060381217387,
|
0.029150936607659956,
|
||||||
0.08949503544160814,
|
0.020928608511559126,
|
||||||
0.010110585812541543,
|
0.003524558735906156,
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
"Gesamt_Verluste": 2807.7292841655817,
|
"Gesamt_Verluste": 3425.4668727209255,
|
||||||
"Gesamtbilanz_Euro": 0.8879905947253857,
|
"Gesamtbilanz_Euro": 0.9585224311392879,
|
||||||
"Gesamteinnahmen_Euro": 0.9758637598724098,
|
"Gesamteinnahmen_Euro": 1.1316277804018695,
|
||||||
"Gesamtkosten_Euro": 1.8638543545977955,
|
"Gesamtkosten_Euro": 2.0901502115411574,
|
||||||
"Home_appliance_wh_per_hour": [
|
"Home_appliance_wh_per_hour": [
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -320,14 +320,14 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.001880612819166666,
|
0.07557452231671152,
|
||||||
0.026623430000091274,
|
0.026623430000000066,
|
||||||
4.482711108977355e-14,
|
4.55656845588237e-17,
|
||||||
0.008763569215739961,
|
0.001414013162203277,
|
||||||
0.018335381563380656,
|
0.005881449073870462,
|
||||||
0.06267084962493971,
|
0.05258762370598476,
|
||||||
0.05480703000000003,
|
0.1614775630079859,
|
||||||
0.225316611,
|
0.2338232746714084,
|
||||||
0.0,
|
0.0,
|
||||||
0.26650619799999997,
|
0.26650619799999997,
|
||||||
0.19588158,
|
0.19588158,
|
||||||
@@ -343,15 +343,15 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.02844719513362201,
|
0.009619897970888898,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0004412281431084693,
|
2.3325608707865465e-05,
|
||||||
0.008372170029774037,
|
0.0021886029750169123,
|
||||||
0.03130999506792791,
|
0.013012984677295973,
|
||||||
0.0,
|
0.0,
|
||||||
0.08231598,
|
0.17784012884918773,
|
||||||
0.174597189,
|
0.19011028252189552,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.16484566
|
0.16484566
|
||||||
@@ -360,14 +360,14 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
10.008583390988111,
|
402.20607938643707,
|
||||||
144.8500000004966,
|
144.85000000000036,
|
||||||
2.236881790906864e-10,
|
2.2737367544323206e-13,
|
||||||
39.870651572975255,
|
6.433180901743754,
|
||||||
80.77260600608219,
|
25.909467285772962,
|
||||||
209.11194402715952,
|
175.4675465665157,
|
||||||
171.54000000000008,
|
505.40708296709204,
|
||||||
731.31,
|
758.9200735845777,
|
||||||
0.0,
|
0.0,
|
||||||
912.38,
|
912.38,
|
||||||
704.61,
|
704.61,
|
||||||
@@ -383,15 +383,15 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
135.91588692604878,
|
45.96224544141853,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
2.201737241060226,
|
0.11639525303326081,
|
||||||
38.089945540373236,
|
9.957247384062384,
|
||||||
137.92949369131236,
|
57.32592368852852,
|
||||||
0.0,
|
0.0,
|
||||||
257.64,
|
556.6201215937018,
|
||||||
566.69,
|
617.0408390843736,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
592.97
|
592.97
|
||||||
@@ -402,10 +402,12 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
2900.550693468291,
|
3011.0764460656555,
|
||||||
2713.7893484270244,
|
2760.093180901744,
|
||||||
1833.4073939939178,
|
1940.089467285773,
|
||||||
604.8380559728405,
|
989.4175465665157,
|
||||||
|
333.86708296709196,
|
||||||
|
27.61007358457772,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -424,31 +426,29 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
2687.8012679381163,
|
||||||
0.0,
|
2176.6272473840627,
|
||||||
2336.807862510588,
|
1473.7559236885286,
|
||||||
2128.580054459627,
|
416.4419515379994,
|
||||||
1278.5005063086878,
|
298.9801215937018,
|
||||||
144.4369401791649,
|
50.35083908437366,
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
"Verluste_Pro_Stunde": [
|
"Verluste_Pro_Stunde": [
|
||||||
16.744090909090914,
|
97.85621559422765,
|
||||||
2.817272727272737,
|
101.92059640365329,
|
||||||
29.157272727272726,
|
114.42806532440363,
|
||||||
2.358169993081436,
|
51.82392952637247,
|
||||||
600.0000000000002,
|
599.9999999999995,
|
||||||
173.00391678377832,
|
159.7408264721214,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
133.72909090909081,
|
133.7321802766326,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
70.41409090909087,
|
70.41409090909087,
|
||||||
@@ -458,18 +458,18 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
109.0704545454546,
|
109.07302361034766,
|
||||||
109.96227272727276,
|
116.99491129525349,
|
||||||
47.952272727272714,
|
95.61900944932736,
|
||||||
16.031648664443644,
|
98.21987178167876,
|
||||||
55.9754990365584,
|
123.8591383343284,
|
||||||
143.33449356887422,
|
165.15986945297027,
|
||||||
21.973794868109557,
|
116.9350623689079,
|
||||||
538.2984000000038,
|
538.2984000000001,
|
||||||
161.2428480298022,
|
119.40181527778998,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
44.91187685729284,
|
81.2380484620021,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
134.59227272727276,
|
134.59227272727276,
|
||||||
@@ -478,35 +478,35 @@
|
|||||||
],
|
],
|
||||||
"akku_soc_pro_stunde": [
|
"akku_soc_pro_stunde": [
|
||||||
80.0,
|
80.0,
|
||||||
79.4714617768595,
|
79.16421888032488,
|
||||||
79.38253271349862,
|
78.69989843940196,
|
||||||
78.46216425619835,
|
77.45653455559739,
|
||||||
78.52766897822838,
|
78.89608815355218,
|
||||||
95.19433564489505,
|
95.56275482021886,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
95.77875344352617,
|
95.77874174137638,
|
||||||
95.77875344352617,
|
95.77874174137638,
|
||||||
95.77875344352617,
|
95.77874174137638,
|
||||||
93.55608643250687,
|
93.5560747303571,
|
||||||
89.8196453168044,
|
89.81963361465462,
|
||||||
86.83092286501376,
|
86.83091116286398,
|
||||||
84.21044249311292,
|
84.21043079096314,
|
||||||
84.21044249311292,
|
84.21043079096314,
|
||||||
84.21044249311292,
|
84.21043079096314,
|
||||||
84.21044249311292,
|
84.21043079096314,
|
||||||
80.76756198347105,
|
80.76754055001486,
|
||||||
77.2965306473829,
|
77.26987043147221,
|
||||||
75.78288567493111,
|
75.57566963810356,
|
||||||
75.93522642877453,
|
75.69097315408206,
|
||||||
76.44194846937079,
|
76.9218097513005,
|
||||||
80.42346217961729,
|
81.5095839027719,
|
||||||
80.56829866584056,
|
81.73054957561693,
|
||||||
95.52103199917215,
|
96.68328290895028,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
|
|||||||
+103
-103
@@ -157,7 +157,7 @@
|
|||||||
1063.91,
|
1063.91,
|
||||||
1320.56,
|
1320.56,
|
||||||
1132.03,
|
1132.03,
|
||||||
1308.5200000002487,
|
1308.5200000000004,
|
||||||
1176.82,
|
1176.82,
|
||||||
1216.22,
|
1216.22,
|
||||||
1103.78,
|
1103.78,
|
||||||
@@ -238,10 +238,12 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.20303854854278033,
|
0.21077535122459587,
|
||||||
0.1899652543898917,
|
0.19320652266312205,
|
||||||
0.12833851757957424,
|
0.1358062627100041,
|
||||||
0.04233866391809883,
|
0.0692592282596561,
|
||||||
|
0.023370695807696434,
|
||||||
|
0.0019327051509204403,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -260,22 +262,20 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.24478962017661132,
|
||||||
0.0,
|
0.15146384621553569,
|
||||||
0.16452297290911175,
|
0.10316291465819699,
|
||||||
0.1455575560489687,
|
0.05435777788576338,
|
||||||
0.08949503544160814,
|
0.020928608511559126,
|
||||||
0.024046008596276005,
|
0.003524558735906156,
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
"Gesamt_Verluste": 2717.1309464905708,
|
"Gesamt_Verluste": 3110.842855499046,
|
||||||
"Gesamtbilanz_Euro": 0.998163925609791,
|
"Gesamtbilanz_Euro": 1.1850381627731374,
|
||||||
"Gesamteinnahmen_Euro": 0.9873025574263097,
|
"Gesamteinnahmen_Euro": 1.2125780919995675,
|
||||||
"Gesamtkosten_Euro": 1.9854664830361006,
|
"Gesamtkosten_Euro": 2.397616254772705,
|
||||||
"Home_appliance_wh_per_hour": [
|
"Home_appliance_wh_per_hour": [
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -320,14 +320,14 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.001880612819166666,
|
0.07557452231671152,
|
||||||
0.026623430000091274,
|
0.026623430000000066,
|
||||||
4.482711108977355e-14,
|
4.55656845588237e-17,
|
||||||
0.008763569215739961,
|
0.001414013162203277,
|
||||||
0.018335381563380656,
|
0.005881449073870462,
|
||||||
0.06267084962493971,
|
0.05258762370598476,
|
||||||
0.05480703000000003,
|
0.1614775630079859,
|
||||||
0.225316611,
|
0.2338232746714084,
|
||||||
0.0,
|
0.0,
|
||||||
0.26650619799999997,
|
0.26650619799999997,
|
||||||
0.19588158,
|
0.19588158,
|
||||||
@@ -340,18 +340,18 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.08545095,
|
0.1306329312971816,
|
||||||
0.008254784724581705,
|
0.07362195915902499,
|
||||||
0.028650916976410694,
|
0.060401289430882174,
|
||||||
0.02844719513362201,
|
0.009619897970888898,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0004412281431084693,
|
2.3325608707865465e-05,
|
||||||
0.0,
|
0.0,
|
||||||
0.03130999506792791,
|
0.013012984677295973,
|
||||||
0.04620342776708689,
|
0.08357424731947552,
|
||||||
0.08231598,
|
0.17784012884918773,
|
||||||
0.174597189,
|
0.19011028252189552,
|
||||||
0.293043269,
|
0.293043269,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
@@ -360,14 +360,14 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
10.008583390988111,
|
402.20607938643707,
|
||||||
144.8500000004966,
|
144.85000000000036,
|
||||||
2.236881790906864e-10,
|
2.2737367544323206e-13,
|
||||||
39.870651572975255,
|
6.433180901743754,
|
||||||
80.77260600608219,
|
25.909467285772962,
|
||||||
209.11194402715952,
|
175.4675465665157,
|
||||||
171.54000000000008,
|
505.40708296709204,
|
||||||
731.31,
|
758.9200735845777,
|
||||||
0.0,
|
0.0,
|
||||||
912.38,
|
912.38,
|
||||||
704.61,
|
704.61,
|
||||||
@@ -380,18 +380,18 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
351.65,
|
537.58407941227,
|
||||||
36.20519616044607,
|
322.9033296448464,
|
||||||
129.52494112301397,
|
273.0618871197205,
|
||||||
135.91588692604878,
|
45.96224544141853,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
2.201737241060226,
|
0.11639525303326081,
|
||||||
0.0,
|
0.0,
|
||||||
137.92949369131236,
|
57.32592368852852,
|
||||||
154.1655914817714,
|
278.85968408233407,
|
||||||
257.64,
|
556.6201215937018,
|
||||||
566.69,
|
617.0408390843736,
|
||||||
987.01,
|
987.01,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
@@ -402,10 +402,12 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
2900.550693468291,
|
3011.0764460656555,
|
||||||
2713.7893484270244,
|
2760.093180901744,
|
||||||
1833.4073939939178,
|
1940.089467285773,
|
||||||
604.8380559728405,
|
989.4175465665157,
|
||||||
|
333.86708296709196,
|
||||||
|
27.61007358457772,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -424,31 +426,29 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
3496.9945739515906,
|
||||||
0.0,
|
2163.76923165051,
|
||||||
2350.3281844158823,
|
1473.7559236885286,
|
||||||
2079.39365784241,
|
776.5396840823341,
|
||||||
1278.5005063086878,
|
298.9801215937018,
|
||||||
343.51440851822866,
|
50.35083908437366,
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
"Verluste_Pro_Stunde": [
|
"Verluste_Pro_Stunde": [
|
||||||
16.744090909090914,
|
97.85621559422765,
|
||||||
2.817272727272737,
|
101.92059640365329,
|
||||||
29.157272727272726,
|
114.42806532440363,
|
||||||
2.358169993081436,
|
51.82392952637247,
|
||||||
600.0000000000002,
|
599.9999999999995,
|
||||||
173.00391678377832,
|
159.7408264721214,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
133.72909090909081,
|
133.7321802766326,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
70.41409090909087,
|
70.41409090909087,
|
||||||
@@ -458,16 +458,16 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
69.12409090909085,
|
69.12409090909085,
|
||||||
109.0704545454546,
|
109.07302361034766,
|
||||||
109.96227272727276,
|
116.99491129525349,
|
||||||
0.0,
|
22.312089529472388,
|
||||||
11.094576460746453,
|
54.18759955738153,
|
||||||
38.31300706523831,
|
86.6234264543665,
|
||||||
143.33449356887422,
|
165.15986945297027,
|
||||||
21.973794868109557,
|
116.9350623689079,
|
||||||
538.2984000000038,
|
538.2984000000001,
|
||||||
159.62040940116685,
|
22.298618556173096,
|
||||||
11.096451076844168,
|
2.900768349489402,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -478,35 +478,35 @@
|
|||||||
],
|
],
|
||||||
"akku_soc_pro_stunde": [
|
"akku_soc_pro_stunde": [
|
||||||
80.0,
|
80.0,
|
||||||
79.4714617768595,
|
79.16421888032488,
|
||||||
79.38253271349862,
|
78.69989843940196,
|
||||||
78.46216425619835,
|
77.45653455559739,
|
||||||
78.52766897822838,
|
78.89608815355218,
|
||||||
95.19433564489505,
|
95.56275482021886,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
95.77875344352617,
|
95.77874174137638,
|
||||||
95.77875344352617,
|
95.77874174137638,
|
||||||
95.77875344352617,
|
95.77874174137638,
|
||||||
93.55608643250687,
|
93.5560747303571,
|
||||||
89.8196453168044,
|
89.81963361465462,
|
||||||
86.83092286501376,
|
86.83091116286398,
|
||||||
84.21044249311292,
|
84.21043079096314,
|
||||||
84.21044249311292,
|
84.21043079096314,
|
||||||
84.21044249311292,
|
84.21043079096314,
|
||||||
82.02849517906333,
|
82.02848347691355,
|
||||||
78.58561466942146,
|
78.58559323596526,
|
||||||
75.1145833333333,
|
75.08792311742263,
|
||||||
75.1145833333333,
|
75.7077033821302,
|
||||||
75.42276601279848,
|
77.21291448094635,
|
||||||
76.4870162090551,
|
79.61912077134542,
|
||||||
80.4685299193016,
|
84.20689492281682,
|
||||||
80.61336640552487,
|
84.42786059566187,
|
||||||
95.56609973885648,
|
99.38059392899518,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
|
|||||||
+102
-102
@@ -204,18 +204,18 @@
|
|||||||
"Last_Wh_pro_Stunde": [
|
"Last_Wh_pro_Stunde": [
|
||||||
1053.07,
|
1053.07,
|
||||||
10240.91,
|
10240.91,
|
||||||
14186.56,
|
14186.477801352086,
|
||||||
11620.03,
|
11620.03,
|
||||||
11218.67,
|
10686.11504084929,
|
||||||
7609.82,
|
7609.82,
|
||||||
9082.22,
|
9082.22,
|
||||||
10280.78,
|
10280.78,
|
||||||
2177.92,
|
2177.92,
|
||||||
1178.71,
|
1178.71,
|
||||||
1050.98,
|
1050.98,
|
||||||
1988.56,
|
1488.5587949275546,
|
||||||
912.38,
|
912.38,
|
||||||
1704.6100000000001,
|
2204.61,
|
||||||
516.37,
|
516.37,
|
||||||
868.05,
|
868.05,
|
||||||
694.34,
|
694.34,
|
||||||
@@ -288,6 +288,10 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
|
0.005594705401588846,
|
||||||
|
0.0016437871377312284,
|
||||||
|
0.028240415856282213,
|
||||||
|
0.023370695807696434,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -306,25 +310,21 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.09023487592359272,
|
||||||
0.0,
|
0.2577866264025879,
|
||||||
0.0,
|
0.15146384621553569,
|
||||||
0.0,
|
0.09798107754792196,
|
||||||
0.00826914812545985,
|
0.029150936607659956,
|
||||||
0.25743585772309185,
|
0.020928608511559126,
|
||||||
0.1455575560489687,
|
0.003524558735906156,
|
||||||
0.07702723513376727,
|
|
||||||
0.010110585812541543,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
"Gesamt_Verluste": 5776.448801897527,
|
"Gesamt_Verluste": 6008.882879266785,
|
||||||
"Gesamtbilanz_Euro": 12.789452797857164,
|
"Gesamtbilanz_Euro": 13.081213953988335,
|
||||||
"Gesamteinnahmen_Euro": 0.49840038284382926,
|
"Gesamteinnahmen_Euro": 0.7099201341480623,
|
||||||
"Gesamtkosten_Euro": 13.287853180700994,
|
"Gesamtkosten_Euro": 13.791134088136397,
|
||||||
"Home_appliance_wh_per_hour": [
|
"Home_appliance_wh_per_hour": [
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -370,16 +370,16 @@
|
|||||||
2.034522392,
|
2.034522392,
|
||||||
2.737606326,
|
2.737606326,
|
||||||
1.9651220859999998,
|
1.9651220859999998,
|
||||||
0.9557324300000001,
|
0.9176848434271027,
|
||||||
0.4189863,
|
0.5064650351737862,
|
||||||
1.1236923319999998,
|
1.1459236583154115,
|
||||||
1.64866014,
|
1.6539907068609285,
|
||||||
0.07038454500000005,
|
0.1912938683161112,
|
||||||
0.05480703000000003,
|
0.1614775630079859,
|
||||||
0.0,
|
0.0,
|
||||||
0.588063892,
|
0.4396171120740812,
|
||||||
0.0,
|
0.0,
|
||||||
0.47388158,
|
0.61288158,
|
||||||
0.174739608,
|
0.174739608,
|
||||||
0.28801899,
|
0.28801899,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -390,8 +390,8 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.008254784724581705,
|
0.07362195915902499,
|
||||||
0.028650916976410694,
|
0.060401289430882174,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -399,8 +399,8 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.08231598,
|
0.17784012884918773,
|
||||||
0.174597189,
|
0.19011028252189552,
|
||||||
0.293043269,
|
0.293043269,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
@@ -410,16 +410,16 @@
|
|||||||
9197.66,
|
9197.66,
|
||||||
13079.82,
|
13079.82,
|
||||||
10458.34,
|
10458.34,
|
||||||
5199.85,
|
4992.84463235638,
|
||||||
2090.75,
|
2527.2706345997312,
|
||||||
5112.339999999999,
|
5213.483431826258,
|
||||||
7262.820000000001,
|
7286.3026733961615,
|
||||||
234.85000000000014,
|
638.2845122326032,
|
||||||
171.54000000000008,
|
505.40708296709204,
|
||||||
0.0,
|
0.0,
|
||||||
1980.6799999999998,
|
1480.6908456520082,
|
||||||
0.0,
|
0.0,
|
||||||
1704.6100000000001,
|
2204.61,
|
||||||
516.37,
|
516.37,
|
||||||
868.05,
|
868.05,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -430,8 +430,8 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
36.20519616044607,
|
322.9033296448464,
|
||||||
129.52494112301397,
|
273.0618871197205,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -439,8 +439,8 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
257.64,
|
556.6201215937018,
|
||||||
566.69,
|
617.0408390843736,
|
||||||
987.01,
|
987.01,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
@@ -452,6 +452,10 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
|
79.92436287984066,
|
||||||
|
23.482673396160408,
|
||||||
|
403.4345122326031,
|
||||||
|
333.86708296709196,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -470,36 +474,32 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
1289.0696560513247,
|
||||||
0.0,
|
3682.6660914655417,
|
||||||
0.0,
|
2163.76923165051,
|
||||||
0.0,
|
1399.729679256028,
|
||||||
118.1306875065693,
|
416.4419515379994,
|
||||||
3677.655110329884,
|
298.9801215937018,
|
||||||
2079.39365784241,
|
50.35083908437366,
|
||||||
1100.3890733395326,
|
|
||||||
144.4369401791649,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
"Verluste_Pro_Stunde": [
|
"Verluste_Pro_Stunde": [
|
||||||
16.744090909090914,
|
97.85621559422765,
|
||||||
483.0,
|
483.0,
|
||||||
1014.0,
|
1014.0000000000001,
|
||||||
552.0,
|
552.0,
|
||||||
465.0,
|
440.15935588276557,
|
||||||
207.0,
|
259.38247615196775,
|
||||||
414.0,
|
416.54628827357027,
|
||||||
483.0,
|
483.0,
|
||||||
55.200000000000045,
|
55.200000000000045,
|
||||||
0.0,
|
0.0,
|
||||||
99.72409090909093,
|
106.80230977350088,
|
||||||
120.0,
|
60.001301478240954,
|
||||||
124.41545454545451,
|
124.41545454545451,
|
||||||
120.0,
|
180.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
94.68272727272722,
|
94.68272727272722,
|
||||||
@@ -507,18 +507,18 @@
|
|||||||
75.86045454545456,
|
75.86045454545456,
|
||||||
66.66681818181814,
|
66.66681818181814,
|
||||||
0.0,
|
0.0,
|
||||||
109.0704545454546,
|
109.07302361034766,
|
||||||
109.96227272727276,
|
116.99491129525349,
|
||||||
47.952272727272714,
|
95.61900944932736,
|
||||||
11.094576460746453,
|
54.18759955738153,
|
||||||
38.31300706523831,
|
86.6234264543665,
|
||||||
161.86847814969906,
|
171.42744837680007,
|
||||||
21.973794868109557,
|
116.9350623689079,
|
||||||
524.1227174992155,
|
383.6100412738411,
|
||||||
0.6414151879949013,
|
0.03390853445803674,
|
||||||
11.096451076844168,
|
2.900768349489402,
|
||||||
40.181939277841224,
|
16.700320743972142,
|
||||||
44.91187685729284,
|
81.2380484620021,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -527,34 +527,34 @@
|
|||||||
],
|
],
|
||||||
"akku_soc_pro_stunde": [
|
"akku_soc_pro_stunde": [
|
||||||
80.0,
|
80.0,
|
||||||
79.4714617768595,
|
79.16421888032488,
|
||||||
79.4714617768595,
|
79.16421888032488,
|
||||||
96.13812844352617,
|
95.83088554699157,
|
||||||
96.13812844352617,
|
95.83088554699157,
|
||||||
99.4714617768595,
|
98.47420098817949,
|
||||||
99.4714617768595,
|
99.9292697701786,
|
||||||
99.4714617768595,
|
100.0,
|
||||||
99.4714617768595,
|
100.0,
|
||||||
99.4714617768595,
|
100.0,
|
||||||
99.4714617768595,
|
100.0,
|
||||||
96.32360537190083,
|
96.82533215994886,
|
||||||
99.65693870523415,
|
98.49203497878888,
|
||||||
95.72968319559227,
|
94.56477946914701,
|
||||||
99.0630165289256,
|
99.564779469147,
|
||||||
99.0630165289256,
|
99.564779469147,
|
||||||
99.0630165289256,
|
99.564779469147,
|
||||||
96.07429407713497,
|
96.57605701735636,
|
||||||
93.45381370523414,
|
93.95557664545554,
|
||||||
91.05922865013771,
|
91.56099159035911,
|
||||||
88.95484676308537,
|
89.45660970330677,
|
||||||
88.95484676308537,
|
89.45660970330677,
|
||||||
85.51196625344349,
|
86.01371946235848,
|
||||||
82.04093491735533,
|
82.51604934381585,
|
||||||
80.52728994490354,
|
80.82184855044719,
|
||||||
80.83547262436872,
|
82.32705964926333,
|
||||||
81.89972282062534,
|
84.7332659396624,
|
||||||
85.29619913880036,
|
89.12319984732603,
|
||||||
85.44103562502363,
|
89.34416552017109,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
|
|||||||
+112
-112
@@ -204,22 +204,22 @@
|
|||||||
"Last_Wh_pro_Stunde": [
|
"Last_Wh_pro_Stunde": [
|
||||||
16541.07,
|
16541.07,
|
||||||
8929.91,
|
8929.91,
|
||||||
8875.56,
|
8375.540038207693,
|
||||||
6376.03,
|
6376.03,
|
||||||
5096.67,
|
5096.67,
|
||||||
12853.82,
|
12853.82,
|
||||||
8960.220000000001,
|
8960.220000000001,
|
||||||
13969.78,
|
13936.309375923789,
|
||||||
1129.12,
|
1129.12,
|
||||||
1178.71,
|
1178.71,
|
||||||
1050.98,
|
1050.98,
|
||||||
988.56,
|
988.56,
|
||||||
1912.38,
|
1412.38,
|
||||||
704.61,
|
704.61,
|
||||||
516.37,
|
516.37,
|
||||||
868.05,
|
868.05,
|
||||||
694.34,
|
694.34,
|
||||||
2108.79,
|
2608.79,
|
||||||
556.31,
|
556.31,
|
||||||
488.89,
|
488.89,
|
||||||
506.91,
|
506.91,
|
||||||
@@ -286,7 +286,12 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.06455049999999336,
|
0.024981227816847,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.038217249326498136,
|
||||||
|
0.023370695807696434,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -305,26 +310,21 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.19904591069188757,
|
||||||
0.0,
|
0.2577866264025879,
|
||||||
0.0,
|
0.15146384621553569,
|
||||||
0.0,
|
0.09798107754792196,
|
||||||
0.0,
|
0.05435777788576338,
|
||||||
0.12279079241545988,
|
|
||||||
0.25743585772309185,
|
|
||||||
0.1455575560489687,
|
|
||||||
0.07702723513376727,
|
|
||||||
0.024046008596276005,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
"Gesamt_Verluste": 6842.366945701403,
|
"Gesamt_Verluste": 7095.270423117038,
|
||||||
"Gesamtbilanz_Euro": 14.10088133917546,
|
"Gesamtbilanz_Euro": 14.155149367775268,
|
||||||
"Gesamteinnahmen_Euro": 0.6914079499175572,
|
"Gesamteinnahmen_Euro": 0.847204411694738,
|
||||||
"Gesamtkosten_Euro": 14.792289289093018,
|
"Gesamtkosten_Euro": 15.002353779470006,
|
||||||
"Home_appliance_wh_per_hour": [
|
"Home_appliance_wh_per_hour": [
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -367,80 +367,80 @@
|
|||||||
],
|
],
|
||||||
"Kosten_Euro_pro_Stunde": [
|
"Kosten_Euro_pro_Stunde": [
|
||||||
3.55926012,
|
3.55926012,
|
||||||
1.7445291920000001,
|
1.7445413792641737,
|
||||||
1.6260140259999998,
|
1.5214016280281666,
|
||||||
0.979774486,
|
0.9799189133780641,
|
||||||
0.0,
|
0.0,
|
||||||
0.5881238999999999,
|
0.6146086578009714,
|
||||||
1.0968767320000004,
|
1.120219902993293,
|
||||||
2.4860631399999997,
|
2.4860631399999997,
|
||||||
0.06267084962493971,
|
0.05258762370598476,
|
||||||
0.05480703000000003,
|
0.1614775630079859,
|
||||||
0.0,
|
0.0,
|
||||||
0.291163892,
|
0.29116746986009023,
|
||||||
0.558606198,
|
0.41255619800000004,
|
||||||
0.0,
|
0.0,
|
||||||
0.174739608,
|
0.174739608,
|
||||||
0.28801899,
|
0.28801899,
|
||||||
0.0,
|
0.0,
|
||||||
0.692315757,
|
0.856465757,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.16677339,
|
0.16677339,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.008254784724581705,
|
0.07362195915902499,
|
||||||
0.028650916976410694,
|
0.060401289430882174,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.04620342776708689,
|
0.08357424731947552,
|
||||||
0.0,
|
0.0,
|
||||||
0.174597189,
|
0.19011028252189552,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.16484566
|
0.16484566
|
||||||
],
|
],
|
||||||
"Netzbezug_Wh_pro_Stunde": [
|
"Netzbezug_Wh_pro_Stunde": [
|
||||||
15610.789999999999,
|
15610.789999999999,
|
||||||
7886.66,
|
7886.7150961309835,
|
||||||
7768.82,
|
7268.9996561307535,
|
||||||
5214.34,
|
5215.108639585227,
|
||||||
0.0,
|
0.0,
|
||||||
2934.75,
|
3066.9094700647274,
|
||||||
4990.340000000001,
|
5096.541869851197,
|
||||||
10951.82,
|
10951.82,
|
||||||
209.11194402715952,
|
175.4675465665157,
|
||||||
171.54000000000008,
|
505.40708296709204,
|
||||||
0.0,
|
0.0,
|
||||||
980.68,
|
980.6920507244535,
|
||||||
1912.38,
|
1412.38,
|
||||||
0.0,
|
0.0,
|
||||||
516.37,
|
516.37,
|
||||||
868.05,
|
868.05,
|
||||||
0.0,
|
0.0,
|
||||||
2108.79,
|
2608.79,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
506.91,
|
506.91,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
36.20519616044607,
|
322.9033296448464,
|
||||||
129.52494112301397,
|
273.0618871197205,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
154.1655914817714,
|
278.85968408233407,
|
||||||
0.0,
|
0.0,
|
||||||
566.69,
|
617.0408390843736,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
592.97
|
592.97
|
||||||
@@ -450,7 +450,12 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
922.1499999999053,
|
356.87468309781434,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
545.9607046642591,
|
||||||
|
333.86708296709196,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -469,16 +474,11 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
2843.513009884108,
|
||||||
0.0,
|
3682.6660914655417,
|
||||||
0.0,
|
2163.76923165051,
|
||||||
0.0,
|
1399.729679256028,
|
||||||
0.0,
|
776.5396840823341,
|
||||||
1754.1541773637127,
|
|
||||||
3677.655110329884,
|
|
||||||
2079.39365784241,
|
|
||||||
1100.3890733395326,
|
|
||||||
343.51440851822866,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -487,40 +487,40 @@
|
|||||||
],
|
],
|
||||||
"Verluste_Pro_Stunde": [
|
"Verluste_Pro_Stunde": [
|
||||||
1152.0,
|
1152.0,
|
||||||
414.0,
|
414.0066115357181,
|
||||||
465.0,
|
405.0215587356905,
|
||||||
276.0,
|
276.0922367502273,
|
||||||
207.00000000001197,
|
333.15830172751845,
|
||||||
1083.0,
|
1098.8591364077674,
|
||||||
276.0,
|
288.74422438214356,
|
||||||
1014.0,
|
1013.9999999999997,
|
||||||
72.5805667167408,
|
53.21482102827082,
|
||||||
0.0,
|
0.0,
|
||||||
99.72409090909093,
|
106.80230977350088,
|
||||||
0.0,
|
0.0014460869344160802,
|
||||||
120.0,
|
60.0,
|
||||||
96.08318181818186,
|
96.08318181818186,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
94.68272727272722,
|
94.68272727272722,
|
||||||
180.0,
|
240.0,
|
||||||
75.86045454545456,
|
75.86045454545456,
|
||||||
66.66681818181814,
|
66.66681818181814,
|
||||||
0.0,
|
0.0,
|
||||||
109.0704545454546,
|
109.07302361034766,
|
||||||
109.96227272727276,
|
116.99491129525349,
|
||||||
47.952272727272714,
|
95.61900944932736,
|
||||||
11.094576460746453,
|
54.18759955738153,
|
||||||
38.31300706523831,
|
86.6234264543665,
|
||||||
161.86847814969906,
|
171.42744837680007,
|
||||||
21.973794868109557,
|
116.9350623689079,
|
||||||
327.79989871635826,
|
197.07683881390702,
|
||||||
0.6414151879949013,
|
0.03390853445803674,
|
||||||
11.096451076844168,
|
2.900768349489402,
|
||||||
40.181939277841224,
|
16.700320743972142,
|
||||||
0.0,
|
|
||||||
35.132727272727266,
|
|
||||||
0.0,
|
0.0,
|
||||||
|
111.78035844493081,
|
||||||
|
6.04210069012484,
|
||||||
134.59227272727276,
|
134.59227272727276,
|
||||||
100.08954545454549,
|
100.08954545454549,
|
||||||
0.0
|
0.0
|
||||||
@@ -528,42 +528,42 @@
|
|||||||
"akku_soc_pro_stunde": [
|
"akku_soc_pro_stunde": [
|
||||||
80.0,
|
80.0,
|
||||||
96.66666666666667,
|
96.66666666666667,
|
||||||
96.66666666666667,
|
96.66685032043661,
|
||||||
|
98.33411584087247,
|
||||||
|
98.33667797282322,
|
||||||
|
100.0,
|
||||||
|
81.50113762748849,
|
||||||
|
81.85514386032581,
|
||||||
|
98.52181052699248,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
96.82533215994886,
|
||||||
81.06060606060606,
|
96.82537232903037,
|
||||||
81.06060606060606,
|
98.49203899569704,
|
||||||
97.72727272727273,
|
95.45911027668879,
|
||||||
99.74339958051553,
|
95.45911027668879,
|
||||||
99.74339958051553,
|
95.45911027668879,
|
||||||
96.59554317555684,
|
92.47038782489815,
|
||||||
96.59554317555684,
|
99.13705449156481,
|
||||||
99.92887650889017,
|
96.7424694364684,
|
||||||
96.89594778988192,
|
94.63808754941604,
|
||||||
96.89594778988192,
|
94.63808754941604,
|
||||||
96.89594778988192,
|
91.19519730846775,
|
||||||
93.90722533809128,
|
87.69752718992511,
|
||||||
98.90722533809128,
|
86.00332639655647,
|
||||||
96.51264028299485,
|
87.50853749537262,
|
||||||
94.40825839594251,
|
89.91474378577168,
|
||||||
94.40825839594251,
|
94.30467769343532,
|
||||||
90.96537788630063,
|
94.52564336628036,
|
||||||
87.49434655021247,
|
|
||||||
85.9807015777607,
|
|
||||||
86.28888425722586,
|
|
||||||
87.35313445348248,
|
|
||||||
90.7496107716575,
|
|
||||||
90.89444725788077,
|
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
100.0,
|
100.0,
|
||||||
98.89101239669421,
|
98.60068046043587,
|
||||||
98.89101239669421,
|
98.76851659071711,
|
||||||
94.64251893939394,
|
94.52002313341684,
|
||||||
91.48312672176309
|
91.360630915786
|
||||||
],
|
],
|
||||||
"Electricity_price": [
|
"Electricity_price": [
|
||||||
0.000228,
|
0.000228,
|
||||||
|
|||||||
+313
-272
@@ -111,8 +111,8 @@
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
1,
|
|
||||||
0,
|
0,
|
||||||
|
1,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -123,6 +123,23 @@
|
|||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -130,87 +147,71 @@
|
|||||||
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
|
||||||
],
|
],
|
||||||
|
"battery_grid_export_allowed": [],
|
||||||
"eautocharge_hours_float": [
|
"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,
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.375,
|
||||||
|
0.375,
|
||||||
|
0.875,
|
||||||
1.0,
|
1.0,
|
||||||
0.5,
|
0.875,
|
||||||
0.625,
|
|
||||||
1.0,
|
|
||||||
0.75,
|
|
||||||
0.5,
|
|
||||||
0.625,
|
0.625,
|
||||||
0.375,
|
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.375,
|
||||||
0.625,
|
0.375,
|
||||||
0.875,
|
0.1,
|
||||||
0.5,
|
|
||||||
0.5,
|
|
||||||
0.75,
|
|
||||||
0.75,
|
|
||||||
0.75,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.875,
|
0.0,
|
||||||
0.75,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
0.75,
|
0.0,
|
||||||
0.625,
|
0.0,
|
||||||
0.875,
|
0.0,
|
||||||
0.5,
|
0.0,
|
||||||
0.875
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
],
|
],
|
||||||
"result": {
|
"result": {
|
||||||
"Last_Wh_pro_Stunde": [
|
"Last_Wh_pro_Stunde": [
|
||||||
1053.07,
|
4986.07,
|
||||||
11551.91,
|
4996.91,
|
||||||
6564.5599999999995,
|
12997.56,
|
||||||
7687.03,
|
14120.029999999999,
|
||||||
14151.67,
|
10340.67,
|
||||||
11542.82,
|
7731.82,
|
||||||
6460.22,
|
5149.22,
|
||||||
7658.78,
|
5036.78,
|
||||||
5062.12,
|
5062.12,
|
||||||
1178.71,
|
2227.51,
|
||||||
1050.98,
|
1050.98,
|
||||||
988.56,
|
988.56,
|
||||||
912.38,
|
912.38,
|
||||||
@@ -242,43 +243,43 @@
|
|||||||
],
|
],
|
||||||
"EAuto_SoC_pro_Stunde": [
|
"EAuto_SoC_pro_Stunde": [
|
||||||
5.0,
|
5.0,
|
||||||
5.0,
|
11.555,
|
||||||
22.48,
|
18.11,
|
||||||
31.22,
|
33.405,
|
||||||
42.144999999999996,
|
50.885000000000005,
|
||||||
59.62499999999999,
|
66.18,
|
||||||
72.735,
|
77.105,
|
||||||
81.475,
|
83.66,
|
||||||
92.4,
|
90.215,
|
||||||
98.955,
|
96.77,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955,
|
98.518,
|
||||||
98.955
|
98.518
|
||||||
],
|
],
|
||||||
"Einnahmen_Euro_pro_Stunde": [
|
"Einnahmen_Euro_pro_Stunde": [
|
||||||
0.0,
|
0.0,
|
||||||
@@ -320,13 +321,11 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
"Gesamt_Verluste": 7647.623819992857,
|
"Gesamt_Verluste": 8404.44594732788,
|
||||||
"Gesamtbilanz_Euro": 7.824605715847156,
|
"Gesamtbilanz_Euro": 7.836546975121494,
|
||||||
"Gesamteinnahmen_Euro": 0.0,
|
"Gesamteinnahmen_Euro": 0.0,
|
||||||
"Gesamtkosten_Euro": 7.824605715847156,
|
"Gesamtkosten_Euro": 7.836546975121494,
|
||||||
"Home_appliance_wh_per_hour": [
|
"Home_appliance_wh_per_hour": [
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
2500.0,
|
2500.0,
|
||||||
@@ -362,23 +361,19 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
"Kosten_Euro_pro_Stunde": [
|
"Kosten_Euro_pro_Stunde": [
|
||||||
0.027996119999999992,
|
0.9248695241652183,
|
||||||
1.351235592,
|
0.8749092776800845,
|
||||||
1.1423217259999998,
|
1.5678286259999998,
|
||||||
1.226111386,
|
2.4348720859999995,
|
||||||
1.4948178300000001,
|
0.8528744919675278,
|
||||||
1.2071595,
|
0.5282751491259897,
|
||||||
0.0,
|
0.0,
|
||||||
1.0534661399999998,
|
0.4965507655670841,
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.19588158,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -387,10 +382,6 @@
|
|||||||
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,
|
||||||
@@ -398,6 +389,16 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
|
0.11338560853163265,
|
||||||
|
0.04079284310894048,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0021886029750169123,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -405,20 +406,14 @@
|
|||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
"Netzbezug_Wh_pro_Stunde": [
|
"Netzbezug_Wh_pro_Stunde": [
|
||||||
122.78999999999996,
|
4056.445281426396,
|
||||||
6108.66,
|
3955.2860654615033,
|
||||||
5457.82,
|
7490.82,
|
||||||
6525.34,
|
12958.339999999998,
|
||||||
8132.85,
|
4640.231185895146,
|
||||||
6023.75,
|
2636.103538552843,
|
||||||
0.0,
|
0.0,
|
||||||
4640.82,
|
2187.4483064629258,
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
704.61,
|
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -427,10 +422,6 @@
|
|||||||
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,
|
||||||
@@ -438,6 +429,16 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
|
466.6074425170068,
|
||||||
|
178.91597854798454,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
9.957247384062384,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -485,20 +486,20 @@
|
|||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
"Verluste_Pro_Stunde": [
|
"Verluste_Pro_Stunde": [
|
||||||
0.0,
|
207.07863377116755,
|
||||||
1152.0,
|
207.1951278553804,
|
||||||
276.0,
|
1083.0,
|
||||||
345.0,
|
|
||||||
552.0,
|
552.0,
|
||||||
414.0,
|
521.2057423074175,
|
||||||
615.5918181818183,
|
395.8024246263412,
|
||||||
345.0,
|
458.55183787620945,
|
||||||
632.3249999999998,
|
227.23539677555112,
|
||||||
23.391818181818195,
|
640.3849261442235,
|
||||||
99.72409090909093,
|
253.88850337853552,
|
||||||
133.72909090909081,
|
106.80230977350088,
|
||||||
|
133.7321802766326,
|
||||||
124.41545454545451,
|
124.41545454545451,
|
||||||
0.0,
|
96.08318181818186,
|
||||||
70.41409090909087,
|
70.41409090909087,
|
||||||
118.37045454545455,
|
118.37045454545455,
|
||||||
94.68272727272722,
|
94.68272727272722,
|
||||||
@@ -506,63 +507,63 @@
|
|||||||
75.86045454545456,
|
75.86045454545456,
|
||||||
66.66681818181814,
|
66.66681818181814,
|
||||||
69.12409090909085,
|
69.12409090909085,
|
||||||
109.0704545454546,
|
109.07302361034766,
|
||||||
101.01681818181828,
|
116.99491129525349,
|
||||||
0.0,
|
31.990721833371907,
|
||||||
11.233982308648535,
|
73.82223834331725,
|
||||||
48.41330768174522,
|
123.8591383343284,
|
||||||
161.62968357967037,
|
171.42744837680007,
|
||||||
21.962728535423857,
|
116.9350623689079,
|
||||||
538.2984000000038,
|
538.2984000000001,
|
||||||
441.95211196761403,
|
441.9538395103232,
|
||||||
260.56941082122324,
|
261.1952696860876,
|
||||||
171.99990368477063,
|
184.66788225469543,
|
||||||
62.214291413756285,
|
131.21108264656203,
|
||||||
35.132727272727266,
|
111.78035844493081,
|
||||||
77.27590909090907,
|
90.18403329253945,
|
||||||
134.59227272727276,
|
134.59227272727276,
|
||||||
100.08954545454549,
|
100.08954545454549,
|
||||||
80.85954545454547
|
80.85954545454547
|
||||||
],
|
],
|
||||||
"akku_soc_pro_stunde": [
|
"akku_soc_pro_stunde": [
|
||||||
80.0,
|
80.0,
|
||||||
80.0,
|
80.00218427142131,
|
||||||
61.06060606060606,
|
80.00760448962633,
|
||||||
61.06060606060606,
|
61.06821055023239,
|
||||||
61.06060606060606,
|
61.06821055023239,
|
||||||
61.06060606060606,
|
62.12948116988288,
|
||||||
61.06060606060606,
|
63.5406596317257,
|
||||||
50.341167355371894,
|
58.120614791285504,
|
||||||
50.341167355371894,
|
58.682709146161926,
|
||||||
36.91550447658402,
|
45.22651624410048,
|
||||||
36.17712637741047,
|
39.85140827675753,
|
||||||
33.0292699724518,
|
36.67674043670639,
|
||||||
28.808023415977967,
|
32.45548217808278,
|
||||||
24.88076790633609,
|
28.528226668440908,
|
||||||
24.88076790633609,
|
25.495297949432643,
|
||||||
22.658100895316807,
|
23.27263093841336,
|
||||||
18.921659779614323,
|
19.53618982271088,
|
||||||
15.932937327823693,
|
16.54746737092025,
|
||||||
13.312456955922865,
|
13.926986999019423,
|
||||||
10.917871900826448,
|
11.532401943923004,
|
||||||
8.813490013774107,
|
9.428020056870663,
|
||||||
6.63154269972452,
|
7.2460727428210765,
|
||||||
3.1886621900826473,
|
3.8031825018727887,
|
||||||
0.0,
|
0.30551238333016156,
|
||||||
0.0,
|
0.6197802647075666,
|
||||||
0.3120550641291261,
|
1.5052110988161542,
|
||||||
1.07020564583707,
|
2.736047696034607,
|
||||||
4.578139530578446,
|
7.125981603698244,
|
||||||
4.728141236897871,
|
7.346947276543289,
|
||||||
19.68087457022947,
|
22.29968060987662,
|
||||||
31.94341995234899,
|
34.57523424809509,
|
||||||
38.90006700591099,
|
41.83065840604197,
|
||||||
42.674787887051245,
|
46.49642400356206,
|
||||||
43.17025540478875,
|
47.884563842022054,
|
||||||
42.06126780148296,
|
46.48524430245792,
|
||||||
39.622002994320425,
|
43.99708508544073,
|
||||||
35.373509537020155,
|
39.74859162814045,
|
||||||
32.214117319389295
|
36.5891994105096
|
||||||
],
|
],
|
||||||
"Electricity_price": [
|
"Electricity_price": [
|
||||||
0.000228,
|
0.000228,
|
||||||
@@ -603,6 +604,46 @@
|
|||||||
0.0002969,
|
0.0002969,
|
||||||
0.0002921,
|
0.0002921,
|
||||||
0.000278
|
0.000278
|
||||||
|
],
|
||||||
|
"Feed_in_tariff": [
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05,
|
||||||
|
7e-05
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"eauto_obj": {
|
"eauto_obj": {
|
||||||
@@ -619,16 +660,16 @@
|
|||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.375,
|
||||||
|
0.375,
|
||||||
|
0.875,
|
||||||
1.0,
|
1.0,
|
||||||
0.5,
|
0.875,
|
||||||
0.625,
|
|
||||||
1.0,
|
|
||||||
0.75,
|
|
||||||
0.5,
|
|
||||||
0.625,
|
0.625,
|
||||||
0.375,
|
0.375,
|
||||||
0.0,
|
0.375,
|
||||||
|
0.375,
|
||||||
|
0.1,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
@@ -712,26 +753,26 @@
|
|||||||
"capacity_wh": 60000,
|
"capacity_wh": 60000,
|
||||||
"charging_efficiency": 0.95,
|
"charging_efficiency": 0.95,
|
||||||
"max_charge_power_w": 11040,
|
"max_charge_power_w": 11040,
|
||||||
"soc_wh": 59373.0,
|
"soc_wh": 59110.8,
|
||||||
"initial_soc_percentage": 5
|
"initial_soc_percentage": 5
|
||||||
},
|
},
|
||||||
"start_solution": [
|
"start_solution": [
|
||||||
|
2.0,
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
0.0,
|
0.0,
|
||||||
|
2.0,
|
||||||
0.0,
|
0.0,
|
||||||
|
1.0,
|
||||||
2.0,
|
2.0,
|
||||||
1.0,
|
1.0,
|
||||||
0.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
2.0,
|
|
||||||
2.0,
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
0.0,
|
0.0,
|
||||||
1.0,
|
1.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
|
||||||
1.0,
|
1.0,
|
||||||
0.0,
|
0.0,
|
||||||
1.0,
|
1.0,
|
||||||
@@ -739,6 +780,23 @@
|
|||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
0.0,
|
0.0,
|
||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
@@ -748,71 +806,54 @@
|
|||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
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,
|
5.0,
|
||||||
4.0,
|
|
||||||
2.0,
|
|
||||||
6.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,
|
1.0,
|
||||||
2.0,
|
|
||||||
3.0,
|
|
||||||
3.0,
|
|
||||||
2.0,
|
|
||||||
3.0,
|
|
||||||
3.0,
|
|
||||||
0.0,
|
0.0,
|
||||||
3.0,
|
|
||||||
2.0,
|
|
||||||
2.0,
|
|
||||||
6.0,
|
6.0,
|
||||||
1.0,
|
1.0,
|
||||||
3.0,
|
3.0,
|
||||||
5.0,
|
5.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
5.0,
|
||||||
|
6.0,
|
||||||
|
5.0,
|
||||||
|
3.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
4.0,
|
||||||
|
2.0,
|
||||||
|
6.0,
|
||||||
|
5.0,
|
||||||
|
5.0,
|
||||||
|
3.0,
|
||||||
2.0,
|
2.0,
|
||||||
2.0,
|
2.0,
|
||||||
4.0,
|
6.0,
|
||||||
4.0,
|
5.0,
|
||||||
|
2.0,
|
||||||
|
5.0,
|
||||||
4.0,
|
4.0,
|
||||||
0.0,
|
0.0,
|
||||||
|
4.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
5.0,
|
5.0,
|
||||||
4.0,
|
0.0,
|
||||||
6.0,
|
0.0,
|
||||||
6.0,
|
2.0,
|
||||||
4.0,
|
|
||||||
3.0,
|
3.0,
|
||||||
5.0,
|
4.0,
|
||||||
|
4.0,
|
||||||
2.0,
|
2.0,
|
||||||
5.0,
|
5.0,
|
||||||
14.0
|
3.0,
|
||||||
|
12.0
|
||||||
],
|
],
|
||||||
"washingstart": 14
|
"washingstart": 12
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user