flexmeasures.utils.bound_utils

Utility module for bounding values: snapping them into intervals and clipping them to a range.

The same lower, upper and snap settings shape a forecaster’s output, and clean the readings a data generator takes from a referenced sensor. Bounds are given as numbers or quantity strings, and are read in the unit of the sensor they apply to.

Functions

flexmeasures.utils.bound_utils.apply_bounds_to_values(values: ndarray, lower_value: float | None, upper_value: float | None, snap_intervals: list[tuple[float, float, float]]) ndarray

Snap and then clip an array of values, returning a new array.

Snapping runs first, against the unmodified values, so intervals cannot cascade into each other. Clipping runs afterwards and always takes precedence, so a snap target outside the bounds is still clipped back into range. NaN values are left alone by both steps.

Parameters:
  • values – The values to bound.

  • lower_value – Lower clip bound in the same unit, or None to leave the lower side unbounded.

  • upper_value – Upper clip bound in the same unit, or None to leave the upper side unbounded.

  • snap_intervals(target, first, second) triples, as parsed by parse_bounds().

Returns:

A new array of bounded values.

flexmeasures.utils.bound_utils.bound_validation_errors(lower: Any, upper: Any, snap: dict | None, sensor_unit: str | None = None, label: str = 'Forecast post-processing') dict[str, list[str]]

Collect what is wrong with configured bounds, per bound, for a schema to report.

Without a sensor unit, only whether each bound can be read as a quantity is checked. With one, the bounds are also parsed in full, so a bound in an incompatible unit, a snap target outside its interval and a lower bound above the upper bound are caught too.

Parameters:
  • lower – Optional lower bound, as a number or a quantity string.

  • upper – Optional upper bound, as a number or a quantity string.

  • snap – Optional mapping from snap targets to two-bound intervals.

  • sensor_unit – Unit of the sensor the bounds apply to, if already known.

  • label – Suffix for error messages, naming what is being bounded.

Returns:

Error messages per bound name (lower, upper or snap), empty if the bounds are valid.

flexmeasures.utils.bound_utils.parse_bounds(lower: Any, upper: Any, snap: dict | None, sensor_unit: str, label: str = 'Forecast post-processing') tuple[float | None, float | None, list[tuple[float, float, float]]]

Parse configured bounds into plain magnitudes in the sensor unit.

Parameters:
  • lower – Optional lower bound, as a number or a quantity string.

  • upper – Optional upper bound, as a number or a quantity string.

  • snap – Optional mapping from snap targets to two-bound intervals.

  • sensor_unit – Unit the bounds are converted into.

  • label – Suffix for error messages, naming what is being bounded.

Returns:

(lower_value, upper_value, snap_intervals), ready for apply_bounds_to_values().

Raises:

ValueError – If a bound cannot be parsed, cannot be converted to the sensor unit, or contradicts another bound.