Skip to content

Report the measured aggregate of a device group - #2648

Open
Ahmad-Wahid wants to merge 6 commits into
mainfrom
feature/2573-aggregate-device-group
Open

Ahmad-Wahid wants to merge 6 commits into
mainfrom
feature/2573-aggregate-device-group

Conversation

@Ahmad-Wahid

Copy link
Copy Markdown
Contributor

Description

Closes #2573. Supersedes #2579, and follows the review on #2525, which this replaces.

A site's topology already lives in the flex-config. It says which devices sit behind which piece of
shared equipment (their group), which sensor records each of them, which sign means consumption or
production, and which sensor a group's aggregate belongs on. A reporter that describes the same site a
second time — by sensor name, by unit, by walking the subtree — gives two descriptions of one site with
nothing keeping them in sync. Add an inverter and the scheduler learns about it, because it has to,
while the report keeps running against a stale list and keeps returning an answer.

So the reporter now reads the description that already exists.

group. The config names a device group, and the report aggregates that group's measured values:

# config
group:
  asset: 2
# parameters
start: "2026-06-01T00:00:00+02:00"
end: "2026-06-02T00:00:00+02:00"
belief_horizon: PT0H

No sensor ids in either file. Membership comes from the same group field, through the same resolver
(resolve_group_reference), that the scheduler reads, so adding a device to a group adds it to the
report. The group's own entry says where the aggregate is recorded, and whether it is consumption- or
production-positive; a member whose own convention differs is negated.

members. A group is a piece of equipment, so it holds everything behind it. To report a category
instead, filter it:

group:
  asset: 1
members:
  asset-type: solar

Asset type says what a device is, which does not change when the way it is modelled changes, so a PV
installation that later becomes curtailable stays in the aggregate.

Units and resolutions. Each sensor is converted to the output sensor's unit at its own
resolution, before being resampled to the output's. Resampling then follows the quantity: energy adds
up over a longer event where power averages, and a sensor recording more coarsely than the report is
carried across its own event instead of leaving gaps. A member recording something the output cannot
express, such as a temperature onto a power sensor, stops the report with a clear message.

Smaller fixes from the review of #2579. The sensor a group records on is never read back in, so a
second run cannot fold the first into itself. Two input entries for one sensor both survive instead of
one being silently dropped. An entry already narrowed by a source filter no longer trips the "Missing
attribute 'sources'" error. output_sensors no longer crashes on a reference that fails to load.

  • Added changelog item in documentation/changelog.rst

A bug this fixes

The unit conversion used to be done as though the data were already at the report's resolution.
Converting a stock to a flow divides by the duration of an event, so a sensor recording energy more
finely than the report came out too low: 1 kWh per quarter of an hour was reported as 0.001 MW instead
of 0.004 MW, with nothing to indicate it.

Felix described this as converting after resampling. The order alone turns out not to matter, since
both steps are linear — the fault is which resolution the conversion uses. The fix pins each
sensor's own resolution, and the test is named after that rather than after the ordering.

How to test

pytest flexmeasures/data/models/reporting/tests/test_aggregator.py flexmeasures/data/schemas/tests/test_reporting.py

Fourteen tests are added, on a fixture that builds the asset tree of a PV group: a roof recording
400 kW quarter-hourly, a carport recording 0.15 MW hourly, a weather sensor that declares no
membership, a forecast on one member, and a schedule already sitting on the group's own sensor. They
cover the worked aggregate (0.55 MW per quarter-hour), membership driving the report, the weather
sensor being left out, the group's own sensor not being read back in, a realized report excluding a
forecast, the member filter, adding a member without touching the report, an incomparable member, a
group that says nowhere to record, a filter without a group, converting at the sensor's own
resolution, an energy aggregate summing over a longer event, and a coarser sensor carried across a
finer output.

Each was verified to fail with the code it covers disabled. Two did not fail on the first attempt and
so were asserting nothing: the one about the group's own sensor (that sensor was never a member
candidate in the fixture, so the exclusion was a no-op) and the energy-sum branch (the output unit was
power, so the sum path was never taken). Both were rebuilt until they turned red, which is how the
fixture ended up with a member that wrongly names the group's own sensor, and with a separate
energy-to-energy case.

435 tests pass across flexmeasures/data/models/reporting and flexmeasures/data/schemas, and the
report-trigger API suite is green.

A test that had to change

test_specialized_reporter_schemas_preserve_required_dataflow_fields asserted that the aggregator's
output stays required. A group report takes both its inputs and its output from the flex-config, so
that is no longer the contract. The guard now holds the aggregator to recording on at most one
sensor, and the "something must supply an output" rule is enforced at compute time with its own
message and its own test. The ProfitOrLossReporter half is untouched. Both halves were verified to
still fail when the constraint they name is removed.

Not done here

These are open decisions rather than missing code, so they are left alone:

  • How much filtering. @nhoening asked whether filtering by kind of device is enough, or whether
    more will be needed. Only the asset-type filter is built.
  • Selecting a forecast aggregate. A realized report works. A forecast one does not yet: the
    parameters can bound the belief horizon from above but not from below, so a longer horizon still
    admits the measurement and the most recent belief wins. This needs horizons_at_least in the report
    parameters.
  • Groups used only for aggregation. A group entry is currently expected to carry a capacity. One
    that exists only to be aggregated should be allowed without one, and documented as such.
  • Site-level aggregates. Treating the whole site as the implicit root group, so that the
    flex-context's aggregate-consumption/aggregate-production are the same mechanism, is a follow-up.

One deliberate deviation from the review: membership is resolved by walking the serialized flex-models
of the group's subtree and reading their group field, rather than by building a full DeviceInventory.
The inventory needs deserialized entries, which today means running the storage scheduler's flex-model
deserialization pipeline; factoring that out is a larger change than this PR. The group key comes from
the same resolver either way, so the topology still has one source of truth.


Sign-off

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on code under GPL or another incompatible license.

Context:
- Issue #2573, and the review on PR #2525 and PR #2579: a site's topology already
  lives in the flex-config, which says which devices sit behind which equipment
  (their `group`), which sensor records each of them, which sign means consumption
  or production, and which sensor a group's aggregate belongs on. A reporter that
  describes the same site a second time, by sensor name or unit, gives two
  descriptions with nothing to keep them in sync.

Change:
- The AggregatorReporter takes a `group` reference and reports that group's measured
  aggregate. Membership comes from the same `group` field, through the same resolver,
  that the scheduler reads, so adding a device to a group adds it to the report.
- The group's own entry says where the aggregate is recorded, and whether the
  aggregate is consumption- or production-positive; members whose own convention
  differs are negated.
- An optional `members` filter narrows a group to a category, by asset type, since a
  group is a piece of equipment and holds everything behind it.
- Each sensor is converted to the output sensor's unit at its own resolution, before
  being resampled. Converting a stock to a flow divides by the duration of an event,
  so converting as though the data were already at the output's resolution reported a
  sensor recording energy more finely than the report four times too low.
- Resampling follows the quantity: energy adds up over a longer event where power
  averages, and a power recorded more coarsely than the report is carried across its
  own event rather than leaving gaps.
- The sensor a group records on is never read back in, so a second run cannot fold
  the first run into itself, and two input entries for one sensor both survive.
- Replaces the name and unit selection of PR #2525 and the `portfolio` field of
  PR #2579, neither of which is released.

Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Context:
- Issue #2573 lets the AggregatorReporter report the measured aggregate of a device
  group described in the flex-config.

Change:
- Add a fixture building the asset tree of a PV group, with members recording in
  different units at different resolutions, a weather sensor that declares no
  membership, a forecast on one member and a schedule already on the group's sensor.
- Cover the worked aggregate, membership driving the report, the weather sensor being
  left out, the group's own sensor not being read back in, a realized report excluding
  a forecast, the member filter, adding a member without touching the report, a member
  recording an incomparable quantity, a group that says nowhere to record, a filter
  without a group, converting at the sensor's own resolution, an energy aggregate
  summing over a longer event, and a coarser sensor carried across a finer output.
- Update the specialized-schema guard: a group report takes both its inputs and its
  output from the flex-config, so the aggregator's `output` is optional by design and
  the guard holds it to recording on at most one sensor instead.

Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Context:
- Issue #2573 replaces the reporter's own sensor selection with a reference to a
  device group of the flex-config.

Change:
- Work through a farm with two PV installations: the flex-models that describe it, the
  three-line report configuration, how units and resolutions are reconciled with the
  output sensor, and how a member filter narrows a group to a category.

Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
Change:
- A New features entry for reporting a device group's measured aggregate, and a
  Bugfixes entry for the unit conversion that used the report's resolution rather
  than each sensor's own.

Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
…e-device-group

Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>

# Conflicts:
#	documentation/changelog.rst
@Ahmad-Wahid Ahmad-Wahid self-assigned this Oct 2, 2026
Change:
- The two entries referenced #2579, the PR this work supersedes; they now
  reference #2648, which carries it.

Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
@read-the-docs-community

Copy link
Copy Markdown

@Ahmad-Wahid
Ahmad-Wahid requested a balanced review from Copilot October 2, 2026 18:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Ahmad-Wahid
Ahmad-Wahid requested a review from Flix6x October 2, 2026 18:16
@Flix6x Flix6x added this to the 1.1.0 milestone Oct 7, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use the flex-config to look up the aggregated portfolio

3 participants