Expression Rule Cookbook

A reference for building fault detection rules in open-fdd. Rules use YAML with expression type: when the expression evaluates to True, a fault is flagged. open-fdd injects NumPy as np into expression evaluation, so you can use np.maximum, np.abs, np.sqrt, etc. for vectorized math. Tuning: Change params in YAML and trigger an FDD run (or wait for the schedule); the platform hot-reloads rules each run, so no restart is needed. See Fault rules overview and Configuration (rules_dir is RDF-driven via GET /config).


100% Brick-model driven (no column in rules)

Rule inputs use Brick class names only (e.g. Supply_Air_Temperature_Sensor). The Brick TTL is the source of truth: SPARQL resolves each Brick point to its external timeseries reference (ref:TimeseriesReference / ref:hasTimeseriesId), which yields the DataFrame column. Open-FDD embraces Brick timeseries storage at the heart of FDD — rules never reference column; the data model provides the mapping. See the SPARQL cookbook (Recipe 5) and run similar queries via Data Model Testing or scripts/automated_testing/sparql/.

  • Platform (DB): Points have brick_type and external_id; TTL is built from DB. PATCH points or use data-model import to set brick_type (e.g. Supply_Air_Temperature_Sensor for SA-T).
  • Disambiguation: When multiple points share a Brick class, use ofdd:mapsToRuleInput in TTL; the runner resolves BrickClass|rule_input.

How to define expressions

  1. Inputs — Declare Brick classes only (brick: Supply_Air_Temperature_Sensor). The runner resolves these via the Brick TTL (SPARQL) and Brick external timeseries references to DataFrame columns. Do not add column in YAML.
  2. Params — Thresholds and constants go in params. Reference by name (e.g. err_thresh, vfd_max).
  3. Expression — Must evaluate to a boolean Series (True = fault). Use & (AND), | (OR), ~ (NOT). Use .diff(), .rolling(), .notna() for time-series logic.

Minimal example (TTL-driven; no column):

name: high_temp_check
type: expression
flag: high_temp_flag

inputs:
  Supply_Air_Temperature_Sensor:
    brick: Supply_Air_Temperature_Sensor

params:
  max_temp: 90.0

expression: |
  Supply_Air_Temperature_Sensor > max_temp

The Brick TTL (and SPARQL) maps each Brick class to the actual column via external timeseries references. Set points.brick_type in the DB and sync TTL so the engine can resolve columns from the Brick model.


AHU rules (reference-style)

The following rules follow common industry practice for air-handling fault detection. Rules A through M are adapted from ASHRAE Guideline 36 (GL36) AFDD guidance. Thresholds and logic are tunable; adjust params for your site.

Rule A — Duct static below setpoint at full fan speed

Static pressure under setpoint while supply fan runs near maximum. May indicate duct leakage, undersized fan, or terminal damper issues. Adapted from GL36 AFDD guidance.

name: duct_static_low_at_full_speed
description: Static pressure below setpoint when fan at full speed (GL36-inspired)
type: expression
flag: rule_a_flag
equipment_type: [VAV_AHU]

inputs:
  Supply_Air_Static_Pressure_Sensor:
    brick: Supply_Air_Static_Pressure_Sensor
  Supply_Air_Static_Pressure_Setpoint:
    brick: Supply_Air_Static_Pressure_Setpoint
  Supply_Fan_Speed_Command:
    brick: Supply_Fan_Speed_Command

params:
  sp_margin: 0.12
  drv_hi_frac: 0.93
  drv_near_hi: 0.06

expression: |
  (Supply_Air_Static_Pressure_Sensor < Supply_Air_Static_Pressure_Setpoint - sp_margin) & (Supply_Fan_Speed_Command >= drv_hi_frac - drv_near_hi)

Rule B — Blended air temp below expected band

Blended air temp should lie between outdoor and return. If below both (minus tolerance), suspect sensor or mixing fault. Adapted from GL36 AFDD guidance.

