No description
Find a file
Michiel Berger fc90e65271 refactor: drop PV synthesis, model raw P1 net signal directly
The simulator used to reconstruct "gross household demand" by adding
back a synthesized PV trace (irradiance × kWp peak-match) to the P1 net
meter, then re-subtract a different synthesized PV per scenario. That
reconstruction was leaky — Michiel's horizontal pyranometer is at a
different location and orientation than dad's SE-facing array, so the
synthesis can't reproduce dad's actual production curve. Result: 511
hours of negative "gross demand" and phantom export inflation up to
~6 kW peak in scenarios where pv_kwp ≠ 3.

New shape: simulator works on a single signed signal, raw_demand_kw
(the P1 reading as recorded). No solar synthesis. Whatever the meter
shows is the input.

Concretely:
  - sim.py: drop synthesize_pv, reconstruct_gross_demand,
    schedule_with_planning_pv, no_foresight_schedule, groundhog_schedule,
    _oracle_daily_schedule_legacy. Rename column convention demand_kwh →
    raw_demand_kw. Plug-in discharge cap becomes max(0, raw_demand_kw).
  - web.py: drop pv_kwp/pv_yield/strategy form params. Demand slider
    now applies as an *additive* baseline shift (not multiplicative —
    multiplying scaled the export bursts too, which is wrong since dad's
    PV stays the same regardless of household consumption). Default
    demand_kwh = 2325 (dad's actual full-year net per his quote;
    extrapolated 8-month window comes out to ~1515, partial coverage).
    Saturation metric now measures (surplus ≥ pc_max), not (charge ≥
    pc_max) — the latter conflated arbitrage top-off with power-bottleneck.
  - templates/index.html: drop PV input, drop strategy radios, drop
    irradiance chart. Modal charts collapsed from 4 to 3: price, net
    meter (toggles between with/without battery), SoC.
  - app.js: mirror the above, drop pv_kwp/strategy plumbing.
  - tests: rebase fixtures on raw_demand_kw, drop synthesize_pv test.
  - scripts: drop --pv-kwp/--pv-yield flags throughout, switch column
    references to raw_demand_kw.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 21:32:59 +02:00
data/raw chore(catalog): add Zendure SolarFlow 2400AC 5.8 kWh plug-in set 2026-05-08 21:32:39 +02:00
docs Initial import: home-battery ROI simulator + cracked thuisbatterijgids calc 2026-04-30 13:46:27 +02:00
scripts refactor: drop PV synthesis, model raw P1 net signal directly 2026-05-08 21:32:59 +02:00
src/pluginbattery refactor: drop PV synthesis, model raw P1 net signal directly 2026-05-08 21:32:59 +02:00
tests refactor: drop PV synthesis, model raw P1 net signal directly 2026-05-08 21:32:59 +02:00
.env.example Initial import: home-battery ROI simulator + cracked thuisbatterijgids calc 2026-04-30 13:46:27 +02:00
.gitignore Move custom batteries from web.py into data/raw/extra_batteries.json 2026-04-30 15:52:16 +02:00
CLAUDE.md Initial import: home-battery ROI simulator + cracked thuisbatterijgids calc 2026-04-30 13:46:27 +02:00
Dockerfile Dockerfile: gunicorn 2 workers x 4 threads (gthread) 2026-04-30 17:39:13 +02:00
Procfile Initial import: home-battery ROI simulator + cracked thuisbatterijgids calc 2026-04-30 13:46:27 +02:00
pyproject.toml Initial import: home-battery ROI simulator + cracked thuisbatterijgids calc 2026-04-30 13:46:27 +02:00
README.md Initial import: home-battery ROI simulator + cracked thuisbatterijgids calc 2026-04-30 13:46:27 +02:00
uv.lock Initial import: home-battery ROI simulator + cracked thuisbatterijgids calc 2026-04-30 13:46:27 +02:00

pluginbattery

Backtest the actual ROI of a home battery against real historical electricity prices and solar irradiance.

Answers: "If I had owned this battery (plug-in or installed) over the last N years, what would it have actually saved me?"

See CLAUDE.md for project intent, open decisions, and assumptions.