Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
179 changes: 179 additions & 0 deletions documentation/concepts/commitments.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
.. _commitments:

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

Overview
--------

A **Commitment** is the economic abstraction FlexMeasures uses to express
market positions and soft constraints (preferences) inside the scheduler.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would quickly list two or three examples right at the start, to let the reader imagine why this concept applies to them.

Market positions:

  • passive imbalance
  • PPAs
  • gas price baseline

Soft constraints:

  • contracted (consumption|production) capacity
  • Previous peaks
  • CO2 levels

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
market positions and soft constraints (preferences) inside the scheduler.
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.

Commitments are converted to linear objective terms; all non-negotiable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would tell the reader what this document delivers ("This document will explain what commitments are on a technical level, and then give examples how the scheduler uses them and how you can create your own commitments in the flex-context to great effect.")

I suggested a few section headers here, maybe include a TOC.

Then, start a subsection here: "What is a commitment?"

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.

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'`` (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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New section: "How FlexMeasures uses commitments in the scheduler"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the grouping section can be here already.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A short example
How the scheduler interpretes commitments: A short example

(I don't really understand who benefits from this section - hosts, FlexMeasures devs?)

---------------

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that we add a new section here: "How you can use commitments: An example"

Then provide a flex context example with some price blocks maybe - whatever comes off easy.

Add a few words how this would affect the schedules that are being made.

(so far, the examples are only from the internal use of commitments)

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

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