Skip to content

feat: add /optimize/level-schedule endpoint for solar-first strategy#98

Draft
1va13n7in wants to merge 4 commits into
evcc-io:mainfrom
1va13n7in:feature/solar-first
Draft

feat: add /optimize/level-schedule endpoint for solar-first strategy#98
1va13n7in wants to merge 4 commits into
evcc-io:mainfrom
1va13n7in:feature/solar-first

Conversation

@1va13n7in

Copy link
Copy Markdown
Contributor

Add /optimize/level-schedule endpoint for solar-first strategy.

Finds the optimal flat export ceiling that charges a battery from solar
surplus while minimizing peak grid export. Response uses the same format
as /optimize/charge-schedule for identical downstream processing.

Depends on #97 (charge_profile).

Part of evcc-io/evcc#31963

Lars Valentin added 2 commits July 22, 2026 08:02
Add CC-CV taper modeling to the optimizer. Batteries can now specify
a charge_profile (e.g. lifepo4_conservative) that limits charge power
as a function of SoC, matching real-world BMS behavior.

The MILP solver uses iterative constraint tightening: solve, extract
SoC trajectory, compute per-slot P_max(SoC) from the profile, add
upper bound constraints, re-solve until convergence (max 5 iterations).

New API fields in BatteryConfig:
- charge_profile: named profile (e.g. lifepo4_conservative)
- charge_knee: SoC% where taper begins (override)
- charge_k: exponential decay constant (override)
- charge_c_rate_float: minimum C-rate at float (override)

New module: charging_profiles.py with profile definitions and
max_charge_power() calculation.
Lars Valentin added 2 commits July 22, 2026 14:17
Replace the iterative constraint tightening (3-6 re-solves) with
piecewise-linear upper bounds added at model build time. The CC-CV
taper curve is convex, so tangent lines at 4 breakpoints (knee to
100% SoC) provide valid linear constraints on charge power as a
function of s[i][t-1]. One solve, no stale bounds, bounded wall clock.
Add a new optimization endpoint that finds the optimal flat export
ceiling for charging a battery from solar surplus. Instead of
minimizing cost (MILP), this maximizes self-use by flattening the
grid export curve.

The algorithm finds the export level where all active charging slots
reduce the forecasted feed-in power by the same amount. Charge power
per slot is variable and respects the CC-CV taper profile.

The response uses the same format as /optimize/charge-schedule
(batteries[].charging_power/discharging_power/state_of_charge),
so consumers can process both endpoints with identical code.

New endpoint: POST /optimize/level-schedule
New function: find_optimal_level() in charging_profiles.py
New function: _level_to_milp_format() in app.py
@1va13n7in
1va13n7in force-pushed the feature/solar-first branch from ddf87de to ac5f77e Compare July 22, 2026 15:19
@1va13n7in
1va13n7in marked this pull request as ready for review July 22, 2026 16:32
@andig
andig marked this pull request as draft July 23, 2026 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant