Skip to content

control_loop_health

control_loop_health ¤

ControlLoopHealthEvents ¤

ControlLoopHealthEvents(
    dataframe: DataFrame,
    setpoint_uuid: str,
    actual_uuid: str,
    *,
    output_uuid: str | None = None,
    event_uuid: str = "eng:control_loop_health",
    value_column: str = "value_double",
    time_column: str = "systime"
)

Bases: Base

Engineering: Control Loop Health

Continuously assess PID/control loop health from setpoint + actual pairs, independent of setpoint changes. Computes error integrals, detects oscillation in the error signal, and checks for valve saturation.

Methods: - error_integrals: Per-window IAE, ISE, ITAE, bias. - detect_oscillation: Sustained oscillation in the error signal. - output_saturation: Valve pegged at limits. - loop_health_summary: Shift-level report card.

error_integrals ¤

error_integrals(
    window: str = "1h", *, max_gap: str | None = None
) -> pd.DataFrame

Per-window error integrals for control loop performance.

Parameters:

Name Type Description Default
window str

Resample window (e.g. '1h', '8h').

'1h'
max_gap str | None

Default None weights each error by the time since the previous sample, with no gap cap. When set, each error is held until the next sample (zero-order hold, i.e. weighted by the time after it) and any interval longer than max_gap contributes nothing, so a communication gap is not integrated as one huge error.

None

Returns:

Type Description
DataFrame

DataFrame with columns: start, end, duration_seconds, iae, ise,

DataFrame

itae, bias, sample_count. end is start + window.

detect_oscillation ¤

detect_oscillation(
    window: str = "5min", min_crossings: int = 4
) -> pd.DataFrame

Detect sustained oscillation in the error signal.

Parameters:

Name Type Description Default
window str

Analysis window size.

'5min'
min_crossings int

Minimum zero-crossings to flag oscillation.

4

Returns:

Type Description
DataFrame

DataFrame with columns: start, end, duration_seconds, uuid,

DataFrame

source_uuid (setpoint and actual uuids, comma-joined), is_delta,

DataFrame

crossing_count, estimated_period_seconds, amplitude,

DataFrame

damping_direction.

output_saturation ¤

output_saturation(
    upper_limit: float = 100.0,
    lower_limit: float = 0.0,
    window: str = "1h",
) -> pd.DataFrame

Detect when controller output is pegged at limits.

Requires output_uuid in constructor.

Parameters:

Name Type Description Default
upper_limit float

Upper output limit, in the output signal's units. Samples within 1% of the upper_limit - lower_limit range of it count as saturated high. Default 100.0.

100.0
lower_limit float

Lower output limit, in the output signal's units. Samples within 1% of the range of it count as saturated low. Default 0.0.

0.0
window str

Resample window as a pandas offset string (e.g. '1h').

'1h'

Returns:

Type Description
DataFrame

DataFrame with columns: start, end, duration_seconds, uuid,

DataFrame

source_uuid (the output uuid), pct_time_at_high, pct_time_at_low,

DataFrame

pct_time_saturated, longest_saturation_seconds. One row per

DataFrame

window; end is start + window.

DataFrame

Canonical twin columns: time_at_high_pct (after pct_time_at_high),

DataFrame

time_at_low_pct (after pct_time_at_low), time_saturated_pct

DataFrame

(after pct_time_saturated); each holds the same values as the deprecated

DataFrame

column right before it, which is removed in the next major release.

loop_health_summary ¤

loop_health_summary(
    window: str = "8h",
    oscillation_window: str = "5min",
    min_crossings: int = 4,
) -> pd.DataFrame

Shift-level report card combining all loop health metrics.

Oscillation is detected with :meth:detect_oscillation on short analysis windows of oscillation_window (not on the report window: a whole shift would almost always contain enough zero-crossings). oscillation_count is the number of merged oscillation episodes that start inside each report window.

Parameters:

Name Type Description Default
window str

Report window as a pandas offset string (e.g. '8h' for a shift). Used for the error integrals, the saturation share and to bucket the oscillation episodes.

'8h'
oscillation_window str

Analysis window passed to :meth:detect_oscillation (default '5min').

'5min'
min_crossings int

Minimum zero-crossings per oscillation_window to flag oscillation, passed to :meth:detect_oscillation.

4

Returns:

Type Description
DataFrame

DataFrame with columns: start, end, duration_seconds, iae, bias,

DataFrame

oscillation_count, pct_saturated, health_grade. end is

DataFrame

start + window.

DataFrame

Canonical twin columns: saturated_pct (after pct_saturated); each

DataFrame

holds the same values as the deprecated column right before it, which is

DataFrame

removed in the next major release.