Planner: grouped planning, cost tolerant#24289
Conversation
|
@andig adapted planner for small slot duration |
revert shorter implementation
|
|
||
| firstSlot := plan[0] | ||
| slotDuration := firstSlot.End.Sub(firstSlot.Start) | ||
| assert.True(t, slotDuration <= time.Hour, "first slot should be at most 60min") |
There was a problem hiding this comment.
Das ergibt keinen Sinn. Bei Stundenslots ist das immer der Fall.
| assert.Equal(t, simplePlan, plan, "expected simple plan") | ||
| } | ||
|
|
||
| func TestPrecondition(t *testing.T) { |
There was a problem hiding this comment.
Funktionieren diese Tests auch mit dem alten Planner? Falls ja wäre es schön die alten Tests stehen zu lassen und nur weitere hinzu zu fügen. Haben die Änderungen mit Slot Bundling überhaupt etwas zu tun?
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
habe die alten Testfälle wiederhergestellt, es brauchte zwei Korrekturen:
- bezüglich der Verschiebung in einem Zeitfenster (m.E. vernachlässigbar, daher angepasst)
- Berücksichtigung von slotDuration statt time.Hour, die im erweiterten Testfall nötig wird
Durch die Aufteilung kann die neue Logik gegen die alten Tests geprüft werden, und die Neuen sind klar getrennt.
| } | ||
|
|
||
| // TestPreconditionLimiting verifies that precondition is limited to required duration | ||
| func TestPreconditionLimiting(t *testing.T) { |
| // Applied as percentage of average cost - fragmentation only occurs if it saves more than this | ||
| // | ||
| // Special values: | ||
| // - 0.00: No penalty, pure cost optimization (maximum fragmentation, not recommended) |
There was a problem hiding this comment.
This is the current behavior. All existing tests should pass?
There was a problem hiding this comment.
yes, they do - checking against the new behaviour needs adaption as it's currently taking a prepared test scenario to validate the penality. Needs double checking.
| // MaxChargingWindows limits the number of separate charging windows | ||
| // This protects hardware (contactors, battery management) from excessive switching cycles | ||
| // Recommended: 2-4 windows for most use cases | ||
| MaxChargingWindows = 3 |
There was a problem hiding this comment.
Number of windows only makes sense relative to total length of session. Why shouldn't we interrupt a 10h session 10 times if it helps?
There was a problem hiding this comment.
if you interrupt it 10 times, it might be because of the prices up-down in quarters. Yes, you have 10h session length but if you interrupt it 2 times per hour at beginning of the session. Wanted?
There was a problem hiding this comment.
10 1-hour windows found be fine for me. 10 15min windows wouldn't be. But may that's subjective. Still, 3 gaps for a 45min plan out of 1:15 total time (i.e. on-off-on-off-on) is not a good plan.
There was a problem hiding this comment.
really difficult, either adapt the percentage or a maximum. I agree setting 3 is not optimal but may be kind of sweet-spot. I like the idea of a slider for the percentage, using this makes maximum slots obsolete.
It was to ensure pauses of short slots aren't causing trouble for the hardware as they might happen in extreme situations.
We could skip the constant or go for a formula instead (duration / windows count > x) for security. Let's discuss.
|
|
||
| // Costs are essentially equal - prefer longer duration for hardware protection | ||
| // This reduces fragmentation when costs are identical | ||
| if a.duration > b.duration { |
There was a problem hiding this comment.
Isn't this condition already part of the cost comparison if gaps have penalties?
| var bestCandidate *planCandidate | ||
|
|
||
| // Strategy 1: Try single continuous window first (best for hardware) | ||
| for i := range windows { |
There was a problem hiding this comment.
Is this needed? 1 windows seems like a special case of N windows.
| } | ||
|
|
||
| if overlaps { | ||
| continue |
There was a problem hiding this comment.
Overlap heisst ja nicht, dass es nicht dennoch Zeit zu Anfang oder Ende hinzu fügen könnte- ist das korrekt?
requires early end / late start adaption only
|
|
||
| // generateChargingWindows creates all possible continuous charging windows | ||
| // AND individual slots as single-slot windows | ||
| func (t *Planner) generateChargingWindows(slots api.Rates) []chargingWindow { |
There was a problem hiding this comment.
Bei 2 Tagen Forcast sind das wieviele Kombinationen (48h*4)?
|
Can I add if during solar hours it's not really start/stopping usually will be ramp up (slot) and ramp down (back to solar) so no hardware wear and tear. (Also in Australia on amber so forecasts are almost meaningless and should be taken as a super rough guide so it's very often evcc will just start the plan now because the current price slot is cheaper than the forecast-ed prices) |
|
Make "min. Abstand" a "Continuity"/"Kontinuität"? Not a fixed value but a cost parameter? |
|
Not self explaining, yet? It's not really related to any cost function. It just defines how many slots between elements of plan are allowed to kept free (minimum) |
|
It‘s irrelevant how long the gap is when you have many gaps. I think we want low number of gaps, ie. Continuity. |
This comment has been minimized.
This comment has been minimized.
|
superseeded by #24423 |



Choose time windows instead of single slots, cost-optimized. Avoid short charge durations to protect devices in case of short slot durations. The optimization uses a constant (InterruptionPenaltyPercent) to define when it's valid to fragment charging windows to reduce total costs. Other optimizations are also possible. It respects preconditioning and actually sorts windows by:
It defines also new tests as the logic for choosen slots changed a lot.
Additionally I've added some statistics to clarify usage of "penalty" and added a selector to limit fragmented charging by a maximum charging windows contraint.
It has been created using Claude.
TODO:
Screenshot:
