feat: multi-peak priceCurveAlgo + Peak.dynamic.minEndOfPeakSoe (v2.1.1)#63
Merged
Conversation
Two backward-compatible additions to priceCurveAlgo. Both default to legacy behaviour, so existing YAMLs are unaffected. 1. peaks: [...] — multi-peak support PriceCurveAlgo accepts a `peaks` list alongside the legacy single `peak`. The two are mutually exclusive (validated in __post_init__); resolve_peaks() returns a list view that works for either form, including the no-peak case. get_peak_power and get_peak_approach_energies now take List[Peak]. Active-peak resolution: scan list for the peak whose period contains t; raise ValueError if more than one matches (overlap = config error). Approach-energy resolution: pick the next-upcoming peak today; if no peak left today, return zero. 2. dynamic.minEndOfPeakSoe — reserve SoE for post-peak niv-chase New optional float on PeakDynamic, default 0.0. In get_peak_power the time-to-empty calc uses dischargeable_soe = max(0, soe - min_end_of_peak_soe) instead of full soe. This creates slack in the peak window so the dynamic HOLD-on-LONG branch can actually fire instead of falling through to forced full discharge in every HH. The prioritise_residual_load=true reserve-energy calc also switched to dischargeable_soe, so the residual-load reserve sits inside the dischargeable budget. Tests: 14 -> 34 unit/integration tests pass. Existing single-peak integrationTestPriceCurve summary unchanged within tolerance — backward-compat verified at every test run. New integrationTestPriceCurveMultiPeak end-to-end fixture exercises weekday morning + evening windows. 5 unit tests for _find_active_peak/_find_next_peak (overlap detection, day-class filtering, transitions). 5 unit tests for minEndOfPeakSoe (default-zero legacy, slack-creates-hold-on-long, short-still-discharges, late-in-peak-forces-full, min-above-soe-clamps-to-zero-dischargeable).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two backward-compatible additions to
priceCurveAlgo. Both default to legacy behaviour, so existing YAMLs are unaffected.peaks: [...]— multi-peak supportPriceCurveAlgoaccepts apeakslist alongside the legacy singlepeak. The two are mutually exclusive (validated in__post_init__);resolve_peaks()returns a list view that works for either form, including the no-peak case.get_peak_powerandget_peak_approach_energiesnow takeList[Peak]. Active-peak resolution: scan list for the peak whose period containst; raiseValueErrorif more than one matches (overlap = config error). Approach-energy resolution: pick the next-upcoming peak today; if no peak left today, return zero.dynamic.minEndOfPeakSoe— reserve SoE for post-peak niv-chaseNew optional float on
PeakDynamic, default0.0. Inget_peak_powerthe time-to-empty calc usesdischargeable_soe = max(0, soe - min_end_of_peak_soe)instead of fullsoe. This creates slack in the peak window so the dynamic HOLD-on-LONG branch can actually fire instead of falling through to forced full discharge in every HH.The
prioritise_residual_load=truereserve-energy calc also switched todischargeable_soe, so the residual-load reserve sits inside the dischargeable budget.Tests
integrationTestPriceCurvesummary unchanged within tolerance — backward-compat verified at every test run.integrationTestPriceCurveMultiPeakend-to-end fixture exercises weekday morning + evening windows._find_active_peak/_find_next_peak(overlap detection, day-class filtering, transitions).minEndOfPeakSoe(default-zero legacy, slack-creates-hold-on-long, short-still-discharges, late-in-peak-forces-full, min-above-soe-clamps-to-zero-dischargeable).Test plan
PYTHONPATH=src python -m unittest discover --start-directory src— 34 tests passskypro --versionreports 2.1.1