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
|
||||
grid. The seasonal price extrapolation is resolution-agnostic and stays identical
|
||||
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)
|
||||
|
||||
|
||||
@@ -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]. |
|
||||
| device_id | `str` | `rw` | `<unknown>` | ID of device |
|
||||
| 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_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]. |
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
| Name | Type | Read-Only | Default | Description |
|
||||
| ---- | ---- | --------- | ------- | ----------- |
|
||||
| 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 -->
|
||||
|
||||
|
||||
@@ -176,6 +176,8 @@
|
||||
"horizon_hours": 24,
|
||||
"interval": 3600,
|
||||
"algorithm": "GENETIC",
|
||||
"visualize_pdf": true,
|
||||
"terminal_value_euro_per_kwh": 0.0,
|
||||
"genetic": {
|
||||
"individuals": 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. |
|
||||
| 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. |
|
||||
| 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 -->
|
||||
|
||||
@@ -27,6 +29,8 @@
|
||||
"horizon_hours": 24,
|
||||
"interval": 3600,
|
||||
"algorithm": "GENETIC",
|
||||
"visualize_pdf": true,
|
||||
"terminal_value_euro_per_kwh": 0.0,
|
||||
"genetic": {
|
||||
"individuals": 400,
|
||||
"generations": 400,
|
||||
@@ -51,6 +55,8 @@
|
||||
"horizon_hours": 24,
|
||||
"interval": 3600,
|
||||
"algorithm": "GENETIC",
|
||||
"visualize_pdf": true,
|
||||
"terminal_value_euro_per_kwh": 0.0,
|
||||
"genetic": {
|
||||
"individuals": 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
|
||||
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}
|
||||
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
|
||||
@@ -224,8 +229,9 @@ The behavior of the genetic algorithm can be customized using the following conf
|
||||
```json
|
||||
{
|
||||
"optimization": {
|
||||
"hours": 24,
|
||||
"horizon_hours": 24,
|
||||
"interval": 3600,
|
||||
"terminal_value_euro_per_kwh": 0.20,
|
||||
"genetic" : {
|
||||
"individuals": 300,
|
||||
"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
|
||||
|
||||
**Example:**
|
||||
|
||||
@@ -70,6 +70,7 @@ to `DISABLED` in the configuration.
|
||||
"pv_akku": {
|
||||
"device_id": "battery1",
|
||||
"capacity_wh": 26400,
|
||||
"levelized_cost_of_storage_kwh": 0.12,
|
||||
"max_charge_power_w": 5000,
|
||||
"initial_soc_percentage": 80,
|
||||
"min_soc_percentage": 15
|
||||
@@ -112,12 +113,15 @@ to `DISABLED` in the configuration.
|
||||
|
||||
### Energy Management System (EMS)
|
||||
|
||||
#### Battery Cost (`preis_euro_pro_wh_akku`)
|
||||
#### Battery Terminal Value (`preis_euro_pro_wh_akku`)
|
||||
|
||||
- Unit: €/Wh
|
||||
- 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
|
||||
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`)
|
||||
|
||||
@@ -145,6 +149,51 @@ to `DISABLED` in the configuration.
|
||||
- Format: Array of hourly values
|
||||
- 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`)
|
||||
|
||||
- Unit: €/Wh
|
||||
@@ -162,8 +211,27 @@ Verify prices against your local tariffs.
|
||||
- `capacity_wh`: Total battery capacity in Wh
|
||||
- `charging_efficiency`: Charging 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
|
||||
|
||||
#### 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)
|
||||
|
||||
- `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`
|
||||
|
||||
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`,
|
||||
`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:
|
||||
- `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
|
||||
- `factor` = `optimization.genetic.penalties.ac_charge_break_even` (default `1.0`)
|
||||
|
||||
|
||||
+65
-5
@@ -2213,8 +2213,9 @@
|
||||
},
|
||||
"levelized_cost_of_storage_kwh": {
|
||||
"type": "number",
|
||||
"minimum": 0.0,
|
||||
"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,
|
||||
"examples": [
|
||||
0.12
|
||||
@@ -2367,8 +2368,9 @@
|
||||
},
|
||||
"levelized_cost_of_storage_kwh": {
|
||||
"type": "number",
|
||||
"minimum": 0.0,
|
||||
"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,
|
||||
"examples": [
|
||||
0.12
|
||||
@@ -4886,7 +4888,7 @@
|
||||
"preis_euro_pro_wh_akku": {
|
||||
"type": "number",
|
||||
"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": {
|
||||
"items": {
|
||||
@@ -5111,6 +5113,14 @@
|
||||
"type": "array",
|
||||
"title": "Electricity Price",
|
||||
"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,
|
||||
@@ -6860,7 +6870,7 @@
|
||||
"maximum": 3600.0,
|
||||
"minimum": 900.0,
|
||||
"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,
|
||||
"examples": [
|
||||
3600,
|
||||
@@ -6876,6 +6886,26 @@
|
||||
"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": {
|
||||
"$ref": "#/components/schemas/GeneticCommonSettings",
|
||||
"description": "Genetic optimization algorithm configuration.",
|
||||
@@ -6910,7 +6940,7 @@
|
||||
"maximum": 3600.0,
|
||||
"minimum": 900.0,
|
||||
"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,
|
||||
"examples": [
|
||||
3600,
|
||||
@@ -6926,6 +6956,26 @@
|
||||
"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": {
|
||||
"$ref": "#/components/schemas/GeneticCommonSettings",
|
||||
"description": "Genetic optimization algorithm configuration.",
|
||||
@@ -8877,6 +8927,16 @@
|
||||
],
|
||||
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,
|
||||
|
||||
@@ -50,8 +50,12 @@ class BatteriesCommonSettings(DevicesBaseSettings):
|
||||
|
||||
levelized_cost_of_storage_kwh: float = Field(
|
||||
default=0.0,
|
||||
ge=0.0,
|
||||
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],
|
||||
},
|
||||
)
|
||||
|
||||
@@ -34,6 +34,11 @@ class Battery:
|
||||
self.initial_soc_percentage = self.parameters.initial_soc_percentage
|
||||
self.charging_efficiency = self.parameters.charging_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
|
||||
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)
|
||||
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:
|
||||
"""Sets the discharge values for each hour."""
|
||||
if len(discharge_array) != self.prediction_hours:
|
||||
|
||||
@@ -14,9 +14,6 @@ class Inverter:
|
||||
battery: Optional[Battery] = None,
|
||||
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.battery: Optional[Battery] = battery
|
||||
self.slot_duration_h: float = slot_duration_h
|
||||
@@ -28,16 +25,13 @@ class Inverter:
|
||||
logger.error(error_msg)
|
||||
raise ValueError(error_msg)
|
||||
self.self_consumption_predictor = get_eos_load_interpolator()
|
||||
# max_power_wh is supplied as a power [W] that the legacy hourly code
|
||||
# treats as Wh-per-hour. Scale it to the actual slot length so a 15-min
|
||||
# slot can move at most a quarter of that energy.
|
||||
self.max_power_wh = (
|
||||
self.parameters.max_power_wh * self.slot_duration_h
|
||||
) # Maximum energy the inverter can move in one optimization slot
|
||||
# max_power_wh is supplied as power [W] but used as the maximum energy
|
||||
# the inverter can move during one optimization slot.
|
||||
self.max_power_wh = self.parameters.max_power_wh * self.slot_duration_h
|
||||
self.dc_to_ac_efficiency = self.parameters.dc_to_ac_efficiency
|
||||
self.ac_to_dc_efficiency = self.parameters.ac_to_dc_efficiency
|
||||
# max_ac_charge_power_w stays in Watts. It feeds a dimensionless,
|
||||
# slot-agnostic power-ratio cap in genetic.py simulate().
|
||||
# This value remains a power [W]. GeneticSimulation converts it into a
|
||||
# slot-independent charge-factor limit.
|
||||
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]:
|
||||
@@ -58,128 +52,86 @@ class Inverter:
|
||||
hour: int,
|
||||
allow_battery_grid_export: bool = False,
|
||||
) -> 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
|
||||
grid_export = 0.0
|
||||
grid_import = 0.0
|
||||
self_consumption = 0.0
|
||||
generation = max(float(generation), 0.0)
|
||||
consumption = max(float(consumption), 0.0)
|
||||
|
||||
# Cache inverter DC→AC efficiency for discharge path
|
||||
dc_to_ac_eff = self.dc_to_ac_efficiency
|
||||
|
||||
if generation >= consumption:
|
||||
if consumption > self.max_power_wh:
|
||||
# If consumption exceeds maximum inverter power
|
||||
losses += generation - self.max_power_wh
|
||||
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
|
||||
# Convert interval energy [Wh] to mean power [W] for the probability
|
||||
# lookup, then convert its expected direct power back to slot energy.
|
||||
if generation > 0.0 and consumption > 0.0:
|
||||
expected_direct_power_w = (
|
||||
self.self_consumption_predictor.calculate_expected_direct_consumption(
|
||||
consumption / self.slot_duration_h,
|
||||
generation / self.slot_duration_h,
|
||||
)
|
||||
|
||||
# Remaining power after consumption
|
||||
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
|
||||
direct_pv_energy = expected_direct_power_w * self.slot_duration_h
|
||||
else:
|
||||
from_battery_ac = 0.0
|
||||
direct_pv_energy = 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
|
||||
# Direct PV is bounded by both input energies and by the AC energy the
|
||||
# inverter can move during this slot.
|
||||
direct_pv_energy = min(
|
||||
max(direct_pv_energy, 0.0),
|
||||
generation,
|
||||
consumption,
|
||||
self.max_power_wh,
|
||||
)
|
||||
remaining_surplus = remaining_power - (charged_energie + charge_losses)
|
||||
else:
|
||||
remaining_surplus = remaining_power
|
||||
remaining_load = max(consumption - direct_pv_energy, 0.0)
|
||||
pv_surplus = max(generation - direct_pv_energy, 0.0)
|
||||
remaining_inverter_ac_capacity = max(self.max_power_wh - direct_pv_energy, 0.0)
|
||||
|
||||
# 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
|
||||
# Load gaps and PV surplus may both occur within the same coarse slot.
|
||||
# Cover the load gap first; this preserves the existing chronological
|
||||
# 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
|
||||
|
||||
grid_import = remaining_load
|
||||
|
||||
# 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
|
||||
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)
|
||||
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) * dc_to_ac_eff
|
||||
self.battery.remaining_discharge_energy_wh(hour) * self.dc_to_ac_efficiency
|
||||
)
|
||||
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:
|
||||
# Case 2: Insufficient generation, cover shortfall
|
||||
shortfall = consumption - generation
|
||||
available_ac_power = max(self.max_power_wh - generation, 0)
|
||||
|
||||
# Discharge battery to cover shortfall, if possible
|
||||
if self.battery:
|
||||
# Need shortfall in AC, request more DC from battery for DC→AC conversion
|
||||
ac_needed = min(shortfall, available_ac_power)
|
||||
dc_request = ac_needed / dc_to_ac_eff
|
||||
battery_discharge_dc, discharge_losses = self.battery.discharge_energy(
|
||||
dc_request, hour
|
||||
)
|
||||
# Convert DC output to AC
|
||||
battery_discharge_ac = battery_discharge_dc * dc_to_ac_eff
|
||||
inverter_discharge_losses = battery_discharge_dc - battery_discharge_ac
|
||||
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)
|
||||
grid_import = shortfall - battery_discharge_ac
|
||||
self_consumption = generation + battery_discharge_ac
|
||||
|
||||
if allow_battery_grid_export and self.battery and grid_import <= 0.0:
|
||||
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)
|
||||
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
|
||||
|
||||
@@ -450,7 +450,18 @@ class GeneticSimulation(PydanticBaseModel):
|
||||
feed_in_tariff_per_hour[hour_idx] = hourly_feed_in_tariff
|
||||
|
||||
# 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
|
||||
|
||||
total_cost = np.nansum(costs_per_hour)
|
||||
@@ -1244,8 +1255,14 @@ class GeneticOptimization(OptimizationBase):
|
||||
if charge_price <= 0:
|
||||
continue
|
||||
|
||||
# Price that a future discharge hour must reach to break even
|
||||
break_even_price = charge_price / round_trip_eff
|
||||
# Price that a future AC discharge hour must reach to break
|
||||
# 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]
|
||||
|
||||
|
||||
@@ -98,6 +98,17 @@ class BaseBatteryParameters(DeviceParameters):
|
||||
class SolarPanelBatteryParameters(BaseBatteryParameters):
|
||||
"""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()
|
||||
|
||||
|
||||
|
||||
@@ -54,7 +54,10 @@ class GeneticEnergyManagementParameters(GeneticParametersBaseModel):
|
||||
)
|
||||
preis_euro_pro_wh_akku: float = Field(
|
||||
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(
|
||||
@@ -416,7 +419,6 @@ class GeneticOptimizationParameters(
|
||||
cls.config.devices.max_batteries = 1
|
||||
if cls.config.devices.max_batteries == 0:
|
||||
battery_params = None
|
||||
battery_lcos_kwh = 0
|
||||
else:
|
||||
if cls.config.devices.batteries is None:
|
||||
logger.info("No battery device data available - defaulting to demo data.")
|
||||
@@ -428,6 +430,9 @@ class GeneticOptimizationParameters(
|
||||
capacity_wh=battery_config.capacity_wh,
|
||||
charging_efficiency=battery_config.charging_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,
|
||||
min_soc_percentage=battery_config.min_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}]
|
||||
# Retry
|
||||
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
|
||||
try:
|
||||
initial_soc_factor = cls.measurement.key_to_value(
|
||||
@@ -654,7 +651,9 @@ class GeneticOptimizationParameters(
|
||||
strompreis_euro_pro_wh=elecprice_marketprice_wh,
|
||||
einspeiseverguetung_euro_pro_wh=feed_in_tariff_wh,
|
||||
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,
|
||||
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(
|
||||
default_factory=GeneticCommonSettings,
|
||||
json_schema_extra={
|
||||
|
||||
@@ -17,8 +17,32 @@ class SelfConsumptionProbabilityInterpolator:
|
||||
self.interpolator: RegularGridInterpolator = pickle.load(file) # noqa: S301
|
||||
self.load_power_min_w = float(self.interpolator.grid[0][0])
|
||||
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])
|
||||
|
||||
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(
|
||||
self, mean_load_power_w: float, pv_power_w: float
|
||||
) -> tuple[np.ndarray, np.ndarray]:
|
||||
@@ -39,7 +63,12 @@ class SelfConsumptionProbabilityInterpolator:
|
||||
|
||||
@cache_energy_management
|
||||
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.
|
||||
|
||||
@@ -54,6 +83,46 @@ class SelfConsumptionProbabilityInterpolator:
|
||||
probabilities = self.interpolator(points)
|
||||
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:
|
||||
# """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 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)
|
||||
|
||||
battery.reset()
|
||||
|
||||
assert battery.discharged_energy_wh(0) == 0.0
|
||||
|
||||
@@ -338,15 +338,15 @@ def test_simulation(genetic_simulation):
|
||||
|
||||
# Verify the total balance
|
||||
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."
|
||||
|
||||
# Check total revenue and total costs
|
||||
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."
|
||||
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."
|
||||
|
||||
# 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))
|
||||
monkeypatch.setattr(
|
||||
inverter.self_consumption_predictor,
|
||||
"calculate_self_consumption",
|
||||
Mock(return_value=1.0),
|
||||
"calculate_expected_direct_consumption",
|
||||
Mock(side_effect=min),
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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(
|
||||
{"prediction": {"hours": 2}, "optimization": {"horizon_hours": 2}}
|
||||
)
|
||||
@@ -426,6 +430,7 @@ def _direct_marketing_battery_export_simulation(config_eos) -> GeneticSimulation
|
||||
min_soc_percentage=0,
|
||||
charging_efficiency=1.0,
|
||||
discharging_efficiency=1.0,
|
||||
levelized_cost_of_storage_kwh=levelized_cost_of_storage_kwh,
|
||||
max_charge_power_w=500,
|
||||
),
|
||||
prediction_hours=config_eos.prediction.hours,
|
||||
@@ -435,6 +440,7 @@ def _direct_marketing_battery_export_simulation(config_eos) -> GeneticSimulation
|
||||
device_id="inverter1",
|
||||
max_power_wh=500.0,
|
||||
battery_id=battery.parameters.device_id,
|
||||
dc_to_ac_efficiency=dc_to_ac_efficiency,
|
||||
),
|
||||
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 simulation.battery is not None
|
||||
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
|
||||
slot_duration_h = 0.25
|
||||
|
||||
hourly = interpolator.calculate_self_consumption(hourly_load_wh, hourly_pv_wh)
|
||||
quarter_hour = interpolator.calculate_self_consumption(
|
||||
hourly = interpolator.calculate_expected_direct_consumption(hourly_load_wh, hourly_pv_wh)
|
||||
quarter_hour = interpolator.calculate_expected_direct_consumption(
|
||||
(hourly_load_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 > 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.inverter import Inverter, InverterParameters
|
||||
from akkudoktoreos.optimization.genetic.geneticdevices import SolarPanelBatteryParameters
|
||||
from akkudoktoreos.optimization.genetic.geneticdevices import (
|
||||
SolarPanelBatteryParameters,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -20,7 +22,7 @@ def mock_battery() -> Mock:
|
||||
@pytest.fixture
|
||||
def inverter(mock_battery) -> Inverter:
|
||||
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(
|
||||
"akkudoktoreos.devices.genetic.inverter.get_eos_load_interpolator",
|
||||
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
|
||||
mock_battery.charge_energy.assert_called_once_with(400.0, hour)
|
||||
mock_battery.discharge_energy.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
||||
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||
consumption, generation
|
||||
)
|
||||
|
||||
@@ -100,7 +102,8 @@ def test_process_energy_excess_generation_interpolator(inverter, mock_battery):
|
||||
# Battery charges 100 Wh with 10 Wh loss
|
||||
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
||||
mock_battery.discharge_energy.return_value = (20.0, 2.0)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.return_value = 0.95
|
||||
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
|
||||
consumption = 200.0
|
||||
@@ -110,19 +113,71 @@ def test_process_energy_excess_generation_interpolator(inverter, mock_battery):
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == pytest.approx(
|
||||
270.0, rel=1e-2
|
||||
) # 290 Wh feed-in - 5% of generation-consumption self consumption after battery charges
|
||||
assert grid_export == pytest.approx(300.0, rel=1e-2)
|
||||
assert grid_import == pytest.approx(0.0, rel=1e-2) # No grid draw
|
||||
assert losses == 12.0 # Battery charging losses
|
||||
assert self_consumption == 220.0 # All consumption is met
|
||||
mock_battery.charge_energy.assert_called_once_with(pytest.approx(380.0, rel=1e-2), hour)
|
||||
assert losses == 22.0 # Battery/inverter losses plus curtailed PV
|
||||
assert self_consumption == 200.0 # 180 Wh direct PV + 20 Wh battery
|
||||
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)
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
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):
|
||||
generation = 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.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
|
||||
)
|
||||
|
||||
@@ -163,7 +218,9 @@ def test_process_energy_battery_discharges(inverter, mock_battery):
|
||||
assert self_consumption == 200.0 # Generation + battery discharge
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(150.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
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):
|
||||
@@ -183,7 +240,7 @@ def test_process_energy_allows_battery_grid_export(inverter, mock_battery):
|
||||
assert losses == 0.0
|
||||
assert self_consumption == 100.0
|
||||
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):
|
||||
@@ -203,7 +260,9 @@ def test_process_energy_battery_empty(inverter, mock_battery):
|
||||
assert self_consumption == 100.0 # Only generation is consumed
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(200.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
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):
|
||||
@@ -225,7 +284,7 @@ def test_process_energy_battery_full_at_start(inverter, mock_battery):
|
||||
assert self_consumption == 200.0 # Only consumption is met
|
||||
mock_battery.charge_energy.assert_called_once_with(300.0, hour)
|
||||
mock_battery.discharge_energy.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
||||
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||
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
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(400.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
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):
|
||||
@@ -272,7 +333,9 @@ def test_process_energy_insufficient_generation_battery_assists(inverter, mock_b
|
||||
assert self_consumption == 250.0 # Generation + battery discharge
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(200.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_called_once_with(
|
||||
consumption, generation
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(300.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_not_called()
|
||||
|
||||
|
||||
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
|
||||
mock_battery.charge_energy.assert_called_once_with(500.0, hour)
|
||||
mock_battery.discharge_energy.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
||||
consumption, generation
|
||||
)
|
||||
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_not_called()
|
||||
|
||||
|
||||
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
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
||||
consumption, generation
|
||||
)
|
||||
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_not_called()
|
||||
|
||||
|
||||
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
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(200.0, 12)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
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):
|
||||
@@ -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
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(400.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
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):
|
||||
@@ -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
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(500.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_expected_direct_consumption.assert_not_called()
|
||||
|
||||
@@ -38,7 +38,7 @@ def _make_inverter(
|
||||
) -> Inverter:
|
||||
"""Create an Inverter with custom efficiency parameters and a mock battery."""
|
||||
mock_self_consumption_predictor = Mock()
|
||||
mock_self_consumption_predictor.calculate_self_consumption.return_value = 1.0
|
||||
mock_self_consumption_predictor.calculate_expected_direct_consumption.side_effect = min
|
||||
|
||||
params = InverterParameters(
|
||||
device_id="inv1",
|
||||
@@ -165,12 +165,14 @@ class TestDcToAcEfficiency:
|
||||
assert losses == pytest.approx(10.0, rel=1e-5) # Only battery losses
|
||||
|
||||
def test_discharge_surplus_path_with_efficiency(self, mock_battery):
|
||||
"""When generation > consumption but SCR < 1, discharge goes through inverter."""
|
||||
mock_battery.discharge_energy.return_value = (50.0, 5.0)
|
||||
"""A probabilistic load gap discharges through the inverter."""
|
||||
mock_battery.discharge_energy.return_value = (30.0 / 0.90, 5.0)
|
||||
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
||||
|
||||
inv = _make_inverter(dc_to_ac_efficiency=0.90, mock_battery=mock_battery)
|
||||
cast(Mock, inv.self_consumption_predictor).calculate_self_consumption.return_value = 0.90
|
||||
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
|
||||
consumption = 200.0
|
||||
@@ -180,18 +182,23 @@ class TestDcToAcEfficiency:
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
# surplus = 300, remaining_power = 300*0.9 = 270, remaining_load_evq = 300*0.1 = 30
|
||||
# DC request for discharge = 30 / 0.90 = 33.333
|
||||
# Expected direct PV is 170 Wh, leaving 30 Wh of load gap and
|
||||
# 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
|
||||
mock_battery.discharge_energy.assert_called_once_with(
|
||||
pytest.approx(expected_dc_request, rel=1e-3), hour
|
||||
)
|
||||
|
||||
# Battery delivers 50 Wh DC → 45 Wh AC
|
||||
from_battery_ac = 50.0 * 0.90 # 45 Wh
|
||||
inverter_discharge_loss = 50.0 - from_battery_ac # 5 Wh
|
||||
# Battery delivers 33.333 Wh DC -> 30 Wh AC.
|
||||
from_battery_dc = 30.0 / 0.90
|
||||
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)
|
||||
min_soc_wh: float = 0.0,
|
||||
max_charge_power_w: float = 5_000.0,
|
||||
levelized_cost_of_storage_kwh: float = 0.0,
|
||||
# Arrays (must be same length)
|
||||
ac_charge_hours: list | None = None,
|
||||
elect_price_hourly: list | None = None,
|
||||
@@ -517,6 +525,7 @@ def _make_mock_simulation(
|
||||
initial_soc_percentage=initial_soc_percentage,
|
||||
min_soc_wh=min_soc_wh,
|
||||
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),
|
||||
)
|
||||
|
||||
@@ -744,6 +753,28 @@ class TestAcChargeBreakEvenPenalty:
|
||||
# Fitness must be worse (higher) than base
|
||||
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
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
@@ -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,
|
||||
1320.56,
|
||||
1132.03,
|
||||
1308.5200000002487,
|
||||
1308.5200000000004,
|
||||
1176.82,
|
||||
1216.22,
|
||||
1103.78,
|
||||
@@ -238,10 +238,12 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.20303854854278033,
|
||||
0.1899652543898917,
|
||||
0.12833851757957424,
|
||||
0.04233866391809883,
|
||||
0.21077535122459587,
|
||||
0.19320652266312205,
|
||||
0.1358062627100041,
|
||||
0.0692592282596561,
|
||||
0.023370695807696434,
|
||||
0.0019327051509204403,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -260,22 +262,20 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.16357655037574115,
|
||||
0.14900060381217387,
|
||||
0.08949503544160814,
|
||||
0.010110585812541543,
|
||||
0.0,
|
||||
0.0,
|
||||
0.18814608875566813,
|
||||
0.15236390731688437,
|
||||
0.10316291465819699,
|
||||
0.029150936607659956,
|
||||
0.020928608511559126,
|
||||
0.003524558735906156,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Gesamt_Verluste": 2807.7292841655817,
|
||||
"Gesamtbilanz_Euro": 0.8879905947253857,
|
||||
"Gesamteinnahmen_Euro": 0.9758637598724098,
|
||||
"Gesamtkosten_Euro": 1.8638543545977955,
|
||||
"Gesamt_Verluste": 3425.4668727209255,
|
||||
"Gesamtbilanz_Euro": 0.9585224311392879,
|
||||
"Gesamteinnahmen_Euro": 1.1316277804018695,
|
||||
"Gesamtkosten_Euro": 2.0901502115411574,
|
||||
"Home_appliance_wh_per_hour": [
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -320,14 +320,14 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.001880612819166666,
|
||||
0.026623430000091274,
|
||||
4.482711108977355e-14,
|
||||
0.008763569215739961,
|
||||
0.018335381563380656,
|
||||
0.06267084962493971,
|
||||
0.05480703000000003,
|
||||
0.225316611,
|
||||
0.07557452231671152,
|
||||
0.026623430000000066,
|
||||
4.55656845588237e-17,
|
||||
0.001414013162203277,
|
||||
0.005881449073870462,
|
||||
0.05258762370598476,
|
||||
0.1614775630079859,
|
||||
0.2338232746714084,
|
||||
0.0,
|
||||
0.26650619799999997,
|
||||
0.19588158,
|
||||
@@ -343,15 +343,15 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.02844719513362201,
|
||||
0.009619897970888898,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0004412281431084693,
|
||||
0.008372170029774037,
|
||||
0.03130999506792791,
|
||||
2.3325608707865465e-05,
|
||||
0.0021886029750169123,
|
||||
0.013012984677295973,
|
||||
0.0,
|
||||
0.08231598,
|
||||
0.174597189,
|
||||
0.17784012884918773,
|
||||
0.19011028252189552,
|
||||
0.0,
|
||||
0.0,
|
||||
0.16484566
|
||||
@@ -360,14 +360,14 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
10.008583390988111,
|
||||
144.8500000004966,
|
||||
2.236881790906864e-10,
|
||||
39.870651572975255,
|
||||
80.77260600608219,
|
||||
209.11194402715952,
|
||||
171.54000000000008,
|
||||
731.31,
|
||||
402.20607938643707,
|
||||
144.85000000000036,
|
||||
2.2737367544323206e-13,
|
||||
6.433180901743754,
|
||||
25.909467285772962,
|
||||
175.4675465665157,
|
||||
505.40708296709204,
|
||||
758.9200735845777,
|
||||
0.0,
|
||||
912.38,
|
||||
704.61,
|
||||
@@ -383,15 +383,15 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
135.91588692604878,
|
||||
45.96224544141853,
|
||||
0.0,
|
||||
0.0,
|
||||
2.201737241060226,
|
||||
38.089945540373236,
|
||||
137.92949369131236,
|
||||
0.11639525303326081,
|
||||
9.957247384062384,
|
||||
57.32592368852852,
|
||||
0.0,
|
||||
257.64,
|
||||
566.69,
|
||||
556.6201215937018,
|
||||
617.0408390843736,
|
||||
0.0,
|
||||
0.0,
|
||||
592.97
|
||||
@@ -402,10 +402,12 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
2900.550693468291,
|
||||
2713.7893484270244,
|
||||
1833.4073939939178,
|
||||
604.8380559728405,
|
||||
3011.0764460656555,
|
||||
2760.093180901744,
|
||||
1940.089467285773,
|
||||
989.4175465665157,
|
||||
333.86708296709196,
|
||||
27.61007358457772,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -424,31 +426,29 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
2336.807862510588,
|
||||
2128.580054459627,
|
||||
1278.5005063086878,
|
||||
144.4369401791649,
|
||||
0.0,
|
||||
0.0,
|
||||
2687.8012679381163,
|
||||
2176.6272473840627,
|
||||
1473.7559236885286,
|
||||
416.4419515379994,
|
||||
298.9801215937018,
|
||||
50.35083908437366,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Verluste_Pro_Stunde": [
|
||||
16.744090909090914,
|
||||
2.817272727272737,
|
||||
29.157272727272726,
|
||||
2.358169993081436,
|
||||
600.0000000000002,
|
||||
173.00391678377832,
|
||||
97.85621559422765,
|
||||
101.92059640365329,
|
||||
114.42806532440363,
|
||||
51.82392952637247,
|
||||
599.9999999999995,
|
||||
159.7408264721214,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
133.72909090909081,
|
||||
133.7321802766326,
|
||||
0.0,
|
||||
0.0,
|
||||
70.41409090909087,
|
||||
@@ -458,18 +458,18 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
109.0704545454546,
|
||||
109.96227272727276,
|
||||
47.952272727272714,
|
||||
16.031648664443644,
|
||||
55.9754990365584,
|
||||
143.33449356887422,
|
||||
21.973794868109557,
|
||||
538.2984000000038,
|
||||
161.2428480298022,
|
||||
109.07302361034766,
|
||||
116.99491129525349,
|
||||
95.61900944932736,
|
||||
98.21987178167876,
|
||||
123.8591383343284,
|
||||
165.15986945297027,
|
||||
116.9350623689079,
|
||||
538.2984000000001,
|
||||
119.40181527778998,
|
||||
0.0,
|
||||
0.0,
|
||||
44.91187685729284,
|
||||
81.2380484620021,
|
||||
0.0,
|
||||
0.0,
|
||||
134.59227272727276,
|
||||
@@ -478,35 +478,35 @@
|
||||
],
|
||||
"akku_soc_pro_stunde": [
|
||||
80.0,
|
||||
79.4714617768595,
|
||||
79.38253271349862,
|
||||
78.46216425619835,
|
||||
78.52766897822838,
|
||||
95.19433564489505,
|
||||
79.16421888032488,
|
||||
78.69989843940196,
|
||||
77.45653455559739,
|
||||
78.89608815355218,
|
||||
95.56275482021886,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
95.77875344352617,
|
||||
95.77875344352617,
|
||||
95.77875344352617,
|
||||
93.55608643250687,
|
||||
89.8196453168044,
|
||||
86.83092286501376,
|
||||
84.21044249311292,
|
||||
84.21044249311292,
|
||||
84.21044249311292,
|
||||
84.21044249311292,
|
||||
80.76756198347105,
|
||||
77.2965306473829,
|
||||
75.78288567493111,
|
||||
75.93522642877453,
|
||||
76.44194846937079,
|
||||
80.42346217961729,
|
||||
80.56829866584056,
|
||||
95.52103199917215,
|
||||
95.77874174137638,
|
||||
95.77874174137638,
|
||||
95.77874174137638,
|
||||
93.5560747303571,
|
||||
89.81963361465462,
|
||||
86.83091116286398,
|
||||
84.21043079096314,
|
||||
84.21043079096314,
|
||||
84.21043079096314,
|
||||
84.21043079096314,
|
||||
80.76754055001486,
|
||||
77.26987043147221,
|
||||
75.57566963810356,
|
||||
75.69097315408206,
|
||||
76.9218097513005,
|
||||
81.5095839027719,
|
||||
81.73054957561693,
|
||||
96.68328290895028,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
|
||||
+103
-103
@@ -157,7 +157,7 @@
|
||||
1063.91,
|
||||
1320.56,
|
||||
1132.03,
|
||||
1308.5200000002487,
|
||||
1308.5200000000004,
|
||||
1176.82,
|
||||
1216.22,
|
||||
1103.78,
|
||||
@@ -238,10 +238,12 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.20303854854278033,
|
||||
0.1899652543898917,
|
||||
0.12833851757957424,
|
||||
0.04233866391809883,
|
||||
0.21077535122459587,
|
||||
0.19320652266312205,
|
||||
0.1358062627100041,
|
||||
0.0692592282596561,
|
||||
0.023370695807696434,
|
||||
0.0019327051509204403,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -260,22 +262,20 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.16452297290911175,
|
||||
0.1455575560489687,
|
||||
0.08949503544160814,
|
||||
0.024046008596276005,
|
||||
0.0,
|
||||
0.0,
|
||||
0.24478962017661132,
|
||||
0.15146384621553569,
|
||||
0.10316291465819699,
|
||||
0.05435777788576338,
|
||||
0.020928608511559126,
|
||||
0.003524558735906156,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Gesamt_Verluste": 2717.1309464905708,
|
||||
"Gesamtbilanz_Euro": 0.998163925609791,
|
||||
"Gesamteinnahmen_Euro": 0.9873025574263097,
|
||||
"Gesamtkosten_Euro": 1.9854664830361006,
|
||||
"Gesamt_Verluste": 3110.842855499046,
|
||||
"Gesamtbilanz_Euro": 1.1850381627731374,
|
||||
"Gesamteinnahmen_Euro": 1.2125780919995675,
|
||||
"Gesamtkosten_Euro": 2.397616254772705,
|
||||
"Home_appliance_wh_per_hour": [
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -320,14 +320,14 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.001880612819166666,
|
||||
0.026623430000091274,
|
||||
4.482711108977355e-14,
|
||||
0.008763569215739961,
|
||||
0.018335381563380656,
|
||||
0.06267084962493971,
|
||||
0.05480703000000003,
|
||||
0.225316611,
|
||||
0.07557452231671152,
|
||||
0.026623430000000066,
|
||||
4.55656845588237e-17,
|
||||
0.001414013162203277,
|
||||
0.005881449073870462,
|
||||
0.05258762370598476,
|
||||
0.1614775630079859,
|
||||
0.2338232746714084,
|
||||
0.0,
|
||||
0.26650619799999997,
|
||||
0.19588158,
|
||||
@@ -340,18 +340,18 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.08545095,
|
||||
0.008254784724581705,
|
||||
0.028650916976410694,
|
||||
0.02844719513362201,
|
||||
0.1306329312971816,
|
||||
0.07362195915902499,
|
||||
0.060401289430882174,
|
||||
0.009619897970888898,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0004412281431084693,
|
||||
2.3325608707865465e-05,
|
||||
0.0,
|
||||
0.03130999506792791,
|
||||
0.04620342776708689,
|
||||
0.08231598,
|
||||
0.174597189,
|
||||
0.013012984677295973,
|
||||
0.08357424731947552,
|
||||
0.17784012884918773,
|
||||
0.19011028252189552,
|
||||
0.293043269,
|
||||
0.0,
|
||||
0.0
|
||||
@@ -360,14 +360,14 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
10.008583390988111,
|
||||
144.8500000004966,
|
||||
2.236881790906864e-10,
|
||||
39.870651572975255,
|
||||
80.77260600608219,
|
||||
209.11194402715952,
|
||||
171.54000000000008,
|
||||
731.31,
|
||||
402.20607938643707,
|
||||
144.85000000000036,
|
||||
2.2737367544323206e-13,
|
||||
6.433180901743754,
|
||||
25.909467285772962,
|
||||
175.4675465665157,
|
||||
505.40708296709204,
|
||||
758.9200735845777,
|
||||
0.0,
|
||||
912.38,
|
||||
704.61,
|
||||
@@ -380,18 +380,18 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
351.65,
|
||||
36.20519616044607,
|
||||
129.52494112301397,
|
||||
135.91588692604878,
|
||||
537.58407941227,
|
||||
322.9033296448464,
|
||||
273.0618871197205,
|
||||
45.96224544141853,
|
||||
0.0,
|
||||
0.0,
|
||||
2.201737241060226,
|
||||
0.11639525303326081,
|
||||
0.0,
|
||||
137.92949369131236,
|
||||
154.1655914817714,
|
||||
257.64,
|
||||
566.69,
|
||||
57.32592368852852,
|
||||
278.85968408233407,
|
||||
556.6201215937018,
|
||||
617.0408390843736,
|
||||
987.01,
|
||||
0.0,
|
||||
0.0
|
||||
@@ -402,10 +402,12 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
2900.550693468291,
|
||||
2713.7893484270244,
|
||||
1833.4073939939178,
|
||||
604.8380559728405,
|
||||
3011.0764460656555,
|
||||
2760.093180901744,
|
||||
1940.089467285773,
|
||||
989.4175465665157,
|
||||
333.86708296709196,
|
||||
27.61007358457772,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -424,31 +426,29 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
2350.3281844158823,
|
||||
2079.39365784241,
|
||||
1278.5005063086878,
|
||||
343.51440851822866,
|
||||
0.0,
|
||||
0.0,
|
||||
3496.9945739515906,
|
||||
2163.76923165051,
|
||||
1473.7559236885286,
|
||||
776.5396840823341,
|
||||
298.9801215937018,
|
||||
50.35083908437366,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Verluste_Pro_Stunde": [
|
||||
16.744090909090914,
|
||||
2.817272727272737,
|
||||
29.157272727272726,
|
||||
2.358169993081436,
|
||||
600.0000000000002,
|
||||
173.00391678377832,
|
||||
97.85621559422765,
|
||||
101.92059640365329,
|
||||
114.42806532440363,
|
||||
51.82392952637247,
|
||||
599.9999999999995,
|
||||
159.7408264721214,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
133.72909090909081,
|
||||
133.7321802766326,
|
||||
0.0,
|
||||
0.0,
|
||||
70.41409090909087,
|
||||
@@ -458,16 +458,16 @@
|
||||
0.0,
|
||||
0.0,
|
||||
69.12409090909085,
|
||||
109.0704545454546,
|
||||
109.96227272727276,
|
||||
0.0,
|
||||
11.094576460746453,
|
||||
38.31300706523831,
|
||||
143.33449356887422,
|
||||
21.973794868109557,
|
||||
538.2984000000038,
|
||||
159.62040940116685,
|
||||
11.096451076844168,
|
||||
109.07302361034766,
|
||||
116.99491129525349,
|
||||
22.312089529472388,
|
||||
54.18759955738153,
|
||||
86.6234264543665,
|
||||
165.15986945297027,
|
||||
116.9350623689079,
|
||||
538.2984000000001,
|
||||
22.298618556173096,
|
||||
2.900768349489402,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -478,35 +478,35 @@
|
||||
],
|
||||
"akku_soc_pro_stunde": [
|
||||
80.0,
|
||||
79.4714617768595,
|
||||
79.38253271349862,
|
||||
78.46216425619835,
|
||||
78.52766897822838,
|
||||
95.19433564489505,
|
||||
79.16421888032488,
|
||||
78.69989843940196,
|
||||
77.45653455559739,
|
||||
78.89608815355218,
|
||||
95.56275482021886,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
95.77875344352617,
|
||||
95.77875344352617,
|
||||
95.77875344352617,
|
||||
93.55608643250687,
|
||||
89.8196453168044,
|
||||
86.83092286501376,
|
||||
84.21044249311292,
|
||||
84.21044249311292,
|
||||
84.21044249311292,
|
||||
82.02849517906333,
|
||||
78.58561466942146,
|
||||
75.1145833333333,
|
||||
75.1145833333333,
|
||||
75.42276601279848,
|
||||
76.4870162090551,
|
||||
80.4685299193016,
|
||||
80.61336640552487,
|
||||
95.56609973885648,
|
||||
95.77874174137638,
|
||||
95.77874174137638,
|
||||
95.77874174137638,
|
||||
93.5560747303571,
|
||||
89.81963361465462,
|
||||
86.83091116286398,
|
||||
84.21043079096314,
|
||||
84.21043079096314,
|
||||
84.21043079096314,
|
||||
82.02848347691355,
|
||||
78.58559323596526,
|
||||
75.08792311742263,
|
||||
75.7077033821302,
|
||||
77.21291448094635,
|
||||
79.61912077134542,
|
||||
84.20689492281682,
|
||||
84.42786059566187,
|
||||
99.38059392899518,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
|
||||
+102
-102
@@ -204,18 +204,18 @@
|
||||
"Last_Wh_pro_Stunde": [
|
||||
1053.07,
|
||||
10240.91,
|
||||
14186.56,
|
||||
14186.477801352086,
|
||||
11620.03,
|
||||
11218.67,
|
||||
10686.11504084929,
|
||||
7609.82,
|
||||
9082.22,
|
||||
10280.78,
|
||||
2177.92,
|
||||
1178.71,
|
||||
1050.98,
|
||||
1988.56,
|
||||
1488.5587949275546,
|
||||
912.38,
|
||||
1704.6100000000001,
|
||||
2204.61,
|
||||
516.37,
|
||||
868.05,
|
||||
694.34,
|
||||
@@ -288,6 +288,10 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.005594705401588846,
|
||||
0.0016437871377312284,
|
||||
0.028240415856282213,
|
||||
0.023370695807696434,
|
||||
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.00826914812545985,
|
||||
0.25743585772309185,
|
||||
0.1455575560489687,
|
||||
0.07702723513376727,
|
||||
0.010110585812541543,
|
||||
0.0,
|
||||
0.0,
|
||||
0.09023487592359272,
|
||||
0.2577866264025879,
|
||||
0.15146384621553569,
|
||||
0.09798107754792196,
|
||||
0.029150936607659956,
|
||||
0.020928608511559126,
|
||||
0.003524558735906156,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Gesamt_Verluste": 5776.448801897527,
|
||||
"Gesamtbilanz_Euro": 12.789452797857164,
|
||||
"Gesamteinnahmen_Euro": 0.49840038284382926,
|
||||
"Gesamtkosten_Euro": 13.287853180700994,
|
||||
"Gesamt_Verluste": 6008.882879266785,
|
||||
"Gesamtbilanz_Euro": 13.081213953988335,
|
||||
"Gesamteinnahmen_Euro": 0.7099201341480623,
|
||||
"Gesamtkosten_Euro": 13.791134088136397,
|
||||
"Home_appliance_wh_per_hour": [
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -370,16 +370,16 @@
|
||||
2.034522392,
|
||||
2.737606326,
|
||||
1.9651220859999998,
|
||||
0.9557324300000001,
|
||||
0.4189863,
|
||||
1.1236923319999998,
|
||||
1.64866014,
|
||||
0.07038454500000005,
|
||||
0.05480703000000003,
|
||||
0.9176848434271027,
|
||||
0.5064650351737862,
|
||||
1.1459236583154115,
|
||||
1.6539907068609285,
|
||||
0.1912938683161112,
|
||||
0.1614775630079859,
|
||||
0.0,
|
||||
0.588063892,
|
||||
0.4396171120740812,
|
||||
0.0,
|
||||
0.47388158,
|
||||
0.61288158,
|
||||
0.174739608,
|
||||
0.28801899,
|
||||
0.0,
|
||||
@@ -390,8 +390,8 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.008254784724581705,
|
||||
0.028650916976410694,
|
||||
0.07362195915902499,
|
||||
0.060401289430882174,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -399,8 +399,8 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.08231598,
|
||||
0.174597189,
|
||||
0.17784012884918773,
|
||||
0.19011028252189552,
|
||||
0.293043269,
|
||||
0.0,
|
||||
0.0
|
||||
@@ -410,16 +410,16 @@
|
||||
9197.66,
|
||||
13079.82,
|
||||
10458.34,
|
||||
5199.85,
|
||||
2090.75,
|
||||
5112.339999999999,
|
||||
7262.820000000001,
|
||||
234.85000000000014,
|
||||
171.54000000000008,
|
||||
4992.84463235638,
|
||||
2527.2706345997312,
|
||||
5213.483431826258,
|
||||
7286.3026733961615,
|
||||
638.2845122326032,
|
||||
505.40708296709204,
|
||||
0.0,
|
||||
1980.6799999999998,
|
||||
1480.6908456520082,
|
||||
0.0,
|
||||
1704.6100000000001,
|
||||
2204.61,
|
||||
516.37,
|
||||
868.05,
|
||||
0.0,
|
||||
@@ -430,8 +430,8 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
36.20519616044607,
|
||||
129.52494112301397,
|
||||
322.9033296448464,
|
||||
273.0618871197205,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -439,8 +439,8 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
257.64,
|
||||
566.69,
|
||||
556.6201215937018,
|
||||
617.0408390843736,
|
||||
987.01,
|
||||
0.0,
|
||||
0.0
|
||||
@@ -452,6 +452,10 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
79.92436287984066,
|
||||
23.482673396160408,
|
||||
403.4345122326031,
|
||||
333.86708296709196,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -470,36 +474,32 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
118.1306875065693,
|
||||
3677.655110329884,
|
||||
2079.39365784241,
|
||||
1100.3890733395326,
|
||||
144.4369401791649,
|
||||
0.0,
|
||||
0.0,
|
||||
1289.0696560513247,
|
||||
3682.6660914655417,
|
||||
2163.76923165051,
|
||||
1399.729679256028,
|
||||
416.4419515379994,
|
||||
298.9801215937018,
|
||||
50.35083908437366,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Verluste_Pro_Stunde": [
|
||||
16.744090909090914,
|
||||
97.85621559422765,
|
||||
483.0,
|
||||
1014.0,
|
||||
1014.0000000000001,
|
||||
552.0,
|
||||
465.0,
|
||||
207.0,
|
||||
414.0,
|
||||
440.15935588276557,
|
||||
259.38247615196775,
|
||||
416.54628827357027,
|
||||
483.0,
|
||||
55.200000000000045,
|
||||
0.0,
|
||||
99.72409090909093,
|
||||
120.0,
|
||||
106.80230977350088,
|
||||
60.001301478240954,
|
||||
124.41545454545451,
|
||||
120.0,
|
||||
180.0,
|
||||
0.0,
|
||||
0.0,
|
||||
94.68272727272722,
|
||||
@@ -507,18 +507,18 @@
|
||||
75.86045454545456,
|
||||
66.66681818181814,
|
||||
0.0,
|
||||
109.0704545454546,
|
||||
109.96227272727276,
|
||||
47.952272727272714,
|
||||
11.094576460746453,
|
||||
38.31300706523831,
|
||||
161.86847814969906,
|
||||
21.973794868109557,
|
||||
524.1227174992155,
|
||||
0.6414151879949013,
|
||||
11.096451076844168,
|
||||
40.181939277841224,
|
||||
44.91187685729284,
|
||||
109.07302361034766,
|
||||
116.99491129525349,
|
||||
95.61900944932736,
|
||||
54.18759955738153,
|
||||
86.6234264543665,
|
||||
171.42744837680007,
|
||||
116.9350623689079,
|
||||
383.6100412738411,
|
||||
0.03390853445803674,
|
||||
2.900768349489402,
|
||||
16.700320743972142,
|
||||
81.2380484620021,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -527,34 +527,34 @@
|
||||
],
|
||||
"akku_soc_pro_stunde": [
|
||||
80.0,
|
||||
79.4714617768595,
|
||||
79.4714617768595,
|
||||
96.13812844352617,
|
||||
96.13812844352617,
|
||||
99.4714617768595,
|
||||
99.4714617768595,
|
||||
99.4714617768595,
|
||||
99.4714617768595,
|
||||
99.4714617768595,
|
||||
99.4714617768595,
|
||||
96.32360537190083,
|
||||
99.65693870523415,
|
||||
95.72968319559227,
|
||||
99.0630165289256,
|
||||
99.0630165289256,
|
||||
99.0630165289256,
|
||||
96.07429407713497,
|
||||
93.45381370523414,
|
||||
91.05922865013771,
|
||||
88.95484676308537,
|
||||
88.95484676308537,
|
||||
85.51196625344349,
|
||||
82.04093491735533,
|
||||
80.52728994490354,
|
||||
80.83547262436872,
|
||||
81.89972282062534,
|
||||
85.29619913880036,
|
||||
85.44103562502363,
|
||||
79.16421888032488,
|
||||
79.16421888032488,
|
||||
95.83088554699157,
|
||||
95.83088554699157,
|
||||
98.47420098817949,
|
||||
99.9292697701786,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
96.82533215994886,
|
||||
98.49203497878888,
|
||||
94.56477946914701,
|
||||
99.564779469147,
|
||||
99.564779469147,
|
||||
99.564779469147,
|
||||
96.57605701735636,
|
||||
93.95557664545554,
|
||||
91.56099159035911,
|
||||
89.45660970330677,
|
||||
89.45660970330677,
|
||||
86.01371946235848,
|
||||
82.51604934381585,
|
||||
80.82184855044719,
|
||||
82.32705964926333,
|
||||
84.7332659396624,
|
||||
89.12319984732603,
|
||||
89.34416552017109,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
|
||||
+112
-112
@@ -204,22 +204,22 @@
|
||||
"Last_Wh_pro_Stunde": [
|
||||
16541.07,
|
||||
8929.91,
|
||||
8875.56,
|
||||
8375.540038207693,
|
||||
6376.03,
|
||||
5096.67,
|
||||
12853.82,
|
||||
8960.220000000001,
|
||||
13969.78,
|
||||
13936.309375923789,
|
||||
1129.12,
|
||||
1178.71,
|
||||
1050.98,
|
||||
988.56,
|
||||
1912.38,
|
||||
1412.38,
|
||||
704.61,
|
||||
516.37,
|
||||
868.05,
|
||||
694.34,
|
||||
2108.79,
|
||||
2608.79,
|
||||
556.31,
|
||||
488.89,
|
||||
506.91,
|
||||
@@ -286,7 +286,12 @@
|
||||
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,
|
||||
@@ -305,26 +310,21 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.12279079241545988,
|
||||
0.25743585772309185,
|
||||
0.1455575560489687,
|
||||
0.07702723513376727,
|
||||
0.024046008596276005,
|
||||
0.19904591069188757,
|
||||
0.2577866264025879,
|
||||
0.15146384621553569,
|
||||
0.09798107754792196,
|
||||
0.05435777788576338,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Gesamt_Verluste": 6842.366945701403,
|
||||
"Gesamtbilanz_Euro": 14.10088133917546,
|
||||
"Gesamteinnahmen_Euro": 0.6914079499175572,
|
||||
"Gesamtkosten_Euro": 14.792289289093018,
|
||||
"Gesamt_Verluste": 7095.270423117038,
|
||||
"Gesamtbilanz_Euro": 14.155149367775268,
|
||||
"Gesamteinnahmen_Euro": 0.847204411694738,
|
||||
"Gesamtkosten_Euro": 15.002353779470006,
|
||||
"Home_appliance_wh_per_hour": [
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -367,80 +367,80 @@
|
||||
],
|
||||
"Kosten_Euro_pro_Stunde": [
|
||||
3.55926012,
|
||||
1.7445291920000001,
|
||||
1.6260140259999998,
|
||||
0.979774486,
|
||||
1.7445413792641737,
|
||||
1.5214016280281666,
|
||||
0.9799189133780641,
|
||||
0.0,
|
||||
0.5881238999999999,
|
||||
1.0968767320000004,
|
||||
0.6146086578009714,
|
||||
1.120219902993293,
|
||||
2.4860631399999997,
|
||||
0.06267084962493971,
|
||||
0.05480703000000003,
|
||||
0.05258762370598476,
|
||||
0.1614775630079859,
|
||||
0.0,
|
||||
0.291163892,
|
||||
0.558606198,
|
||||
0.29116746986009023,
|
||||
0.41255619800000004,
|
||||
0.0,
|
||||
0.174739608,
|
||||
0.28801899,
|
||||
0.0,
|
||||
0.692315757,
|
||||
0.856465757,
|
||||
0.0,
|
||||
0.0,
|
||||
0.16677339,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.008254784724581705,
|
||||
0.028650916976410694,
|
||||
0.07362195915902499,
|
||||
0.060401289430882174,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.04620342776708689,
|
||||
0.08357424731947552,
|
||||
0.0,
|
||||
0.174597189,
|
||||
0.19011028252189552,
|
||||
0.0,
|
||||
0.0,
|
||||
0.16484566
|
||||
],
|
||||
"Netzbezug_Wh_pro_Stunde": [
|
||||
15610.789999999999,
|
||||
7886.66,
|
||||
7768.82,
|
||||
5214.34,
|
||||
7886.7150961309835,
|
||||
7268.9996561307535,
|
||||
5215.108639585227,
|
||||
0.0,
|
||||
2934.75,
|
||||
4990.340000000001,
|
||||
3066.9094700647274,
|
||||
5096.541869851197,
|
||||
10951.82,
|
||||
209.11194402715952,
|
||||
171.54000000000008,
|
||||
175.4675465665157,
|
||||
505.40708296709204,
|
||||
0.0,
|
||||
980.68,
|
||||
1912.38,
|
||||
980.6920507244535,
|
||||
1412.38,
|
||||
0.0,
|
||||
516.37,
|
||||
868.05,
|
||||
0.0,
|
||||
2108.79,
|
||||
2608.79,
|
||||
0.0,
|
||||
0.0,
|
||||
506.91,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
36.20519616044607,
|
||||
129.52494112301397,
|
||||
322.9033296448464,
|
||||
273.0618871197205,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
154.1655914817714,
|
||||
278.85968408233407,
|
||||
0.0,
|
||||
566.69,
|
||||
617.0408390843736,
|
||||
0.0,
|
||||
0.0,
|
||||
592.97
|
||||
@@ -450,7 +450,12 @@
|
||||
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,
|
||||
@@ -469,16 +474,11 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1754.1541773637127,
|
||||
3677.655110329884,
|
||||
2079.39365784241,
|
||||
1100.3890733395326,
|
||||
343.51440851822866,
|
||||
2843.513009884108,
|
||||
3682.6660914655417,
|
||||
2163.76923165051,
|
||||
1399.729679256028,
|
||||
776.5396840823341,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -487,40 +487,40 @@
|
||||
],
|
||||
"Verluste_Pro_Stunde": [
|
||||
1152.0,
|
||||
414.0,
|
||||
465.0,
|
||||
276.0,
|
||||
207.00000000001197,
|
||||
1083.0,
|
||||
276.0,
|
||||
1014.0,
|
||||
72.5805667167408,
|
||||
414.0066115357181,
|
||||
405.0215587356905,
|
||||
276.0922367502273,
|
||||
333.15830172751845,
|
||||
1098.8591364077674,
|
||||
288.74422438214356,
|
||||
1013.9999999999997,
|
||||
53.21482102827082,
|
||||
0.0,
|
||||
99.72409090909093,
|
||||
0.0,
|
||||
120.0,
|
||||
106.80230977350088,
|
||||
0.0014460869344160802,
|
||||
60.0,
|
||||
96.08318181818186,
|
||||
0.0,
|
||||
0.0,
|
||||
94.68272727272722,
|
||||
180.0,
|
||||
240.0,
|
||||
75.86045454545456,
|
||||
66.66681818181814,
|
||||
0.0,
|
||||
109.0704545454546,
|
||||
109.96227272727276,
|
||||
47.952272727272714,
|
||||
11.094576460746453,
|
||||
38.31300706523831,
|
||||
161.86847814969906,
|
||||
21.973794868109557,
|
||||
327.79989871635826,
|
||||
0.6414151879949013,
|
||||
11.096451076844168,
|
||||
40.181939277841224,
|
||||
0.0,
|
||||
35.132727272727266,
|
||||
109.07302361034766,
|
||||
116.99491129525349,
|
||||
95.61900944932736,
|
||||
54.18759955738153,
|
||||
86.6234264543665,
|
||||
171.42744837680007,
|
||||
116.9350623689079,
|
||||
197.07683881390702,
|
||||
0.03390853445803674,
|
||||
2.900768349489402,
|
||||
16.700320743972142,
|
||||
0.0,
|
||||
111.78035844493081,
|
||||
6.04210069012484,
|
||||
134.59227272727276,
|
||||
100.08954545454549,
|
||||
0.0
|
||||
@@ -528,42 +528,42 @@
|
||||
"akku_soc_pro_stunde": [
|
||||
80.0,
|
||||
96.66666666666667,
|
||||
96.66666666666667,
|
||||
96.66685032043661,
|
||||
98.33411584087247,
|
||||
98.33667797282322,
|
||||
100.0,
|
||||
81.50113762748849,
|
||||
81.85514386032581,
|
||||
98.52181052699248,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
81.06060606060606,
|
||||
81.06060606060606,
|
||||
97.72727272727273,
|
||||
99.74339958051553,
|
||||
99.74339958051553,
|
||||
96.59554317555684,
|
||||
96.59554317555684,
|
||||
99.92887650889017,
|
||||
96.89594778988192,
|
||||
96.89594778988192,
|
||||
96.89594778988192,
|
||||
93.90722533809128,
|
||||
98.90722533809128,
|
||||
96.51264028299485,
|
||||
94.40825839594251,
|
||||
94.40825839594251,
|
||||
90.96537788630063,
|
||||
87.49434655021247,
|
||||
85.9807015777607,
|
||||
86.28888425722586,
|
||||
87.35313445348248,
|
||||
90.7496107716575,
|
||||
90.89444725788077,
|
||||
96.82533215994886,
|
||||
96.82537232903037,
|
||||
98.49203899569704,
|
||||
95.45911027668879,
|
||||
95.45911027668879,
|
||||
95.45911027668879,
|
||||
92.47038782489815,
|
||||
99.13705449156481,
|
||||
96.7424694364684,
|
||||
94.63808754941604,
|
||||
94.63808754941604,
|
||||
91.19519730846775,
|
||||
87.69752718992511,
|
||||
86.00332639655647,
|
||||
87.50853749537262,
|
||||
89.91474378577168,
|
||||
94.30467769343532,
|
||||
94.52564336628036,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
100.0,
|
||||
98.89101239669421,
|
||||
98.89101239669421,
|
||||
94.64251893939394,
|
||||
91.48312672176309
|
||||
98.60068046043587,
|
||||
98.76851659071711,
|
||||
94.52002313341684,
|
||||
91.360630915786
|
||||
],
|
||||
"Electricity_price": [
|
||||
0.000228,
|
||||
|
||||
+313
-272
@@ -111,8 +111,8 @@
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
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,
|
||||
0,
|
||||
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
|
||||
],
|
||||
"battery_grid_export_allowed": [],
|
||||
"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.375,
|
||||
0.375,
|
||||
0.875,
|
||||
1.0,
|
||||
0.5,
|
||||
0.625,
|
||||
1.0,
|
||||
0.75,
|
||||
0.5,
|
||||
0.875,
|
||||
0.625,
|
||||
0.375,
|
||||
0.5,
|
||||
0.625,
|
||||
0.625,
|
||||
0.5,
|
||||
0.625,
|
||||
0.625,
|
||||
0.0,
|
||||
0.625,
|
||||
0.5,
|
||||
0.5,
|
||||
1.0,
|
||||
0.375,
|
||||
0.625,
|
||||
0.875,
|
||||
0.5,
|
||||
0.5,
|
||||
0.75,
|
||||
0.75,
|
||||
0.75,
|
||||
0.375,
|
||||
0.1,
|
||||
0.0,
|
||||
0.875,
|
||||
0.75,
|
||||
1.0,
|
||||
1.0,
|
||||
0.75,
|
||||
0.625,
|
||||
0.875,
|
||||
0.5,
|
||||
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,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"result": {
|
||||
"Last_Wh_pro_Stunde": [
|
||||
1053.07,
|
||||
11551.91,
|
||||
6564.5599999999995,
|
||||
7687.03,
|
||||
14151.67,
|
||||
11542.82,
|
||||
6460.22,
|
||||
7658.78,
|
||||
4986.07,
|
||||
4996.91,
|
||||
12997.56,
|
||||
14120.029999999999,
|
||||
10340.67,
|
||||
7731.82,
|
||||
5149.22,
|
||||
5036.78,
|
||||
5062.12,
|
||||
1178.71,
|
||||
2227.51,
|
||||
1050.98,
|
||||
988.56,
|
||||
912.38,
|
||||
@@ -242,43 +243,43 @@
|
||||
],
|
||||
"EAuto_SoC_pro_Stunde": [
|
||||
5.0,
|
||||
5.0,
|
||||
22.48,
|
||||
31.22,
|
||||
42.144999999999996,
|
||||
59.62499999999999,
|
||||
72.735,
|
||||
81.475,
|
||||
92.4,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955,
|
||||
98.955
|
||||
11.555,
|
||||
18.11,
|
||||
33.405,
|
||||
50.885000000000005,
|
||||
66.18,
|
||||
77.105,
|
||||
83.66,
|
||||
90.215,
|
||||
96.77,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518,
|
||||
98.518
|
||||
],
|
||||
"Einnahmen_Euro_pro_Stunde": [
|
||||
0.0,
|
||||
@@ -320,13 +321,11 @@
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Gesamt_Verluste": 7647.623819992857,
|
||||
"Gesamtbilanz_Euro": 7.824605715847156,
|
||||
"Gesamt_Verluste": 8404.44594732788,
|
||||
"Gesamtbilanz_Euro": 7.836546975121494,
|
||||
"Gesamteinnahmen_Euro": 0.0,
|
||||
"Gesamtkosten_Euro": 7.824605715847156,
|
||||
"Gesamtkosten_Euro": 7.836546975121494,
|
||||
"Home_appliance_wh_per_hour": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
2500.0,
|
||||
@@ -362,23 +361,19 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Kosten_Euro_pro_Stunde": [
|
||||
0.027996119999999992,
|
||||
1.351235592,
|
||||
1.1423217259999998,
|
||||
1.226111386,
|
||||
1.4948178300000001,
|
||||
1.2071595,
|
||||
0.9248695241652183,
|
||||
0.8749092776800845,
|
||||
1.5678286259999998,
|
||||
2.4348720859999995,
|
||||
0.8528744919675278,
|
||||
0.5282751491259897,
|
||||
0.0,
|
||||
1.0534661399999998,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.19588158,
|
||||
0.4965507655670841,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -387,10 +382,6 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.01995551999999987,
|
||||
0.08545095,
|
||||
0.007989913613567745,
|
||||
0.012219458233588571,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -398,6 +389,16 @@
|
||||
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,
|
||||
@@ -405,20 +406,14 @@
|
||||
0.0
|
||||
],
|
||||
"Netzbezug_Wh_pro_Stunde": [
|
||||
122.78999999999996,
|
||||
6108.66,
|
||||
5457.82,
|
||||
6525.34,
|
||||
8132.85,
|
||||
6023.75,
|
||||
4056.445281426396,
|
||||
3955.2860654615033,
|
||||
7490.82,
|
||||
12958.339999999998,
|
||||
4640.231185895146,
|
||||
2636.103538552843,
|
||||
0.0,
|
||||
4640.82,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
704.61,
|
||||
2187.4483064629258,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -427,10 +422,6 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
65.59999999999957,
|
||||
351.65,
|
||||
35.04348076126204,
|
||||
55.24167375040041,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -438,6 +429,16 @@
|
||||
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,
|
||||
@@ -485,20 +486,20 @@
|
||||
0.0
|
||||
],
|
||||
"Verluste_Pro_Stunde": [
|
||||
0.0,
|
||||
1152.0,
|
||||
276.0,
|
||||
345.0,
|
||||
207.07863377116755,
|
||||
207.1951278553804,
|
||||
1083.0,
|
||||
552.0,
|
||||
414.0,
|
||||
615.5918181818183,
|
||||
345.0,
|
||||
632.3249999999998,
|
||||
23.391818181818195,
|
||||
99.72409090909093,
|
||||
133.72909090909081,
|
||||
521.2057423074175,
|
||||
395.8024246263412,
|
||||
458.55183787620945,
|
||||
227.23539677555112,
|
||||
640.3849261442235,
|
||||
253.88850337853552,
|
||||
106.80230977350088,
|
||||
133.7321802766326,
|
||||
124.41545454545451,
|
||||
0.0,
|
||||
96.08318181818186,
|
||||
70.41409090909087,
|
||||
118.37045454545455,
|
||||
94.68272727272722,
|
||||
@@ -506,63 +507,63 @@
|
||||
75.86045454545456,
|
||||
66.66681818181814,
|
||||
69.12409090909085,
|
||||
109.0704545454546,
|
||||
101.01681818181828,
|
||||
0.0,
|
||||
11.233982308648535,
|
||||
48.41330768174522,
|
||||
161.62968357967037,
|
||||
21.962728535423857,
|
||||
538.2984000000038,
|
||||
441.95211196761403,
|
||||
260.56941082122324,
|
||||
171.99990368477063,
|
||||
62.214291413756285,
|
||||
35.132727272727266,
|
||||
77.27590909090907,
|
||||
109.07302361034766,
|
||||
116.99491129525349,
|
||||
31.990721833371907,
|
||||
73.82223834331725,
|
||||
123.8591383343284,
|
||||
171.42744837680007,
|
||||
116.9350623689079,
|
||||
538.2984000000001,
|
||||
441.9538395103232,
|
||||
261.1952696860876,
|
||||
184.66788225469543,
|
||||
131.21108264656203,
|
||||
111.78035844493081,
|
||||
90.18403329253945,
|
||||
134.59227272727276,
|
||||
100.08954545454549,
|
||||
80.85954545454547
|
||||
],
|
||||
"akku_soc_pro_stunde": [
|
||||
80.0,
|
||||
80.0,
|
||||
61.06060606060606,
|
||||
61.06060606060606,
|
||||
61.06060606060606,
|
||||
61.06060606060606,
|
||||
61.06060606060606,
|
||||
50.341167355371894,
|
||||
50.341167355371894,
|
||||
36.91550447658402,
|
||||
36.17712637741047,
|
||||
33.0292699724518,
|
||||
28.808023415977967,
|
||||
24.88076790633609,
|
||||
24.88076790633609,
|
||||
22.658100895316807,
|
||||
18.921659779614323,
|
||||
15.932937327823693,
|
||||
13.312456955922865,
|
||||
10.917871900826448,
|
||||
8.813490013774107,
|
||||
6.63154269972452,
|
||||
3.1886621900826473,
|
||||
0.0,
|
||||
0.0,
|
||||
0.3120550641291261,
|
||||
1.07020564583707,
|
||||
4.578139530578446,
|
||||
4.728141236897871,
|
||||
19.68087457022947,
|
||||
31.94341995234899,
|
||||
38.90006700591099,
|
||||
42.674787887051245,
|
||||
43.17025540478875,
|
||||
42.06126780148296,
|
||||
39.622002994320425,
|
||||
35.373509537020155,
|
||||
32.214117319389295
|
||||
80.00218427142131,
|
||||
80.00760448962633,
|
||||
61.06821055023239,
|
||||
61.06821055023239,
|
||||
62.12948116988288,
|
||||
63.5406596317257,
|
||||
58.120614791285504,
|
||||
58.682709146161926,
|
||||
45.22651624410048,
|
||||
39.85140827675753,
|
||||
36.67674043670639,
|
||||
32.45548217808278,
|
||||
28.528226668440908,
|
||||
25.495297949432643,
|
||||
23.27263093841336,
|
||||
19.53618982271088,
|
||||
16.54746737092025,
|
||||
13.926986999019423,
|
||||
11.532401943923004,
|
||||
9.428020056870663,
|
||||
7.2460727428210765,
|
||||
3.8031825018727887,
|
||||
0.30551238333016156,
|
||||
0.6197802647075666,
|
||||
1.5052110988161542,
|
||||
2.736047696034607,
|
||||
7.125981603698244,
|
||||
7.346947276543289,
|
||||
22.29968060987662,
|
||||
34.57523424809509,
|
||||
41.83065840604197,
|
||||
46.49642400356206,
|
||||
47.884563842022054,
|
||||
46.48524430245792,
|
||||
43.99708508544073,
|
||||
39.74859162814045,
|
||||
36.5891994105096
|
||||
],
|
||||
"Electricity_price": [
|
||||
0.000228,
|
||||
@@ -603,6 +604,46 @@
|
||||
0.0002969,
|
||||
0.0002921,
|
||||
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": {
|
||||
@@ -619,16 +660,16 @@
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.375,
|
||||
0.375,
|
||||
0.875,
|
||||
1.0,
|
||||
0.5,
|
||||
0.625,
|
||||
1.0,
|
||||
0.75,
|
||||
0.5,
|
||||
0.875,
|
||||
0.625,
|
||||
0.375,
|
||||
0.0,
|
||||
0.375,
|
||||
0.375,
|
||||
0.1,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -712,26 +753,26 @@
|
||||
"capacity_wh": 60000,
|
||||
"charging_efficiency": 0.95,
|
||||
"max_charge_power_w": 11040,
|
||||
"soc_wh": 59373.0,
|
||||
"soc_wh": 59110.8,
|
||||
"initial_soc_percentage": 5
|
||||
},
|
||||
"start_solution": [
|
||||
2.0,
|
||||
1.0,
|
||||
2.0,
|
||||
0.0,
|
||||
2.0,
|
||||
0.0,
|
||||
1.0,
|
||||
2.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0,
|
||||
2.0,
|
||||
2.0,
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
0.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,
|
||||
0.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,
|
||||
5.0,
|
||||
4.0,
|
||||
2.0,
|
||||
6.0,
|
||||
3.0,
|
||||
3.0,
|
||||
5.0,
|
||||
3.0,
|
||||
5.0,
|
||||
5.0,
|
||||
0.0,
|
||||
6.0,
|
||||
2.0,
|
||||
3.0,
|
||||
6.0,
|
||||
4.0,
|
||||
2.0,
|
||||
3.0,
|
||||
1.0,
|
||||
2.0,
|
||||
3.0,
|
||||
3.0,
|
||||
2.0,
|
||||
3.0,
|
||||
3.0,
|
||||
0.0,
|
||||
3.0,
|
||||
2.0,
|
||||
2.0,
|
||||
6.0,
|
||||
1.0,
|
||||
3.0,
|
||||
5.0,
|
||||
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,
|
||||
4.0,
|
||||
4.0,
|
||||
6.0,
|
||||
5.0,
|
||||
2.0,
|
||||
5.0,
|
||||
4.0,
|
||||
0.0,
|
||||
4.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
5.0,
|
||||
4.0,
|
||||
6.0,
|
||||
6.0,
|
||||
4.0,
|
||||
0.0,
|
||||
0.0,
|
||||
2.0,
|
||||
3.0,
|
||||
5.0,
|
||||
4.0,
|
||||
4.0,
|
||||
2.0,
|
||||
5.0,
|
||||
14.0
|
||||
3.0,
|
||||
12.0
|
||||
],
|
||||
"washingstart": 14
|
||||
"washingstart": 12
|
||||
}
|
||||
Reference in New Issue
Block a user