Skip to content

feat: multi-peak priceCurveAlgo + Peak.dynamic.minEndOfPeakSoe (v2.1.1)#63

Merged
damonrand merged 1 commit into
mainfrom
feature/support-axle-flex
May 8, 2026
Merged

feat: multi-peak priceCurveAlgo + Peak.dynamic.minEndOfPeakSoe (v2.1.1)#63
damonrand merged 1 commit into
mainfrom
feature/support-axle-flex

Conversation

@damonrand
Copy link
Copy Markdown
Contributor

Summary

Two backward-compatible additions to priceCurveAlgo. Both default to legacy behaviour, so existing YAMLs are unaffected.

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.

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).

Test plan

  • PYTHONPATH=src python -m unittest discover --start-directory src — 34 tests pass
  • Existing single-peak YAMLs continue to load and run unchanged
  • skypro --version reports 2.1.1

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).
@damonrand damonrand merged commit 8fedd79 into main May 8, 2026
4 checks passed
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