Skip to content

Planner: load management-aware multi-loadpoint planning - #31902

Draft
andig wants to merge 13 commits into
masterfrom
feat/loadmanagement-aware-planning
Draft

Planner: load management-aware multi-loadpoint planning#31902
andig wants to merge 13 commits into
masterfrom
feat/loadmanagement-aware-planning

Conversation

@andig

@andig andig commented Jul 18, 2026

Copy link
Copy Markdown
Member

Problem

Planning is per-loadpoint and circuit-blind: each loadpoint independently picks the globally cheapest slots to meet its own target (core/loadpoint_plan.go, core/planner/planner.go), unaware of the shared circuit budget or the other loadpoints. Circuit enforcement (Loadpoint.setLimitcircuit.ValidatePower/ValidateCurrent) is reactive and order-greedy: the first loadpoint in site iteration order grabs budget, later ones get the remainder. So two loadpoints that both planned the same cheap slot get clamped to half power, under-deliver, and miss their deadline — and minSoc forced charging consumes budget another loadpoint's plan assumed it had, tripping load management (over current detected).

Plan (phased TODOs)

  • Phase 0 — reproduce (2 LPs on one circuit + minSoc, same cheap slot) + observe (log/metric when a plan-active/minSoc LP is clamped).
  • Phase 1 — deterministic priority-aware circuit clamp (forced > plan-active > priority) instead of iteration order.
  • Phase 2 (core fix) — per-circuit/per-slot residual-capacity ledger; extend planner.Plan/optimalPlan with a per-slot power cap; site reserves forced load first, then plans loadpoints in priority order against the ledger.
  • Phase 3 — joint optimisation via the optimizer service, only if greedy proves insufficient.

Open decisions (defaults in the doc)

  1. Priority source — reuse loadpoint priority: yes
  2. minSoc vs cost — forced load reserves first: yes since it is happening anyway
  3. Scope — power-only first, phases later: yes, scoped to power constraints
  4. Limits — static first, dynamic later: yes

Note that this somewhat doubles what the optimizer already does. It may still be helpful until actionable optimizer lands.

🤖 Generated with Claude Code

Tracking doc for making tariff planning respect shared circuit capacity
across multiple loadpoints, so per-loadpoint plans plus minSoc forced
charging can no longer overcommit a circuit. Phased TODO checklist, no
behaviour change.
@andig andig added the enhancement New feature or request label Jul 18, 2026
andig added 4 commits July 18, 2026 12:51
Phase 0 observability for loadmanagement-aware planning: per-loadpoint
plans ignore shared circuit capacity, so a planned or minSoc charge can be
silently capped by the circuit and miss its target. Surface this as a
warning in setLimit and cover it with a test.
Phase 0 observability, corrected: warn in the planner (not the loadpoint)
and only on an actual goal miss - the required duration no longer fits
before the target, so charging will overrun. Warned once per target to
avoid per-cycle spam. Replaces the earlier loadpoint-side throttle warning,
which fired on any transient circuit cap even when the goal was still met.
requiredDuration already clamps to the circuit's total max power via
EffectiveMaxPower; that total-clamp is acceptable. The gap is shared,
per-slot capacity: each loadpoint assumes it owns the whole circuit.
Also correct the enforcement model to sequential lagging feedback.
Parallel planned sessions under load management must each be granted a whole
effectiveMinPower chunk (charger runs >= min or off). At most
floor(circuitBudget/effectiveMinPower) sessions fit a slot; the rest shift.
Same semi-continuous rule as evcc-io/optimizer#91.
@andig andig changed the title Planner: loadmanagement-aware planning (design + TODOs) Planner: load management-aware multi-loadpoint planning Jul 18, 2026
andig added 8 commits July 18, 2026 13:14
Under a shared circuit the reactive clamp grants budget in update order, so
process fast/deadline-bound then higher-priority loadpoints first - they
establish draw and the clamp throttles lower-priority ones, deterministic
instead of config order. Foundation for loadmanagement-aware planning.
Reordering the round-robin update sequence does not help: updates are also
event-driven and the circuit clamp reacts to measured power, so steady-state
allocation is order-independent. Priority belongs in planner allocation
(Phase 2), not the control loop. Round-robin sequence restored.
Tracks residual charging power per time slot on a shared circuit:
Available/Reserve plus CanHost, the semi-continuous min-power gate (a
charger runs >= min or off). Foundation for priority-ordered, circuit-aware
planning. Single circuit + static budget for now; hierarchy TODO.
Generalise optimalPlan into planCapped: with no cap it is the old full-power
duration accounting (optimalPlan delegates, behaviour unchanged); with a
per-slot availability a slot delivers min(maxPower, avail) and is skipped
below minPower (semi-continuous), so a power-limited slot counts less and the
plan spills into more slots. Not yet wired into Plan - needs the ledger.
AllocateShared ties CapacityLedger + planCapped together: orders loadpoints
forced-first then priority-desc, plans each against the residual budget with
the semi-continuous min-power gate, and reserves its actual per-slot draw so
later loadpoints see the remainder. Tests cover joint feasibility, slot
sharing, and forced-beats-priority. Live site wiring still pending.
Without clamping, planCapped could pick globally-cheapest slots after the
deadline. Add now and clamp per request; test that no slot is scheduled past
the target.
computeSharedPlans groups planning loadpoints by circuit each cycle, calls
AllocateShared, and stores each plan on the loadpoint; GetPlan returns it
when set. Only power-limited circuits with 2+ contending loadpoints are
allocated - single/no-circuit, precondition, continuous and no-tariff keep
the independent path, and the reactive clamp stays the safety backstop.
The earlier revert (a42eeef) removed the test and doc but its site.go
change was never committed, so the reorder stayed live. Remove the helper
and restore the plain round-robin sequence; priority belongs in planner
allocation, not the control loop.
@github-actions github-actions Bot added the stale Outdated and ready to close label Jul 25, 2026
@andig andig added backlog Things to do later and removed enhancement New feature or request stale Outdated and ready to close labels Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backlog Things to do later

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant