flexmeasures.data.models.reporting.aggregator

Classes

class flexmeasures.data.models.reporting.aggregator.AggregatorReporter(config: dict | None = None, save_config=True, save_parameters=False, **kwargs)

Aggregate the sensors of a device group, or sensors named one by one.

A site’s flex-config already describes its topology: which devices sit behind which piece of equipment (their group), which sensor records each of them, which sign means consumption or production, and which sensor a group’s aggregate belongs on. Naming a group reports the measured aggregate of that group, so the report needs no topology of its own and cannot drift from the one the scheduler uses. A members filter narrows a group down to a category of its devices, such as every PV installation behind a connection.

Sensors can still be named one by one as input parameters, which is the way to aggregate something other than a site’s power, or to subtract one sensor’s data from another’s using weights.

Values are converted to the unit of the output sensor at each sensor’s own resolution, before being resampled to the output’s resolution, because converting between a stock and a flow (kWh to MW, say) depends on the resolution of the data being converted.

static _check_one_source_per_event(df, sensor: Sensor, source, input_description: dict) → None

Refuse data that holds several sources for one event, unless the caller narrowed the sources down.

Different versions of one source are not several sources, and neither is data a source filter has already narrowed, which is why a filtered search is accepted here even when more than one source remains.

_collect_input_descriptions(input: list[dict[str, Any]], output_sensor: Sensor) → list[dict[str, Any]]

List what to read, combining the input parameters with the members of a configured group.

The input descriptions are copied, so that reading them does not consume the parameters the reporter was given. The output sensor is never aggregated into itself, which would fold each run’s own result into the next one.

_compute_report(start: datetime, end: datetime, output: list[dict[str, Any]] | None = None, input: list[dict[str, Any]] | None = None, resolution: timedelta | None = None, belief_time: datetime | None = None, belief_horizon: timedelta | None = None) → list[dict[str, Any]]

Read every sensor, express it in the output sensor’s unit and resolution, and aggregate.

_filter_members(members: list[GroupMember]) → list[GroupMember]

Narrow the group’s members down with the members filter, if one is configured.

_group_entry() → dict | None

Return the flex-model entry of the group itself, which says where its aggregate is recorded.

_group_members() → list[GroupMember]

Resolve the members of the configured group from the flex-models in its subtree.

Membership is read from the same group field, through the same resolver, that the scheduler reads it from, so that adding a device to a group adds it to this report as well, with nothing to keep in sync.

_group_output_sensor(raise_if_missing: bool = True) → Sensor | None

Return the sensor the group’s aggregate is recorded on.

A group referenced by sensor records on that sensor. A group referenced by asset records on the production or consumption output sensor its own entry names.

_group_root_asset() → GenericAsset

Return the asset whose subtree holds the group and its members.

A group referenced by asset is that asset; one referenced by sensor is the asset the sensor sits on. Members are looked for in that asset’s subtree, which is where the equipment a group stands for puts them.

_group_sign() → bool

Return whether positive values on the output sensor mean consumption.

The group’s own entry decides: an aggregate recorded under production is production-positive, one recorded under consumption is consumption-positive.

static _referenced_sensor(reference: Any) → Sensor | None

Resolve a serialized sensor reference, which may be an id, a {“sensor”: id} dict, or a Sensor.

static _resample(values: Series, resolution: timedelta, sensor_resolution: timedelta, unit: str) → Series

Resample one sensor’s converted values to the resolution the report is recorded at.

Whether a quantity adds up or averages over a longer event depends on what it is: energy adds up, power averages, so the unit of the output sensor decides which. Going the other way, to a finer resolution, a power holds through the event it was recorded over, while an energy is divided over it.

_resolve_output(output: list[dict[str, Any]]) → list[dict[str, Any]]

Settle which sensor the aggregate is recorded on.

An output named in the parameters wins, so one report can be sent elsewhere. Otherwise a configured group’s own entry says where its aggregate belongs.

_to_output_unit(values: Series, sensor: Sensor, output_sensor: Sensor) → Series

Convert one sensor’s values to the unit of the output sensor, at the sensor’s own resolution.

The resolution matters: converting between a stock and a flow (kWh to MW, say) divides by the duration of an event, so this has to happen before the values are resampled to the output’s resolution.

property input_sensors: list

Return the sensors read by this reporter, including the members of a configured group.

property output_sensors: list

Return the sensor this reporter records on, which a group takes from its own flex-model entry.

class flexmeasures.data.models.reporting.aggregator.GroupMember(sensor: Sensor, asset: GenericAsset, consumption_is_positive: bool | None)

One device of a group, as the flex-config describes it.

Only what aggregating needs is read off the entry: the sensor recording the device, whether positive values on it mean consumption, and the asset it sits on, so that members can be filtered by type.

__init__(sensor: Sensor, asset: GenericAsset, consumption_is_positive: bool | None) → None