name: blend_temp_below_band
description: Blended air temp below expected range (OAT/RAT) (GL36-inspired)
type: expression
flag: rule_b_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Mixed_Air_Temperature_Sensor:
    brick: Mixed_Air_Temperature_Sensor
  Return_Air_Temperature_Sensor:
    brick: Return_Air_Temperature_Sensor
  Outside_Air_Temperature_Sensor:
    brick: Outside_Air_Temperature_Sensor
  Supply_Fan_Speed_Command:
    brick: Supply_Fan_Speed_Command

params:
  blend_tol: 1.15
  rat_tol: 1.15
  oat_tol: 1.15

expression: |
  (Mixed_Air_Temperature_Sensor - blend_tol < np.minimum(Return_Air_Temperature_Sensor - rat_tol, Outside_Air_Temperature_Sensor - oat_tol)) & (Supply_Fan_Speed_Command > 0.01)

Rule C — Blended air temp above expected band

Blended air temp above the higher of OAT and RAT (plus tolerance) indicates mixing or sensor fault. Adapted from GL36 AFDD guidance.

name: blend_temp_above_band
description: Blended air temp above expected range (OAT/RAT) (GL36-inspired)
type: expression
flag: rule_c_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Mixed_Air_Temperature_Sensor:
    brick: Mixed_Air_Temperature_Sensor
  Return_Air_Temperature_Sensor:
    brick: Return_Air_Temperature_Sensor
  Outside_Air_Temperature_Sensor:
    brick: Outside_Air_Temperature_Sensor
  Supply_Fan_Speed_Command:
    brick: Supply_Fan_Speed_Command

params:
  blend_tol: 1.15
  rat_tol: 1.15
  oat_tol: 1.15

expression: |
  (Mixed_Air_Temperature_Sensor - blend_tol > np.maximum(Return_Air_Temperature_Sensor + rat_tol, Outside_Air_Temperature_Sensor + oat_tol)) & (Supply_Fan_Speed_Command > 0.01)

Hunting/oscillation — see Hunting Rule.

Rule D — Discharge air cold when heating commanded

Discharge air temp below blended air when heating valve is open. Indicates heating coil or valve failure. Adapted from GL36 AFDD guidance.

name: discharge_cold_when_heating
description: Discharge air below blended air when heating active (GL36-inspired)
type: expression
flag: rule_d_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Mixed_Air_Temperature_Sensor:
    brick: Mixed_Air_Temperature_Sensor
  Supply_Air_Temperature_Sensor:
    brick: Supply_Air_Temperature_Sensor
  Valve_Command:
    brick: Valve_Command
  Supply_Fan_Speed_Command:
    brick: Supply_Fan_Speed_Command

params:
  blend_tol: 1.15
  sat_tol: 1.15
  fan_delta_t: 0.55

expression: |
  (Supply_Air_Temperature_Sensor + sat_tol <= Mixed_Air_Temperature_Sensor - blend_tol + fan_delta_t) & (Valve_Command > 0.01) & (Supply_Fan_Speed_Command > 0.01)

OA fraction — see OA Fraction Rule.

Rule E — SAT too low with full heating

Heating valve fully open but SAT remains below setpoint. Indicates undersized coil or valve failure. Adapted from GL36 AFDD guidance.

name: sat_too_low_full_heating
description: SAT below setpoint with heating valve fully open (GL36-inspired)
type: expression
flag: rule_e_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Supply_Air_Temperature_Sensor:
    brick: Supply_Air_Temperature_Sensor
  Supply_Air_Temperature_Setpoint:
    brick: Supply_Air_Temperature_Setpoint
  Valve_Command:
    brick: Valve_Command
  Supply_Fan_Speed_Command:
    brick: Supply_Fan_Speed_Command

params:
  supply_err_thres: 1.0

expression: |
  (Supply_Air_Temperature_Sensor < Supply_Air_Temperature_Setpoint - supply_err_thres) & (Valve_Command > 0.9) & (Supply_Fan_Speed_Command > 0)

Rule F — SAT/MAT mismatch in economizer mode

In economizer mode (min mechanical cooling), SAT should approximate MAT. Large deviation suggests coil bypass or sensor error. Adapted from GL36 AFDD guidance.

name: discharge_blend_mismatch_econ
description: Discharge and blended air diverge in economizer mode (GL36-inspired)
type: expression
flag: rule_f_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Mixed_Air_Temperature_Sensor:
    brick: Mixed_Air_Temperature_Sensor
  Supply_Air_Temperature_Sensor:
    brick: Supply_Air_Temperature_Sensor
  Damper_Position_Command:
    brick: Damper_Position_Command
  Valve_Command:
    brick: Valve_Command

params:
  fan_delta_t: 0.55
  blend_tol: 1.15
  sat_tol: 1.15
  econ_min_open: 0.12

expression: |
  (np.abs(Supply_Air_Temperature_Sensor - fan_delta_t - Mixed_Air_Temperature_Sensor) > np.sqrt(sat_tol**2 + blend_tol**2)) & (Damper_Position_Command > econ_min_open) & (Valve_Command < 0.1)

Rule G — Ambient too warm for free cooling

Outside air temperature exceeds SAT setpoint while economizer is active and mechanical cooling is off. Economizer should not be providing “free” cooling under these conditions. Adapted from GL36 AFDD guidance.

name: ambient_warm_free_cool
description: Outdoor air above setpoint in free cooling mode (GL36-inspired)
type: expression
flag: rule_g_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Outside_Air_Temperature_Sensor:
    brick: Outside_Air_Temperature_Sensor
  Supply_Air_Temperature_Setpoint:
    brick: Supply_Air_Temperature_Setpoint
  Damper_Position_Command:
    brick: Damper_Position_Command
  Valve_Command:
    brick: Valve_Command

params:
  oat_tol: 1.15
  fan_delta_t: 0.55
  sat_tol: 1.15
  econ_min_open: 0.12

expression: |
  (Outside_Air_Temperature_Sensor - oat_tol > Supply_Air_Temperature_Setpoint - fan_delta_t + sat_tol) & (Damper_Position_Command > econ_min_open) & (Valve_Command < 0.1)

Rule H — Ambient vs blended mismatch (econ + mech cooling)

When both economizer and mechanical cooling are active, MAT should approach OAT. Large deviation suggests inadequate mixing or damper fault. Adapted from GL36 AFDD guidance.

name: ambient_blend_mismatch_econ_mech
description: Outdoor and blended air diverge in econ+mech cooling (GL36-inspired)
type: expression
flag: rule_h_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Outside_Air_Temperature_Sensor:
    brick: Outside_Air_Temperature_Sensor
  Mixed_Air_Temperature_Sensor:
    brick: Mixed_Air_Temperature_Sensor
  Valve_Command:
    brick: Valve_Command
  Damper_Position_Command:
    brick: Damper_Position_Command

params:
  oat_tol: 1.15
  blend_tol: 1.15

expression: |
  (np.abs(Mixed_Air_Temperature_Sensor - Outside_Air_Temperature_Sensor) > np.sqrt(blend_tol**2 + oat_tol**2)) & (Valve_Command > 0.01) & (Damper_Position_Command > 0.9)

Rule I — Ambient vs blended mismatch (econ-only)

In economizer-only mode, MAT should match OAT. Deviation indicates damper or mixing fault. Adapted from GL36 AFDD guidance.

name: ambient_blend_mismatch_econ
description: Outdoor and blended air diverge in economizer-only mode (GL36-inspired)
type: expression
flag: rule_i_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Outside_Air_Temperature_Sensor:
    brick: Outside_Air_Temperature_Sensor
  Mixed_Air_Temperature_Sensor:
    brick: Mixed_Air_Temperature_Sensor
  Damper_Position_Command:
    brick: Damper_Position_Command

params:
  oat_tol: 1.15
  blend_tol: 1.15

expression: |
  (np.abs(Mixed_Air_Temperature_Sensor - Outside_Air_Temperature_Sensor) > np.sqrt(blend_tol**2 + oat_tol**2)) & (Damper_Position_Command > 0.9)

Rule J — Discharge above blended in cooling

SAT exceeds MAT when cooling (econ+mech or mech-only) is active. Indicates underperforming cooling coil or valve. Adapted from GL36 AFDD guidance.

name: discharge_above_blend_cooling
description: Discharge air above blended air in cooling modes (GL36-inspired)
type: expression
flag: rule_j_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Supply_Air_Temperature_Sensor:
    brick: Supply_Air_Temperature_Sensor
  Mixed_Air_Temperature_Sensor:
    brick: Mixed_Air_Temperature_Sensor
  Valve_Command:
    brick: Valve_Command
  Damper_Position_Command:
    brick: Damper_Position_Command

params:
  fan_delta_t: 0.55
  blend_tol: 1.15
  sat_tol: 1.15
  econ_min_open: 0.12

expression: |
  (Supply_Air_Temperature_Sensor > Mixed_Air_Temperature_Sensor + np.sqrt(sat_tol**2 + blend_tol**2) + fan_delta_t) & (((Damper_Position_Command > 0.9) & (Valve_Command > 0)) | ((Damper_Position_Command <= econ_min_open) & (Valve_Command > 0.9)))

Rule K — Discharge above setpoint in full cooling

SAT above setpoint with cooling at full capacity. Suggests undersized coil or plant limits. Adapted from GL36 AFDD guidance.

name: discharge_above_sp_full_cool
description: Discharge air above setpoint in full cooling mode (GL36-inspired)
type: expression
flag: rule_k_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Supply_Air_Temperature_Sensor:
    brick: Supply_Air_Temperature_Sensor
  Supply_Air_Temperature_Setpoint:
    brick: Supply_Air_Temperature_Setpoint
  Valve_Command:
    brick: Valve_Command
  Damper_Position_Command:
    brick: Damper_Position_Command

params:
  sat_tol: 1.15
  econ_min_open: 0.12

expression: |
  (Supply_Air_Temperature_Sensor > Supply_Air_Temperature_Setpoint + sat_tol) & (((Damper_Position_Command > 0.9) & (Valve_Command > 0.9)) | ((Damper_Position_Command <= econ_min_open) & (Valve_Command > 0.9)))

Rule L — Cooling coil delta-T when inactive

Temperature drop across cooling coil when it should be off. Indicates leaking CHW valve or coil bypass. Adapted from GL36 AFDD guidance.

name: clg_coil_drop_when_off
description: Temperature drop across cooling coil when it should be off (GL36-inspired)
type: expression
flag: rule_l_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Cooling_Coil_Entering_Air_Temperature_Sensor:
    brick: Cooling_Coil_Entering_Air_Temperature_Sensor
  Cooling_Coil_Leaving_Air_Temperature_Sensor:
    brick: Cooling_Coil_Leaving_Air_Temperature_Sensor
  Heating_Valve_Command:
    brick: Valve_Command
  Cooling_Valve_Command:
    brick: Valve_Command
  Damper_Position_Command:
    brick: Damper_Position_Command

params:
  enter_tol: 1.15
  leave_tol: 1.15
  econ_min_open: 0.12

expression: |
  ((Cooling_Coil_Entering_Air_Temperature_Sensor - Cooling_Coil_Leaving_Air_Temperature_Sensor) > np.sqrt(enter_tol**2 + leave_tol**2)) & (((Heating_Valve_Command > 0) & (Cooling_Valve_Command == 0) & (Damper_Position_Command <= econ_min_open)) | ((Heating_Valve_Command == 0) & (Cooling_Valve_Command == 0) & (Damper_Position_Command > econ_min_open)))

Rule M — Heating coil delta-T when inactive

Temperature rise across heating coil when it should be off. Indicates leaking HW valve. Adapted from GL36 AFDD guidance.

name: htg_coil_rise_when_off
description: Temperature rise across heating coil when it should be off (GL36-inspired)
type: expression
flag: rule_m_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Heating_Coil_Entering_Air_Temperature_Sensor:
    brick: Heating_Coil_Entering_Air_Temperature_Sensor
  Heating_Coil_Leaving_Air_Temperature_Sensor:
    brick: Heating_Coil_Leaving_Air_Temperature_Sensor
  Heating_Valve_Command:
    brick: Valve_Command
  Cooling_Valve_Command:
    brick: Valve_Command
  Damper_Position_Command:
    brick: Damper_Position_Command

params:
  enter_tol: 1.15
  leave_tol: 1.15
  fan_delta_t: 0.55
  econ_min_open: 0.12

expression: |
  ((Heating_Coil_Leaving_Air_Temperature_Sensor - Heating_Coil_Entering_Air_Temperature_Sensor) > np.sqrt(enter_tol**2 + leave_tol**2) + fan_delta_t) & (((Heating_Valve_Command == 0) & (Cooling_Valve_Command == 0) & (Damper_Position_Command > econ_min_open)) | ((Heating_Valve_Command == 0) & (Cooling_Valve_Command > 0) & (Damper_Position_Command > 0.9)) | ((Heating_Valve_Command == 0) & (Cooling_Valve_Command > 0) & (Damper_Position_Command <= econ_min_open)))

Heat exchanger effectiveness — see ERV/Heat Exchanger Rule.


Central plant

Differential pressure at max pump speed

Variable-speed pump cannot meet differential pressure setpoint at full speed. Indicates piping issues, undersized pump, or blocked strainers.

name: dp_below_sp_pump_max
description: Differential pressure below setpoint with pump at full speed
type: expression
flag: dp_pump_flag

inputs:
  Differential_Pressure_Sensor:
    brick: Differential_Pressure_Sensor
  Differential_Pressure_Setpoint:
    brick: Differential_Pressure_Setpoint
  Pump_Speed_Command:
    brick: Pump_Speed_Command

params:
  dp_margin: 2.2
  pmp_hi_frac: 0.93
  pmp_near_hi: 0.06

expression: |
  (Differential_Pressure_Sensor < Differential_Pressure_Setpoint - dp_margin) & (Pump_Speed_Command >= pmp_hi_frac - pmp_near_hi)

Plant flow high at max pump

Flow unusually high with pump at high speed. Suggests short circuit or flow meter error.

name: flow_high_pump_max
description: Water flow unusually high with pump at full speed
type: expression
flag: flow_high_flag

inputs:
  Water_Flow_Sensor:
    brick: Water_Flow_Sensor
  Pump_Speed_Command:
    brick: Pump_Speed_Command

params:
  flow_hi_limit: 1100.0
  pmp_hi_frac: 0.93
  pmp_near_hi: 0.06

expression: |
  (Water_Flow_Sensor > flow_hi_limit) & (Pump_Speed_Command >= pmp_hi_frac - pmp_near_hi)

Plant supply temp outside deadband

CHW supply temperature outside deadband while pump runs. Requires coil entering/leaving or plant supply temp sensors.

name: plant_supply_temp_deadband
description: Supply water temp outside deadband during pump operation
type: expression
flag: chw_temp_fault

inputs:
  Chilled_Water_Supply_Temperature_Sensor:
    brick: Chilled_Water_Supply_Temperature_Sensor
  Chilled_Water_Supply_Temperature_Setpoint:
    brick: Chilled_Water_Supply_Temperature_Setpoint
  Pump_Speed_Command:
    brick: Pump_Speed_Command

params:
  sp_band: 2.2

expression: |
  (Pump_Speed_Command > 0.01) & ((Chilled_Water_Supply_Temperature_Sensor < Chilled_Water_Supply_Temperature_Setpoint - sp_band) | (Chilled_Water_Supply_Temperature_Sensor > Chilled_Water_Supply_Temperature_Setpoint + sp_band))

Chiller runtime over daily limit

Chiller running beyond a daily threshold (e.g. 23 hours). Often indicates over-cooling or schedules that bypass lockout. Use a rolling window sized for your data interval (e.g. 5‑min data → 276 samples ≈ 23 h).

name: chiller_excessive_runtime
description: Chiller runtime exceeds daily threshold (rolling window)
type: expression
flag: chiller_runtime_fault

inputs:
  Chiller_Status:
    brick: Chiller_Status

params:
  # For 5-min data: 23 hours ≈ 276 samples; max_runtime = count of "on" samples in window
  rolling_samples: 276
  max_runtime_samples: 264

expression: |
  Chiller_Status.rolling(window=rolling_samples).sum() > max_runtime_samples

Note: Adjust rolling_samples and max_runtime_samples for your data interval. For 5‑min data, 264 samples ≈ 22 hours.


Heat pumps

Discharge cold when heating

Flags when discharge air temperature is below a minimum (e.g. 80°F) while the supply fan is running. Indicates the heat pump is not heating effectively—possible issues with compressor, refrigerant, or reversing valve. Tunable via min_discharge_temp. Logic: if zone temp < 69°F (heating mode), the discharge should be warm; a cold discharge with the fan on and a cold zone indicates the heat pump is failing to heat.


