Reusable prerequisite macros

Composable guards used across rules. Compile to SQL CASE predicates or Pandas boolean masks. Reduces false positives from startup, unoccupied, override, and bad-sensor states.


macro.occupancy_cooling

Intent: Rule applies only when zone/building is occupied and needs cooling (or neutral).

DataFusion SQL

-- occ_mode: 'occupied' | 'unoccupied' | 'bypass' (site-specific enum)
(occ_mode = 'occupied' OR occ_mode IS NULL)

Pandas

def macro_occupancy_cooling(d: pd.DataFrame) -> pd.Series:
    occ = d.get("occ_mode")
    if occ is None:
        return pd.Series(True, index=d.index)
    return occ.eq("occupied") | occ.isna()

macro.fan_proven_on

Intent: Supply fan commanded and proven (status feedback or inferred from static/flow).

DataFusion SQL

(
  CASE WHEN fan_cmd > 1.0 THEN fan_cmd / 100.0 ELSE fan_cmd END >= 0.05
)
AND (
  fan_status IS NULL
  OR fan_status = true
  OR CASE WHEN fan_cmd > 1.0 THEN fan_cmd / 100.0 ELSE fan_cmd END >= 0.05
)

Pandas

def macro_fan_proven_on(d: pd.DataFrame) -> pd.Series:
    cmd = norm_cmd(d["fan_cmd"]) if "fan_cmd" in d else 0
    stat = d["fan_status"] if "fan_status" in d else True
    on = pd.Series(cmd, index=d.index) >= 0.05
    if isinstance(stat, pd.Series):
        return on & (stat.isna() | stat.astype(bool))
    return on

macro.mode_delay

Intent: Suppress faults for N minutes after mode change (GL36-style startup delay).

DataFusion SQL

-- Requires mode_change_ts column or infer from occ_mode / fan_cmd edges
-- Default: 300 s after fan transitions off→on
NOT (
  fan_cmd_rising
  AND timestamp < mode_change_ts + INTERVAL '300' SECOND
)

Pandas

MODE_DELAY_SECONDS = 300  # site-adjustable

def macro_mode_delay(d: pd.DataFrame, col: str = "fan_cmd") -> pd.Series:
    cmd = norm_cmd(d[col]) if col in d else pd.Series(0, index=d.index)
    rising = (cmd >= 0.05) & (cmd.shift(1).fillna(0) < 0.05)
    t0 = d["timestamp"].where(rising).ffill()
    elapsed = (d["timestamp"] - t0).dt.total_seconds()
    return elapsed.isna() | (elapsed >= MODE_DELAY_SECONDS)

macro.steady_state_window

Intent: Require signal stability before evaluating control faults (e.g. hunting).

DataFusion SQL

-- Use rolling stddev over last N samples; DataFusion window:
-- stddev(sat) OVER (ORDER BY timestamp ROWS BETWEEN 11 PRECEDING AND CURRENT ROW) < 0.5
true  -- implement per-rule with window frame

Pandas

def macro_steady_state(series: pd.Series, window: int = 12, max_std: float = 0.5) -> pd.Series:
    return series.rolling(window, min_periods=window).std() <= max_std

macro.reset_enabled

Intent: Reset logic is active (not fixed setpoint mode).

DataFusion SQL

(reset_enable IS NULL OR reset_enable = true)
AND (override_active IS NULL OR override_active = false)

Pandas

def macro_reset_enabled(d: pd.DataFrame) -> pd.Series:
    en = d.get("reset_enable", True)
    ovr = d.get("override_active", False)
    if isinstance(en, pd.Series):
        base = en.isna() | en.astype(bool)
    else:
        base = pd.Series(True, index=d.index)
    if isinstance(ovr, pd.Series):
        return base & ~(ovr.fillna(False).astype(bool))
    return base

macro.override_suppression

Intent: Skip fault when manual override, hand, or bypass active.

DataFusion SQL

NOT (
  COALESCE(override_active, false) = true
  OR COALESCE(hand_mode, false) = true
  OR COALESCE(bypass_active, false) = true
)

Pandas

def macro_override_suppression(d: pd.DataFrame) -> pd.Series:
    flags = ["override_active", "hand_mode", "bypass_active"]
    mask = pd.Series(True, index=d.index)
    for f in flags:
        if f in d.columns:
            mask &= ~d[f].fillna(False).astype(bool)
    return mask

macro.sensor_quality_gate

Intent: Exclude samples where prerequisite sensors fail quality checks.

DataFusion SQL

oa_t IS NOT NULL AND oa_t >= 40.0 AND oa_t <= 110.0
AND (oa_t_stale IS NULL OR oa_t_stale = false)

Pandas

def macro_sensor_quality_gate(d: pd.DataFrame, col: str, lo: float, hi: float) -> pd.Series:
    s = d[col]
    ok = s.notna() & (s >= lo) & (s <= hi)
    stale_col = f"{col}_stale"
    if stale_col in d.columns:
        ok &= ~d[stale_col].fillna(False).astype(bool)
    return ok

Composing macros in a rule

SQL pattern

SELECT timestamp, equipment_id,
  CASE
    WHEN NOT (<macro.fan_proven_on>) THEN false
    WHEN NOT (<macro.override_suppression>) THEN false
    WHEN NOT (<macro.sensor_quality_gate on oa_t>) THEN false
    WHEN <detect condition> THEN true
    ELSE false
  END AS fault_raw
FROM telemetry_pivot
WHERE equipment_id = 'equip:your-ahu'

Pandas pattern

base = macro_fan_proven_on(d) & macro_override_suppression(d) & macro_sensor_quality_gate(d, "oa_t", 40, 110)
mask = base & detect_condition
d = apply_fault(d, mask)