Skip to content

Value the state of charge at the end of the scheduling horizon#2310

Open
Flix6x wants to merge 5 commits into
mainfrom
feat/soc-end-value
Open

Value the state of charge at the end of the scheduling horizon#2310
Flix6x wants to merge 5 commits into
mainfrom
feat/soc-end-value

Conversation

@Flix6x

@Flix6x Flix6x commented Jul 13, 2026

Copy link
Copy Markdown
Member

Summary

Adds a per-device soc-value-at-end flex-model field for storage devices: a marginal value (fixed quantity or sensor reference, e.g. "60 EUR/MWh" or {"sensor": 123}) assigned to energy left in storage at the end of the scheduling horizon.

Without it, the scheduler sees no benefit in ending the horizon with a non-zero state of charge, which leads to myopic behaviour: batteries and thermal buffers tend towards their minimum SoC at the end of the planning window.

Implementation

  • Modern port of the device_future_reward tech spike on the feature/planning/relaxed-scheduler branch (commit "feature: final SoC has tiny value"), now expressed as a StockCommitment priced only in the final time slot (negative up/down deviation prices around a zero-SoC baseline give a linear reward on the final SoC).
  • Field lives in the flex-model (per device) rather than the flex-context, because the appropriate value differs per device — e.g. a battery's charging efficiency (~1) vs. a heat pump's COP means the same stored MWh has different replacement value. A sensor reference lets an external system (e.g. a CEM or simulation runner) set the value dynamically.
  • Added to both StorageFlexModelSchema and DBStorageFlexModelSchema, plus metadata/OpenAPI docs and the scheduling docs table.

Tests

New parametrized solver test on the day-2 price profile (expensive/cheap/expensive): with no or tiny soc-value-at-end the battery still sells out at the end; with a value above the highest price it ends the horizon full.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LQFkeGxDwErydUV5ZmHmki

Flix6x and others added 2 commits July 13, 2026 23:31
Add a per-device soc-value-at-end flex-model field (fixed quantity or
sensor reference) that assigns a marginal value to energy left in
storage at the end of the planning window, implemented as a
StockCommitment priced only in the final time slot. This counters
myopic depletion of storage devices towards the end of the horizon.

Modern port of the device_future_reward tech spike from the
feature/planning/relaxed-scheduler branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQFkeGxDwErydUV5ZmHmki
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQFkeGxDwErydUV5ZmHmki
@nhoening

Copy link
Copy Markdown
Member

a battery's charging efficiency (~1) vs. a heat pump's COP means the same stored MWh has different replacement value.

but this is all information we know from the flex-model, so we can imagine pricing electricity per site - which would be easier.

Introduce a PriceField subclass of VariableQuantityField marking monetary
fields. The flex-context's currency check now selects fields by type
instead of by name suffix, and the storage scheduler validates, upon
deserialization, that flex-model price fields (soc-value-at-end) use a
currency convertible to the flex-context's shared_currency_unit, so unit
conversion cannot fail later inside a queued scheduling job.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQFkeGxDwErydUV5ZmHmki
@Flix6x

Flix6x commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

Added cross-validation of price units between flex-model and flex-context (815c7de):

  • New PriceField subclass of VariableQuantityField marks monetary fields.
  • FlexContextSchema's currency check now selects fields by isinstance(field, PriceField) instead of the *price name-suffix convention (same ten fields as before).
  • On deserialize_flex_config, the storage scheduler validates each flex-model PriceField (currently soc-value-at-end, incl. sensor references via their unit) against the flex-context's shared_currency_unit, raising a marshmallow ValidationError with the usual example-unit hint — so a wrong currency or a mis-referenced sensor fails at trigger time rather than inside the queued job.
  • Test: soc-value-at-end in USD against an EUR flex-context is rejected with the field name in the error.

Mark CommitmentSchema's up-price/down-price as PriceFields and extend
the flex-context currency validation to the nested commitments, so
commitment prices in a different currency are rejected at trigger time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQFkeGxDwErydUV5ZmHmki
@Flix6x

Flix6x commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

a battery's charging efficiency (~1) vs. a heat pump's COP means the same stored MWh has different replacement value.

but this is all information we know from the flex-model, so we can imagine pricing electricity per site - which would be easier.

You are correct. But it is less flexible and I need to experiment with this for a project first. Not meant for merging soon. But I will definitely take your comment into account. I am also taking into account the option of moving this valuation entirely into FlexMeasures, to by default not entirely discount the future.

Regardless, I think having some prices in flex-models may be inescapable, so I factored out the refactoring that enables flex-model price fields in #2311 (and adds validation and test coverage for prices in custom commitments).

Signed-off-by: F.N. Claessen <felix@seita.nl>
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.

2 participants