Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9dd779a
docs: new section describing commitments
Flix6x Dec 4, 2025
13e9308
docs: rewrite the overview in commitments section
Flix6x Dec 4, 2025
1179198
docs: keep ems variables, but explain them as representing the site c…
Flix6x Dec 4, 2025
1b10360
docs: cross-reference the commitments section and the linear problem …
Flix6x Dec 4, 2025
0d25b25
Merge remote-tracking branch 'refs/remotes/origin/main' into docs/com…
Flix6x Dec 8, 2025
6f6e52b
fix: cross-references
Flix6x Dec 8, 2025
3599379
docs: add commitments section to index
Flix6x Dec 8, 2025
ce1533d
Merge remote-tracking branch 'refs/remotes/origin/main' into docs/com…
Flix6x Jan 23, 2026
98eb7e3
docs: changelog entry
Flix6x Feb 13, 2026
8668486
docs: update commitments section after review
Flix6x Feb 13, 2026
39b250a
docs: remove developer example
Flix6x Feb 13, 2026
4138e5f
docs: move grouping section
Flix6x Feb 13, 2026
82d8348
docs: fix typo
Flix6x Feb 13, 2026
fdd3781
docs: fix code-block formatting
Flix6x Feb 13, 2026
e52ec49
docs: add note about UI support
Flix6x Feb 13, 2026
7879314
docs: fix example json
Flix6x Feb 13, 2026
bdfbf0f
docs: put value first for better legibility without horizontal scrolling
Flix6x Feb 13, 2026
37d610b
docs: use info icon for admonition (_static/css/custom_css has custom…
Flix6x Feb 13, 2026
bb0690a
Merge branch 'main' into docs/commitments-section
nhoening Feb 16, 2026
21aff2f
improve intro and a few other sentences
nhoening Feb 16, 2026
5d71b20
two more clarifications in intro and in a note about how to edit comm…
nhoening Feb 16, 2026
248c439
Merge branch 'main' into docs/commitments-section
nhoening Feb 16, 2026
6d820b2
small change to headings
nhoening Feb 16, 2026
7f9eae2
Merge branch 'docs/commitments-section' of github.com:FlexMeasures/fl…
nhoening Feb 16, 2026
3ef8461
slight improvement of the grouping discussion
nhoening Feb 18, 2026
37d44d4
Merge branch 'main' into docs/commitments-section
nhoening Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .vscode/spellright.dict
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,5 @@ async
href
setpoints
organisation
modelled
1 change: 1 addition & 0 deletions documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ New features
* Allow testing out the scheduling CLI without saving anything, using ``flexmeasures add schedule --dry-run`` [see `PR #1892 <https://www.github.com/FlexMeasures/flexmeasures/pull/1892>`_]
* Allow unsupported ``flex-context`` or ``flex-model`` fields to be shown in the UI editors (they will be un-editable) [see `PR #1915 <https://www.github.com/FlexMeasures/flexmeasures/pull/1915>`_]
* Add back save buttons to both ``flex-context`` and ``flex-model`` UI editors [see `PR #1916 <https://www.github.com/FlexMeasures/flexmeasures/pull/1916>`_]
* Add a documentation section on the concept of ``Commitments`` [see `PR #1849 <https://www.github.com/FlexMeasures/flexmeasures/pull/1849>`_]


Infrastructure / Support
Expand Down
229 changes: 229 additions & 0 deletions documentation/concepts/commitments.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
.. _commitments:

Commitments
===========

Commitments are a key concept in FlexMeasures' flexibility modeling. With commitments, you can express economic preferences and pre-existing market positions to the scheduler in a flexible way.
You can expand beyond only using one dynamic tariff signal, for example to also including peak prices and minimum fill rates. Or you can model passive imbalance.

Commitments are used in the background without you having to worry about them explicitly (you can use "syntactic sugar" in the ``flex-context`` to enjoy them).
However, they are also exposed as a powerful advanced tool to model your custom preferences or contracts.

This document explains what commitments are on a technical level, and then gives examples of how the scheduler uses them and how you can create your own commitments in the flex-context to great effect.

.. contents::
:local:
:depth: 1


What is a commitment?
---------------------

A **Commitment** is the economic abstraction FlexMeasures uses to express market positions and soft constraints (preferences) inside the scheduler.
They are a powerful modeling concept used in current flex-context fields, but can also model new circumstances.

.. admonition:: Examples of commitments
:class: info-icon

.. list-table::
:widths: 55 45
:header-rows: 1

* - **Market positions**
- **Soft constraints**
* - - dynamic electricity tariffs
- contracted (consumption|production) capacity
- peak pricing
- passive imbalance
- PPAs
- gas contracts
- - Desired fill levels
- Preferred power levels or idle states
- Preference to advance charging and postpone discharging
- Preference to postpone curtailment
- CO₂ intensity

Commitments are converted to linear objective terms; all non-negotiable operational limits are modelled separately as Pyomo constraints.

A commitment describes:

- a **baseline quantity** over time (the contracted or preferred position), and
- marginal prices for **upwards** and **downwards deviations** from that baseline.

Per default, a commitment is applied per time slot and across all devices on the site,
but it is possible to apply commitments across other groups of schedule values (more on that below).

The scheduler converts all provided commitments into terms in the optimization
objective function so that the solver *minimizes the total deviation cost*
across the schedule horizon. Absolute physical limitations (for example generator or
line capacities) are *not* modelled as commitments — those are enforced as
Pyomo constraints.


Key properties
--------------

Each Commitment has the following important attributes (high level):

- ``name`` — a logical string identifier (e.g. ``"energy"``, ``"production peak"``).
- ``device`` — optional: restricts the commitment to a single device; otherwise
it is an EMS/site-level commitment.
- ``index`` — the DatetimeIndex (time grid) on which the series are defined.
- ``quantity`` — the baseline Series (per slot or per group).
- ``upwards_deviation_price`` — Series defining marginal cost/reward for upward deviations.
- ``downwards_deviation_price`` — Series defining marginal cost/reward for downward deviations.
- ``_type`` — grouping indicator: ``'each'`` or ``'any'`` (defaults to ``'each'``; see more info below on grouping).


Sign convention (flows vs stocks)
---------------------------------

- **Flow commitments** (e.g. power/energy flows):

- A *positive* baseline quantity denotes **consumption**.

- Actual > baseline → *upwards* deviation (more consumption).
- Actual < baseline → *downwards* deviation (less consumption).
- A *negative* baseline quantity denotes **production** (feed-in).

- Actual less negative (i.e. closer to zero) → *upwards* deviation (less production).
- Actual more negative → *downwards* deviation (more production).

- **Stock commitments** (e.g. state of charge for storage):

- ``quantity`` is the target stock level; deviations above/below that target
are priced via the upwards/downwards price series, respectively.


How FlexMeasures uses commitments in the scheduler
--------------------------------------------------

Soft vs hard constraints
^^^^^^^^^^^^^^^^^^^^^^^^^

Commitments in FlexMeasures are **soft** by design: they represent economic
penalties or rewards that the optimizer considers when building schedules.
Hard operational constraints (such as physical power limits or strict device
interlocks) are expressed separately as Pyomo constraints in the scheduling
model. If a “hard” behaviour is required from a commitment, assign very large
penalty prices, but we prefer modelling non-negotiable limits as Pyomo constraints.

Grouping across time and devices
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

It is possible to define what group of schedule values is expected to not deviate.

The ``'device'`` attribute already lets us reduce from the values for all devices to just one.
The ``' _type'``attribute offers more powerful grouping options.
For now, this extra grouping can happen across different definitions of time slots, soon also per groups of devices.

- ``_type == 'each'``: penalise deviations per time slot (this is the default for time series).
- ``_type == 'any'``: treat the whole commitment horizon as one group (useful
for peak-style penalties where only the maximum over the window should be
counted).

.. note::

Near-term feature: support for **grouping over devices** is planned and
will be documented here. When enabled, grouping over devices lets you express
soft constraints that aggregate deviations across a set of devices,
for example, an intermediate capacity constraint from a feeder shared by a group of devices (via **flow commitments**), or multiple power-to-heat devices that feed a shared thermal buffer (via **stock commitments**).


How flex-context fields are converted into commitments
--------------------------------------------------------

Users may supply preferences and price fields in the ``flex-context``. The
scheduler then translates the relevant fields into one or more `Commitment` objects
before calling the optimizer.

Typical translations include:

- tariffs (``consumption-price``, ``production-price``) → an ``"energy"`` FlowCommitment with zero baseline so net consumption/production is priced;
- peak/excess limits (``site-peak-production``, ``site-peak-production-price``, etc.) → dedicated peak FlowCommitment(s);
- storage-related fields (``soc-minima``, ``soc-minima-breach-price``, etc.) → StockCommitment(s).


Let us look at some concrete examples.
The examples below map the most common `flex-context` semantics to the
commitments the scheduler constructs.

1. **Energy (tariff)**

- *Fields used*: ``consumption-price``, ``production-price``.
- *Commitment*: Flow commitment named ``"energy"`` with zero baseline and
the two price series as upwards/downwards deviation prices.

2. **Peak consumption**

- *Fields used*: ``site-peak-consumption`` (baseline) and ``site-peak-consumption-price`` (upwards-deviation price); the downwards price is set to ``0``.
- *Commitment*: Flow commitment named ``"consumption peak"``; positive baseline
values denote the prior consumption peak associated with sunk costs, and the upwards price penalises going beyond that baseline.

3. **Peak production / peak feed-in**

- *Fields used*: ``site-peak-production`` (baseline) and ``site-peak-production-price`` (downwards-deviation price); the upwards price is set to ``0``.
- *Commitment*: Flow commitment named ``"production peak"``; negative baseline
values denote the prior production peak associated with sunk costs, and the downwards price penalises going beyond that baseline.

4. **Consumption capacity**

- *Fields used*: ``site-consumption-capacity`` (baseline), and ``site-consumption-breach-price`` (upwards-deviation price); the downwards price is set to ``0``.
- *Commitment*: Flow commitment named ``"consumption breach"``; positive baseline
values denote the allowed consumption limit and the upwards price penalises going
beyond that limit.

5. **Production capacity**

- *Fields used*: ``site-production-capacity`` (baseline) and ``site-production-breach-price`` (downwards-deviation price); the upwards price is set to ``0``.
- *Commitment*: Flow commitment named ``"production breach"``; negative baseline
values denote the allowed production limit and the downwards price penalises going
beyond that limit.

6. **SOC minima / maxima (storage preferences)**

- *Fields used*: ``soc-minima``, ``soc-minima-breach-price``, ``soc-maxima`` and ``soc-maxima-breach-price``.
- *Commitment*: StockCommitment(s) that price deviations below minima or
above maxima. Hard storage capacities are set through ``soc-min`` and ``soc-max`` instead and are modelled as Pyomo constraints.

7. **Power bands per device**

- *Fields used*: ``consumption-capacity`` and ``production-capacity`` (baselines), ``consumption-breach-price`` (upwards-deviation price, with 0 downwards) and ``production-breach-price`` (downwards-deviation price, with 0 upwards).
- *Commitment*: FlowCommitment with either baseline and corresponding prices.


How you can use commitments for custom purposes: an example
------------------------------------------------------------

Suppose a site is asked to stay under a 500 kW maximum import capacity from 4 to 9 PM, and exceeding this triggers a penalty.
Then you could add this to the ``flex-context`` field of the site asset:

.. code-block:: json

{
"commitments": [
{
"name": "congestion pricing",
"baseline": [
{"value": "500 kW", "start": "2026-02-01T16:00:00+01:00", "end": "2026-02-01T21:00:00+01:00"}
],
"up-price": "250 EUR/MW",
"down-price": "0 EUR/MW",
}
]
}


The scheduler then takes into account that exceeding 500 kW consumption during the congested period will lead to additional costs of 0.25 EUR for every kW it goes over the limit.

.. note::

The ``"commitments"`` field in the ``flex-context`` of an asset is not yet supported to be edited in the flex-context editor in the UI.
Once it is, you will be able to use fixed quantities or sensors (for the baseline, up-price and down-price) to store custom commitments in the database.
Passing the ``"commitments"`` field in the API call for schedule triggering is already supported (and probably preferrable for one-off commitments like the example above).
And you can also use the FlexMeasures-Client to edit the ``flex-context`` on the asset level.

Advanced: mathematical formulation
----------------------------------

For a compact formulation of how commitments enter the optimization problem, see :ref:`storage_device_scheduler`.
15 changes: 8 additions & 7 deletions documentation/concepts/device_scheduler.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ Storage device scheduler: Linear model

Introduction
--------------
This generic storage device scheduler is able to handle an EMS with multiple devices, with various types of constraints on the EMS level and on the device level,
and with multiple market commitments on the EMS level.
This generic storage device scheduler is able to handle a site with multiple devices, with various types of constraints on the site level and on the device level,
and with multiple market commitments on the site level.

A typical example is a house with many devices. The commitments are assumed to be with regard to the flow of energy to the device (positive for consumption, negative for production). In practice, this generic scheduler is used in the **StorageScheduler** to schedule a storage device.

The solver minimizes the costs of deviating from the commitments.
For a more detailed explanation of commitments in FlexMeasures, see :ref:`commitments`.



Expand Down Expand Up @@ -45,9 +46,9 @@ Symbol Variable in the Code
:math:`\epsilon(d,j)` efficiencies Stock energy losses.
:math:`P_{max}(d,j)` device_derivative_max Maximum flow of device :math:`d` during time period :math:`j`.
:math:`P_{min}(d,j)` device_derivative_min Minimum flow of device :math:`d` during time period :math:`j`.
:math:`P^{ems}_{min}(j)` ems_derivative_min Minimum flow of the EMS during time period :math:`j`.
:math:`P^{ems}_{max}(j)` ems_derivative_max Maximum flow of the EMS during time period :math:`j`.
:math:`Commitment(c,j)` commitment_quantity Commitment c (at EMS level) over time step :math:`j`.
:math:`P^{ems}_{min}(j)` ems_derivative_min Minimum flow of the site's grid connection point during time period :math:`j`.
:math:`P^{ems}_{max}(j)` ems_derivative_max Maximum flow of the site's grid connection point during time period :math:`j`.
:math:`Commitment(c,j)` commitment_quantity Commitment c (at site level) over time step :math:`j`.
:math:`M` M Large constant number, upper bound of :math:`Power_{up}(d,j)` and :math:`|Power_{down}(d,j)|`.
:math:`D(d,j)` stock_delta Explicit energy gain or loss of device :math:`d` during time period :math:`j`.
================================ ================================================ ==============================================================================================================
Expand All @@ -58,8 +59,8 @@ Variables
================================ ================================================ ==============================================================================================================
Symbol Variable in the Code Description
================================ ================================================ ==============================================================================================================
:math:`\Delta_{up}(c,j)` commitment_upwards_deviation Upwards deviation from the power commitment :math:`c` of the EMS during time period :math:`j`.
:math:`\Delta_{down}(c,j)` commitment_downwards_deviation Downwards deviation from the power commitment :math:`c` of the EMS during time period :math:`j`.
:math:`\Delta_{up}(c,j)` commitment_upwards_deviation Upwards deviation from the power commitment :math:`c` of the site during time period :math:`j`.
:math:`\Delta_{down}(c,j)` commitment_downwards_deviation Downwards deviation from the power commitment :math:`c` of the site during time period :math:`j`.
:math:`\Delta Stock(d,j)` n/a Change of stock of device :math:`d` at the end of time period :math:`j`.
:math:`P_{up}(d,j)` device_power_up Upwards power of device :math:`d` during time period :math:`j`.
:math:`P_{down}(d,j)` device_power_down Downwards power of device :math:`d` during time period :math:`j`.
Expand Down
1 change: 1 addition & 0 deletions documentation/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ In :ref:`getting_started`, we have some helpful tips how to dive into this docum
concepts/flexibility
concepts/data-model
concepts/security_auth
concepts/commitments
concepts/device_scheduler


Expand Down