flexmeasures.data.schemas.scheduling.storage

Classes

class flexmeasures.data.schemas.scheduling.storage.DBStorageFlexModelSchema(*args, **kwargs)

Schema for flex-models stored in the db. Supports fixed quantities and sensor references, while disallowing time series specs.

__init__(*args, **kwargs)
_validate_array_fields(data: dict)

Validate power array fields.

_validate_energy_fields(data: dict)

Validate energy fields.

_validate_field(data: dict, field: str, unit_validator: Callable)

Validate fields based on type and unit validator.

_validate_power_fields(data: dict)

Validate power fields.

forbid_time_series_specs(data: dict, **kwargs)

Do not allow time series specs for the flex-model fields saved in the db.

validate_fields_unit(data: dict, **kwargs)

Check that each field value has a valid unit.

class flexmeasures.data.schemas.scheduling.storage.EfficiencyField(*args, **kwargs)

Field that deserializes to a Quantity with % units. Fixed values must be greater than 0% and less than or equal to 100%.

Examples:

>>> ef = EfficiencyField()
>>> ef.deserialize(0.9)
<Quantity(90.0, 'percent')>
>>> ef.deserialize("90%")
<Quantity(90, 'percent')>
>>> ef.deserialize("0%")
Traceback (most recent call last):
...
marshmallow.exceptions.ValidationError: ['Must be greater than 0 % and less than or equal to 100 %.']
__init__(*args, **kwargs)
class flexmeasures.data.schemas.scheduling.storage.GroupReferenceSchema(*, only: Sequence[str] | AbstractSet[str] | None = None, exclude: Sequence[str] | AbstractSet[str] = (), many: bool | None = None, load_only: Sequence[str] | AbstractSet[str] = (), dump_only: Sequence[str] | AbstractSet[str] = (), partial: bool | Sequence[str] | AbstractSet[str] | None = None, unknown: Literal['exclude', 'include', 'raise'] | None = None)

Reference to a group of devices whose aggregate power is constrained.

Accepts exactly one of:
  • {"sensor": <id>}: the group’s aggregate power is stored on this power sensor (the sensor must itself carry a flex-model entry defining the group’s constraints).

  • {"asset": <id>}: the group is identified by the flex-model entry on this asset (typically a sub-EMS/asset in the tree). Such a group entry defines no power sensor of its own; instead it may define consumption and/or production output sensors on which the group’s aggregate power gets saved, following the usual output-sensor conventions.

Inherits from SharedSensorReferenceSchema (not SensorReferenceSchema) so it accepts only sensor/asset – a group is a device-group identifier, not a belief-query reference, so the source-* filter fields do not apply.

class Meta
fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

class flexmeasures.data.schemas.scheduling.storage.OperationModeSchema(*, only: Sequence[str] | AbstractSet[str] | None = None, exclude: Sequence[str] | AbstractSet[str] = (), many: bool | None = None, load_only: Sequence[str] | AbstractSet[str] = (), dump_only: Sequence[str] | AbstractSet[str] = (), partial: bool | Sequence[str] | AbstractSet[str] | None = None, unknown: Literal['exclude', 'include', 'raise'] | None = None)

One operation mode of a device, in the sense of the S2 standard.

A device with operation modes can only run within one of the declared modes’ power ranges at any given time. Each range is given with an explicit sign convention: consumption-range (non-negative, positive means consumption) and/or production-range (non-negative, positive means production). A mode may use either or both; using both forms a single band through zero (so both must then start at 0). The S2 standard’s signed power-range maps to the FM consumption-range (S2 fixes one sign convention for power, whereas FM leaves it to the user). A device that can only be off or run at exactly 883.7 W of consumption declares:

[{“consumption-range”: [“0 W”, “0 W”]}, {“consumption-range”: [“883.7 W”, “883.7 W”]}]

A mode may also carry an optional running-cost: an additional per-time cost (a rate in the flex-context currency per hour, e.g. "1200 EUR/h") incurred while the mode is active, excluding commodity cost. This mirrors the S2 standard’s FRBC.OperationModeElement.running_costs and models the wear / O&M / standing cost of keeping a unit on regardless of its output. The per-timestep charge is the rate scaled by the timestep duration, so the total cost of a given on-duration is resolution-independent. When absent, the running cost is 0, leaving existing behaviour unchanged:

[{“consumption-range”: [“0 MW”, “0 MW”]},

{“consumption-range”: [“4 MW”, “55 MW”], “running-cost”: “1200 EUR/h”}]

Note: a unit’s no-load fuel consumption is more faithfully modelled as a commodity requirement (a fuel/gas power flow declared in the operation mode) than as a running cost; running-cost is for non-commodity costs. That is a possible follow-up.

fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

class flexmeasures.data.schemas.scheduling.storage.SoCTarget
class flexmeasures.data.schemas.scheduling.storage.StorageFlexModelSchema(start: datetime, sensor: Sensor | None, *args, default_soc_unit: str | None = None, **kwargs)

This schema lists fields we require when scheduling storage assets. Some fields are not required, as they might live on the Sensor.attributes. You can use StorageScheduler.deserialize_flex_config to get that filled in.

__init__(start: datetime, sensor: Sensor | None, *args, default_soc_unit: str | None = None, **kwargs)

Pass the schedule’s start, so we can use it to validate soc-target datetimes.

check_redundant_efficiencies(data: dict, **kwargs)
Check that none of the following cases occurs:
  1. flex-model contains both a round-trip efficiency and a charging efficiency

  2. flex-model contains both a round-trip efficiency and a discharging efficiency

  3. flex-model contains a round-trip efficiency, a charging efficiency and a discharging efficiency

Raise:

ValidationError

fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

post_load_sequence(data: dict, **kwargs) dict

Perform some checks and corrections after we loaded.