Update the device scheduler's (MI)LP formulation in the docs - #2394
Update the device scheduler's (MI)LP formulation in the docs#2394Flix6x wants to merge 7 commits into
Conversation
The formulation had drifted from the model the scheduler actually builds. It now covers sub-commitments (deviation variables indexed by commitment group rather than by time step), stock groups and the recursive stock balance, per-commodity EMS constraint groups, device-scoped and stock commitments, hard flow coupling, balance groups for internal commodity nodes, and power bands (S2 operation modes), plus the binary variables that make this a mixed-integer program. Also drops the perfect/left/right efficiency variants, which the scheduler no longer models (it always uses the linear treatment; the other variants live on in apply_stock_changes_and_losses). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UKx19DmqHiTBS7iEeFTijs Signed-off-by: F.N. Claessen <felix@seita.nl>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UKx19DmqHiTBS7iEeFTijs Signed-off-by: F.N. Claessen <felix@seita.nl>
Documentation build overview
7 files changed ·
|
There was a problem hiding this comment.
Pull request overview
This PR updates the device scheduler documentation to match the current mixed-integer linear model the scheduler actually constructs, and adds a changelog entry to surface the documentation refresh to end users/plugin developers.
Changes:
- Rewrite
documentation/concepts/device_scheduler.rstto describe the current (MI)LP formulation: sub-commitments, stock groups, EMS constraint groups, device/stock-scoped commitments, coupling/balance groups, and power bands. - Add explanatory material about when binaries are introduced (non-convex deviation costs, power sign, and band choice) and clarify backend parity (Pyomo vs direct HiGHS).
- Add a main changelog entry announcing the documentation update.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| documentation/concepts/device_scheduler.rst | Major rewrite of the scheduler model documentation to reflect current formulation and concepts |
| documentation/changelog.rst | Adds a changelog entry noting the documentation update |
Suppressed comments (2)
documentation/concepts/device_scheduler.rst:46
- Several symbols used later in the equations are not introduced in the Notation section (e.g.
s(d),E(e),G(c,g),N(n), and\mathcal{D}(c)). Adding a short definition here would prevent readers from having to infer these mappings/sets from context.
.. note::
The time index :math:`j` has two interpretations: a time period or an instantaneous moment at the end of time period :math:`j`.
For example, :math:`j` in flow constraints correspond to time periods, whereas :math:`j` used in a stock constraint refers to the end of time period :math:`j`.
documentation/concepts/device_scheduler.rst:141
- In the stock recursion, the initial condition uses
Stock_0(s), but the parameters table definesStock_0(d)(initial stock per device, shared within a stock group). This mismatch makes the notation internally inconsistent.
with :math:`Stock(s, -1) = Stock_0(s)`, and with the loss coefficients
| The model below is built by ``flexmeasures.data.models.planning.linear_optimization.device_scheduler`` using Pyomo. | ||
| A second backend (``highspy_optimization``) builds the same model directly with the HiGHS Python API, which is much faster to construct. | ||
| Both are fed by the same input preparation step (``scheduling_problem.prepare_scheduling_problem``), and the symbols below refer to its output. |
…module paths Adds a "Sets and mappings" table introducing s(d), S(s), E(e), D(c), G(c,g) and N(n), which the constraints sum over but which were only inferable from context. The stock recursion's initial condition now uses Stock_0(d), as the parameters table defines it, rather than an undefined per-group Stock_0(s). Also spells out the HiGHS backend and problem-preparation entrypoints in full. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UKx19DmqHiTBS7iEeFTijs Signed-off-by: F.N. Claessen <felix@seita.nl>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
documentation/concepts/device_scheduler.rst:234
- The text says the commitment-deviation sign binaries are only added when the cost curve is non-convex, but in the Pyomo backend
commitment_signis declared unconditionally and only the constraints are gated (see flexmeasures/data/models/planning/linear_optimization.py:500 and theif not convex_cost_curveblock at ~653). Consider adjusting the wording to avoid claiming the binaries themselves are conditionally added.
These two constraints (and their binary variables) are only added when the summed deviation prices do not describe a convex cost curve.
For a convex cost curve, deviating in both directions is never optimal anyway, and leaving the binaries out keeps the problem a pure LP.
documentation/concepts/device_scheduler.rst:77
Stock_{min}(d,j)/Stock_{max}(d,j)are described here as bounds on the absolute stock at the end of the period, but later (and in the implementation) they bound the stock change relative to the initial stock. Updating these parameter descriptions would avoid that mismatch.
This issue also appears on line 233 of the same file.
:math:`Stock_{min}(d,j)` device_min Minimum quantity for the stock of device :math:`d` at the end of time period :math:`j`.
:math:`Stock_{max}(d,j)` device_max Maximum quantity for the stock of device :math:`d` at the end of time period :math:`j`.
…the sign binary The parameters table described device_min/device_max as absolute stock bounds, while both the constraint below it and the implementation bound the stock change since the start of the schedule. Also corrects the claim that the commitment-sign binaries are only added for a non-convex cost curve: only the constraints are gated in the Pyomo backend, which declares the variable either way. The direct HiGHS backend does skip the variable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UKx19DmqHiTBS7iEeFTijs Signed-off-by: F.N. Claessen <felix@seita.nl>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (1)
documentation/concepts/device_scheduler.rst:35
- The index description for :math:
cimplies sub-commitments are always split by deviation direction. In the implementation, commitments are split by deviation direction only when a group spans multiple time steps; single-row groups remain a single sub-commitment carrying both prices. Updating this wording here would better match the explanation below and the scheduler implementation.
:math:`c` c Sub-commitments: one per commitment group and per deviation direction (see `Commitments and sub-commitments`_).
…irection The index table claimed sub-commitments are always split per deviation direction, while the split only happens for groups spanning several time steps, as the section below already explained. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UKx19DmqHiTBS7iEeFTijs Signed-off-by: F.N. Claessen <felix@seita.nl>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
documentation/concepts/device_scheduler.rst:85
- In the Parameters table, the “Variable in the Code” entry for the coupling coefficient \u03b3(k,d) is listed as “coupling coefficient”, but the prepared-problem field is
coupling_device_specs(a list of (group, device, coefficient) triples). Using the actual identifier here makes it easier to find in the codebase.
:math:`\gamma(k,d)` coupling coefficient Fixed proportion of device :math:`d` within coupling group :math:`k`. Positive for inputs (consuming), negative for outputs (producing).
documentation/concepts/device_scheduler.rst:107
- The Variables table no longer lists the auxiliary \u0394Stock(s,j) symbol, even though it is introduced and used later in the “State dynamics” section. Adding it back (as documentation-only) makes the notation section self-contained.
======================== ============================== =====================================================================================================================
Symbol Variable in the Code Description
======================== ============================== =====================================================================================================================
:math:`\Delta_{up}(c)` commitment_upwards_deviation Upwards deviation from sub-commitment :math:`c` (:math:`\geq 0`). One variable per sub-commitment, not per time step.
:math:`\Delta_{down}(c)` commitment_downwards_deviation Downwards deviation from sub-commitment :math:`c` (:math:`\leq 0`).
:math:`\sigma_c(c)` commitment_sign Binary. Upwards deviation allowed if :math:`\sigma_c(c)=1`, downwards deviation otherwise.
:math:`P_{up}(d,j)` device_power_up Upwards (consuming) power of device :math:`d` during time period :math:`j` (:math:`\geq 0`).
:math:`P_{down}(d,j)` device_power_down Downwards (producing) power of device :math:`d` during time period :math:`j` (:math:`\leq 0`).
:math:`P^{ems}(d,j)` ems_power Net flow of device :math:`d` during time period :math:`j`.
:math:`\sigma(d,j)` device_power_sign Binary. Upwards power activation if :math:`\sigma(d,j)=1`, downwards power activation otherwise.
:math:`Stock(s,j)` group_stock Stock of stock group :math:`s` at the end of time period :math:`j`.
:math:`\alpha(k,j)` coupling_alpha Common normalised flow level of coupling group :math:`k` during time period :math:`j`.
:math:`y(d,b,j)` device_band Binary. Device :math:`d` operates in power band :math:`b` during time period :math:`j`.
======================== ============================== =====================================================================================================================
…ock change Points the coupling coefficient at the prepared-problem field that actually holds it, and puts the documentation-only Delta Stock(s,j) symbol back in the variables table, so the notation section covers everything the equations below use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UKx19DmqHiTBS7iEeFTijs Signed-off-by: F.N. Claessen <felix@seita.nl>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
documentation/concepts/device_scheduler.rst:112
- To support the new ``:ref:`commitments_and_sub_commitments``` reference (and to avoid Sphinx 'unknown target' warnings), add an explicit label for this section.
Commitments and sub-commitments
--------------------------------
documentation/concepts/device_scheduler.rst:35
- The reference
(see `Commitments and sub-commitments`_)uses a named link target that isn’t defined anywhere in the document, which will typically raise a Sphinx warning (unknown target). Prefer an explicit Sphinx section reference via:ref:(with a label defined at the section).
This issue also appears on line 110 of the same file.
:math:`c` c Sub-commitments: one per commitment group, and one per deviation direction where a group spans several time steps (see `Commitments and sub-commitments`_).
|
Not acting on the latest review round: the two remaining suggestions are based on a false premise. They claim that Verified on the built HTML:
The four earlier rounds were all valid and are addressed. |
| The model below is built by ``flexmeasures.data.models.planning.linear_optimization.device_scheduler`` using Pyomo. | ||
| A second backend, ``flexmeasures.data.models.planning.highspy_optimization.device_scheduler_highspy``, | ||
| builds the same model directly with the HiGHS Python API, which is much faster to construct. | ||
| Both are fed by the same input preparation step, ``flexmeasures.data.models.planning.scheduling_problem.prepare_scheduling_problem``, |
There was a problem hiding this comment.
Let's link these to the actual place in the _autosummary docs and shorten the text to just the module, so the text alignment / wrapping doesn't leave so much white space (e.g. between "second" and "backend").
There was a problem hiding this comment.
Done in c96af4b. They are now :func:/:mod: roles with the ~ prefix, so they link into the _autosummary pages — down to the function anchor — while rendering as just device_scheduler(), highspy_optimization, prepare_scheduling_problem() and apply_stock_changes_and_losses(). That also takes the long unbreakable literals out of the paragraph flow.
Verified in a local build (with the branch's source on PYTHONPATH, so autosummary picks up highspy_optimization and scheduling_problem, which are newer than my installed checkout):
href="../_autosummary/...linear_optimization.html#...linear_optimization.device_scheduler"
href="../_autosummary/...highspy_optimization.html#module-...highspy_optimization"
href="../_autosummary/...scheduling_problem.html#...scheduling_problem.prepare_scheduling_problem"
href="../_autosummary/flexmeasures.utils.calculations.html#...apply_stock_changes_and_losses"
I applied the same treatment to the apply_stock_changes_and_losses reference further down, which had the same problem.
Replaces the fully qualified literals with :func:/:mod: roles, which resolve into the _autosummary pages (down to the function anchor) while rendering as just device_scheduler(), highspy_optimization, and so on, so the paragraphs no longer wrap around long unbreakable literals. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UKx19DmqHiTBS7iEeFTijs Signed-off-by: F.N. Claessen <felix@seita.nl>
Why
documentation/concepts/device_scheduler.rsthad drifted a long way from the model the scheduler actually builds. It still described a single-stock, single-EMS-constraint LP with per-time-step commitment deviations, while the scheduler has since gained sub-commitments, stock groups, per-commodity EMS constraint groups, device-scoped and stock commitments, flow coupling, balance groups and power bands.What changed
Δ_up(c),Δ_down(c)), not by time step, and deviation prices are per sub-commitment. Added a section explaining the group → sub-commitment split.loss_coefficients); the other treatments live on inapply_stock_changes_and_losses, which is noted.(c,j,g)constraint family), hard flow coupling (P = γ·α), balance groups for internal commodity nodes, and power bands (S2 operation modes).MdandMcinstead of oneM, plus the commitment-sign binaries, which are only added when the cost curve is non-convex.storage_device_schedulerlabel is unchanged, so the existing:ref:s still resolve.How to test
sphinx-buildoverdocumentation/completes without warnings.