name: hp_discharge_cold_when_heating
description: Discharge air below minimum when fan on and zone is cold (heating mode)
type: expression
flag: hp_discharge_cold_flag
equipment_type: [Heat_Pump]

inputs:
  Supply_Air_Temperature_Sensor:
    brick: Supply_Air_Temperature_Sensor
  Zone_Temperature_Sensor:
    brick: Zone_Temperature_Sensor
  Supply_Fan_Status:
    brick: Supply_Fan_Status

params:
  min_discharge_temp: 85
  zone_cold_threshold: 69.0
  fan_on_threshold: 0.01

expression: |
  (Supply_Fan_Status > fan_on_threshold) & (Zone_Temperature_Sensor < zone_cold_threshold) & (Supply_Air_Temperature_Sensor < min_discharge_temp)


VAV zones

Excessive heating during warm weather

Reheat valve open when outdoor air is warm. Suggests over-cooling or setpoint issues.

name: zone_reheat_warm_ambient
description: Heating valve open when OAT is high
type: expression
flag: excessive_heating_flag
equipment_type: [VAV]

inputs:
  Outside_Air_Temperature_Sensor:
    brick: Outside_Air_Temperature_Sensor
  Reheat_Valve_Command:
    brick: Valve_Command

params:
  t_amb_cutoff: 78.0
  reheat_open_min: 0.52

expression: |
  (Outside_Air_Temperature_Sensor > t_amb_cutoff) & (Reheat_Valve_Command > reheat_open_min)

Damper or valve at full open

Damper or reheat valve consistently at full open. Indicates override, undersized equipment, or control fault.

name: zone_damper_valve_full_open
description: Damper or valve at full open for extended period
type: expression
flag: damper_100_flag
equipment_type: [VAV]

inputs:
  Damper_Position_Command:
    brick: Damper_Position_Command

params:
  full_open_pct: 97.5
  roll_samples: 105

expression: |
  (Damper_Position_Command > full_open_pct) & (Damper_Position_Command.rolling(roll_samples).min() > full_open_pct)

Zone and IAQ bounds

For CO2 and zone temperature out-of-range checks, use the Bounds Ruleco2_bounds and zone_temp_bounds examples.


Opportunistic rules (economizer & ventilation)

Economizing when outdoor conditions are unfavorable

Economizer active when outdoor air is too warm or humid. Use OAT (or enthalpy if available) vs. threshold.

name: econ_active_warm_ambient
description: OA damper open when outdoor conditions do not favor economizing
type: expression
flag: econ_when_shouldnt_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Outside_Air_Temperature_Sensor:
    brick: Outside_Air_Temperature_Sensor
  Damper_Position_Command:
    brick: Damper_Position_Command

params:
  t_amb_econ_cutoff: 63.0
  dpr_econ_min: 0.42

expression: |
  (Outside_Air_Temperature_Sensor > t_amb_econ_cutoff) & (Damper_Position_Command > dpr_econ_min)

Mechanical cooling when econ could suffice

Cooling valve open when outdoor air is cool enough for economizer. Opportunity to reduce mechanical cooling.

name: mech_cool_when_econ_available
description: Mechanical cooling active when economizing could suffice
type: expression
flag: cooling_when_econ_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Outside_Air_Temperature_Sensor:
    brick: Outside_Air_Temperature_Sensor
  Damper_Position_Command:
    brick: Damper_Position_Command
  Valve_Command:
    brick: Valve_Command

params:
  t_amb_econ_cutoff: 63.0
  dpr_not_econ_max: 0.32

expression: |
  (Outside_Air_Temperature_Sensor < t_amb_econ_cutoff) & (Damper_Position_Command < dpr_not_econ_max) & (Valve_Command > 0.01)

Low ventilation (estimated OA fraction)

For units without airflow meters, estimate OA fraction from OAT, MAT, RAT. Flag when below minimum design OA.

