From 9dd779ad2b4a2c80473ec4cdfa364555bd5f58c5 Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Thu, 4 Dec 2025 11:27:27 +0100 Subject: [PATCH 01/20] docs: new section describing commitments Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 169 +++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 documentation/concepts/commitments.rst diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst new file mode 100644 index 0000000000..5521f6ac1d --- /dev/null +++ b/documentation/concepts/commitments.rst @@ -0,0 +1,169 @@ +Commitments +=========== + +Overview +-------- + +A **Commitment** is the central economic abstraction used by FlexMeasures to +express *soft constraints, preferences and market positions* in the scheduler. + +A commitment describes: + +- a **baseline quantity** over time (the target or assumed position), and +- marginal prices for **upwards** and **downwards deviations** from that baseline. + +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 limits (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'`` (see Grouping below). + +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. + +Soft vs hard semantics +---------------------- + +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 prefer modelling non-negotiable limits as Pyomo constraints. + +Converting flex-context fields into commitments +----------------------------------------------- + +Users may supply preferences and price fields in the ``flex-context``. The +scheduler 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). + +A short example +--------------- + +Below is a compact example showing how the scheduler conceptually creates an +``"energy"`` flow commitment from a (per-slot) tariff: + +.. code-block:: python + + from pandas import Series, date_range + from flexmeasures.data.models.planning import FlowCommitment + + index = date_range(start="2025-01-01 00:00", periods=24, freq="H") + # zero baseline → the asset may consume or produce; deviations are priced. + baseline = Series(0.0, index=index) + + # consumption and production tariffs (per kWh) + consumption_price = Series(0.20, index=index) # 0.20 EUR/kWh for consumption + production_price = Series(-0.05, index=index) # -0.05 EUR/kWh reward for production + + energy_commitment = FlowCommitment( + name="energy", + index=index, + quantity=baseline, + upwards_deviation_price=consumption_price, + downwards_deviation_price=production_price, + _type="each" + ) + +The scheduler sets up such commitments (site-level and device-level) and, together with any prior commitments, hands them to the linear optimizer. + +Examples (commitments commonly derived from flex-context) +-------------------------------------------------------- + +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. + +Grouping across time and devices +-------------------------------- + +- ``_type == 'each'``: penalise deviations per time slot (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 + 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**). From 13e93086fd3c0b928ce6d3fcc6e3c764d58a9a0e Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Thu, 4 Dec 2025 13:44:08 +0100 Subject: [PATCH 02/20] docs: rewrite the overview in commitments section Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index 5521f6ac1d..24d3143ad1 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -4,17 +4,19 @@ Commitments Overview -------- -A **Commitment** is the central economic abstraction used by FlexMeasures to -express *soft constraints, preferences and market positions* in the scheduler. +A **Commitment** is the economic abstraction FlexMeasures uses to express +market positions and soft constraints (preferences) inside the scheduler. +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 target or assumed position), and +- a **baseline quantity** over time (the contracted or preferred position), and - marginal prices for **upwards** and **downwards deviations** from that baseline. 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 limits (for example generator or +across the schedule horizon. Absolute physical limitations (for example generator or line capacities) are *not* modelled as commitments — those are enforced as Pyomo constraints. From 117919806ca403cb2873c5eb2db509a6c0c8d1d2 Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Thu, 4 Dec 2025 13:52:05 +0100 Subject: [PATCH 03/20] docs: keep ems variables, but explain them as representing the site context Signed-off-by: F.N. Claessen --- documentation/concepts/device_scheduler.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/concepts/device_scheduler.rst b/documentation/concepts/device_scheduler.rst index 7d85e57415..3b87ac7963 100644 --- a/documentation/concepts/device_scheduler.rst +++ b/documentation/concepts/device_scheduler.rst @@ -5,8 +5,8 @@ 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. @@ -45,9 +45,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`. ================================ ================================================ ============================================================================================================== @@ -58,8 +58,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`. From 1b103601a5d080f892cec0590d0aae61a8486e4b Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Thu, 4 Dec 2025 13:55:24 +0100 Subject: [PATCH 04/20] docs: cross-reference the commitments section and the linear problem formulation Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 8 ++++++++ documentation/concepts/device_scheduler.rst | 1 + 2 files changed, 9 insertions(+) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index 24d3143ad1..d21094e522 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -1,3 +1,5 @@ +.. _commitments: + Commitments =========== @@ -169,3 +171,9 @@ Grouping across time and devices 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**). + + +Advanced: mathematical formulation +---------------------------------- + +For a compact formulation of how commitments enter the optimization problem, see :ref:``. diff --git a/documentation/concepts/device_scheduler.rst b/documentation/concepts/device_scheduler.rst index 3b87ac7963..cb36d0dc46 100644 --- a/documentation/concepts/device_scheduler.rst +++ b/documentation/concepts/device_scheduler.rst @@ -11,6 +11,7 @@ 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:``. From 6f6e52b0290a4af369b4cc1f898b8ca30dd07fd8 Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Mon, 8 Dec 2025 15:54:12 +0100 Subject: [PATCH 05/20] fix: cross-references Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 2 +- documentation/concepts/device_scheduler.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index d21094e522..11ab4cc955 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -176,4 +176,4 @@ Grouping across time and devices Advanced: mathematical formulation ---------------------------------- -For a compact formulation of how commitments enter the optimization problem, see :ref:``. +For a compact formulation of how commitments enter the optimization problem, see :ref:`storage_device_scheduler`. diff --git a/documentation/concepts/device_scheduler.rst b/documentation/concepts/device_scheduler.rst index cb36d0dc46..289f40cfc8 100644 --- a/documentation/concepts/device_scheduler.rst +++ b/documentation/concepts/device_scheduler.rst @@ -11,7 +11,7 @@ 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:``. +For a more detailed explanation of commitments in FlexMeasures, see :ref:`commitments`. From 359937985193a7cf4e018df6e29b079e081eeaee Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Mon, 8 Dec 2025 15:54:26 +0100 Subject: [PATCH 06/20] docs: add commitments section to index Signed-off-by: F.N. Claessen --- documentation/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/index.rst b/documentation/index.rst index 337beb2585..748b160395 100644 --- a/documentation/index.rst +++ b/documentation/index.rst @@ -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 From 98eb7e309b0b3383eba8a2351df8d4f03d45e625 Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Fri, 13 Feb 2026 15:16:07 +0100 Subject: [PATCH 07/20] docs: changelog entry Signed-off-by: F.N. Claessen --- documentation/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/changelog.rst b/documentation/changelog.rst index 296d8aab76..e00414dc3c 100644 --- a/documentation/changelog.rst +++ b/documentation/changelog.rst @@ -27,6 +27,7 @@ New features * Allow testing out the scheduling CLI without saving anything, using ``flexmeasures add schedule --dry-run`` [see `PR #1892 `_] * Allow unsupported ``flex-context`` or ``flex-model`` fields to be shown in the UI editors (they will be un-editable) [see `PR #1915 `_] * Add back save buttons to both ``flex-context`` and ``flex-model`` UI editors [see `PR #1916 `_] +* Add a documentation section on the concept of ``Commitments`` [see `PR #1849 `_] Infrastructure / Support From 866848695cc6cecc04774246d623873d3537e80b Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Fri, 13 Feb 2026 16:18:30 +0100 Subject: [PATCH 08/20] docs: update commitments section after review Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 71 +++++++++++++++++++++----- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index 11ab4cc955..fdfe9d5f2f 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -3,13 +3,35 @@ Commitments =========== -Overview --------- +This document will explain 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. -A **Commitment** is the economic abstraction FlexMeasures uses to express -market positions and soft constraints (preferences) inside the scheduler. -Commitments are converted to linear objective terms; all non-negotiable -operational limits are modelled separately as Pyomo constraints. +What is a commitments? +---------------------- + +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 + + .. 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: @@ -37,7 +59,7 @@ Each Commitment has the following important attributes (high level): - ``_type`` — grouping indicator: ``'each'`` or ``'any'`` (see Grouping below). Sign convention (flows vs stocks) --------------------------------- +--------------------------------- - **Flow commitments** (e.g. power/energy flows): @@ -53,17 +75,17 @@ Sign convention (flows vs stocks) - **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. + are priced via the upwards/downwards price series, respectively. -Soft vs hard semantics ----------------------- +How FlexMeasures uses commitments in the scheduler +-------------------------------------------------- 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 prefer modelling non-negotiable limits as Pyomo constraints. +penalty prices, but we prefer modelling non-negotiable limits as Pyomo constraints. Converting flex-context fields into commitments ----------------------------------------------- @@ -78,8 +100,8 @@ Typical translations include: - 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). -A short example ---------------- +A short example for developers +------------------------------ Below is a compact example showing how the scheduler conceptually creates an ``"energy"`` flow commitment from a (per-slot) tariff: @@ -157,6 +179,29 @@ commitments the scheduler constructs. - *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: 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 your flex-context: + +```json +{ + "commitments": [ + { + "name": "congestion pricing", + "baseline": [ + {"start": "2026-02-01T16:00:00+01:00", "start": "2026-02-01T21:00:00+01:00", "value": "500 kW"} + ], + "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. + Grouping across time and devices -------------------------------- From 39b250ae1af293689d69673830b371d6e8a6a5e6 Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Fri, 13 Feb 2026 16:19:27 +0100 Subject: [PATCH 09/20] docs: remove developer example Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 29 -------------------------- 1 file changed, 29 deletions(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index fdfe9d5f2f..3bbbf9c3f1 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -100,35 +100,6 @@ Typical translations include: - 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). -A short example for developers ------------------------------- - -Below is a compact example showing how the scheduler conceptually creates an -``"energy"`` flow commitment from a (per-slot) tariff: - -.. code-block:: python - - from pandas import Series, date_range - from flexmeasures.data.models.planning import FlowCommitment - - index = date_range(start="2025-01-01 00:00", periods=24, freq="H") - # zero baseline → the asset may consume or produce; deviations are priced. - baseline = Series(0.0, index=index) - - # consumption and production tariffs (per kWh) - consumption_price = Series(0.20, index=index) # 0.20 EUR/kWh for consumption - production_price = Series(-0.05, index=index) # -0.05 EUR/kWh reward for production - - energy_commitment = FlowCommitment( - name="energy", - index=index, - quantity=baseline, - upwards_deviation_price=consumption_price, - downwards_deviation_price=production_price, - _type="each" - ) - -The scheduler sets up such commitments (site-level and device-level) and, together with any prior commitments, hands them to the linear optimizer. Examples (commitments commonly derived from flex-context) -------------------------------------------------------- From 4138e5f55426df6d8c79fb58a9acc462430b988b Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Fri, 13 Feb 2026 16:22:18 +0100 Subject: [PATCH 10/20] docs: move grouping section Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 35 +++++++++++++++----------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index 3bbbf9c3f1..b808dae5e9 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -5,6 +5,7 @@ Commitments This document will explain 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. + What is a commitments? ---------------------- @@ -44,6 +45,7 @@ across the schedule horizon. Absolute physical limitations (for example generato line capacities) are *not* modelled as commitments — those are enforced as Pyomo constraints. + Key properties -------------- @@ -58,6 +60,7 @@ Each Commitment has the following important attributes (high level): - ``downwards_deviation_price`` — Series defining marginal cost/reward for downward deviations. - ``_type`` — grouping indicator: ``'each'`` or ``'any'`` (see Grouping below). + Sign convention (flows vs stocks) --------------------------------- @@ -77,6 +80,7 @@ Sign convention (flows vs stocks) - ``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 -------------------------------------------------- @@ -87,6 +91,21 @@ 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. +Commitments are grouping across time and devices: + +- ``_type == 'each'``: penalise deviations per time slot (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 + 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**). + + Converting flex-context fields into commitments ----------------------------------------------- @@ -150,6 +169,7 @@ commitments the scheduler constructs. - *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: an example --------------------------------------- @@ -173,21 +193,6 @@ Then you could add this to your flex-context: 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. -Grouping across time and devices --------------------------------- - -- ``_type == 'each'``: penalise deviations per time slot (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 - 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**). - Advanced: mathematical formulation ---------------------------------- From 82d8348370a72341829e49872ee39bf51b98fb87 Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Fri, 13 Feb 2026 16:25:38 +0100 Subject: [PATCH 11/20] docs: fix typo Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index b808dae5e9..1341cb5076 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -6,8 +6,8 @@ Commitments This document will explain 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. -What is a commitments? ----------------------- +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. From fdd378167cf17dbea2c52f6f2525573208f8a06c Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Fri, 13 Feb 2026 16:26:48 +0100 Subject: [PATCH 12/20] docs: fix code-block formatting Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index 1341cb5076..cbdc96e21f 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -176,20 +176,21 @@ How you can use commitments: 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 your flex-context: -```json -{ - "commitments": [ +.. code-block:: json + { - "name": "congestion pricing", - "baseline": [ - {"start": "2026-02-01T16:00:00+01:00", "start": "2026-02-01T21:00:00+01:00", "value": "500 kW"} - ], - "up-price": "250 EUR/MW", - "down-price": "0 EUR/MW", + "commitments": [ + { + "name": "congestion pricing", + "baseline": [ + {"start": "2026-02-01T16:00:00+01:00", "start": "2026-02-01T21:00:00+01:00", "value": "500 kW"} + ], + "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. From e52ec49491035ca6c74d2e4b468f8727d6f39ee7 Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Fri, 13 Feb 2026 16:35:59 +0100 Subject: [PATCH 13/20] docs: add note about UI support Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index cbdc96e21f..bd4abe89ab 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -194,6 +194,11 @@ Then you could add this to your flex-context: 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 is not yet supported 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 is already supported. Advanced: mathematical formulation ---------------------------------- From 7879314af4850fc77df876bb633cc3c1468d9240 Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Fri, 13 Feb 2026 16:36:41 +0100 Subject: [PATCH 14/20] docs: fix example json Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index bd4abe89ab..4447837901 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -183,7 +183,7 @@ Then you could add this to your flex-context: { "name": "congestion pricing", "baseline": [ - {"start": "2026-02-01T16:00:00+01:00", "start": "2026-02-01T21:00:00+01:00", "value": "500 kW"} + {"start": "2026-02-01T16:00:00+01:00", "end": "2026-02-01T21:00:00+01:00", "value": "500 kW"} ], "up-price": "250 EUR/MW", "down-price": "0 EUR/MW", From bdfbf0f9817daa8214cffbb2f357fc4996690ec8 Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Fri, 13 Feb 2026 16:37:21 +0100 Subject: [PATCH 15/20] docs: put value first for better legibility without horizontal scrolling Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index 4447837901..7bfe59f618 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -183,7 +183,7 @@ Then you could add this to your flex-context: { "name": "congestion pricing", "baseline": [ - {"start": "2026-02-01T16:00:00+01:00", "end": "2026-02-01T21:00:00+01:00", "value": "500 kW"} + {"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", From 37d610b76b068ca69c2e575ed02c5e0144a12800 Mon Sep 17 00:00:00 2001 From: "F.N. Claessen" Date: Fri, 13 Feb 2026 16:47:28 +0100 Subject: [PATCH 16/20] docs: use info icon for admonition (_static/css/custom_css has custom styling for this) Signed-off-by: F.N. Claessen --- documentation/concepts/commitments.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index 7bfe59f618..e5c40b253c 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -13,6 +13,7 @@ A **Commitment** is the economic abstraction FlexMeasures uses to express market 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 From 21aff2fadca8f273524f99b91ab7065f992f9082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20H=C3=B6ning?= Date: Mon, 16 Feb 2026 14:33:57 +0100 Subject: [PATCH 17/20] improve intro and a few other sentences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicolas Höning --- documentation/concepts/commitments.rst | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index e5c40b253c..82eeca2337 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -3,7 +3,17 @@ Commitments =========== -This document will explain 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. +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. + +Commitments are used in the background without you having to worry about them explicitly (you can use "syntactic sugar" to enjoy them). +However, they are also exposed as a powerful advanced tool to model your own preferences. + +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: 2 What is a commitment? @@ -175,7 +185,7 @@ How you can use commitments: 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 your flex-context: +Then you could add this to the ``flex-context`` field of the site asset: .. code-block:: json @@ -197,9 +207,9 @@ The scheduler then takes into account that exceeding 500 kW consumption during t .. note:: - The ``"commitments"`` field is not yet supported in the flex-context editor in the UI. + 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 is already supported. + Passing the ``"commitments"`` field in the API call is already supported, and you can also use the FlexMeasures-Client to edit the ``flex-context``. Advanced: mathematical formulation ---------------------------------- From 5d71b20878b03faa9ae8e842a97c4082b6c809be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20H=C3=B6ning?= Date: Mon, 16 Feb 2026 16:03:30 +0100 Subject: [PATCH 18/20] two more clarifications in intro and in a note about how to edit commitments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicolas Höning --- documentation/concepts/commitments.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index 82eeca2337..e7706fb93f 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -4,10 +4,10 @@ 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. +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" to enjoy them). -However, they are also exposed as a powerful advanced tool to model your own preferences. +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. @@ -209,7 +209,8 @@ The scheduler then takes into account that exceeding 500 kW consumption during t 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 is already supported, and you can also use the FlexMeasures-Client to edit the ``flex-context``. + 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 ---------------------------------- From 6d820b21e9d786e8db9bab046ad24982e0b0f72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20H=C3=B6ning?= Date: Mon, 16 Feb 2026 16:34:33 +0100 Subject: [PATCH 19/20] small change to headings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicolas Höning --- documentation/concepts/commitments.rst | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index e7706fb93f..aa9849029e 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -13,7 +13,7 @@ This document explains what commitments are on a technical level, and then gives .. contents:: :local: - :depth: 2 + :depth: 1 What is a commitment? @@ -117,11 +117,11 @@ Commitments are grouping across time and 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**). -Converting flex-context fields into commitments ------------------------------------------------ +How flex-context fields are converted into commitments +-------------------------------------------------------- Users may supply preferences and price fields in the ``flex-context``. The -scheduler translates the relevant fields into one or more `Commitment` objects +scheduler then translates the relevant fields into one or more `Commitment` objects before calling the optimizer. Typical translations include: @@ -131,9 +131,7 @@ Typical translations include: - storage-related fields (``soc-minima``, ``soc-minima-breach-price``, etc.) → StockCommitment(s). -Examples (commitments commonly derived from flex-context) --------------------------------------------------------- - +Let us look at some concrete examples. The examples below map the most common `flex-context` semantics to the commitments the scheduler constructs. @@ -181,8 +179,8 @@ commitments the scheduler constructs. - *Commitment*: FlowCommitment with either baseline and corresponding prices. -How you can use commitments: an example ---------------------------------------- +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: From 3ef8461308efe9863c4921a0b7bd2d99c34f8c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20H=C3=B6ning?= Date: Wed, 18 Feb 2026 13:36:58 +0100 Subject: [PATCH 20/20] slight improvement of the grouping discussion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicolas Höning --- .vscode/spellright.dict | 2 ++ documentation/concepts/commitments.rst | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.vscode/spellright.dict b/.vscode/spellright.dict index 4de6f1a8a1..0cb1775a94 100644 --- a/.vscode/spellright.dict +++ b/.vscode/spellright.dict @@ -302,3 +302,5 @@ async href setpoints organisation +— +modelled diff --git a/documentation/concepts/commitments.rst b/documentation/concepts/commitments.rst index aa9849029e..82cb1d4f9c 100644 --- a/documentation/concepts/commitments.rst +++ b/documentation/concepts/commitments.rst @@ -50,6 +50,9 @@ 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 @@ -69,7 +72,7 @@ Each Commitment has the following important attributes (high level): - ``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'`` (see Grouping below). +- ``_type`` — grouping indicator: ``'each'`` or ``'any'`` (defaults to ``'each'``; see more info below on grouping). Sign convention (flows vs stocks) @@ -95,6 +98,9 @@ Sign convention (flows vs stocks) 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 @@ -102,9 +108,16 @@ 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. -Commitments are grouping across time and devices: +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 (default for time series). +- ``_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). @@ -112,7 +125,7 @@ Commitments are grouping across time and devices: .. note:: Near-term feature: support for **grouping over devices** is planned and - documented here. When enabled, grouping over devices lets you express + 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**).