10 KiB
Real-life scenario review: EV energy, home battery, and card plausibility
This document reviews where EV energy comes from, how it is counted, and whether the persona/product card calculations are consistent and plausible.
1. Where does the "missing" energy for the EV come from?
There is no missing energy in the model. The optimizer is built so that charge energy = drive energy every day.
- Drive energy (per day):
distance_km × (consumption_kwh_per_100km / 100). This is the EV’s actual need. - Charge energy (per day): The optimizer allocates exactly this amount into the cheapest hours in the 00–06 UTC window (at max EV power per hour until the day’s need is covered).
So over the week:
- Total EV need =
total_drive_energy_kwh(from the weekly EV plan). - Total EV charging = sum of
daily_summaries[].energy_charge_kwh= same value.
The source of that energy in the “with solar” view is split into:
-
Solar (via home battery or direct)
- Modelled as:
solar_for_ev = min(expected_solar_kwh_per_day, energy_charge_kwh_per_day)per day. - Stored as
weekly_solar_for_ev_kwh(e.g. 73.9 kWh in your example).
- Modelled as:
-
Grid
- The rest:
grid_energy = energy_charge_kwh - solar_for_evper day. - Only this part is paid at grid price; that cost is aggregated into
weekly_cost_with_solar_eur.
- The rest:
So:
- “Missing” in the sense “EV need not covered by solar” = the grid share. It is fully included in the energy balance and in the cost.
- “Missing” in the sense “energy not accounted for” = there is none; charge = drive, and the split solar vs grid is explicit.
Relevant code: backend/app/weekly_optimizer.py (solar coupling loop, ~266–288): solar_for_ev, grid_energy, cost_grid, and weekly_cost_with_solar_eur.
2. Is the EV energy included in “energy usage”?
Yes. The EV’s energy usage is the drive energy (and equivalently the charge energy).
- Optimised (no solar): All of that is assumed from the grid; cost =
weekly_cost_optimized_eur(cheapest hours in 00–06). - With solar: Part is solar (
weekly_solar_for_ev_kwh), part is grid; only the grid part is charged, givingweekly_cost_with_solar_eur.
So:
- Energy usage (kWh) = total charge (= total drive) — no double-counting.
- Cost = grid part only when solar is considered; that grid part is exactly what is used to compute “effectieve kosten met zon”.
3. Home battery table: context and plausibility
The table “Thuisbatterijstatus (SoC)” is produced by the home battery SoC trajectory in the weekly optimizer.
Model (daily, per day):
- SoC start = previous day’s SoC end (or 50% on day 1).
- Solar in = solar production of the day, capped by battery headroom
(1 − SoC_start) × capacity, then efficiency applied for SoC. - SoC after solar = SoC after that charge step.
- Demand from battery =
household_daily_kwh + daily_solar_for_ev_kwh[day]
So: household consumption + the share of EV charging that is attributed to solar. - Energy out = min(demand, available energy in battery), then efficiency applied for SoC.
- SoC end = SoC after discharge.
So:
- Energie in (kWh) = solar that actually goes into the battery (capped by headroom), in kWh.
- Energie uit (kWh) = energy delivered from the battery to household + EV (solar share).
Interpretation:
“Zon laadt de batterij; huishouden en EV (zondeel) onttrekken eraan” is correct: solar charges the battery; household and the solar share of EV draw from it. The rest of the EV charging is from the grid and does not go through the home battery in this model.
Plausibility of your numbers:
- SoC often ending at 0%: possible when
household_daily_kwh + ev_solar_shareis large relative to what’s in the battery after solar (e.g. 15 kWh capacity, ~14–15 kWh out on some days). - Day 1: SoC start 50% → after solar 97% → 14.62 kWh out → 0% end: consistent with high demand and full discharge.
- The fact that “Energie in” and “Energie uit” are on the same order as capacity and daily demand is plausible.
Relevant code: backend/app/weekly_optimizer.py (~332–371): home battery trajectory, demand_kwh = hh_kwh + ev_solar_day, energy_in_kwh, energy_out_kwh.
4. Card context: calculations and plausibility
The “Product templates & ruwe terugverdientijd” block uses:
- Weekly figures:
weekly_cost_baseline_eur,weekly_cost_optimized_eur,weekly_cost_with_solar_eurfrom the optimized plan. - Annual (EV only):
annualBaselineCost = weekly_cost_baseline_eur × 52annualOptimizedCost = weekly_cost_optimized_eur × 52annualCostWithSolar = weekly_cost_with_solar_eur × 52
- Savings:
annualSavings = (weekly_cost_baseline_eur - weekly_cost_with_solar_eur) × 52
(Label: “Geschatte jaarlijkse besparing (EV-laden)”.) - Payback:
paybackYears = totalCapexEnergyStack / annualSavings
withtotalCapexEnergyStack = solar CAPEX + home battery CAPEX(EV CAPEX is intentionally excluded).
Check against your numbers:
| Item | Your value | Formula / check |
|---|---|---|
| Baseline week | 14.90 € | — |
| Geoptimaliseerde week | 13.86 € | — |
| Effectieve kosten met zon | 6.75 € | — |
| Jaarlijkse EV baseline | 775 € | 14.90 × 52 ≈ 775 ✓ |
| Jaarlijkse EV geoptimaliseerd | 721 € | 13.86 × 52 ≈ 721 ✓ |
| Jaarlijkse EV met zon | 351 € | 6.75 × 52 = 351 ✓ |
| Jaarlijkse besparing (EV) | 424 € | (14.90 − 6.75) × 52 ≈ 424 ✓ |
| Terugverdientijd | 43.7 yr | 18 500 / 424 ≈ 43.6 ✓ |
| Huishoudelijk verbruik | ~5100 kWh | — |
| Huishoudelijke kosten (baseline) | 528 € | baseline €/kWh × 5100 ✓ (€/kWh from EV baseline) |
| Totaal baseline | 1303 € | 775 + 528 = 1303 ✓ |
| Totaal geoptimaliseerd + zon | 879 € | 351 + 528 = 879 ✓ |
So the arithmetic is consistent with the code.
Design choices that affect interpretation:
-
Household cost is not reduced by solar in the card
“Jaarlijkse energiekosten huishouden + EV (geoptimaliseerd + zon)” uses the same household cost as baseline (528 €). Only the EV cost is reduced (351 €). So the 424 € savings are EV-only (baseline → optimised + solar). That is why payback is so long: CAPEX (solar + battery) is divided only by EV-related savings. -
Savings mix optimisation + solar
“Geschatte jaarlijkse besparing (EV-laden)” = baseline − (optimised + solar), so it includes both:- gain from time-of-use (00–06, cheapest hours), and
- gain from solar covering part of EV charging.
So it is not “solar-only” savings. If you wanted a “solar-only” figure, you would use(weekly_cost_optimized_eur - weekly_cost_with_solar_eur) × 52.
-
Payback is conservative
Because only EV savings are used and household consumption is still valued at baseline price, the 43.7 years is a conservative (pessimistic) estimate. If household self-consumption from solar/battery were included in savings, payback would be shorter.
Relevant code: frontend/src/UseCaseView.tsx (~313–322 payback, ~325–351 annual costs and combined totals).
5. Summary
| Question / topic | Answer |
|---|---|
| Where does “missing” EV energy come from? | There is no missing energy. The part not supplied by solar comes from the grid and is fully counted in weekly_cost_with_solar_eur. |
| Is EV energy in “energy usage”? | Yes. EV usage = drive energy = charge energy; cost is grid share when solar is applied. |
| Home battery table correct? | Yes. “Energie in” = solar into battery; “Energie uit” = household + EV solar share; logic and numbers are plausible. |
| Card calculations correct? | Yes. Weekly → annual × 52, combined totals, and payback formula match the code and your numbers. |
| Card plausibility / caveats | Payback is conservative (EV savings only; household cost unchanged with solar). Savings label is “EV-laden” and combines optimisation + solar. |
5b. Average buy and sell price used in the models
Single price source: All cost and revenue calculations use day-ahead (spot) electricity prices from the same series (Utilitarian Spot / ENTSO-E), stored in electricity_prices as value_eur_mwh per timestamp.
Average buy price (grid import): Used for EV baseline, EV optimized cost per hour, home battery baseline cost, and household cost. The weekly optimizer builds a 24-hour pattern over the last 7 days (configurable, max 30) for the zone. For each UTC hour it averages that hour's prices; the overall average is the mean of those 24 hourly averages: weekly_avg_price_eur_mwh (e.g. 103.46 €/MWh) or weekly_avg_price_eur_kwh.
Sell price: No separate sell/export price. Solar revenue uses the same day-ahead price (value = kwh × price). So buy price = sell price = day-ahead spot. Code: app/price_patterns.py, app/weekly_optimizer.py, app/spot_prices.py, app/trading.py.
6. UI clarity. The real-life simulation UI now includes: (1) a note under the optimised plan that charging covers the full drive need and effective cost with solar is grid-only cost (usecase.optimisedPlanEnergyNote); (2) the existing home battery intro (solar charges battery; household + EV solar share draw); (3) a note that payback uses EV savings only and household stays at baseline (usecase.productTemplatesPaybackNote).
If you want to change behaviour, possible next steps are: (1) add household self-consumption savings to the card and payback, or (2) show “solar-only” EV savings separately from “optimisation-only” savings.