name: low_oa_fraction_estimated
description: Estimated OA fraction below minimum (OAT, MAT, RAT method)
type: expression
flag: low_vent_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Mixed_Air_Temperature_Sensor:
    brick: Mixed_Air_Temperature_Sensor
  Return_Air_Temperature_Sensor:
    brick: Return_Air_Temperature_Sensor
  Outside_Air_Temperature_Sensor:
    brick: Outside_Air_Temperature_Sensor
  Supply_Fan_Speed_Command:
    brick: Supply_Fan_Speed_Command

params:
  oa_min_pct: 21.0
  t_rat_oat_min_gap: 2.2

expression: |
  (Supply_Fan_Speed_Command > 0.01) & (np.abs(Return_Air_Temperature_Sensor - Outside_Air_Temperature_Sensor) > t_rat_oat_min_gap) & (((Mixed_Air_Temperature_Sensor - Return_Air_Temperature_Sensor) / (Outside_Air_Temperature_Sensor - Return_Air_Temperature_Sensor) * 100) < oa_min_pct)

Note: Guard against division-by-zero when outdoor and return temps are close.

Preheat over-conditioning

Preheat coil leaving temp higher than needed (e.g. above OAT when OAT > SAT SP, or above SAT SP when OAT < SAT SP). Indicates wasted heating energy.

name: preheat_excess_temp
description: Preheat coil leaving temp above required level
type: expression
flag: preheat_waste_flag
equipment_type: [AHU, VAV_AHU]

inputs:
  Preheat_Coil_Leaving_Air_Temperature_Sensor:
    brick: Preheat_Coil_Leaving_Air_Temperature_Sensor
  Supply_Air_Temperature_Setpoint:
    brick: Supply_Air_Temperature_Setpoint
  Outside_Air_Temperature_Sensor:
    brick: Outside_Air_Temperature_Sensor
  Valve_Command:
    brick: Valve_Command

params:
  excess_tol: 2.2

expression: |
  (Valve_Command > 0.01) & (((Outside_Air_Temperature_Sensor > Supply_Air_Temperature_Setpoint) & (Preheat_Coil_Leaving_Air_Temperature_Sensor - Outside_Air_Temperature_Sensor > excess_tol)) | ((Outside_Air_Temperature_Sensor < Supply_Air_Temperature_Setpoint) & (Preheat_Coil_Leaving_Air_Temperature_Sensor - Supply_Air_Temperature_Setpoint > excess_tol)))

Blended air damper deviation

Expected MAT (from OAT, RAT, damper positions) differs from measured MAT. Indicates damper leakage or faulty mixing.

Requires airflow or damper position data; logic is more involved. A simplified version compares MAT to a weighted blend of OAT and RAT when dampers suggest significant OA.


Weather station

weather_temp_stuck (flatline) — see Flatline Rule.

Unrealistic temperature spike

Temperature change between consecutive readings exceeds physical limit.

name: weather_temp_spike
description: Unrealistic temperature change between readings
type: expression
flag: fault_temp_spike

inputs:
  Outside_Air_Temperature_Sensor:
    brick: Outside_Air_Temperature_Sensor

params:
  spike_limit: 16.0

expression: |
  Outside_Air_Temperature_Sensor.diff().abs() > spike_limit

RH bounds

For relative humidity out-of-range, use the Bounds Rulerh_bounds example.

Wind gust vs sustained

name: weather_gust_lt_wind
description: Wind gust reported lower than sustained wind
type: expression
flag: fault_gust_lt_wind

inputs:
  Wind_Gust_Speed_Sensor:
    brick: Wind_Gust_Speed_Sensor
  Wind_Speed_Sensor:
    brick: Wind_Speed_Sensor

expression: |
  Wind_Gust_Speed_Sensor.notna() & Wind_Speed_Sensor.notna() & (Wind_Gust_Speed_Sensor < Wind_Speed_Sensor)

Sensor validation (bounds & flatline)

Use the Bounds Rule and Flatline Rule for generic sensor checks. Typical bounds:

Sensor type Min Max
Zone temp 40 100
Supply air temp 40 150
Air pressure (inH2O) -5 10
RH 0 100
Chilled water temp 35 100
Hot water temp 50 212
Condenser water 50 110
CO2 (ppm) 400 2000

Next: Flat Line Sensor Tutorial