flexmeasures.data.schemas.scheduling

Modules

flexmeasures.data.schemas.scheduling.metadata

These descriptions are used in documentation/features/scheduling.rst and in OpenAPI. If you need to use a new .rst directive, update make_openapi_compatible accordingly, so it shows up nicely in OpenAPI. For instance: - the X directive is converted to a <abbr title="Y">X</abbr> HTML tag. - any footnote references, such as [#quantity_field]_, are stripped (these are meant for .rst docs to explain field types, which in OpenAPI is redundant, given that each field is already documented as being of an explicit type).

flexmeasures.data.schemas.scheduling.process

flexmeasures.data.schemas.scheduling.storage

Classes

class flexmeasures.data.schemas.scheduling.AssetTriggerSchema(*, 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)
{

“start”: “2025-01-21T15:00+01”, “flex-model”: [

{

“sensor”: 1, “soc-at-start”: “10 kWh”

}, {

“sensor”: 2, “soc-at-start”: “20 kWh”

},

]

}

check_flex_model_sensors(data, **kwargs)

Verify that the flex-model’s sensors live under the asset for which a schedule is triggered.

fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

normalize_flex_context_format(data, **kwargs)

Normalize flex_context to always be a dict.

Accepts both: - Single commodity dict: {“commodity”: “electricity”, …} - List of commodity dicts: [{“commodity”: “electricity”, …}, {“commodity”: “heat”, …}] - MultiDict with multiple ‘flex-context’ entries (when JSON list is parsed by webargs)

If a list is provided, it is wrapped under the ‘commodities’ field. If a dict is provided, it is kept as-is. This ensures downstream code always sees a dict structure.

class flexmeasures.data.schemas.scheduling.CommitmentSchema(*, 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)
fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

class flexmeasures.data.schemas.scheduling.CommodityFlexContextSchema(*args, **kwargs)
__init__(*args, **kwargs)
_default_zero_capacity_as_soft_constraint(data: dict, field: str, zero_capacity: Quantity)

Default a site capacity field to 0, as a soft constraint.

Also fills in a default breach price for that direction (unless one was already set), so the 0 capacity is enforced as a soft constraint (breaching is possible, but penalized) rather than a hard, potentially infeasible, one. This mirrors FlexContextSchema.check_prices, but scoped to a single commodity context, and only fired for capacities defaulted here (not for capacities the caller explicitly set to 0).

fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

fill_grid_connection_defaults(data: dict, original_data: dict, **kwargs)

Fill in smarter defaults for a commodity context’s grid-connection fields.

A commodity context (an entry of the top-level commodities list) may omit some or all of the grid-connection fields (consumption-price, production-price, site-consumption-capacity, site-production-capacity, site-power-capacity). Rather than leaving those simply unset (which, for consumption-price, would make the scheduler fail, since it requires one), we derive sensible defaults from which of those five fields were explicitly given (inspecting the original input, not post-default-fill presence).

A price given for a direction (consumption or production) implies a grid connection in that direction, with an unlimited capacity unless a capacity is also given; a capacity given for a direction (without a price) implies a 0 price in that direction; and anything not implied by a given field defaults to “no connection” (0 capacity, as a soft constraint). The exception is site-power-capacity given on its own, which sets a hard (symmetric) capacity limit instead. See Smart defaults for commodity-context grid connections for the full user-facing explanation, including worked examples.

Precedence (single-field triggers):

  1. None of the five given (e.g. just {“commodity”: “gas”}): no grid connection at all. site-consumption-capacity and site-production-capacity default to 0, as soft constraints (a default breach price is filled in, so breaching is possible but penalized – this relies on relax-constraints/relax-site-capacity-constraints, which default to True). site-power-capacity is left unlimited (unset).

  2. Only consumption-price given: assume a grid connection for consumption. site-power-capacity and site-consumption-capacity stay unlimited. site-production-capacity defaults to 0 (soft).

  3. Only production-price given: the mirror image of (2), for production.

  4. Only site-consumption-capacity given: site-power-capacity stays unlimited; consumption-price defaults to 0; site-production-capacity (and, transitively, production-price) default to 0.

  5. Only site-production-capacity given: the mirror image of (4).

  6. Only site-power-capacity given: a hard constraint at that capacity. site-consumption-capacity and site-production-capacity are both set equal to it (no breach price is filled in, so the constraint stays hard); consumption-price and production-price default to 0.

As a safety net (since the scheduler requires a resolvable consumption price), consumption-price defaults to 0 if still unset after applying the rules above (production-price already falls back to consumption-price at the scheduler level, so no separate safety net is needed for it).

A commodity context with no user-given price fields does not trip a spurious cross-currency error against a differently-currencied portfolio; its 0-price/breach-price fields instead inherit the portfolio’s real currency where determinable (from a top-level price or a sibling commodity context).

class flexmeasures.data.schemas.scheduling.DBCommitmentSchema(*, 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)
fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

class flexmeasures.data.schemas.scheduling.DBFlexContextSchema(*, 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)
_forbid_fixed_prices(data: dict, **kwargs)

Do not allow fixed consumption price or fixed production price in the flex-context fields saved in the db.

This is a temporary restriction as future iterations will allow fixed prices on these fields as well.

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

Validate fields based on type and unit validator.

_validate_inflexible_device_sensors(data: dict)

Validate inflexible device sensors.

_validate_power_fields(data: dict)

Validate power fields.

_validate_price_fields(data: dict)

Validate price fields.

fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

validate_fields_unit(data: dict, **kwargs)

Check that each field value has a valid unit.

class flexmeasures.data.schemas.scheduling.FlexContextSchema(*, 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)

This schema defines fields that provide context to the portfolio to be optimized.

_check_deprecated_price_sensor_migration(data: dict, original_data: dict)

New price fields can only be used after updating to consumption-price/production-price.

_reconcile_commodity_context_currencies(data: dict) str

Backfill price-free contexts’ currency with the portfolio’s real currency.

Determines the portfolio’s real (user-given) shared currency, if any: the top-level one, unless it’s itself just a fallback (no user-given price fields at the top level), in which case falls back to the first non-default commodity context’s currency, if any. Then rebases any price-free (“default currency”) commodity context onto that real currency, so their 0-price/breach-price fills inherit it. Returns the (possibly just-updated) top-level shared_currency_unit.

check_prices(data: dict, original_data: dict, **kwargs)

Check assumptions about prices.

  1. The flex-context must contain at most 1 consumption price and at most 1 production price field.

  2. All prices must share the same currency.

fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

validate_commodity_contexts_shared_currency(commodity_contexts: list[dict], **kwargs)

Validate that all prices across commodity contexts share the same currency.

Each commodity context already computed its own normalized shared_currency_unit (a base-unit currency string, e.g. “EUR”) via the inherited _try_to_convert_price_units schema-level validator. We simply compare those.

validate_commodity_contexts_unique(commodity_contexts: list[dict], **kwargs)

Validate that each commodity is listed at most once.

_get_commodity_contexts (storage.py) builds a dict keyed by commodity, so duplicate entries would otherwise silently overwrite each other.

class flexmeasures.data.schemas.scheduling.GetScheduleSchema(*, 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)
fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

class flexmeasures.data.schemas.scheduling.MultiSensorFlexModelSchema(*, 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)

This schema is agnostic to the underlying type of flex-model, which is governed by the chosen Scheduler instead. Therefore, the underlying type of flex-model is not deserialized.

So:

{

“sensor”: 1, “soc-at-start”: “10 kWh”

}

becomes:

{

“sensor”: <Sensor 1>, “sensor_flex_model”: {

“soc-at-start”: “10 kWh”

}

}

fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

unwrap_envelope(data, **kwargs)

Any field other than ‘sensor’ and ‘asset’ becomes part of the sensor’s flex-model.

wrap_with_envelope(data, **kwargs)

Any field in the ‘sensor-flex-model’ field becomes a main field.

class flexmeasures.data.schemas.scheduling.NoTimeSeriesSpecs(*, 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)
fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

forbid_time_series_specs(data: dict, **kwargs)

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

class flexmeasures.data.schemas.scheduling.ScheduleSignConvention

Named constants for the three sign-convention modes of the get_schedule endpoint.

Variables:
  • CONSUMPTION_POSITIVE – Always return schedules with consumption as positive values and production as negative values. This is the default and matches the view a consumer has of their device.

  • PRODUCTION_POSITIVE – Always return schedules with production as positive values and consumption as negative values. This matches the view a producer (or generator) has of their device.

  • WYSIWYG – Return the raw values from the database without any sign inversion, regardless of the sensor’s consumption_is_positive attribute. Useful when you want to see exactly what was stored.

class flexmeasures.data.schemas.scheduling.SharedSchema(*, 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)

Shared schema for fields common across commodities in flex-context and commodity-context.

classmethod _rebase_default_context_currency(context: dict, new_currency: str)

Re-express a price-free context’s fallback-currency fields in another currency.

Only called for a commodity context that had no user-given price fields (shared_currency_unit_is_default is True), once a real portfolio currency becomes known (e.g. from the top-level flex-context, or from a sibling commodity context). All of that context’s currency-denominated fields were filled with plain quantities in a fallback “EUR”, so their magnitudes carry over unchanged under the new currency label (no FX conversion is implied or attempted).

static _to_currency_per_mwh(price_unit: str) str

Convert a price unit to a base currency used to express that price per MWh.

>>> FlexContextSchema()._to_currency_per_mwh("EUR/MWh")
'EUR'
>>> FlexContextSchema()._to_currency_per_mwh("EUR/kWh")
'EUR'
_try_to_convert_price_units(data: dict, original_data: dict, **kwargs)

Convert price units to the same unit and scale if they can (incl. same currency).

fields: dict[str, Field]

Dictionary mapping field_names -> Field objects

set_default_breach_prices(data: dict, fields: list[str], price: Quantity)

Fill in default breach prices.

This relies on _try_to_convert_price_units to run first, setting a shared currency unit.