Skip to content

Rule thresholds

Every tunable parameter of every built-in rule: its default (read from the rule's constructor), its unit, a sensible range, where the default comes from, and how to re-tune it from your own data. camber rules params [RULE] prints the same information in the terminal, with a ready-to-paste config snippet (--yaml puts each basis and calibration hint beside its value as a comment; --json is machine-readable).

Changing a threshold. Give the rule's entry in a run config params; only the ones you set change, the rest keep their defaults:

{"rules": [{"name": "leaking_valve", "params": {"fan_heat_f": 1.0}}]}

or, in a YAML config (the optional [yaml] extra), with the calibration recorded beside it:

rules:
  - name: leaking_valve
    params:
      # calibrated on <site> AHU-1, 2026-03-01..03-31 (fan on, both valves closed):
      # median mixed-to-supply rise 1.0 F; scored on April-June, not on March
      fan_heat_f: 1.0
    basis:
      fan_heat_f: "calibrated on AHU-1 March 2026 (fan-on, valves closed)"

The optional basis map copies that provenance into each finding of the rule (metrics["param_basis"]). How to calibrate without fooling yourself is in Tuning thresholds with your own data.

Basis says where a default comes from:

  • standard: ... -- the code cites that standard and section for the value;
  • public source: ... -- a published, non-standard source (a PNNL Re-tuning guide chapter, a DOE or LBNL guide, a manufacturer convention);
  • CAMBER judgment -- an engineering choice, usually screening-grade: re-tune it first;
  • calibrated on ... -- fitted to the named dataset or run.

Tier maps. A parameter whose value is a dict of tiers ({"warn": 5.0, "fault": 20.0}) names its keys in the unit column, and its range applies to each value ("each value: 0 to 100"); the calibration note says how the keys must be ordered.

Fixed in code lists thresholds that are not constructor parameters yet, so you can see what you cannot tune from a config today.

The registry is a provisional API (0.98): its shape may still change before 1.0.

Rules at a glance

Rule Tunable parameters Also fixed in code
actuator_stuck 14
airflow_tracking 4
boiler_short_cycle 1
boiler_summer_lockout 1
chiller_approach_fouling 2
chiller_efficiency 1
chiller_staging 2
chiller_staging_fleet 1
chw_plant_reset 4 yes
chw_pump_dp_reset 2 yes
chw_supply_tracking 5
co2_ventilation 3 yes
co2_ventilation_system 3 (passed to co2_ventilation)
cohort_airflow 6
cohort_space_temp 6
compressor_short_cycle 1 yes
compressor_staging 1 yes
condenser_bypass_leak 4
condenser_water_reset 3
control_hunting 4
cooling_tower_approach 4
damper_census 1 yes
dcv_system_verification 25 (passed to dcv_verification) yes
dcv_verification 21
dx_indoor_airflow 11
dx_refrigerant_charge 12
economizer_high_limit 11
filter_fouling 1
free_cooling_missed 11
g36_afdd 16
heatpump_defrost 1 yes
hp_capacity_shortfall 10
hp_mode_vs_need 9
hp_room_imbalance 9
hw_plant_deltat 1
hw_pump_dp_reset 2 yes
hw_pump_summer_lockout 1
leaking_valve 8
night_weekend_setback 8
outdoor_air_fraction 5
overcooling_min_flow 3
overcooling_severity 10
reheat_capacity_shortfall 10
reheat_minimization_g36 2 yes
reheat_penalty 5
sat_cohort_starvation 4
sat_reset_effectiveness 7
sat_rogue_zone_census 5
simultaneous_heat_cool 6
source_loop_deltat 6
static_cohort_starvation 4
static_pressure_reset 2
static_reset_effectiveness 7
static_rogue_zone_census 5
supply_air_control 4
supply_air_reset 1
supply_air_reset_compliance 10
unmet_setpoint_hours 5
zones_heat_cool_census 3

actuator_stuck

Parameter Default Unit Range Basis
roles ["damper", "heat_valve_position", "cool_valve"] role names (a list) "damper", "heat_valve_position", "heat_valve", "cool_valve" CAMBER judgment (0.98, #85): the box and fan-coil actuators a zone's demand drives
tol_pct 0.5 % 0.0 to 5.0 CAMBER judgment (0.98, #85): rounds away the sub-percent jitter of a 0.1 % resolution position trend
min_flat_hours 4.0 h 1.0 to 24.0 calibrated on ornl-frp-vav (default subset, 15-minute data): the box under test's longest flat run on its fault-free day is under 3 h, while healthy neighbours hold one mid-stroke position (26-41 %) for 4-10.5 h on 13 box-days -- so length alone does not decide, and 4 h is the shortest run judged
whole_day_share 0.98 fraction of a day's active samples 0.8 to 1.0 calibrated on ornl-frp-vav: the healthy boxes' longest flat runs cover at most 70 % of a day's occupied samples on the default subset and 95 % on the full one (room 102 on an airflow-test day); a stuck box covers 100 %
limit_pct 2.0 % (from either end of the stroke) 0.0 to 10.0 CAMBER judgment (0.98, #85): a position within 2 % of 0 or 100 reads as at its limit
min_driver_span_f 1.0 °F 0.5 to 5.0 CAMBER judgment (0.98, #85): one degree of zone-temperature or setpoint movement over a day is a demand a modulating actuator should answer
warm_margin_f 1.0 °F 0.5 to 5.0 CAMBER judgment (0.98, #85): the zone over its cooling setpoint (or, for a heating valve, under its heating setpoint) by more than 1 °F is a demand the actuator ignored
satisfied_margin_f 2.0 °F 1.0 to 6.0 CAMBER judgment (0.98, #85): a fully open damper with the zone 2 °F below its cooling setpoint is delivering cooling nobody asked for
min_airflow null cfm (the trended airflow's unit) 0.0 to 5000.0 CAMBER judgment (0.98, #85): with no airflow setpoint trended, a closed damper is judged against the box's minimum airflow when one is given
warn_pct 10.0 % of active samples 0.0 to 100.0 CAMBER judgment (0.98, #85): a tenth of the occupied samples held against demand
fault_pct 50.0 % of active samples 0.0 to 100.0 CAMBER judgment (0.98, #85): half the occupied samples held against the zone's demand
start_hour 7 hour of day (0-23) 0 to 23 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
end_hour 18 hour of day (1-24) 1 to 24 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
occupied_days [0, 1, 2, 3, 4] weekday numbers (Mon=0 ... Sun=6) 0 to 6 CAMBER judgment: a Monday-Friday schedule

How to calibrate:

  • roles: Leave out a role whose point is a command echo rather than a position (a flat echo says nothing about the actuator). heat_valve_position falls back to heat_valve when no position is mapped. Note: A list; each must be one of the range values. An air handler's outdoor-air damper is out of scope.
  • tol_pct: Read a known-stuck or manually held actuator's trend: set this above the jitter it shows while still. Larger values join slow real movement into one run.
  • min_flat_hours: Read the longest flat runs of healthy boxes in a typical week; set it at or above the run length you are willing to call 'held'.
  • whole_day_share: Read the share of each day's occupied samples that healthy boxes' longest run covers, and set it above the largest. Note: Only the unexplained-flat tier (warn at most) uses it.
  • limit_pct: Read where healthy actuators sit when fully shut or fully open (some never read exactly 0 or 100); set it just beyond that offset.
  • min_driver_span_f: Raise it in a zone with a very stable load, so a held mid-stroke position on a calm day is not called unexplained.
  • warm_margin_f: Set it to the zone loop's normal overshoot: read how far healthy zones run over their cooling setpoint in a hot afternoon. Note: The zone must be out by this much for at least 25 % of the run (fixed in code).
  • satisfied_margin_f: Read how far below the cooling setpoint healthy zones sit while their boxes are fully open (normally they do not); set it above that. Note: The zone must be this far inside for at least 50 % of the run (fixed in code).
  • min_airflow: Set the box's scheduled minimum (occupied) airflow from the design or the controller. A closed damper is contradicted when the airflow is at or below 5 % of it. Note: None (default): AIRFLOW_SP when mapped; with neither, a damper shut through occupied hours is judged against the occupied mode alone, and the finding carries a caveat.
  • warn_pct: Lower it to catch one stuck day in a long window; raise it to report only persistent faults. Note: warn_pct <= fault_pct. Flagged runs of either tier count.
  • fault_pct: As warn_pct. Only contradicted runs reach fault; an unexplained flat run is warn at most.
  • start_hour: Set it to the start of the building's occupied mode, read from the BAS schedule or from the hour the supply fan or occupied-mode point switches on in a typical week of trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • end_hour: Set it to the end of occupied mode (exclusive), read from the BAS schedule or trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • occupied_days: List the days the building runs occupied mode, e.g. [0, 1, 2, 3, 4, 5] for a Saturday schedule. Note: A list of integers; each must lie in the range.

airflow_tracking

Parameter Default Unit Range Basis
tol_frac 0.2 fraction of the airflow setpoint 0.05 to 0.5 CAMBER judgment
min_sp 1e-06 cfm (the airflow setpoint's unit) 0.0 to 500.0 CAMBER judgment: a divide-by-zero guard
warn_pct 10.0 % of active samples (setpoint above min_sp) 2.0 to 30.0 CAMBER judgment
fault_pct 25.0 % of active samples (setpoint above min_sp) 10.0 to 60.0 CAMBER judgment

How to calibrate:

  • tol_frac: On a known-good period take the 95th percentile of |flow - setpoint| / setpoint (mean_abs_rel_error summarises it) and set the tolerance just above it; flow sensors read poorly at low flow.
  • min_sp: Raise it to the box's minimum-flow setpoint (or a small fraction of design flow) to leave out samples where the relative error of a tiny setpoint is noise.
  • warn_pct: Read off_setpoint_pct on boxes known to be healthy; set the warn level above their spread.
  • fault_pct: Set it well above warn_pct. Note: Must be at or above warn_pct.

boiler_short_cycle

Parameter Default Unit Range Basis
max_starts_per_day 6.0 per day 2.0 to 24.0 CAMBER judgment: a generic cycling limit; the right value depends on the manufacturer's minimum cycle time (the code says to confirm it against the equipment's controls)

How to calibrate:

  • max_starts_per_day: Take the boiler's minimum on/off cycle time from its manual or burner control and divide the day by it with margin; or use the 95th-percentile daily starts of a known-good heating season. Coarse trends undercount starts, so the count is a floor. Note: Warn at this many firing starts per day, fault at twice it. Firing comes from the boiler run status (boiler_status); without one, from the gas input (gas_input_rate) above 5 % of its own 95th percentile (fixed in code), reported as run_source gas with a caveat.

boiler_summer_lockout

Parameter Default Unit Range Basis
summer_lockout_oat_f 65.0 °F 50.0 to 80.0 CAMBER judgment: a generic mild-climate lockout; the check itself follows PNNL Re-tuning Ch.8 (boiler lockout in summer), which gives no single value

How to calibrate:

  • summer_lockout_oat_f: Use the heating lockout the building's sequence of operations specifies, or the outdoor temperature above which no zone has called for heat in a known-good year of trends. Set it per climate zone: higher in a hot-desert zone, lower in a cool one. Note: Severity is fixed in code: warn when the boiler runs at OAT above this for >= 5 % of its running hours, fault at >= 20 %. Without an OAT point the check is not evaluated. Firing comes from the boiler run status (boiler_status); without one, from the gas input (gas_input_rate) above 5 % of its own 95th percentile (fixed in code), reported as run_source gas with a caveat.

chiller_approach_fouling

Parameter Default Unit Range Basis
cond_design_f 5.0 °F 1.0 to 10.0 CAMBER judgment: a typical clean-tube condenser approach
evap_design_f 4.0 °F 0.5 to 8.0 CAMBER judgment: a typical clean-tube evaporator approach

How to calibrate:

  • cond_design_f: Use the condenser approach from the chiller's selection report or start-up log, or the median of a period just after a tube cleaning at similar load. Note: Severity is fixed in code on the worse leg's median / design: warn at >= 1.5x, fault at >= 2x.
  • evap_design_f: Use the evaporator approach from the chiller's selection report or start-up log, or the median of a known-good period with correct charge and flow. Note: Same warn (1.5x) / fault (2x) ratios as the condenser leg.

chiller_efficiency

Parameter Default Unit Range Basis
design_kw_per_ton 0.85 kW/ton 0.4 to 1.6 CAMBER judgment: a generic ceiling between water-cooled centrifugal (~0.5-0.6) and air-cooled (~1.0-1.2) machines; the code says to set it from the chiller schedule

How to calibrate:

  • design_kw_per_ton: Use the chiller schedule or manufacturer selection (kW/ton at the loads it actually runs, e.g. IPLV/NPLV points), or the median kW/ton of a known-good season after a tube cleaning. Calibrating on the period you score hides a degradation already present. Note: Severity is fixed in code on the median kW/ton: warn at >= 1.2x this, fault at >= 1.5x. pct_hours_inefficient counts hours above 1.15x (metric only).

chiller_staging

Parameter Default Unit Range Basis
max_starts_per_day 6.0 per day 2.0 to 24.0 CAMBER judgment: a generic cycling limit; the right value depends on the manufacturer's minimum cycle time (the code says to confirm it against the equipment's controls)
low_load_pct 40.0 % of the chiller's observed peak tons 10.0 to 70.0 CAMBER judgment: the 'idling' cut-off for the optional load-factor metric

How to calibrate:

  • max_starts_per_day: Take the compressor's minimum start-to-start time (anti-recycle timer) from the chiller controls and allow margin; or use the 95th-percentile daily starts of a known-good cooling season. Coarse trends undercount starts, so the count is a floor. Note: Warn at this many starts per day, fault at twice it.
  • low_load_pct: Set it near the load below which the chiller's part-load curve (from the manufacturer) turns sharply less efficient, commonly 30-40 % of capacity. Note: Needs CHW flow and temperatures. Severity is fixed in code: warn when >= 25 % of running hours sit below this load, fault at >= 50 %. Peak is the observed peak, not nameplate.

chiller_staging_fleet

Parameter Default Unit Range Basis
redundancy_ceiling 0.9 fraction of one chiller's capacity 0.5 to 1.0 CAMBER judgment: how fully one chiller can be loaded before the next must stage on

How to calibrate:

  • redundancy_ceiling: Use the plant's stage-up load from its staging sequence (e.g. 0.85-0.95 of capacity). Capacity is each chiller's 95th-percentile power, so a plant that never reaches full load reads a low capacity; lower this if the flagged hours look like correct staging. Note: Severity is fixed in code: warn when >= 20 % of multi-chiller hours are over-staged, fault at >= 50 %.

chw_plant_reset

Parameter Default Unit Range Basis
design_deltaT_min_f 8.0 °F 3.0 to 20.0 public source: PNNL Building Re-tuning Ch.8 (the low-delta-T threshold, ~8 °F)
expected_reset_sign "negative" choice "negative", "positive", "any" CAMBER judgment: an outdoor-air CHWST reset lowers the supply temperature as OAT rises, a negative slope on OAT
flow_mode "auto" choice "auto", "constant", "variable" CAMBER judgment: a constant-primary-flow plant has a low loop delta-T at part load by design, so delta-T is judged only on variable flow
constant_flow_cv 0.05 fraction (std / mean) 0.01 to 0.2 CAMBER judgment: a constant-speed primary pump varies well under 5 %; the lbnl-chiller plant's chiller-1 flow varies <= 0.11 % on every run, a variable-flow loop by tens of %

How to calibrate:

  • design_deltaT_min_f: Read the loop's design delta-T from the chiller or coil schedules and set this a little below it; or take the 10th percentile of the loop delta-T over a known-good, loaded cooling season. Do not tune it on the period you score. Note: Severity is fixed in code: warn when >= 20 % of running hours sit below this, fault at >= 50 %; not judged on a constant-flow plant (see flow_mode).
  • expected_reset_sign: Read the reset schedule in the sequence of operations. "positive" fits a plant whose supply is deliberately raised in hot weather; "any" accepts either direction (the pre-0.98 behaviour). Note: A clear slope the other way is reported as chwst_reset_direction "reverse", counts as no reset, and warns.
  • flow_mode: Declare "constant" or "variable" from the plant's pumping design; "auto" reads the mapped CHW flow (chw_flow) over the running hours and needs >= 24 of them. Note: Without a flow point, "auto" reports flow_mode "unknown" and judges delta-T.
  • constant_flow_cv: Compute the coefficient of variation of the CHW flow over the running hours of a known period; a constant-flow plant sits near zero, a variable-flow one well above this. Note: Used only when flow_mode is "auto".

Fixed in code: warn when CHWST vs OAT is flat (|slope| < 0.05 °F/°F) or reversed; occupied hours only; without a run status or power, running means CHWST in 38-58 °F; CHWST <= 46 °F is reported as held low (metric only); fixed in code (camber/rules/chwplant_rule.py, camber/chwplant.py)

chw_pump_dp_reset

Parameter Default Unit Range Basis
near_min_pct "auto" % speed, or "auto" "auto", or 10.0 to 60.0 CAMBER judgment: learn the drive's own minimum (0.98, #86); a fixed 25 % missed the lbnl-chiller secondary pump, whose floor is 34.5 %
floor_tol_pct 1.0 % speed 0.1 to 5.0 CAMBER judgment: a VFD parked at its minimum reads within about a point of it

How to calibrate:

  • near_min_pct: Read the drive's minimum speed from the VFD parameters (or the sequence of operations) and set this a point or two above it; or plot a histogram of the running speed and find the pile-up at the bottom. "auto" learns it: the 2nd percentile of the running speed counts as the floor when >= 10 % of running samples sit within floor_tol_pct of it and the 90th percentile is >= 20 points above it (else the band falls back to 25 %). Note: The band used is max(25, learned floor + floor_tol_pct) under "auto", else the number given. Severity is fixed in code: warn when >= 50 % of running time is at or below the band. The metrics vfd_floor_pct, near_min_band_pct and near_min_source say what was used.
  • floor_tol_pct: Look at the spread of the speed readings while the pump sits at its minimum (trend resolution and rounding); set this just wider than that spread. Note: Used only when near_min_pct is "auto".

Fixed in code: fault when the pump runs near full speed (>= 90 % speed) for >= 60 % of its running time; warn at >= 30 % near full, or near minimum (at or below the near_min_pct band) for >= 50 %; running means speed > 5 %; a DP setpoint with a standard deviation under 0.5 (its own units) counts as flat (no reset, reported but not in severity); fixed in code (camber/rules/chwpump_rule.py, camber/chwpump.py)

chw_supply_tracking

Parameter Default Unit Range Basis
above_f 3.0 °F 1.0 to 8.0 CAMBER judgment: outside a healthy loop's ~1 °F control band, ~0.5 °F sensor accuracy and hourly staging transients together
warn_pct 10.0 % of running samples 1.0 to 50.0 CAMBER judgment
fault_pct 25.0 % of running samples 5.0 to 80.0 CAMBER judgment
settle_intervals 1 count (trend intervals) 0 to 12 CAMBER judgment: the first interval after each start is pull-down, not tracking
min_running 24 count (running samples) 6 to 500 CAMBER judgment: a day of hourly samples before judging

How to calibrate:

  • above_f: In a known-good period with the chiller running, take the 95th or 99th percentile of CHWST minus its setpoint and set this just above it. Do not tune it on the period you are scoring: a plant that never makes setpoint would set its own excuse.
  • warn_pct: Look at the share of running time above setpoint + above_f on a healthy plant and set the warn well above it; a summer design-day peak can briefly exceed capacity. Note: Must be below fault_pct.
  • fault_pct: Set it at a share of running time you would dispatch a technician for; keep it above warn_pct. The rule caps at warn when runtime was inferred from temperature.
  • settle_intervals: Measure how long CHWST takes to reach setpoint after a start in trends, and divide by the trend interval (round up). With 15-minute trends this is often 2-4. Note: Applies only when a run status or power gate is available.
  • min_running: Raise it for a short-sample trend (e.g. 96 for a day of 15-minute data) so the rule judges at least a day of running time.

co2_ventilation

Parameter Default Unit Range Basis
exclude_economizer true flag false, true CAMBER judgment: economizer outdoor air is free cooling, not over-ventilation (0.93, #38)
oa_damper_min_pct null % open (OA damper position) 0.0 to 60.0 CAMBER judgment: None derives the minimum from the damper's own trend
econ_high_limit_f 75.0 °F (outdoor air temperature) 55.0 to 80.0 standard: ASHRAE 90.1 -- the top of its fixed dry-bulb economizer high-limit range, as the code cites it (no section cited)

How to calibrate:

  • exclude_economizer: Leave it on wherever the unit economizes. Turn it off only to see the all-hours over-ventilation share, which is also reported as over_vent_all_pct when it is on. Note: Affects only the over-ventilation verdict; under-ventilation is judged on every occupied hour either way. It has no effect on a frame with no ECON_CMD and no OAT with an OA damper or mixed/return temperatures.
  • oa_damper_min_pct: Set it to the minimum-OA damper position from the BAS sequence or the TAB report. To read it from trends, take the 5th percentile of the damper while the fan runs and the damper is open, over a known-good period with no economizing (hot or cold weather). Note: None: the 5th percentile of the damper while open (and the fan on, when trended). An hour counts as economizing only when the damper sits more than 5 points above this minimum and the OAT is below econ_high_limit_f. Used only without an ECON_CMD point.
  • econ_high_limit_f: Set it to the high-limit setpoint programmed in the economizer sequence. From trends, take the highest OAT at which the OA damper still opens beyond its minimum over a summer of known-good operation. Note: Used only when economizer mode is inferred from OAT and the damper; a trended ECON_CMD overrides it. A lower value counts fewer hours as economizing.

Fixed in code: under-ventilated when CO2 is > 700 ppm above outdoor, over-ventilated when < 150 ppm above (camber/iaq.py); fault when under-ventilated >= 20 % of occupied hours, warn at >= 5 %, or when over-ventilated >= 60 % (camber/rules/iaq_rule.py); the occupied schedule is the fixed weekday 07:00-18:00

co2_ventilation_system

Its parameters are passed through to co2_ventilation; set them on this rule's own entry.

cohort_airflow

Parameter Default Unit Range Basis
k 3.5 - (robust z, MAD-scaled) 2.0 to 6.0 CAMBER judgment: the common 3.5 modified-z outlier cutoff (not cited in the cohort code)
min_cohort 3 count 3 to 50 CAMBER judgment: a median and MAD need at least three peers
summary "mean" choice "mean", "peak", "load_factor", "variability" CAMBER judgment: the mean is the most stable summary
group_by_topology false flag false, true CAMBER judgment (0.98, #85): boxes behind different air handlers (or, in a test dataset, on different days) are not peers
normalise null choice "reference", "design_max" CAMBER judgment (0.98, #85): measured on the ORNL test building (one stuck box among ten, one day per position): the share of design airflow flagged one stuck day of six and six healthy box-days; each box against its own fault-free day flagged all six
tail "both" choice "both", "low", "high" CAMBER judgment (0.98, #85): a stuck damper's variability is only ever low, while a healthy box on a busy day is high

How to calibrate:

  • k: Run it on a cohort you believe healthy and read the largest |z|; set k above it. Lower k finds more outliers but flags normal spread in small cohorts.
  • min_cohort: Raise it when the cohort mixes unlike units; small cohorts give unstable z-scores.
  • summary: Use peak to compare maxima (sizing), load_factor (mean / peak) to compare how units cycle, variability (the standard deviation; 0.98, #85) to find a unit that never moves (pair it with tail='low').
  • group_by_topology: Turn it on when the building has more than one air handler, or when the equipment ids mix runs or days. A served-by model (Brick/Haystack) groups exactly; otherwise the naming heuristic groups by id and the finding says so. Note: Groups smaller than min_cohort are left unscored (unscored_small_groups).
  • normalise: Use 'reference' when every unit has a known-good period or twin (declare it in reference); use 'design_max' to even out box sizes (declare design_max, or map AIRFLOW_SP). Size normalisation alone cannot isolate a stuck box. Note: None (default) compares the raw summary. 'reference' divides each unit's summary by its reference unit's (reference units are not scored); 'design_max' divides by the unit's design airflow (design_max, else the peak AIRFLOW_SP). Units with no denominator are left out (left_out).
  • tail: Use 'low' with summary='variability' to look for units that never move, 'high' for units that move or run more than their peers.

Not thresholds: reference (structured input: {equip: reference_equip} for normalise='reference', one entry per unit (described under normalise)); design_max (structured input: {equip: design airflow} for normalise='design_max', one entry per unit (described under normalise)).

cohort_space_temp

Parameter Default Unit Range Basis
k 3.5 - (robust z, MAD-scaled) 2.0 to 6.0 CAMBER judgment: the common 3.5 modified-z outlier cutoff (not cited in the cohort code)
min_cohort 3 count 3 to 50 CAMBER judgment: a median and MAD need at least three peers
summary "mean" choice "mean", "peak", "load_factor", "variability" CAMBER judgment: the mean is the most stable summary
group_by_topology false flag false, true CAMBER judgment (0.98, #85): boxes behind different air handlers (or, in a test dataset, on different days) are not peers
normalise null choice "reference", "design_max" CAMBER judgment (0.98, #85): measured on the ORNL test building (one stuck box among ten, one day per position): the share of design airflow flagged one stuck day of six and six healthy box-days; each box against its own fault-free day flagged all six
tail "both" choice "both", "low", "high" CAMBER judgment (0.98, #85): a stuck damper's variability is only ever low, while a healthy box on a busy day is high

How to calibrate:

  • k: Run it on a cohort you believe healthy and read the largest |z|; set k above it. Lower k finds more outliers but flags normal spread in small cohorts.
  • min_cohort: Raise it when the cohort mixes unlike units; small cohorts give unstable z-scores.
  • summary: Use peak to compare maxima (sizing), load_factor (mean / peak) to compare how units cycle, variability (the standard deviation; 0.98, #85) to find a unit that never moves (pair it with tail='low').
  • group_by_topology: Turn it on when the building has more than one air handler, or when the equipment ids mix runs or days. A served-by model (Brick/Haystack) groups exactly; otherwise the naming heuristic groups by id and the finding says so. Note: Groups smaller than min_cohort are left unscored (unscored_small_groups).
  • normalise: Use 'reference' when every unit has a known-good period or twin (declare it in reference); use 'design_max' to even out box sizes (declare design_max, or map AIRFLOW_SP). Size normalisation alone cannot isolate a stuck box. Note: None (default) compares the raw summary. 'reference' divides each unit's summary by its reference unit's (reference units are not scored); 'design_max' divides by the unit's design airflow (design_max, else the peak AIRFLOW_SP). Units with no denominator are left out (left_out).
  • tail: Use 'low' with summary='variability' to look for units that never move, 'high' for units that move or run more than their peers.

Not thresholds: reference (structured input: {equip: reference_equip} for normalise='reference', one entry per unit (described under normalise)); design_max (structured input: {equip: design airflow} for normalise='design_max', one entry per unit (described under normalise)).

compressor_short_cycle

Parameter Default Unit Range Basis
max_starts_per_day 12.0 compressor starts per day (off-to-on transitions over the trend's span) 6.0 to 72.0 CAMBER judgment: a screening ceiling of about one start every 2 h; it is not derived from a DX minimum off-time (a ~5 min timer alone would permit ~12 starts an hour)

How to calibrate:

  • max_starts_per_day: Take the manufacturer's minimum on/off timers and the unit's staging hysteresis; better, count the starts per day in a known-good week of similar weather and set the ceiling somewhat above its highest day. Calibrating on the period you are scoring would hide a short-cycling fault. Note: Warn at the ceiling, fault at twice it. The span includes days the unit is off, so a trend with long idle stretches lowers the rate.

Fixed in code: warn at max_starts_per_day or more; fault at twice it (the factor 2 is fixed in code)

compressor_staging

Parameter Default Unit Range Basis
max_changes_per_day 24.0 compressor-stage changes per day 8.0 to 96.0 CAMBER judgment: depends on the staging hysteresis; no source is cited

How to calibrate:

  • max_changes_per_day: Count the stage changes per day in a known-good period with a similar load and set the ceiling above its busiest day; the controller's stage-up / stage-down delays bound what a stable sequence can do. Note: Warn at the ceiling, fault at twice it.

Fixed in code: warn at max_changes_per_day or more; fault at twice it (the factor 2 is fixed in code)

condenser_bypass_leak

Parameter Default Unit Range Basis
closed_pct 2.0 % valve command 0.0 to 10.0 CAMBER judgment: a command at or below this is 'shut'
warn_f 2.0 °F 1.0 to 5.0 CAMBER judgment: twice the combined ±0.5 °F accuracy of two plant sensors (screening-grade)
fault_f 5.0 °F 2.0 to 15.0 CAMBER judgment: roughly a 5-8 % rise in chiller kW/ton (screening-grade)
min_samples 24 count (samples) 6 to 500 CAMBER judgment: a day of hourly samples before judging

How to calibrate:

  • closed_pct: Set it to the bypass command the controller writes when fully closed, plus a little for analog noise; a controller that parks at a few percent needs a higher value.
  • warn_f: Compare the two sensors in a known-good period with the bypass shut (e.g. after a valve rebuild) and set this above their median difference plus the sensors' combined accuracy. Note: It also gates both sensor-offset tests (a difference of at least +warn_f or -warn_f).
  • fault_f: Set it at the entering-water rise worth a valve repair on this plant, from the chillers' kW/ton sensitivity to condenser-water temperature (often 1-2 % per °F). Note: Must be above warn_f.
  • min_samples: Raise it for short-interval trends (e.g. 96 for a day of 15-minute data) or when the bypass is rarely shut while a chiller runs. Note: Also the minimum count for the bypassed-fraction estimate and the offset regression.

condenser_water_reset

Parameter Default Unit Range Basis
reset_slope_flat 0.3 °F CW supply per °F wet-bulb 0.05 to 0.8 CAMBER judgment: an ideal reset tracks wet-bulb about 1:1, so below this it is effectively flat
elevation_ft null ft -300.0 to 10000.0 CAMBER judgment: None assumes sea level; a site input, not a threshold
pressure_psia null psia 10.0 to 15.5 CAMBER judgment: None assumes sea level; a site input, not a threshold

How to calibrate:

  • reset_slope_flat: Check the reset schedule in the sequence of operations: a reset limited by a minimum condenser-water temperature has a lower slope over the year; fit the slope over a known-good period with the reset working and set this well below it. Note: No reset is reported as warn (an efficiency opportunity), never fault. A wet-bulb derived from OAT + RH at altitude reads high, more so in dry air, so the slope moves a little: give elevation_ft near this threshold.
  • elevation_ft: Enter the site elevation above sea level from a survey or map. It corrects a wet-bulb derived from OAT + RH; a measured wet-bulb point ignores it. Set it once for the site with the config's top-level site_elevation_ft, which reaches cooling_tower_approach, condenser_water_reset and the tower drift detectors. Note: None = sea-level Stull wet-bulb, which reads high at altitude (about +1.4 °F at 500 m, +2.6 °F at 1,600 m in hot, dry air). pressure_psia takes precedence when both are given; a rule's own value wins over the config's site_elevation_ft.
  • pressure_psia: Enter a typical measured barometric pressure (absolute, not sea-level corrected) at the site, or leave None and give elevation_ft. Note: None = use elevation_ft, or sea level when that is also None.

control_hunting

Parameter Default Unit Range Basis
warn_per_hr 6.0 per hour (direction reversals) 2.0 to 20.0 CAMBER judgment
fault_per_hr 12.0 per hour (direction reversals) 4.0 to 40.0 CAMBER judgment
deadband 5.0 % of stroke 1.0 to 20.0 CAMBER judgment: ignores slow, legitimate modulation
gap_factor 3.0 - (multiple of the median sample interval) 1.5 to 10.0 CAMBER judgment

How to calibrate:

  • warn_per_hr: Read reversals_per_hr on loops known to be stable (1-5 min data) and set the warn level above their spread; the trend must resolve it (max_resolvable_per_hr).
  • fault_per_hr: Set it well above warn_per_hr, at the reversal rate where the controlled variable visibly oscillates. Note: Must be at or above warn_per_hr; a trend too coarse to resolve it caps severity at warn.
  • deadband: Set it above the output's normal step size on a stable loop (the median absolute sample-to-sample move) so that small corrections are not counted as reversals.
  • gap_factor: Raise it for a change-of-value trend whose intervals vary widely; lower it when logger outages are short but real.

cooling_tower_approach

Parameter Default Unit Range Basis
design_approach_f 7.0 °F 2.0 to 15.0 CAMBER judgment: a well-sized, clean tower achieves ~3-7 °F; the code says to set it from the tower schedule
min_effort_pct 90.0 % tower fan speed 50.0 to 100.0 CAMBER judgment: a tower's capability is its approach at full fan (CTI rating practice); at part fan the approach is the controller's choice
elevation_ft null ft -300.0 to 10000.0 CAMBER judgment: None assumes sea level; a site input, not a threshold
pressure_psia null psia 10.0 to 15.5 CAMBER judgment: None assumes sea level; a site input, not a threshold

How to calibrate:

  • design_approach_f: Use the design approach from the tower selection (leaving water minus design wet-bulb). Without one, the median approach at high fan in a known-good period is a calibration, not a design value; a fouled tower calibrated this way will look healthy. Note: Severity is fixed in code on the median approach: warn at >= 1.3x this, fault at >= 1.7x. pct_hours_high_approach counts hours above design + 3 °F (metric only).
  • min_effort_pct: Set it just below the fan speed the tower reaches on a hot afternoon. If the rule declines because the fan never reaches it, lower it, knowing part-fan hours judge control, not the tower. Note: None restores the old 'fan running' gate, which judged cold-weather hours held above a minimum condenser-water temperature. Used only when a fan speed is trended.
  • elevation_ft: Enter the site elevation above sea level from a survey or map. It corrects a wet-bulb derived from OAT + RH; a measured wet-bulb point ignores it. Set it once for the site with the config's top-level site_elevation_ft, which reaches cooling_tower_approach, condenser_water_reset and the tower drift detectors. Note: None = sea-level Stull wet-bulb, which reads high at altitude (about +1.4 °F at 500 m, +2.6 °F at 1,600 m in hot, dry air). pressure_psia takes precedence when both are given; a rule's own value wins over the config's site_elevation_ft.
  • pressure_psia: Enter a typical measured barometric pressure (absolute, not sea-level corrected) at the site, or leave None and give elevation_ft. Note: None = use elevation_ft, or sea level when that is also None.

damper_census

Parameter Default Unit Range Basis
occupancy_gate "trended" choice "trended", "schedule", "off" CAMBER judgment (#84): the census takes each box's median damper over occupied hours; an assumed weekday schedule returned 'no damper data' on the ornl-frp-vav weekend test days although every box trends the tests' 07:00-22:00 every-day occupancy. With the trended occupancy those days get a census (d3_stuck_000 36.8 %, d3_stuck_060 40.3 %, d3_stuck_100 41.8 % fleet median) and the fault-free day moves 39.3 -> 38.7 % (same verdict).

How to calibrate:

  • occupancy_gate: Keep 'trended' when the boxes (or their zones) trend occupancy. Run once with 'schedule' and compare median_damper_pct: a large difference means the building's real hours differ from the weekday office window, and the trended result is the one to trust. Use 'off' only for a 24/7 space whose boxes never shut off: night shut-off samples pull every median down toward 'throttling'. Note: 'trended' (default): each box's occupied samples from its own trended occupancy point (any non-null value), else the assumed weekday 07-18 schedule. 'schedule': always the schedule (the pre-0.98 behaviour). 'off': every sample. Trended warm-up / cool-down flags drop prep-mode samples under 'trended' and 'schedule'. The finding's occupancy_gate metric (and DamperCensusResult.occupancy_gate) reports 'trended occupancy', 'assumed schedule (weekdays 07-18)', 'mixed' (some boxes each way) or 'off'.

Fixed in code: a box is throttling when its median occupied damper is < 50 % and starved when >= 90 %; fault when >= 60 % of boxes throttle or >= 25 % are starved, warn when < 50 % of boxes lie in band; the schedule fallback is the weekday 07:00-18:00 window; fixed in code (camber/rules/static_rule.py, camber/staticpressure.py), not yet constructor parameters

dcv_system_verification

Its parameters are passed through to dcv_verification; set them on this rule's own entry.

Parameter Default Unit Range Basis
agg "max" choice "max", "mean" CAMBER judgment: the critical zone should drive the reset
stuck_std_ppm 5.0 ppm (standard deviation of a zone's plausible CO2) 1.0 to 30.0 CAMBER judgment
unoccupied_excess_ppm 300.0 ppm (unoccupied zone CO2 median above outdoor) 100.0 to 600.0 CAMBER judgment
assumed_outdoor_ppm 420.0 ppm (CO2) 380.0 to 500.0 CAMBER judgment: roughly the present outdoor background, the same value camber.iaq assumes

How to calibrate:

  • agg: Keep 'max' when the sequence resets on the highest zone CO2 (critical-zone reset); use 'mean' when it resets on an average or on return-air CO2. Note: How the trusted zone CO2 series joined to one air handler are combined per timestamp. 'max' follows the worst trusted sensor.
  • stuck_std_ppm: Look at the standard deviation of zone CO2 sensors known to be working over the dataset; set this well below the smallest. A sensor under it is excluded as flat. Note: Taken over the whole series of 250-5000 ppm samples, occupied or not.
  • unoccupied_excess_ppm: Over nights and weekends a healthy zone CO2 decays toward outdoor. Take the unoccupied median minus outdoor on trusted sensors and set this well above it. Note: A zone is excluded as offset / miscalibrated when its unoccupied median exceeds outdoor by more than this (needs 24 unoccupied samples). Unoccupied hours come from the zone's OCCUPANCY point, else the delegated schedule.
  • assumed_outdoor_ppm: Map an OUTDOOR_CO2 sensor if there is one; otherwise use the unoccupied overnight minimum of trusted zone sensors as a site estimate. Note: Used only when the zone frame has no OUTDOOR_CO2 point; only feeds the unoccupied-offset guard.

Fixed in code: a zone needs >= 24 CO2 samples to take part, and >= 24 unoccupied samples for the unoccupied CO2-offset guard, whatever min_samples says (camber/rules/ventilation_rule.py)

dcv_verification

Parameter Default Unit Range Basis
min_modulation 0.1 fraction of the OA signal's p95 ((p95 - p5) / p95) 0.02 to 0.5 CAMBER judgment
co2_setpoint null ppm (CO2) 600.0 to 1500.0 CAMBER judgment: a site input with no default -- it is the BAS DCV setpoint
occupied_only true flag false, true CAMBER judgment: DCV is only responsible for OA while the space is occupied
dcv_engage_ppm null ppm (CO2) 500.0 to 1200.0 CAMBER judgment: DEFAULT_DCV_ENGAGE_PPM (800 ppm) when no setpoint is known
min_demand_span 150.0 ppm (CO2, p90 - p10 of the judged samples) 50.0 to 400.0 CAMBER judgment
min_lift_ppm 50.0 ppm (CO2 with OA raised minus CO2 with OA at its floor) 20.0 to 200.0 CAMBER judgment
econ_high_limit_f 75.0 °F (outdoor air temperature) 55.0 to 80.0 standard: ASHRAE 90.1 -- the top of its fixed dry-bulb high-limit range, as the code cites it (no section cited); chosen high so that more hours are excluded when unsure
oa_floor_cfm null cfm (outdoor airflow) 0.0 to 50000.0 standard: ASHRAE 62.1 §6.2.7 (dynamic reset: the area component Ra·Az; the code notes section numbering varies by edition)
breach_fault_pct 10.0 % of judged samples (occupied, fan on, not economizing, OA not closed) 1.0 to 50.0 CAMBER judgment
below_floor_fault_pct 10.0 % of occupied fan-on samples (before the economizer and closed-OA exclusions) 1.0 to 50.0 CAMBER judgment
excess_warn_pct 50.0 % of low-demand samples (CO2 at or below its p25 and below the engage level) 10.0 to 90.0 CAMBER judgment: DCV that holds OA above its floor at low demand saves no energy (the docs cite ASHRAE 90.1 §6.4.3.8 for why DCV exists, not for this value)
min_samples 24 count (judged samples) 12 to 500 CAMBER judgment: one day of hourly data
start_hour 7 hour of day (0-23) 0 to 23 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
end_hour 18 hour of day (1-24) 1 to 24 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
occupied_days [0, 1, 2, 3, 4] weekday numbers (Mon=0 ... Sun=6) 0 to 6 CAMBER judgment: a Monday-Friday schedule
unventilated_co2_ppm null ppm (CO2) 800.0 to 2000.0 CAMBER judgment: 1100 ppm, the ~700 ppm-above-outdoor level co2_ventilation uses
unventilated_fault_hours 4.0 h (total over the dataset) 1.0 to 24.0 CAMBER judgment: an outage is judged by duration, not share of the dataset
full_outdoor_air false flag false, true CAMBER judgment: a site fact, not a threshold (0.93, #37)
stratify_hour true flag false, true CAMBER judgment: a valve on a time clock and CO2 mixes the two in a pooled lift (0.93, #37)
below_floor_fault_hours null h (one contiguous run of occupied samples) 1.0 to 24.0 CAMBER judgment: a concentrated outage should not vanish in a long record; 4 h mirrors unventilated_fault_hours (half a working day)
fan_off_speed_pct 5.0 % supply fan speed 0.0 to 20.0 CAMBER judgment: a VFD at a few percent moves essentially no air (the tower rules read 5 % as off too); on lbnl-b59 the fan-off days read 1.2-2.6 % while running hours read far above it

How to calibrate:

  • min_modulation: On a unit with a known-fixed OA minimum, the robust range reported as modulation should sit below it; on one with working DCV, well above. Set it between the two. Damper position and cfm give different ranges, so tune per OA signal. Note: Below it, with demand in the DCV range, the verdict is 'static' (warn).
  • co2_setpoint: Read it from the DCV sequence (the CO2 setpoint of the reset loop or the top of its proportional band). Do not fit it from the CO2 trend. Note: None: the CO2-above-setpoint-at-minimum-OA sub-check is not evaluated, the engage level falls back to 800 ppm, and unventilated_co2_ppm falls back to 1100 ppm. When set, the engage level defaults to co2_setpoint - 200.
  • occupied_only: Leave it on. Turn it off only for a space occupied around the clock with no OCCUPANCY point, where the schedule would cut real occupied hours. Note: Off: every sample is judged (still only with the fan on and not economizing), and the unventilated-while-occupied check runs on all hours.
  • dcv_engage_ppm: Set it to the CO2 at which the sequence starts raising OA: the bottom of the proportional band, or about the setpoint for an integral loop. Note: None: co2_setpoint - 200 when co2_setpoint is set, else 800 ppm. Too few judged samples at or above it gives 'insufficient' (demand_below_engage).
  • min_demand_span: Look at demand_span on units whose zones clearly fill and empty; it should sit above this. Lower it for lightly occupied spaces only if you accept a noisier verdict. Note: Below it the verdict is 'insufficient' (no_demand_variation), never a fault.
  • min_lift_ppm: On a unit with DCV verified by a functional test, demand_lift is the CO2 response; set this well under it and well above the lift of a unit with fixed OA. Calibrating it on the unit you are judging is circular. Note: Taken within the hour of day when stratify_hour is on and enough same-hour pairs exist.
  • econ_high_limit_f: Set it to the high-limit setpoint in the economizer sequence. Better, map ECON_CMD so the economizer need not be inferred. Note: Used only without ECON_CMD: samples with OAT at or below it are treated as possibly economizing and excluded (unless the heating valve is above 5 %). CAMBER temperatures are °F; an OAT trended in °C excludes nearly every sample.
  • oa_floor_cfm: Compute Ra·Az for the zones the unit serves from the design documents, or take the design minimum OA from the TAB report. For a multiple-zone system the true intake floor is higher than the sum of Ra·Az, so Ra·Az under-flags (the safe direction). Note: None: the below-floor and excess-at-low-demand sub-checks are not evaluated. A number or a mapping {equip: cfm}. Checked only on OA_AIRFLOW or AIRFLOW, not on a damper position or fan speed.
  • breach_fault_pct: Raise it if short CO2 excursions at minimum OA are accepted by the sequence (e.g. a slow integral loop). Look at co2_breach_at_min_pct on a known-good period and set it above that. Note: Needs co2_setpoint; share of samples with CO2 above the setpoint while OA sits at its floor (oa_floor_cfm + 10 %, else within 5 % of its range above its p5).
  • below_floor_fault_pct: Look at below_floor_total_pct on a known-good period; set this above it. Keep it low: OA below the 62.1 floor while occupied is an under-ventilation fault. Note: Needs oa_floor_cfm. A sample counts when OA is more than 10 % below the floor. The test reads below_floor_total_pct, which includes hours with the supply fan off; since 0.98 (#93) those are also reported apart (fan_off_occupied_pct) and below_floor_pct is the shortfall with the fan running.
  • excess_warn_pct: Check excess_at_low_demand_pct on a unit whose DCV is known to reach its floor when zones are lightly occupied, and set this above it. Note: Needs oa_floor_cfm. Only lifts an otherwise 'ok' verdict to 'warn'.
  • min_samples: Raise it for short-interval data (e.g. 96 for a day of 15-minute samples) so the verdict rests on more than a few hours. Note: Also gates whether a fallback OA-signal segment gets its own line and whether the unventilated-while-occupied check runs.
  • start_hour: Set it to the start of the building's occupied mode, read from the BAS schedule or from the hour the supply fan starts in a typical week of trends. Better still, map the OCCUPANCY point, which replaces the schedule. Note: Used only when the equipment frame carries no OCCUPANCY point: a trended OCCUPANCY point replaces the schedule (WARMUP / COOLDOWN are excluded either way). Ignored for the verdict with occupied_only=False. dcv_system_verification also uses it on each zone frame to find the unoccupied hours of its CO2-offset guard.
  • end_hour: Set it to the end of occupied mode (exclusive), read from the BAS schedule or trends. Note: Used only when the equipment frame carries no OCCUPANCY point: a trended OCCUPANCY point replaces the schedule (WARMUP / COOLDOWN are excluded either way). Ignored for the verdict with occupied_only=False. dcv_system_verification also uses it on each zone frame to find the unoccupied hours of its CO2-offset guard.
  • occupied_days: List the days the building runs occupied mode, e.g. [0, 1, 2, 3, 4, 5] for a Saturday schedule. Note: A list of integers; each must lie in the range. Used only when the equipment frame carries no OCCUPANCY point: a trended OCCUPANCY point replaces the schedule (WARMUP / COOLDOWN are excluded either way). Ignored for the verdict with occupied_only=False. dcv_system_verification also uses it on each zone frame to find the unoccupied hours of its CO2-offset guard.
  • unventilated_co2_ppm: Set it to the CO2 level at which the space must be ventilated, typically the DCV setpoint. Note: None: co2_setpoint when set, else 1100 ppm. A sample counts when the space is occupied, CO2 is at or above it, and the fan is off or OA is at or below 2 % of its p95.
  • unventilated_fault_hours: Set it to how long an occupied space may go without outdoor air before it is a fault; half a working day is a common tolerance. Note: Hours are the count of qualifying samples times the median sample step.
  • full_outdoor_air: Set it True only for a 100 % outdoor-air unit (no return air, no economizer damper), from the mechanical schedule. Note: True: the supply AIRFLOW, then SUPPLY_FAN_SPEED, serve as the OA signal when OA flow and damper are absent, and no economizer exclusion applies.
  • stratify_hour: Leave it on. Turn it off only to reproduce pre-0.93 pooled verdicts; the pooled lift is always reported as demand_lift_pooled. Note: Falls back to the pooled lift when the same-hour strata hold too few pairs (lift_basis).
  • below_floor_fault_hours: Set it to how long an occupied space may stay below its area-based floor before it is a fault. Read below_floor_longest_h on a known-good period and set it well above that. Note: None (the default) = off: only the share test (below_floor_fault_pct) faults. A run is consecutive occupied samples below the floor, so it continues across the unoccupied night between two days; it includes hours with the supply fan off, and the finding says so when they make up most of the run. Needs oa_floor_cfm and an OA flow signal.
  • fan_off_speed_pct: Take the speed the drive reports with the fan stopped (its 99th percentile on known off hours) and set this just above it, below the lowest running speed. Note: Used only without a supply-fan status point and only where the OA floor is checked: an occupied sample below the floor with the fan at or below this speed is counted as fan_off_occupied_pct / _hours, not in below_floor_pct. Severity is unchanged: the share test reads below_floor_total_pct.

Not thresholds: min_corr (deprecated since 0.82 and ignored (warns; removal in 1.0): the verdict no longer uses Pearson correlation -- use min_lift_ppm).

dx_indoor_airflow

Parameter Default Unit Range Basis
freeze_if_missing true flag false, true CAMBER judgment: a first baseline-mode run freezes its fault-free period
targets null °F (a per-unit mapping) 15.0 to 22.0 public source: the installer's target temperature split for the unit
tolerance_f 3.0 °F (either side of the target split) 1.5 to 5.0 CAMBER judgment: ±3 °F; no source is cited
warn_f 1.5 °F (median split residual against the baseline) 1.0 to 4.0 CAMBER judgment: screening-grade; characterized on nist-heatpump-fdd, where max(1.5 °F, 2σ) flagged 2 of 90 fault-free files
fault_f 3.0 °F (median split residual against the baseline) 2.0 to 8.0 CAMBER judgment: screening-grade; characterized on nist-heatpump-fdd
warn_sigma 2.0 σ (standard deviations of the baseline residual) 1.0 to 4.0 CAMBER judgment: screening-grade; characterized on nist-heatpump-fdd
fault_sigma 4.0 σ (standard deviations of the baseline residual) 2.0 to 8.0 CAMBER judgment: screening-grade; characterized on nist-heatpump-fdd
min_samples 10 count (cooling-mode rows) 5 to 200 CAMBER judgment: the smallest sample whose median is worth reporting
min_baseline_samples 30 count (cooling-mode rows) 20 to 1000 CAMBER judgment: the minimum for the regression on outdoor and return air (shared _dxfit default)
min_split_f 8.0 °F (return minus supply air while cooling) 5.0 to 12.0 CAMBER judgment: a DX coil at rated airflow drops roughly 15-22 °F; the 8-28 °F band is wide on purpose (screening-grade)
max_split_f 28.0 °F (return minus supply air while cooling) 24.0 to 35.0 CAMBER judgment: a DX coil at rated airflow drops roughly 15-22 °F; the 8-28 °F band is wide on purpose (screening-grade)

How to calibrate:

  • freeze_if_missing: Leave True for a first run on a known-good period; set False once baselines are frozen so a later run cannot silently freeze a faulty period as the reference. Note: Baseline mode (analyze_periods) only. False with no stored baseline declines the unit.
  • targets: Take the target split from a chart that uses the return wet bulb and dry bulb where you have one (a fixed number ignores latent load), or from commissioning measurements at rated airflow. Deriving it from the scored trend is circular. Note: None: no target, so only min_split_f / max_split_f apply (each a warn). Shape: {'temp_split_f': 20, 'tolerance_f': 3} for every unit, or {'RTU-*': {...}} keyed by fnmatch patterns on the equipment id. The range is for temp_split_f.
  • tolerance_f: Widen it where the return humidity swings widely, since the fixed target ignores latent load; a per-unit 'tolerance_f' in targets overrides it. Note: Target mode only: warn at the tolerance, fault at twice it.
  • warn_f: Score a held-out part of a known-good period against its frozen baseline and set the floor above its largest residual. Note: Baseline mode only: a warn needs |drift| >= max(warn_f, warn_sigma × σ). Airflow faults under about 15% sit inside the fault-free scatter.
  • fault_f: Keep it about twice warn_f. Note: Baseline mode only.
  • warn_sigma: Raise it if a held-out known-good period trips the warn, e.g. with no return dew point to absorb latent-load swings. Note: Baseline mode only.
  • fault_sigma: Keep it about twice warn_sigma. Note: Baseline mode only.
  • min_samples: Raise it for minute data, where 10 rows is a few minutes of running; with hourly or averaged test points keep it low. Note: Target mode: rows needed in the frame. Baseline mode: scoreable rows in the current period.
  • min_baseline_samples: Make the fault-free period long enough to span the weather the unit will be scored in; raise this if the fitted slopes look unstable. Note: Baseline mode only.
  • min_split_f: Only for units with no target: set a target split instead where one is known. Note: Limits mode only; below it is a warn read as high airflow or lost capacity (charge).
  • max_split_f: Only for units with no target: set a target split instead where one is known. Note: Limits mode only; above it is a low-airflow warn.

Not thresholds: store (a frozen-baseline store handle (in-memory when omitted), not a threshold); site (the site id the frozen baseline is stored under, not a threshold); run_id (the run id recorded when a baseline is frozen, not a threshold).

dx_refrigerant_charge

Parameter Default Unit Range Basis
freeze_if_missing true flag false, true CAMBER judgment: a first baseline-mode run freezes its fault-free period
targets null °F (a per-unit mapping) 5.0 to 20.0 public source: the unit's nameplate / installation-manual charging target
tolerance_f 3.0 °F (either side of the target) 1.0 to 5.0 public source: manufacturer charging instructions (the common ±3 °F on target subcooling, as the rule module cites)
metric "subcooling" choice "subcooling", "superheat" CAMBER judgment: TXV / EEV units are charged by subcooling, fixed-orifice units by superheat
warn_f 3.0 °F (median residual against the baseline) 1.5 to 6.0 CAMBER judgment: screening-grade; characterized on nist-heatpump-fdd, where max(3 °F, 1.5σ) caught 91% of charge-fault files at 8% of fault-free files
fault_f 5.0 °F (median residual against the baseline) 3.0 to 10.0 CAMBER judgment: screening-grade; characterized on nist-heatpump-fdd
warn_sigma 1.5 σ (standard deviations of the baseline residual) 1.0 to 4.0 CAMBER judgment: screening-grade; characterized on nist-heatpump-fdd
fault_sigma 3.0 σ (standard deviations of the baseline residual) 2.0 to 6.0 CAMBER judgment: screening-grade; characterized on nist-heatpump-fdd
min_samples 10 count (cooling-mode rows) 5 to 200 CAMBER judgment: the smallest sample whose median is worth reporting
min_baseline_samples 30 count (cooling-mode rows) 20 to 1000 CAMBER judgment: the minimum for the regression on outdoor and return air (shared _dxfit default)
min_subcooling_f 2.0 °F 0.0 to 5.0 CAMBER judgment: below about 2 °F of subcooling a TXV is fed flash gas (screening-grade universal limit)
max_subcooling_f 25.0 °F 15.0 to 35.0 CAMBER judgment: no charging target sits near 25 °F (screening-grade universal limit)

How to calibrate:

  • freeze_if_missing: Leave True for a first run on a known-good period; set False once baselines are frozen so a later run cannot silently freeze a faulty period as the reference. Note: Baseline mode (analyze_periods) only. False with no stored baseline declines the unit.
  • targets: Copy the target subcooling (and superheat, for corroboration) from the manufacturer's charging chart or nameplate, at the conditions the unit mostly runs in. Taking it from the trend you score is circular. Note: None: no target, so only min_subcooling_f / max_subcooling_f apply (each a warn), and metric='superheat' declines. Shape: {'subcooling_f': 10, 'superheat_f': 8, 'tolerance_f': 3} for every unit, or {'RTU-*': {...}} keyed by fnmatch patterns on the equipment id. The range is for subcooling_f.
  • tolerance_f: Use the tolerance printed in the unit's charging instructions; a per-unit 'tolerance_f' in targets overrides it. Note: Target mode only: warn at the tolerance, fault at twice it.
  • metric: Choose 'superheat' for a fixed-orifice (piston) unit, whose charging chart is by superheat; otherwise keep 'subcooling'.
  • warn_f: Score a known-good period against its own frozen baseline (a held-out part of it, not the fitted rows) and set the floor above its largest residual. Note: Baseline mode only: a warn needs |drift| >= max(warn_f, warn_sigma × σ).
  • fault_f: Keep it clear of warn_f; about the residual a 10-15% charge loss gives on the unit, if a charging test or chart tells you. Note: Baseline mode only: a fault needs |drift| >= max(fault_f, fault_sigma × σ).
  • warn_sigma: Raise it if a held-out part of the fault-free period trips the warn; the °F floor governs a very steady baseline. Note: Baseline mode only.
  • fault_sigma: Keep it about twice warn_sigma. Note: Baseline mode only.
  • min_samples: Raise it for minute data, where 10 rows is a few minutes of running; with hourly or averaged test points keep it low. Note: Target mode: rows needed in the frame. Baseline mode: scoreable rows in the current period.
  • min_baseline_samples: Make the fault-free period long enough to span the weather the unit will be scored in; raise this if the fitted slopes look unstable. Note: Baseline mode only.
  • min_subcooling_f: Only for units with no target: set a target instead where the nameplate gives one. Note: Limits mode only (no target, metric='subcooling'); below it is a low-charge warn.
  • max_subcooling_f: Only for units with no target; a unit with an unusually high design subcooling needs a target instead. Note: Limits mode only (no target, metric='subcooling'); above it is an overcharge warn.

Not thresholds: store (a frozen-baseline store handle (in-memory when omitted), not a threshold); site (the site id the frozen baseline is stored under, not a threshold); run_id (the run id recorded when a baseline is frozen, not a threshold).

economizer_high_limit

Parameter Default Unit Range Basis
high_limit_f 65.0 °F 55.0 to 80.0 CAMBER judgment: a typical, not universal, fixed dry-bulb high limit (CA Title 24 sets it by climate zone)
min_damper 0.25 fraction (OA damper, 0-1) 0.0 to 1.0 CAMBER judgment
min_oa_pct null % of supply airflow (OA fraction) 0.0 to 100.0 CAMBER judgment: the design minimum is a building property
oa_margin_pct 5.0 % of supply airflow (percentage points) 1.0 to 15.0 CAMBER judgment: allows for temperature-balance noise
warn_pct 10.0 % of fan-on samples above the high limit 2.0 to 30.0 CAMBER judgment
fault_pct 25.0 % of fan-on samples above the high limit 10.0 to 60.0 CAMBER judgment
denom_min_f 5.0 °F 2.0 to 15.0 public source: PNNL Re-tuning Ch.5 (the same guard as camber/oafraction.py)
assumed_min_oa_pct 20.0 % of supply airflow (OA fraction) 5.0 to 40.0 CAMBER judgment: a generic design minimum
conservative_min_oa_pct 50.0 % of supply airflow (OA fraction) 30.0 to 80.0 CAMBER judgment: above the design minimum of any ordinary mixed-air unit
differential true flag false, true CAMBER judgment: many economizers use a differential dry-bulb changeover
fan_gate true flag false, true CAMBER judgment: with the fan stopped the air temperatures describe still air, not the unit at work

How to calibrate:

  • high_limit_f: Set it to the high-limit setpoint of the unit's economizer sequence or the energy code for its climate zone.
  • min_damper: Set it to the minimum OA damper position of the sequence, or the damper position the unit holds on hot fan-on hours in a known-good period. Used only when no mixed/return air temperatures or airflows are trended. Note: A sample counts as not locked out above min_damper + 0.05.
  • min_oa_pct: Take it from the ventilation design, or measure the OA fraction at minimum damper position on a known-good fan-on period. Note: None = unknown: excess is scored against conservative_min_oa_pct, and OA that is excess only against assumed_min_oa_pct is declined.
  • oa_margin_pct: Set it to the spread of the OA fraction at minimum position on known-good hot hours (for example its interquartile range).
  • warn_pct: Read not_locked_out_pct on units known to lock out correctly; set the warn level above their spread.
  • fault_pct: Set it well above warn_pct. Note: Must be at or above warn_pct.
  • denom_min_f: Raise it when OA fractions scatter on hours with OAT close to RAT (n_masked_small_delta_t shows how many samples it drops).
  • assumed_min_oa_pct: Prefer setting min_oa_pct; change this only to move the generic minimum used in the declined-case caveat.
  • conservative_min_oa_pct: Prefer setting min_oa_pct; lower it only for a portfolio whose design minimums are all known to be lower.
  • differential: Set it from the sequence: false for a fixed dry-bulb high limit, true for a differential (outdoor vs return) changeover.
  • fan_gate: Leave it on. Turn it off only when the fan signal is known to be wrong (stuck status, a speed point that reads 0 while running) and every trended sample is a running sample. Note: The fan is read from supply-fan status, else speed, else airflow; a unit with none of them is judged ungated and the finding says so (the fan_gate metric).

filter_fouling

Parameter Default Unit Range Basis
change_dp_inwc 1.0 in. w.c. 0.3 to 2.0 CAMBER judgment: a common MERV-13 final-pressure-drop alarm (filter and fan dependent)

How to calibrate:

  • change_dp_inwc: Use the filter maker's recommended final resistance for the installed filter, or the differential pressure at which the site changes filters; filter_dp_median_inwc of a freshly changed filter gives the clean baseline. Note: Warn when the median is at or above it; fault at 1.5 times it (fixed in code).

free_cooling_missed

Parameter Default Unit Range Basis
high_limit_f 60.0 °F (outdoor air dry-bulb) 45.0 to 75.0 CAMBER judgment: a deliberately conservative screening default. Below 60 °F an economizer should be cooling with outside air in any climate, so a missed hour is clearly missed. Economizer guidance sets the dry-bulb high limit by climate (ASHRAE 90.1 §6.5.1.1.3, its high-limit table by climate zone; the PNNL economizer guide, reference pnnl-guide-economizer), higher in dry climates and lower in humid ones
active 5.0 % of valve stroke (cooling valve) 1.0 to 20.0 CAMBER judgment: a valve parked at a few percent is not mechanical cooling running
warn_pct 10.0 % of free-cooling samples 2.0 to 30.0 CAMBER judgment
fault_pct 25.0 % of free-cooling samples 10.0 to 60.0 CAMBER judgment
cmd_open_pct 90.0 % of damper stroke (OA damper command) 50.0 to 100.0 CAMBER judgment: the full-outside-air damper test (ECON_DAMPER_MIN_PCT, 90 %); an economizer damper at full open commonly reads a few percent short
oaf_open_pct 80.0 % outdoor-air fraction (temperature balance) 50.0 to 100.0 CAMBER judgment: the full-outside-air OA-fraction test (ECON_OAF_MIN_PCT, 80 %); sensor error and mixing-box stratification keep a true 100 % OA unit from reading 100
stuck_min_share_pct 20.0 % of missed, well-conditioned free-cooling samples 5.0 to 60.0 CAMBER judgment, checked on lbnl-sdahu (stuck-damper runs 40-87 %, fault-free and valve-leak runs 0 %) and lbnl-ddahu (stuck-closed run 95 %, fault-free 0 %)
stuck_min_hours 24.0 hours 6.0 to 168.0 CAMBER judgment: one day of evidence before naming a mechanical cause
stuck_low_oaf_pct 30.0 % outdoor-air fraction 5.0 to 60.0 CAMBER judgment: below 30 % the damper delivers about its minimum-OA share or less
low_limit_f null °F (outdoor air dry-bulb) 20.0 to 50.0 CAMBER judgment: no lockout is assumed, because a low limit is a property of the unit's own sequence (freeze protection of the coils and the mixing box), not a general value
fan_gate true flag false, true CAMBER judgment: with the fan stopped the air temperatures describe still air, not the unit at work

How to calibrate:

  • high_limit_f: Set it to the dry-bulb high limit programmed in the unit's economizer sequence, or to the energy code's high limit for the site's climate zone (a few degrees below it, so only clearly cool weather counts). From trends, take the highest OAT at which the OA damper still opens fully over a summer of known-good operation. See docs/TUNING.md. Note: camber.freecooling.free_cooling_opportunity uses the same default (DEFAULT_FREE_COOLING_HIGH_LIMIT_F, 60 °F; 65 °F before 0.98). The RCx report's economizer page passes economizer_high_limit's high_limit_f to it instead, and states that value. A higher value counts more hours as free-cooling weather.
  • active: Set it just above the cooling-valve position seen with cooling off on a known-good period (its 95th percentile when the chiller or compressor is off).
  • warn_pct: Read missed_pct on units whose economizers are known to work; set the warn level above their spread.
  • fault_pct: Set it well above warn_pct. Note: Must be at or above warn_pct.
  • cmd_open_pct: Read the OA-damper command on a known-good unit's free-cooling hours at full economizer; set it a few percent below the value it holds there. Note: Only the missed_cause metrics use it; severity does not.
  • oaf_open_pct: Compute (RAT - MAT) / (RAT - OAT) on a known-good unit's full-economizer hours with |OAT - RAT| >= 5 °F; set it below their low percentile. Note: Samples with |OAT - RAT| < 5 °F are not judged (the balance is ill-conditioned).
  • stuck_min_share_pct: Read commanded_open_pct on units whose dampers are known to work: set it well above their value (usually 0 %) and below that of a unit with a known stuck damper. Note: Below it, with a damper command trended, missed_cause is economizer_not_commanded.
  • stuck_min_hours: Raise it for long windows or noisy mixed-air sensors; one day is the floor for an hourly trend. Note: With the share met on fewer hours, missed_cause is undetermined.
  • stuck_low_oaf_pct: Set it just above the unit's design minimum OA fraction plus a margin; a damper delivering less while commanded open reads 'stuck low', more reads 'stuck part open'. Note: Recorded on the finding; the recommendation reads it (aso DEFAULT_PARAMS econ_stuck_low_oaf_pct mirrors the default).
  • low_limit_f: Set it to the economizer low-limit lockout programmed in the unit's sequence of operations. From trends, take the OAT below which the OA damper command never leaves its minimum over a winter of known-good operation. Record the source in the config's basis map. Note: None = no lockout: every hour below high_limit_f is free-cooling weather (the pre-0.100 finding, byte-identical). When set, hours below it are neither available nor missed; they are counted in low_limit_excluded_hours (low_limit_cooling_hours of them with mechanical cooling running) with a caveat. Must be below high_limit_f. camber.freecooling.free_cooling_opportunity takes the same parameter, and the RCx report's economizer page passes this rule's value to it.
  • fan_gate: Leave it on. Turn it off only when the fan signal is known to be wrong (stuck status, a speed point that reads 0 while running) and every trended sample is a running sample. Note: The fan is read from supply-fan status, else speed, else airflow; a unit with none of them is judged ungated and the finding says so (the fan_gate metric).

g36_afdd

Parameter Default Unit Range Basis
heating_coil null flag false, true CAMBER judgment: a missing heating-valve point is read as no heating coil
mat_sat_as_coil_temps true flag false, true standard: ASHRAE Guideline 36-2021 §5.16.14 (MAT/SAT may stand in for the coil temperatures, depending on the AHU configuration)
min_oa_pct null % of supply airflow (OA fraction) 0.0 to 100.0 CAMBER judgment: the design minimum is a building property
min_oa_pct_by_month null % of supply airflow, per month 0.0 to 100.0 CAMBER judgment: only for a sequence with a seasonal minimum (as outdoor_air_fraction takes it); G36 judges %OA against the active minimum-OA setpoint, which a seasonal sequence changes
mode_delay_min 30.0 min 0.0 to 120.0 standard: ASHRAE Guideline 36-2021 §5.16.14 (ModeDelay; verified against Addendum p)
alarm_delay_min 30.0 min 0.0 to 120.0 standard: ASHRAE Guideline 36-2021 §5.16.14 (AlarmDelay; verified against Addendum p)
avg_window_min 5.0 min 1.0 to 30.0 standard: ASHRAE Guideline 36-2021 §5.16.14 (five-minute rolling averages)
econ_damper_open 80.0 % of damper stroke (OA damper) 50.0 to 100.0 CAMBER judgment: separates G36 OS#3 (mechanical cooling on 100 % OA) from OS#4
valve_thr 5.0 % of valve stroke 0.0 to 15.0 CAMBER judgment: a noise deadband for 'coil active'
oa_damper_min null % of damper stroke (OA damper) 0.0 to 60.0 standard: ASHRAE Guideline 36-2021 §5.16.14 (operating-state definitions: free cooling is the economizer modulating above its minimum position, and heating (0.99, #95) runs with the OA damper at it); the position itself is the unit's own, set by its minimum outdoor-air control
oa_damper_tol 5.0 percentage points of damper stroke 0.0 to 20.0 CAMBER judgment (#94): a noise margin above the minimum position; G36 gives no damper tolerance. On fault-free lbnl-sdahu the idle economizer hours sit at 47 % or more and the minimum at 10 %, so any margin up to 30 points classifies them the same. Since 0.99 (#95) the same margin decides whether a heating hour is at minimum OA (OS#1).
oa_damper_min_by_month null % of damper stroke (OA damper), per month 0.0 to 60.0 standard: ASHRAE Guideline 36-2021 §5.16.14 (operating-state definitions, as for oa_damper_min); CAMBER judgment: only for a sequence whose minimum position changes with the season, as min_oa_pct_by_month does for FC6
occupancy_gate "off" choice "off", "trended" standard: ASHRAE Guideline 36-2021 §5.16.14 suspends AFDD only while the AHU is not operating (and for ModeDelay after a zone-group mode change), so unoccupied operation is evaluated by default. On the fault-free lbnl-sdahu run the unoccupied FC9 false alarm came from the free-cooling misreading, not from evaluating unoccupied hours: with OS#2 fixed, 'trended' changes no verdict on any lbnl-sdahu or lbnl-ddahu run.
warn_pct 5.0 % of an FC's applicable intervals 1.0 to 30.0 CAMBER judgment: screening-grade severity, not from G36 (G36 alarms every confirmed fault)
fault_pct 20.0 % of an FC's applicable intervals 5.0 to 60.0 CAMBER judgment: screening-grade severity, not from G36
min_applicable_hours 24.0 h 1.0 to 500.0 CAMBER judgment: screening-grade, not from G36

How to calibrate:

  • heating_coil: Set it from the unit's schedule or drawings: true when the AHU has a heating coil whose valve is not trended (the rule then declines), false to ignore a mapped valve. Note: None = inferred from whether a heating-valve point is mapped.
  • mat_sat_as_coil_temps: Leave it on for an AHU with no heating coil between the mixed- and supply-air sensors; turn it off when another component (a preheat coil, an energy wheel) sits between them.
  • min_oa_pct: Take it from the design minimum OA over design airflow, or measure the OA fraction at minimum damper position on a known-good fan-on period. Note: None = FC6 is declined. G36 uses the active minimum-OA setpoint over actual airflow.
  • min_oa_pct_by_month: Enter the sequence's minimum for each month that differs from min_oa_pct, e.g. {6: 11.9, 7: 11.9, 8: 11.9} on lbnl-ddahu (a 28 % damper minimum in Jun-Aug, 45 % otherwise), and use the same values outdoor_air_fraction uses. Note: None = min_oa_pct all year. A {month (1-12): pct} mapping; it needs min_oa_pct, the minimum in the other months. Each value must lie in the range.
  • mode_delay_min: Keep the G36 value; lengthen it only if trends show the unit still settling after a fan start or mode change beyond 30 minutes.
  • alarm_delay_min: Keep the G36 value; it should be several sample intervals long so that a fault must persist across more than one sample.
  • avg_window_min: Keep the G36 value; on trends coarser than 5 minutes the window holds a single sample.
  • econ_damper_open: Set it just below the position the OA damper holds when the sequence calls for full economizer (its 5th percentile on such hours).
  • valve_thr: Set it just above the valve command's reading when commanded closed (its 95th percentile on idle hours).
  • oa_damper_min: Set it to the minimum position from the unit's sequence or balancing report, or read the OA damper command on mechanical-cooling hours with the economizer locked out (its median). The finding's oa_damper_min and oa_damper_min_source show what was used. Note: None = learned: the median OA damper command over fan-on hours of mechanical cooling below econ_damper_open (the OS#4 position); with fewer than 24 such intervals, 0 % (closed). lbnl-sdahu learns 10 %, its documented fixed minimum; lbnl-ddahu learns 28 % (its summer position; its template sets the seasonal minimum, 45 % with oa_damper_min_by_month 28 % in Jun-Aug, since 0.101). The same minimum splits OS#2 (beyond it) and OS#1 (at it) since 0.99.
  • oa_damper_tol: Set it above the scatter of the damper command while it holds its minimum (the spread of the command on mechanical-cooling hours at minimum OA); raise it for a G36 unit whose minimum position moves with airflow.
  • oa_damper_min_by_month: Enter the sequence's minimum position for each month that differs from oa_damper_min, e.g. oa_damper_min 45 with {6: 28, 7: 28, 8: 28} on lbnl-ddahu (inventory section 1.2(ii)). Check it against the damper command on hours the unit holds its minimum. Note: None = oa_damper_min all year. A {month (1-12): position} mapping; it needs oa_damper_min, the position in the other months, so it never combines with a learned minimum. Each value must lie in the range. The finding reports it as oa_damper_min_by_month.
  • occupancy_gate: Keep 'off'. Use 'trended' to screen occupied operation only, when the unit trends an occupied/unoccupied point and its unoccupied runs (setback, purge) are out of scope. Note: 'off' (default): every fan-on hour outside ModeDelay. 'trended': only the hours the trended occupancy point marks occupied; with none trended, every fan-on hour (no assumed-schedule fallback). The finding's occupancy_gate metric reports 'off', 'trended occupancy' or 'none trended (fan-on hours only)', and unoccupied_hours the fan-on hours the gate left out.
  • warn_pct: Read each FC's pct on units known to be healthy; set the warn level above the spread. Tuning it on the unit under test is circular.
  • fault_pct: Set it well above warn_pct. Note: Must be at or above warn_pct.
  • min_applicable_hours: Raise it for long histories so that a rare operating state with a few hours of applicability cannot set the severity; each FC's applicable_hours shows its base.

heatpump_defrost

Parameter Default Unit Range Basis
max_reversals_per_day 24.0 reversing-valve transitions per day 12.0 to 96.0 CAMBER judgment: a screening ceiling of about 12 defrosts a day (two transitions each); defrost at worst about once an hour in cold weather would reach ~48 transitions a day

How to calibrate:

  • max_reversals_per_day: Read the defrost-control interval (time- or demand-based) from the unit's manual, or count the reversals per day in a known-good cold spell and set the ceiling above that. Each defrost counts two transitions (into cooling and back), so allow for that. Note: Warn at the ceiling, fault at twice it. Heating/cooling changeovers count too.

Fixed in code: warn at max_reversals_per_day or more; fault at twice it (the factor 2 is fixed in code)

hp_capacity_shortfall

Parameter Default Unit Range Basis
warn_outside_pct 10.0 % of occupied samples with a mode and zone temperature (per side) 2.0 to 30.0 CAMBER judgment: screening-grade (provisional, 0.93)
fault_outside_pct 25.0 % of occupied samples with a mode and zone temperature (per side) 10.0 to 60.0 CAMBER judgment: screening-grade (provisional, 0.93)
saturated_pct 70.0 % of the out-of-band samples with the unit running in the needed mode 50.0 to 95.0 CAMBER judgment: mostly working while out of band reads as capacity, not control
idle_pct 30.0 % of the out-of-band samples with the unit running in the needed mode 5.0 to 50.0 CAMBER judgment: mostly idle while out of band reads as control, not capacity
min_hours 4.0 h (occupied hours out of band, per side) 1.0 to 24.0 CAMBER judgment: ignore a side with only a few hours out of band
heat_sp_f 68.0 °F 60.0 to 74.0 CAMBER judgment: a stated default comfort band (68-76 °F)
cool_sp_f 76.0 °F 70.0 to 82.0 CAMBER judgment: a stated default comfort band (68-76 °F)
band_tol_f 1.0 °F (past the setpoint) 0.0 to 3.0 CAMBER judgment: the zone must be clearly past a setpoint to count as outside the band
heat_rise_f 15.0 °F (discharge air above the zone) 8.0 to 30.0 CAMBER judgment: a water-to-air heat pump delivers about 85-105 °F air in heating against a ~70 °F room
cool_drop_f 10.0 °F (discharge air below the zone) 5.0 to 20.0 CAMBER judgment: a heat pump in cooling delivers about 52-60 °F air

How to calibrate:

  • warn_outside_pct: Check the share of occupied time out of band in a known-good season; set the warn above it.
  • fault_outside_pct: Keep it well above warn_outside_pct. Note: A 'mixed' verdict (working_pct between idle_pct and saturated_pct) is capped at warn.
  • saturated_pct: Lower it for units that cycle on a thermostat (they run part of every hour even when short of capacity). Note: At or above: a 'capacity' verdict. Must stay above idle_pct.
  • idle_pct: Keep it well below saturated_pct. Note: At or below: a 'control' verdict.
  • min_hours: Raise it for long trends so a single cold morning cannot trigger the rule.
  • heat_sp_f: Set it to the site's occupied heating setpoint from the BAS or the thermostats; better, map the heat_sp point so the rule reads it. Note: Used only when no heat_sp point is mapped (the finding then carries a caveat).
  • cool_sp_f: Set it to the site's occupied cooling setpoint; better, map the cool_sp point. Note: Used only when no cool_sp point is mapped (the finding then carries a caveat).
  • band_tol_f: Set it to about the thermostat's deadband or the zone sensor's accuracy, whichever is larger.
  • heat_rise_f: Look at a known-good week: the discharge-minus-zone difference while the unit heats clusters well above zero; set this below that cluster and above the fan-only rise. Note: Ignored when compressor status and the reversing-valve command are both mapped.
  • cool_drop_f: Look at a known-good week: set it below the zone-minus-discharge difference seen while the unit cools and above the fan-only difference. Note: Ignored when compressor status and the reversing-valve command are both mapped.

hp_mode_vs_need

Parameter Default Unit Range Basis
warn_hours 2.0 h (occupied hours in the wrong mode) 0.5 to 24.0 CAMBER judgment: screening-grade (provisional, 0.93)
fault_hours 10.0 h (occupied hours in the wrong mode) 2.0 to 100.0 CAMBER judgment: screening-grade (provisional, 0.93)
warn_pct 2.0 % of occupied running samples (heating or cooling) 0.5 to 10.0 CAMBER judgment: screening-grade (provisional, 0.93)
fault_pct 10.0 % of occupied running samples (heating or cooling) 5.0 to 50.0 CAMBER judgment: screening-grade (provisional, 0.93)
heat_sp_f 68.0 °F 60.0 to 74.0 CAMBER judgment: a stated default comfort band (68-76 °F)
cool_sp_f 76.0 °F 70.0 to 82.0 CAMBER judgment: a stated default comfort band (68-76 °F)
band_tol_f 1.0 °F (past the setpoint) 0.0 to 3.0 CAMBER judgment: the zone must be clearly past a setpoint to count as outside the band
heat_rise_f 15.0 °F (discharge air above the zone) 8.0 to 30.0 CAMBER judgment: a water-to-air heat pump delivers about 85-105 °F air in heating against a ~70 °F room
cool_drop_f 10.0 °F (discharge air below the zone) 5.0 to 20.0 CAMBER judgment: a heat pump in cooling delivers about 52-60 °F air

How to calibrate:

  • warn_hours: Scale it with the length of the trend you score: a couple of hours over a month is noise, over a day it is not. Note: A warn needs both warn_hours and warn_pct.
  • fault_hours: Scale it with the trend's length, as warn_hours. Note: A fault needs both fault_hours and fault_pct.
  • warn_pct: Check a known-good period: brief wrong-mode samples at changeover set the floor.
  • fault_pct: Keep it well above warn_pct.
  • heat_sp_f: Set it to the site's occupied heating setpoint from the BAS or the thermostats; better, map the heat_sp point so the rule reads it. Note: Used only when no heat_sp point is mapped (the finding then carries a caveat).
  • cool_sp_f: Set it to the site's occupied cooling setpoint; better, map the cool_sp point. Note: Used only when no cool_sp point is mapped (the finding then carries a caveat).
  • band_tol_f: Set it to about the thermostat's deadband or the zone sensor's accuracy, whichever is larger.
  • heat_rise_f: Look at a known-good week: the discharge-minus-zone difference while the unit heats clusters well above zero; set this below that cluster and above the fan-only rise. Note: Ignored when compressor status and the reversing-valve command are both mapped.
  • cool_drop_f: Look at a known-good week: set it below the zone-minus-discharge difference seen while the unit cools and above the fan-only difference. Note: Ignored when compressor status and the reversing-valve command are both mapped.

hp_room_imbalance

Parameter Default Unit Range Basis
warn_fight_pct 2.0 % of co-occupied samples (one unit heating while another cools) 0.5 to 10.0 CAMBER judgment: screening-grade (provisional, 0.93)
fault_fight_pct 10.0 % of co-occupied samples (one unit heating while another cools) 5.0 to 40.0 CAMBER judgment: screening-grade (provisional, 0.93)
warn_duty_spread_pct 40.0 percentage points (difference between the units' active shares) 15.0 to 80.0 CAMBER judgment: screening-grade (provisional, 0.93)
warn_sensor_spread_f 3.0 °F (median spread between the room's zone sensors) 1.0 to 6.0 CAMBER judgment: one room's sensors should agree within a few degrees
heat_sp_f 68.0 °F 60.0 to 74.0 CAMBER judgment: a stated default comfort band (68-76 °F)
cool_sp_f 76.0 °F 70.0 to 82.0 CAMBER judgment: a stated default comfort band (68-76 °F)
band_tol_f 1.0 °F (past the setpoint) 0.0 to 3.0 CAMBER judgment: the zone must be clearly past a setpoint to count as outside the band
heat_rise_f 15.0 °F (discharge air above the zone) 8.0 to 30.0 CAMBER judgment: a water-to-air heat pump delivers about 85-105 °F air in heating against a ~70 °F room
cool_drop_f 10.0 °F (discharge air below the zone) 5.0 to 20.0 CAMBER judgment: a heat pump in cooling delivers about 52-60 °F air

How to calibrate:

  • warn_fight_pct: Check a known-good season: brief overlap at changeover sets the floor.
  • fault_fight_pct: Keep it well above warn_fight_pct.
  • warn_duty_spread_pct: Raise it where the units in a room are deliberately unequal (a lead unit plus a smaller helper). Note: Warn only.
  • warn_sensor_spread_f: Set it from the sensors' accuracy plus the room's normal stratification, e.g. from a spot check with a reference thermometer. Note: Warn only.
  • heat_sp_f: Set it to the site's occupied heating setpoint from the BAS or the thermostats; better, map the heat_sp point so the rule reads it. Note: Used only when no heat_sp point is mapped (the finding then carries a caveat).
  • cool_sp_f: Set it to the site's occupied cooling setpoint; better, map the cool_sp point. Note: Used only when no cool_sp point is mapped (the finding then carries a caveat).
  • band_tol_f: Set it to about the thermostat's deadband or the zone sensor's accuracy, whichever is larger.
  • heat_rise_f: Look at a known-good week: the discharge-minus-zone difference while the unit heats clusters well above zero; set this below that cluster and above the fan-only rise. Note: Ignored when compressor status and the reversing-valve command are both mapped.
  • cool_drop_f: Look at a known-good week: set it below the zone-minus-discharge difference seen while the unit cools and above the fan-only difference. Note: Ignored when compressor status and the reversing-valve command are both mapped.

Not thresholds: rooms (a structural mapping {room: [unit, ...]} that declares which units share a room (else the topology), not a threshold).

hw_plant_deltat

Parameter Default Unit Range Basis
design_deltaT_min_f 20.0 °F 5.0 to 40.0 CAMBER judgment: a typical hot-water loop design delta-T; the code says to confirm it against the loop's own design

How to calibrate:

  • design_deltaT_min_f: Read the design supply-minus-return delta-T from the boiler or coil schedules and set this a little below it. A low-temperature (condensing) loop is often designed for a smaller delta-T than a 180 °F loop. Note: Severity is fixed in code: warn when >= 20 % of boiler-running hours sit below this, fault at >= 50 %. Firing comes from the boiler run status (boiler_status); without one, from the gas input (gas_input_rate) above 5 % of its own 95th percentile (fixed in code), reported as run_source gas with a caveat.

hw_pump_dp_reset

Parameter Default Unit Range Basis
near_min_pct 25.0 % speed, or "auto" "auto", or 10.0 to 60.0 CAMBER judgment: 25 % is a common VFD minimum; hot-water pumps keep the fixed band by default (0.98, #86), with "auto" as an opt-in
floor_tol_pct 1.0 % speed 0.1 to 5.0 CAMBER judgment: a VFD parked at its minimum reads within about a point of it

How to calibrate:

  • near_min_pct: Read the drive's minimum speed from the VFD parameters (or the sequence of operations) and set this a point or two above it; or plot a histogram of the running speed and find the pile-up at the bottom. "auto" learns it: the 2nd percentile of the running speed counts as the floor when >= 10 % of running samples sit within floor_tol_pct of it and the 90th percentile is >= 20 points above it (else the band falls back to 25 %). Note: The band used is max(25, learned floor + floor_tol_pct) under "auto", else the number given. Severity is fixed in code: warn when >= 50 % of running time is at or below the band. The metrics vfd_floor_pct, near_min_band_pct and near_min_source say what was used.
  • floor_tol_pct: Look at the spread of the speed readings while the pump sits at its minimum (trend resolution and rounding); set this just wider than that spread. Note: Used only when near_min_pct is "auto".

Fixed in code: fault when the pump runs near full speed (>= 90 % speed) for >= 60 % of its running time; warn at >= 30 % near full, or near minimum (at or below near_min_pct) for >= 50 %; running means speed > 5 %; a DP setpoint with a standard deviation under 0.5 (its own units) counts as flat (no reset); fixed in code (camber/rules/hwpump_rule.py, camber/chwpump.py)

hw_pump_summer_lockout

Parameter Default Unit Range Basis
summer_lockout_oat_f 65.0 °F 50.0 to 80.0 CAMBER judgment: the same generic mild-climate lockout as boiler_summer_lockout; the check follows PNNL Re-tuning Ch.8 (lock out the hot-water pumps, not only the boiler, in warm weather), which gives no single value

How to calibrate:

  • summer_lockout_oat_f: Use the same heating lockout as boiler_summer_lockout: the one the sequence of operations specifies, or the outdoor temperature above which no zone has called for heat in a known-good year of trends. A plant that serves dehumidification reheat or domestic hot water in summer needs a higher value, or none of this check. Note: Severity is fixed in code, as for the boiler: warn when the pump runs at OAT above this for >= 5 % of its occupied running hours, fault at >= 20 %. Without an OAT point the check is not evaluated. A config that sets this on boiler_summer_lockout but not here passes the boiler's value on (recorded in the finding's param_basis). Running comes from the pump status, else the speed above 5 %, else the loop flow above 5 % of its own 95th percentile (fixed in code).

leaking_valve

Parameter Default Unit Range Basis
fan_heat_f 2.0 °F 0.5 to 4.0 standard: ASHRAE Guideline 36-2021 §5.16.14 (ΔT_SF, Table 5.16.14.7 initial value)
delta_thr_f 3.0 °F 1.0 to 6.0 public source: PNNL Re-tuning Ch.5 with CAMBER judgment (a coil-side shift beyond noise)
valve_closed_thr 5.0 % of valve stroke 0.0 to 15.0 public source: PNNL Re-tuning Ch.5 with CAMBER judgment (a deadband)
coil_sensor_fan_heat false flag false, true CAMBER judgment: coil sensors sit upstream of a draw-through fan
measured_fan_heat_f null °F 0.0 to 4.0 calibrated on the unit's own known-good hours (lbnl-sdahu template: 1.0 from the fault-free run's median supply minus mixed air, 1.06 °F over 1,400 occupied, fan-on, valve-shut hours; in-sample, since that run is also scored)
cool_delta_thr_f null °F 0.5 to 6.0 CAMBER judgment: the cooling margin below the fan-heat line; lbnl-sdahu uses 1.0 with its 1.0 °F fan heat, so a leak must take the supply air below the mixed air (fault-free occupied hours below that line: 2.4 %)
occupied_only false flag false, true calibrated on lbnl-sdahu AHU__fault_free: unoccupied fan cycling reads 24.2 % of valve-shut hours below the cooling-leak line, occupied hours 2.4 %; off by default since a leak shows whenever the unit runs
judge_heating_on_supply_air true flag false, true CAMBER judgment: on a single-duct unit the supply air leaves the heating coil

How to calibrate:

  • fan_heat_f: Run a known-good period with both valves closed and the fan on; the median rise from mixed air to supply air is the fan heat. Record the run in a config comment.
  • delta_thr_f: With both valves known tight, take the spread (e.g. 95th percentile) of the air shift beyond fan heat (median_delta_f and its per-coil values) and set the threshold above it.
  • valve_closed_thr: Set it just above the position the valve commands read when the BAS commands them closed (their 95th percentile on off hours).
  • coil_sensor_fan_heat: Set it from the unit's layout: true for a blow-through unit, where the fan sits ahead of the coils and the coil leaving-air sensors see its heat.
  • measured_fan_heat_f: On a period known to be leak-free, take the hours with both valves shut and the fan on (occupied hours if occupied_only is set) and use the median supply-air minus mixed-air rise. Calibrate on one period and score on another: a value fitted to a run makes that run's verdict in-sample. Note: None = no fan heat credited: a cooling leak must pull the supply air cool_delta_thr_f below the mixed air. When set, a cooling leak is a rise below measured_fan_heat_f - cool_delta_thr_f; applies on the supply-air path (or a coil sensor with coil_sensor_fan_heat), and the cool_shift_f metric reports it.
  • cool_delta_thr_f: With measured_fan_heat_f set, read chw_median_delta_f and its spread on known-good valve-shut hours and set the margin so few of them (a few percent) fall below the line measured_fan_heat_f - margin. Note: None = delta_thr_f.
  • occupied_only: Turn it on when unoccupied fan-on hours (night cycling, morning warm-up) give a fault-free unit a leak signature; compare chw_leak_pct with it on and off on a known-good period. Map an occupancy point where one is trended. Note: Reads the trended occupancy (the OCCUPANCY role) when it has values, else assumes weekdays 07-18; the occupancy_gate metric says which.
  • judge_heating_on_supply_air: Set it false when the mapped supply air does not pass the heating coil -- a dual-duct unit whose supply_air_temp is the cold deck (lbnl-ddahu false-faults a stuck-damper run otherwise). Map the heating coil's leaving air where it is trended. Note: False leaves a heating coil without its own leaving-air sensor unjudged (a caveat says so); a coil with HEAT_COIL_LEAVING_TEMP is judged on it either way.

night_weekend_setback

Parameter Default Unit Range Basis
start_hour 7 hour of day (0-23) 0 to 23 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
end_hour 18 hour of day (1-24) 1 to 24 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
occupied_days [0, 1, 2, 3, 4] weekday numbers (Mon=0 ... Sun=6) 0 to 6 CAMBER judgment: a Monday-Friday schedule
min_unoccupied_run_pct 5.0 % of unoccupied time 0.0 to 20.0 CAMBER judgment: a unit running less than this unoccupied is not missing its setback (#57)
unoccupied_heat_sp_f null °F 45.0 to 68.0 CAMBER judgment: no default; the unoccupied heating setpoint comes from data or config
unoccupied_cool_sp_f null °F 78.0 to 95.0 CAMBER judgment: no default; the unoccupied cooling setpoint comes from data or config
min_setback_depth_f 3.0 °F 1.0 to 10.0 CAMBER judgment: a held setback sits at least 3 °F from occupied comfort
max_hold_duty_pct 90.0 % of the unoccupied hours the fan runs 50.0 to 100.0 CAMBER judgment: a fan at or above 90 % duty in the hours it runs is running through, not cycling

How to calibrate:

  • start_hour: Set it to the start of the building's occupied mode, read from the BAS schedule or from the hour the supply fan or occupied-mode point switches on in a typical week of trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • end_hour: Set it to the end of occupied mode (exclusive), read from the BAS schedule or trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • occupied_days: List the days the building runs occupied mode, e.g. [0, 1, 2, 3, 4, 5] for a Saturday schedule. Note: A list of integers; each must lie in the range.
  • min_unoccupied_run_pct: Read fan_run_unoccupied_pct on units you know are scheduled off; set the floor just above the scattered runtime they show (freeze protection, morning starts).
  • unoccupied_heat_sp_f: Enter the unoccupied (setback) heating setpoint from the BAS program. Note: None means use HEAT_SP read in unoccupied hours; without either, the held-setback test compares the zone with its own occupied temperature.
  • unoccupied_cool_sp_f: Enter the unoccupied (set-up) cooling setpoint from the BAS program. Note: None means use COOL_SP read in unoccupied hours; the cooling side of the held-setback test always needs a known unoccupied setpoint.
  • min_setback_depth_f: Take the difference between the occupied and unoccupied setpoints in the BAS program and set this somewhat below it. Note: Also the minimum gap required between a known unoccupied setpoint and the occupied reference.
  • max_hold_duty_pct: On a unit known to hold a setback, read its duty in the night hours it runs on the coldest nights; set this above that. Note: Needs sub-hourly fan status; a fan that runs whole hours never counts as cycling.

outdoor_air_fraction

Parameter Default Unit Range Basis
min_oa_pct 20.0 % of supply airflow (OA fraction) 0.0 to 100.0 CAMBER judgment: a generic assumed design minimum; the real one is the unit's own
cooling_cutoff_f 70.0 °F 55.0 to 80.0 public source: PNNL Re-tuning Ch.5 with CAMBER judgment (camber/oafraction.py)
denom_min_f 5.0 °F 2.0 to 15.0 public source: PNNL Re-tuning Ch.5 (a stability guard on the temperature balance)
fan_gate true flag false, true CAMBER judgment: with the fan stopped the air temperatures describe still air, not the unit at work
min_oa_pct_by_month null % of supply airflow, per month 0.0 to 100.0 CAMBER judgment: only for a sequence with a seasonal minimum

How to calibrate:

  • min_oa_pct: Take it from the ventilation design or sequence, or measure the OA fraction at the unit's minimum damper position on a fan-on period with |OAT - RAT| of 10 °F or more; a 10 % minimum damper position can be a 1.6 % OA fraction. Note: Excess OA is OAF above min + 5 %; under-ventilation is a median OAF below min - 5 % (warn) or below half the minimum (fault).
  • cooling_cutoff_f: Set it to the economizer high limit of the sequence: above it outdoor air is a cooling penalty, not free cooling.
  • denom_min_f: Raise it when OA fractions scatter widely on mild days (n_masked_small_delta_t shows how many samples it drops); about 2-3 times the combined OAT/RAT sensor error.
  • fan_gate: Leave it on. Turn it off only when the fan signal is known to be wrong (stuck status, a speed point that reads 0 while running) and every trended sample is a running sample. Note: The fan is read from supply-fan status, else speed, else airflow; a unit with none of them is judged ungated and the finding says so (the fan_gate metric).
  • min_oa_pct_by_month: Enter the sequence's minimum for each month that differs from min_oa_pct, e.g. {6: 15, 7: 15, 8: 15}. Note: None = min_oa_pct all year. A {month (1-12): pct} mapping; each value must lie in the range.

overcooling_min_flow

Parameter Default Unit Range Basis
start_hour 7 hour of day (0-23) 0 to 23 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
end_hour 18 hour of day (1-24) 1 to 24 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
occupied_days [0, 1, 2, 3, 4] weekday numbers (Mon=0 ... Sun=6) 0 to 6 CAMBER judgment: a Monday-Friday schedule

How to calibrate:

  • start_hour: Set it to the start of the building's occupied mode, read from the BAS schedule or from the hour the supply fan or occupied-mode point switches on in a typical week of trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • end_hour: Set it to the end of occupied mode (exclusive), read from the BAS schedule or trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • occupied_days: List the days the building runs occupied mode, e.g. [0, 1, 2, 3, 4, 5] for a Saturday schedule. Note: A list of integers; each must lie in the range.

overcooling_severity

Parameter Default Unit Range Basis
tiers null °F below the reference setpoint, per key ('info', 'warn', 'fault') each value: 0.5 to 6.0 CAMBER judgment: info 1 / warn 2 / fault 3 °F below the reference, described in code as Std-55-aligned (no section cited)
window_min 60.0 min 15.0 to 240.0 CAMBER judgment: an excursion must persist an hour to count
relative_to_deadband true flag false, true CAMBER judgment: a space inside the heating-cooling deadband is operating as designed
start_hour 7 hour of day (0-23) 0 to 23 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
end_hour 18 hour of day (1-24) 1 to 24 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
occupied_days [0, 1, 2, 3, 4] weekday numbers (Mon=0 ... Sun=6) 0 to 6 CAMBER judgment: a Monday-Friday schedule
recovery_hours 2.0 h 0.0 to 4.0 CAMBER judgment: morning recovery from setback is not overcooling
reheat_saturated_pct 90.0 % valve open 75.0 to 100.0 CAMBER judgment: a valve at or above 90 % is treated as fully open (heating maxed out)
shortfall_share_pct {"warn": 5.0, "fault": 20.0} % of considered (occupied, fan-on) samples, per key ('warn', 'fault') each value: 0 to 100 CAMBER judgment: the same 5 % / 20 % warn / fault shares reheat_capacity_shortfall uses; a heating shortfall a few hours a year is a weather extreme, not a capacity fault (#85: a stuck-open damper's 0.81 % shortfall graded fault on depth alone)
share_pct null % of considered (occupied, fan-on) samples, per key ('warn', 'fault') each value: 0 to 100 CAMBER judgment: opt-in; overcooling stays graded by depth x duration alone by default so existing verdicts don't move

How to calibrate:

  • tiers: Look at max_depth_f and median_depth_f on zones occupants do not complain about; set the warn tier above the depth those zones sustain. Calibrating on the zones you are scoring would hide a building-wide overcooling problem. Note: None uses the defaults {'info': 1.0, 'warn': 2.0, 'fault': 3.0}. A dict with all three keys, each a depth in °F, mildest first; the info tier is never counted as a fault.
  • window_min: Lengthen it if short excursions (door openings, load swings) raise findings on zones that are comfortable in practice; keep it at or above the trend interval, since one sample at a coarser interval already satisfies it.
  • relative_to_deadband: Leave it on when both setpoints are trended; turn it off only to measure depth below the cooling setpoint deliberately (it over-flags a healthy deadband). Note: With no heating setpoint the rule falls back to the cooling setpoint and caps the result at warn.
  • start_hour: Set it to the start of the building's occupied mode, read from the BAS schedule or from the hour the supply fan or occupied-mode point switches on in a typical week of trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • end_hour: Set it to the end of occupied mode (exclusive), read from the BAS schedule or trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • occupied_days: List the days the building runs occupied mode, e.g. [0, 1, 2, 3, 4, 5] for a Saturday schedule. Note: A list of integers; each must lie in the range.
  • recovery_hours: Measure how long zones take to reach setpoint after occupied mode starts on a cold morning (median across healthy zones) and use that. Used only when no WARMUP point is mapped.
  • reheat_saturated_pct: Read the reheat valve command in known cold, fully-heating hours on healthy boxes; set this just below the level those valves actually reach (some controllers top out at 95-98 %, not 100 %). Note: Shared by overcooling_severity (sets these samples aside as a heating shortfall) and reheat_capacity_shortfall (reports them as its finding); keep the two equal.
  • shortfall_share_pct: Run the rule on a period you know the zone was comfortable and look at shortfall_warn_pct (the share of samples in a sustained shortfall at least the warn depth deep) across your zones: set 'warn' above the healthy zones' spread (their p95) and 'fault' where a zone's cold hours become a standing complaint. shortfall_depth_severity shows the depth-only grade for comparison. Note: The shortfall grade is the lesser of the depth tier and the share tier (share below 'warn' -> info). None grades by depth alone (the pre-0.98 behaviour). Each key's range is 0-100 with warn <= fault.
  • share_pct: Set it when brief deep overcooling (a few cold mornings) should not rate warn/fault: look at warn_pct on zones you consider fine and set 'warn' above their spread; {'warn': 5, 'fault': 20} mirrors the shortfall gate. Note: None (default) = depth alone. When set, the overcooling grade is the lesser of the depth tier and the share tier from warn_pct (share below 'warn' -> info). The no-heating-setpoint cap (fault -> warn) still applies after it.

reheat_capacity_shortfall

Parameter Default Unit Range Basis
heat_sp_f null °F 60.0 to 75.0 CAMBER judgment: no default; the zone heating setpoint must come from data or config
tol_f 1.5 °F 0.5 to 4.0 CAMBER judgment: the unmet_setpoint_hours tolerance (1.5 °F)
reheat_saturated_pct 90.0 % valve open 75.0 to 100.0 CAMBER judgment: a valve at or above 90 % is treated as fully open (heating maxed out)
warn_pct 5.0 % of evaluated occupied samples 1.0 to 20.0 CAMBER judgment: screening-grade, not a standard's (per the module docstring)
fault_pct 20.0 % of evaluated occupied samples 5.0 to 60.0 CAMBER judgment: screening-grade, not a standard's (per the module docstring)
min_hours 10.0 h 1.0 to 100.0 CAMBER judgment: screening-grade, not a standard's (per the module docstring)
recovery_hours 2.0 h 0.0 to 4.0 CAMBER judgment: warm-up after occupancy starts or a setpoint step-up is expected to run the valve wide open below setpoint
start_hour 7 hour of day (0-23) 0 to 23 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
end_hour 18 hour of day (1-24) 1 to 24 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
occupied_days [0, 1, 2, 3, 4] weekday numbers (Mon=0 ... Sun=6) 0 to 6 CAMBER judgment: a Monday-Friday schedule

How to calibrate:

  • heat_sp_f: Enter the zone heating setpoint from the BAS program or the sequence of operations; use a per-box mapping when boxes differ. Note: None means use the trended HEAT_SP; without it the rule declines. One value for every box, or a {equip: °F} mapping. A trended HEAT_SP always wins.
  • tol_f: Set it above the normal control swing of healthy zones below their heating setpoint (a high percentile of setpoint minus zone temperature while heating). Keep it equal to unmet_setpoint_hours' tol_F.
  • reheat_saturated_pct: Read the reheat valve command in known cold, fully-heating hours on healthy boxes; set this just below the level those valves actually reach (some controllers top out at 95-98 %, not 100 %). Note: Shared by overcooling_severity (sets these samples aside as a heating shortfall) and reheat_capacity_shortfall (reports them as its finding); keep the two equal.
  • warn_pct: Compare shortfall_pct across boxes known to heat adequately; set warn above the highest of those. Note: Applies only once min_hours of shortfall have accumulated.
  • fault_pct: Keep it well above warn_pct; raise it if seasonal design-day hours alone push healthy boxes past it. Note: Applies only once min_hours of shortfall have accumulated.
  • min_hours: Raise it for long records so a few cold mornings cannot grade a box; lower it for a short test window. Compare with the finding's shortfall_hours.
  • recovery_hours: Measure how long healthy boxes take to reach the heating setpoint after occupancy starts on a cold morning (median across boxes) and use that. Note: Applied after each occupied-period start and each heating-setpoint step up, in addition to trended WARMUP samples.
  • start_hour: Set it to the start of the building's occupied mode, read from the BAS schedule or from the hour the supply fan or occupied-mode point switches on in a typical week of trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • end_hour: Set it to the end of occupied mode (exclusive), read from the BAS schedule or trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • occupied_days: List the days the building runs occupied mode, e.g. [0, 1, 2, 3, 4, 5] for a Saturday schedule. Note: A list of integers; each must lie in the range.

reheat_minimization_g36

Parameter Default Unit Range Basis
valve_thr 5.0 % valve open 1.0 to 20.0 CAMBER judgment: the dual-max logic follows ASHRAE Guideline 36 §5.6.5; this value is not from G36
flow_margin 1.2 - (multiple of the airflow setpoint) 1.05 to 2.0 CAMBER judgment: 20 % headroom over the minimum-flow setpoint before airflow counts as above minimum

How to calibrate:

  • valve_thr: Set it just above the valve command a closed valve reads in trends (leak-by or offset readings of a few percent), so that only real reheat counts. Note: The warn and fault levels (15 % and 40 % of reheating hours above minimum flow) are fixed in code.
  • flow_margin: On healthy boxes at minimum flow, take the 95th percentile of airflow / airflow setpoint; set the margin just above it so sensor noise and tracking error do not count as violations.

Fixed in code: warn when >= 15 % and fault when >= 40 % of reheating hours run above minimum flow (camber/rules/reheat_min_rule.py)

reheat_penalty

Parameter Default Unit Range Basis
start_hour 7 hour of day (0-23) 0 to 23 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
end_hour 18 hour of day (1-24) 1 to 24 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
occupied_days [0, 1, 2, 3, 4] weekday numbers (Mon=0 ... Sun=6) 0 to 6 CAMBER judgment: a Monday-Friday schedule
fan_heat_f null °F, or "auto" "auto", or 0.0 to 8.0 CAMBER judgment (0.98, #85): a fan-powered box's own fan, and in a parallel box the plenum air it mixes in, lifts the discharge above the entering primary air with the valve shut; on the LBNL fan-powered boxes the discharge rose 6.0 °F (median, parallel box) and 8.7 °F (series box) over the entering air with the valve stuck shut, past the 5 °F no-rise bound, so a valve that delivered no heat looked corroborated
box_type null choice "single_duct", "parallel", "series" CAMBER judgment (0.100, #99), checked in-sample on the labelled LBNL fan-powered runs. A single-duct box has no fan, so its closed-valve lift is duct gains and sensor error (1-3 °F). A parallel box runs its fan in heating only, so its closed-valve lift was 0.0 °F on the fault-free run. A series box's fan always runs and mixes in plenum air, so its lift was 13.7 °F. On the series runs, a 10 °F no-rise cap puts the 15 °F bound between the stuck valves' rise (12.9 °F at most) and the working full-valve rise (17.6 °F at least). Raising the big-rise cap past 8 °F lost the passing-valve caveat on the 50 % and 80 % leaks.

How to calibrate:

  • start_hour: Set it to the start of the building's occupied mode, read from the BAS schedule or from the hour the supply fan or occupied-mode point switches on in a typical week of trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • end_hour: Set it to the end of occupied mode (exclusive), read from the BAS schedule or trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • occupied_days: List the days the building runs occupied mode, e.g. [0, 1, 2, 3, 4, 5] for a Saturday schedule. Note: A list of integers; each must lie in the range.
  • fan_heat_f: Map the entering primary air (MIXED_AIR_TEMP) and read the discharge minus entering air on hours the valve is shut and the box moves air (fan on, where the fan status is trended). Set the typical lift, or use "auto" to estimate it per box from those hours. Single-duct boxes with no fan: leave it None. Note: None (default) adds nothing to the valve-vs-discharge bounds (5 °F no rise at full valve, 10 °F big rise with the valve shut). A number raises both by that many °F. "auto" uses the median closed-valve, airflow-bearing lift over the entering air (>= 12 samples, fan-on only when SUPPLY_FAN_STATUS is mapped), clipped to 0-8 °F; without the entering air or enough samples it adds nothing. The finding reports the value used as fan_heat_f. box_type sets the "auto" caps per box type (0.100, #99).
  • box_type: Declare it from the box schedule or the submittals: series fan-powered (fan runs whenever the zone is occupied), parallel fan-powered (fan runs in heating), or single-duct (no fan). It only matters with fan_heat_f "auto". On a site with several box types, use a separate run for each type. Note: None (default) caps the "auto" estimate at 8 °F on both bounds, as before 0.100. "single_duct" caps it at 3 °F and "parallel" at 8 °F, on both bounds. "series" caps the no-rise allowance (valve open) at 10 °F and keeps the big-rise allowance (valve shut) at 8 °F: the estimate is learned from the closed-valve samples, so a higher cap there would absorb a passing valve's heat. A numeric fan_heat_f is used as given. With box_type set, the finding also reports box_type, fan_lift_f (the uncapped estimate) and fan_heat_closed_f.

sat_cohort_starvation

Parameter Default Unit Range Basis
cohort_frac 0.75 fraction of the group's zones 0.5 to 1.0 CAMBER judgment: screening-grade (provisional-untuned)
sustained_frac 0.5 fraction of active cycles 0.2 to 0.9 CAMBER judgment: screening-grade (provisional-untuned)
min_active_cycles 10 count (request cycles) 5 to 500 CAMBER judgment: screening-grade (provisional-untuned)
min_zones_per_group 3 count 3 to 20 CAMBER judgment: a common-mode pattern needs a cohort, not a pair

How to calibrate:

  • cohort_frac: On a healthy air handler at peak load, read the largest share of its zones requesting at once; set this above it.
  • sustained_frac: Read group_sustained_frac on healthy air handlers over a hot week; set this above it. For SAT, corroborate with OAT: a hot design day can starve a healthy cohort.
  • min_active_cycles: Raise it for long or fine-grained records; zones with fewer usable system-on cycles are reported unevaluable. Note: The cohort-starvation rules also need this many active cycles in a group before calling it starved.
  • min_zones_per_group: Raise it so that a few zones requesting together are not read as a whole starved air handler.

Not thresholds: groups (explicit zone -> air-handler membership ({zone: ahu} or a callable), a structural input; None uses the served-by topology or a building-wide pool).

sat_reset_effectiveness

Parameter Default Unit Range Basis
min_cycles 12 count (T&R cycles) 6 to 500 CAMBER judgment: screening-grade (provisional-untuned)
flat_frac 0.1 fraction of the reset band 0.02 to 0.3 CAMBER judgment: screening-grade (provisional-untuned)
expected_move_frac 0.25 fraction of the reset band 0.1 to 0.8 CAMBER judgment: screening-grade (provisional-untuned)
pinned_frac 0.15 fraction of the reset band 0.05 to 0.3 CAMBER judgment: screening-grade (provisional-untuned)
mode_frac 0.6 fraction of demand (or idle) cycles 0.3 to 0.95 CAMBER judgment: screening-grade (provisional-untuned)
min_mode_cycles 10 count (T&R cycles) 5 to 200 CAMBER judgment: screening-grade (provisional-untuned)
wrong_dir_frac 0.5 fraction of cycles T&R commanded a move 0.3 to 0.9 CAMBER judgment: screening-grade (provisional-untuned)

How to calibrate:

  • min_cycles: Raise it for coarse trends; fewer usable rows than this and the rule declines.
  • flat_frac: On a reset known to work, take the setpoint's range in a quiet period; keep flat_frac well below that range divided by the band. Note: stuck = actual range <= flat_frac x band while the expected range >= expected_move_frac x band.
  • expected_move_frac: Keep it well above flat_frac so a flat setpoint is called stuck only when the requests clearly demanded movement.
  • pinned_frac: Set it to cover the setpoint's normal jitter around an end of its range on a working reset.
  • mode_frac: Read pct_unresponsive and pct_untrimmed on a reset known to work; set mode_frac above both.
  • min_mode_cycles: Raise it on long records to judge the not-responding and not-trimming modes on more evidence.
  • wrong_dir_frac: Raise it when a coarser trend cadence than the controller makes moves look reversed on a reset known to work.

sat_rogue_zone_census

Parameter Default Unit Range Basis
dominance_frac 0.5 fraction of active cycles 0.2 to 0.9 CAMBER judgment: screening-grade (provisional-untuned)
share_mult 2.0 - (multiple of an equal share) 1.2 to 5.0 CAMBER judgment: screening-grade (provisional-untuned)
min_share 0.3 fraction of the group's requests 0.1 to 0.8 CAMBER judgment: screening-grade (provisional-untuned)
min_active_cycles 10 count (request cycles) 5 to 500 CAMBER judgment: screening-grade (provisional-untuned)
min_zones_per_group 2 count 2 to 20 CAMBER judgment: a rogue needs at least one sibling to be compared with

How to calibrate:

  • dominance_frac: On a group with no known rogue, read the highest zone binding fraction; set this above it.
  • share_mult: A rogue needs share >= max(min_share, share_mult / n_zones); raise it to demand a more lopsided share in groups with many zones.
  • min_share: On a healthy group, read the largest zone share of requests; set this above it.
  • min_active_cycles: Raise it for long or fine-grained records; zones with fewer usable system-on cycles are reported unevaluable. Note: The cohort-starvation rules also need this many active cycles in a group before calling it starved.
  • min_zones_per_group: Raise it when small groups flag zones that merely serve a hotter space.

Not thresholds: groups (explicit zone -> air-handler membership ({zone: ahu} or a callable), a structural input; None uses the served-by topology or a building-wide pool).

simultaneous_heat_cool

Parameter Default Unit Range Basis
dehumidification null flag false, true CAMBER judgment: the unit's sequence is unknown until declared
reheat_lift_f 2.0 °F 0.5 to 6.0 CAMBER judgment: a supply-air rise over the cooling-coil leaving air beyond sensor noise means heat is added after the coil
dewpoint_margin_f 2.0 °F 0.0 to 5.0 CAMBER judgment: allows for sensor and dew-point approximation error
humid_rh_pct 55.0 % relative humidity (return air) 40.0 to 70.0 CAMBER judgment: a common upper bound of the comfort humidity band
fault_pct 5.0 % of occupied hours 1.0 to 30.0 CAMBER judgment
warn_pct 1.0 % of occupied hours 0.1 to 10.0 CAMBER judgment

How to calibrate:

  • dehumidification: Read the unit's sequence of operation: true if it has a dehumidification-with-reheat mode, false if it has none. Leave it unset only when the sequence is unknown. Note: None = unknown: each both-open interval is classified from the coil-leaving temperature, dew point and humidity. True accepts reheat after the coil as dehumidification unless a dew point shows the coil dry; False counts every both-open interval.
  • reheat_lift_f: In a known dehumidification period, take the supply-air minus coil-leaving temperature; in a cooling-only period with the heating valve shut, the same difference (fan heat and sensor offset) sets the floor. Choose a value between the two.
  • dewpoint_margin_f: Compare the coil-leaving temperature with the entering dew point on hours the coil is known to condense (drain-pan flow, a falling return humidity); widen the margin if those hours read as dry.
  • humid_rh_pct: Set it to the unit's humidity setpoint or high-humidity limit from the sequence. Used only where no dew point can be formed.
  • fault_pct: Look at simultaneous_hc_pct (unexplained_hc_pct when dehumidification is classified) on units known to be healthy; set the fault level well above their spread. Tuning it on the unit under test is circular. Note: Must be at or above warn_pct.
  • warn_pct: Set it just above the both-open share that healthy units show during valve changeovers (read simultaneous_hc_pct on a known-good period).

source_loop_deltat

Parameter Default Unit Range Basis
design_deltat_f 10.0 °F (loop return minus supply, magnitude) 6.0 to 15.0 public source: ASHRAE / IGSHPA closed-loop heat-pump design practice (about 8-12 °F at roughly 3 gpm per ton), as the rule module cites; not a standard requirement
flat_deltat_f 1.0 °F (loop return minus supply, magnitude) 0.3 to 3.0 CAMBER judgment: under 1 °F the loop carries next to no heat
warn_flat_pct 50.0 % of pumping samples 20.0 to 80.0 CAMBER judgment: screening-grade (provisional, 0.93)
fault_flat_pct 80.0 % of pumping samples 50.0 to 95.0 CAMBER judgment: screening-grade (provisional, 0.93)
min_pump_speed_pct 5.0 % of full pump speed 0.0 to 30.0 CAMBER judgment: any speed above a near-zero command counts as pumping
min_hours 24.0 h (pumping hours with both loop temperatures) 6.0 to 168.0 CAMBER judgment: at least a day of pumping

How to calibrate:

  • design_deltat_f: Use the loop's design ΔT from the mechanical schedules or the design flow and block load. Note: Warn needs the 90th-percentile |ΔT| below half of it; fault, below a quarter.
  • flat_deltat_f: Set it a little above the combined error of the two loop sensors; check with the pumps running and every heat pump off, where |ΔT| should read near zero.
  • warn_flat_pct: Check the flat share in a known-good season with a working DP reset; set the warn above it.
  • fault_flat_pct: Keep it well above warn_flat_pct.
  • min_pump_speed_pct: Set it just above the speed signal the drive reports when stopped. Note: Used only when no pump status is mapped.
  • min_hours: Raise it to a week where the loop load swings with occupancy.

static_cohort_starvation

Parameter Default Unit Range Basis
cohort_frac 0.75 fraction of the group's zones 0.5 to 1.0 CAMBER judgment: screening-grade (provisional-untuned)
sustained_frac 0.5 fraction of active cycles 0.2 to 0.9 CAMBER judgment: screening-grade (provisional-untuned)
min_active_cycles 10 count (request cycles) 5 to 500 CAMBER judgment: screening-grade (provisional-untuned)
min_zones_per_group 3 count 3 to 20 CAMBER judgment: a common-mode pattern needs a cohort, not a pair

How to calibrate:

  • cohort_frac: On a healthy air handler at peak load, read the largest share of its zones requesting at once; set this above it.
  • sustained_frac: Read group_sustained_frac on healthy air handlers over a hot week; set this above it. For SAT, corroborate with OAT: a hot design day can starve a healthy cohort.
  • min_active_cycles: Raise it for long or fine-grained records; zones with fewer usable system-on cycles are reported unevaluable. Note: The cohort-starvation rules also need this many active cycles in a group before calling it starved.
  • min_zones_per_group: Raise it so that a few zones requesting together are not read as a whole starved air handler.

Not thresholds: groups (explicit zone -> air-handler membership ({zone: ahu} or a callable), a structural input; None uses the served-by topology or a building-wide pool).

static_pressure_reset

Parameter Default Unit Range Basis
min_range_inwc 0.15 in. w.c. 0.05 to 0.5 CAMBER judgment
move_min_inwc 0.05 in. w.c. 0.01 to 0.2 CAMBER judgment: about one G36 trim-and-respond response step (SPres ~0.04-0.06 in. w.c.)

How to calibrate:

  • min_range_inwc: On a unit known to run trim-and-respond, read sp_range_inwc over a few weeks and set the threshold well below it; it should exceed setpoint rounding and a manual tweak.
  • move_min_inwc: Set it to about one response step (SPres) of the site's trim-and-respond, and above the setpoint register's rounding.

static_reset_effectiveness

Parameter Default Unit Range Basis
min_cycles 12 count (T&R cycles) 6 to 500 CAMBER judgment: screening-grade (provisional-untuned)
flat_frac 0.1 fraction of the reset band 0.02 to 0.3 CAMBER judgment: screening-grade (provisional-untuned)
expected_move_frac 0.25 fraction of the reset band 0.1 to 0.8 CAMBER judgment: screening-grade (provisional-untuned)
pinned_frac 0.15 fraction of the reset band 0.05 to 0.3 CAMBER judgment: screening-grade (provisional-untuned)
mode_frac 0.6 fraction of demand (or idle) cycles 0.3 to 0.95 CAMBER judgment: screening-grade (provisional-untuned)
min_mode_cycles 10 count (T&R cycles) 5 to 200 CAMBER judgment: screening-grade (provisional-untuned)
wrong_dir_frac 0.5 fraction of cycles T&R commanded a move 0.3 to 0.9 CAMBER judgment: screening-grade (provisional-untuned)

How to calibrate:

  • min_cycles: Raise it for coarse trends; fewer usable rows than this and the rule declines.
  • flat_frac: On a reset known to work, take the setpoint's range in a quiet period; keep flat_frac well below that range divided by the band. Note: stuck = actual range <= flat_frac x band while the expected range >= expected_move_frac x band.
  • expected_move_frac: Keep it well above flat_frac so a flat setpoint is called stuck only when the requests clearly demanded movement.
  • pinned_frac: Set it to cover the setpoint's normal jitter around an end of its range on a working reset.
  • mode_frac: Read pct_unresponsive and pct_untrimmed on a reset known to work; set mode_frac above both.
  • min_mode_cycles: Raise it on long records to judge the not-responding and not-trimming modes on more evidence.
  • wrong_dir_frac: Raise it when a coarser trend cadence than the controller makes moves look reversed on a reset known to work.

static_rogue_zone_census

Parameter Default Unit Range Basis
dominance_frac 0.5 fraction of active cycles 0.2 to 0.9 CAMBER judgment: screening-grade (provisional-untuned)
share_mult 2.0 - (multiple of an equal share) 1.2 to 5.0 CAMBER judgment: screening-grade (provisional-untuned)
min_share 0.3 fraction of the group's requests 0.1 to 0.8 CAMBER judgment: screening-grade (provisional-untuned)
min_active_cycles 10 count (request cycles) 5 to 500 CAMBER judgment: screening-grade (provisional-untuned)
min_zones_per_group 2 count 2 to 20 CAMBER judgment: a rogue needs at least one sibling to be compared with

How to calibrate:

  • dominance_frac: On a group with no known rogue, read the highest zone binding fraction; set this above it.
  • share_mult: A rogue needs share >= max(min_share, share_mult / n_zones); raise it to demand a more lopsided share in groups with many zones.
  • min_share: On a healthy group, read the largest zone share of requests; set this above it.
  • min_active_cycles: Raise it for long or fine-grained records; zones with fewer usable system-on cycles are reported unevaluable. Note: The cohort-starvation rules also need this many active cycles in a group before calling it starved.
  • min_zones_per_group: Raise it when small groups flag zones that merely serve a hotter space.

Not thresholds: groups (explicit zone -> air-handler membership ({zone: ahu} or a callable), a structural input; None uses the served-by topology or a building-wide pool).

supply_air_control

Parameter Default Unit Range Basis
tol_F 2.0 °F 0.5 to 5.0 CAMBER judgment: the ±2 °F band a tuned discharge-air loop holds
warn_pct 10.0 % of fan-on running samples 2.0 to 30.0 CAMBER judgment
fault_pct 25.0 % of fan-on running samples 10.0 to 60.0 CAMBER judgment
occupancy_gate "trended" choice "trended", "schedule", "off" CAMBER judgment (#84): on the fault-free lbnl-sdahu unit, 78 % of the fan-on hours more than 2 F too warm were unoccupied fan cycling (damper shut, warm return air), which graded a healthy unit warn at 12.1 %; gated on its trended occupancy (SYS_CTL) it reads ok at 2.97 %. PNNL's discharge-air guide asks whether the unit meets its setpoint while it serves the building, i.e. in occupied operation.

How to calibrate:

  • tol_F: On a known-good period take the 95th percentile of |SAT - setpoint| over fan-on hours (mean_abs_dev_F is the finding's summary of it) and set the tolerance just above it.
  • warn_pct: Read off_setpoint_pct on units known to control well and set the warn level above their spread; tuning it on the unit under test is circular.
  • fault_pct: Set it well above warn_pct, at the off_setpoint_pct where comfort or reheat complaints begin at the site. Note: Must be at or above warn_pct.
  • occupancy_gate: Keep 'trended' when the unit trends an occupied/unoccupied (or occupied-mode) point. To see what the gate removes, run once with 'off' and compare too_warm_pct / too_cold_pct and n_running: a large drop that sits in night or weekend hours is unoccupied cycling, not a control fault. Use 'schedule' only when no occupancy is trended AND you know the unit follows a weekday office schedule; then set the building's own hours on a rule that takes them, since this one uses the generic Mon-Fri 07-18 window. Note: 'trended' (default): fan-on samples AND the trended occupancy when the unit trends one (any non-null value); with none trended, fan-on samples only (no schedule fallback, so a unit that runs evenings or weekends on purpose keeps those hours). 'schedule': the trended occupancy if present, else the assumed weekday 07-18 schedule. 'off': fan-on samples only (the pre-0.98 behaviour). The gate lives in the running mask, so the evidence chart and the triage violation mask judge the same samples. The finding's occupancy_gate metric reports 'trended occupancy', 'assumed schedule (weekdays 07-18)', 'none trended (fan-on hours only)' or 'off'.

supply_air_reset

Parameter Default Unit Range Basis
fan_gate true flag false, true CAMBER judgment: with the fan stopped the air temperatures describe still air, not the unit at work

How to calibrate:

  • fan_gate: Leave it on. Turn it off only when the fan signal is known to be wrong (stuck status, a speed point that reads 0 while running) and every trended sample is a running sample. Note: The fan is read from supply-fan status, else speed, else airflow; a unit with none of them is judged ungated and the finding says so (the fan_gate metric).

supply_air_reset_compliance

Parameter Default Unit Range Basis
min_clg_sat 55.0 °F 45.0 to 60.0 standard: ASHRAE Guideline 36-2021 §5.16.2.2 (Min_ClgSAT of the OAT-based SAT reset)
t_max 65.0 °F 55.0 to 70.0 standard: ASHRAE Guideline 36-2021 §5.16.2.2 (the upper end of the OAT-based SAT reset)
oat_min 60.0 °F 40.0 to 70.0 standard: ASHRAE Guideline 36-2021 §5.16.2.2 (low end of the OAT reset band)
oat_max 70.0 °F 55.0 to 85.0 standard: ASHRAE Guideline 36-2021 §5.16.2.2 (high end of the OAT reset band)
tol_f 1.0 °F 0.5 to 3.0 CAMBER judgment: screening-grade (docs/TR-RESET.md: provisional, untuned)
warn_pct 40.0 % of gated samples 10.0 to 80.0 CAMBER judgment: screening-grade (docs/TR-RESET.md: provisional, untuned)
warn_gap_f 2.0 °F 0.5 to 5.0 CAMBER judgment: a trivially small persistent gap should not warn
track_gap_f 5.0 °F 2.0 to 10.0 CAMBER judgment: screening-grade (docs/TR-RESET.md)
fan_gate true flag false, true CAMBER judgment: with the fan stopped the air temperatures describe still air, not the unit at work
occupied_only true flag false, true CAMBER judgment: the reset applies in occupied mode

How to calibrate:

  • min_clg_sat: Set it to the unit's minimum cooling supply-air setpoint from its sequence or BAS program; a non-G36 value marks the map as configured (reset_source).
  • t_max: Set it to the highest cooling supply-air setpoint the sequence allows (G36 resets it further by trim-and-respond; use the typical value if it varies). Note: Must be above min_clg_sat.
  • oat_min: Set it to the outdoor temperature at or below which the sequence holds the warmest supply air. Note: Must be below oat_max. Samples at or above it are the warm-weather samples of the tracking check.
  • oat_max: Set it to the outdoor temperature at or above which the sequence holds min_clg_sat.
  • tol_f: Set it to the supply-air sensor's tolerance plus the loop's normal deadband; the mean_abs_error_f of a unit known to follow its reset shows the floor.
  • warn_pct: Read pct_below_g36_target on units known to follow the reset and set the warn level above their spread; scoring the same units you tuned on is circular. Note: Also the share of warm-weather samples above target that trips the NOT-tracking warn.
  • warn_gap_f: Set it to the smallest mean below-target gap worth a reheat fix -- roughly the supply air change that moves reheat energy noticeably at the site (mean_gap_f reports it).
  • track_gap_f: Read mean_above_gap_f on units known to track their reset in warm weather; set it well above that.
  • fan_gate: Leave it on. Turn it off only when the fan signal is known to be wrong (stuck status, a speed point that reads 0 while running) and every trended sample is a running sample. Note: The fan is read from supply-fan status, else speed, else airflow; a unit with none of them is judged ungated and the finding says so (the fan_gate metric).
  • occupied_only: Leave it on. With no trended occupancy the gate assumes weekdays 07-18; map an occupancy point, or turn it off for a unit that runs occupied around the clock.

Not thresholds: reset_source (a provenance label printed in the finding, not a threshold).

unmet_setpoint_hours

Parameter Default Unit Range Basis
tol_F 1.5 °F 0.5 to 4.0 CAMBER judgment
warn_pct 5.0 % of occupied samples 1.0 to 20.0 CAMBER judgment
fault_pct 15.0 % of occupied samples 5.0 to 50.0 CAMBER judgment
start_hour 7 hour of day (0-23) 0 to 23 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
end_hour 18 hour of day (1-24) 1 to 24 CAMBER judgment: a typical weekday office schedule (07:00-18:00)

How to calibrate:

  • tol_F: Set it above the normal control swing of healthy zones around their setpoints (a high percentile of the excursion beyond setpoint in comfortable hours). Note: Keep it equal to reheat_capacity_shortfall's tol_f.
  • warn_pct: Compare unmet_pct across zones without comfort complaints; set warn above the highest of those.
  • fault_pct: Keep it well above warn_pct; the rule counts a symptom, so raise it where design-day hours alone push healthy zones past it.
  • start_hour: Set it to the start of the building's occupied mode, read from the BAS schedule or from the hour the supply fan or occupied-mode point switches on in a typical week of trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • end_hour: Set it to the end of occupied mode (exclusive), read from the BAS schedule or trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.

zones_heat_cool_census

Parameter Default Unit Range Basis
start_hour 7 hour of day (0-23) 0 to 23 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
end_hour 18 hour of day (1-24) 1 to 24 CAMBER judgment: a typical weekday office schedule (07:00-18:00)
occupied_days [0, 1, 2, 3, 4] weekday numbers (Mon=0 ... Sun=6) 0 to 6 CAMBER judgment: a Monday-Friday schedule

How to calibrate:

  • start_hour: Set it to the start of the building's occupied mode, read from the BAS schedule or from the hour the supply fan or occupied-mode point switches on in a typical week of trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • end_hour: Set it to the end of occupied mode (exclusive), read from the BAS schedule or trends. Note: Used only when no occupancy point (the OCCUPANCY role) is mapped: one replaces the schedule.
  • occupied_days: List the days the building runs occupied mode, e.g. [0, 1, 2, 3, 4, 5] for a Saturday schedule. Note: A list of integers; each must lie in the range.

Opt-in drift detectors

Drift detectors that a run config switches on per drift family; they are not in the list above because they compare a current window with a frozen or declared baseline instead of judging one frame. Set their parameters on the family entry, beside the switch:

{"drift": {"families": [{"class": "AHU", "family": "ahu",
                         "reference": {"period": ["2025-01-01", "2025-06-30"]},
                         "coil_leak": ["cooling"],
                         "coil_leak_params": {"warn_sigma": 3.0}}]}}

These entries are a provisional API (0.100).

coil_leak_drift

Parameter Default Unit Range Basis
warn_f 0.5 °F 0.25 to 3.0 CAMBER judgment: screening-grade; above the repeatability of a pair of duct temperature sensors read against their own past (a fixed calibration offset cancels in a drift)
fault_f 1.5 °F 0.5 to 6.0 CAMBER judgment: screening-grade
warn_sigma 2.5 σ (baseline residual standard deviations) 1.5 to 6.0 CAMBER judgment: screening-grade, the same sigma floor as coil_valve_drift
fault_sigma 4.0 σ 2.5 to 10.0 CAMBER judgment: screening-grade, the same sigma floor as coil_valve_drift
valve_closed_thr 5.0 % of valve stroke 0.0 to 15.0 public source: PNNL Re-tuning Ch.5 with CAMBER judgment (leaking_valve's deadband)
fan_on_min 0.5 fraction (mean fan status over a sample) 0.1 to 1.0 CAMBER judgment: a resampled hour counts as fan-on when the fan ran at least half of it, as in the other drift detectors
fan_speed_thr 5.0 % of full fan speed 0.0 to 30.0 CAMBER judgment: any speed above a near-zero command counts as running
occupied_only false flag false, true CAMBER judgment: a leak shows whenever the unit runs (leaking_valve's default); the baseline already holds the unit's own unoccupied behaviour
use_coil_leaving true flag false, true CAMBER judgment: a coil's own leaving-air sensor isolates the coil from the fan and the supply-air sensor
judge_heating_on_supply_air true flag false, true CAMBER judgment: on a single-duct unit the supply air leaves the heating coil
min_mat_span_f 10.0 °F (mixed-air range of the baseline) 5.0 to 30.0 CAMBER judgment: a narrower range does not identify a slope
slack_sigma 0.5 σ 0.25 to 2.0 CAMBER judgment: provisional and untuned, shared by every drift detector
limit_sigma 8.0 σ 4.0 to 20.0 CAMBER judgment: provisional and untuned, shared by every drift detector
clip_sigma 4.0 σ 2.0 to 8.0 CAMBER judgment: provisional and untuned, shared by every drift detector
min_consecutive 6 count (samples) 1 to 48 CAMBER judgment: provisional and untuned, shared by every drift detector

How to calibrate:

  • warn_f: Freeze a baseline on one known-good period and score another known-good period; set the floor above the |coil_leak_drift_f| it reads (lbnl-sdahu: 0.0-0.1 °F; lbnl-fcu: 0.0-0.4 °F). Note: A warn needs both warn_f and warn_sigma, in the leak's direction.
  • fault_f: Keep it well above warn_f.
  • warn_sigma: Score known-good periods against each other and set it above the largest |coil_leak_drift_sigma| they read. The published 10 % leak on lbnl-sdahu reads 3.5-3.7σ, so a floor above about 3.4 misses it; on lbnl-fcu floors from 2.0 to 3.0 change the false alarms from 2/33 to 0/33 and miss no leak.
  • fault_sigma: Keep it above warn_sigma.
  • valve_closed_thr: Set it just above the position the valve commands read when the BAS commands them closed (their 95th percentile on off hours).
  • fan_on_min: Raise it towards 1.0 on a unit whose fan cycles within the hour, so only hours with air moving throughout are judged (a cycling fan-coil's leak hours can then all drop out).
  • fan_speed_thr: Set it just above the speed signal the drive reports when stopped. Note: Used only when no fan status is mapped.
  • occupied_only: Turn it on when unoccupied fan-on hours (night cycling, warm-up) are too few or too erratic to fit; compare coil_leak_baseline_sigma_f with it on and off on a known-good period. Note: Reads the trended occupancy (the OCCUPANCY role) when it has values, else assumes weekdays 07-18; the coil_leak_occupancy_gate metric says which.
  • use_coil_leaving: Turn it off only to compare with the supply-air path; the baseline must be fitted on the same sensor it scores (the detector declines otherwise).
  • judge_heating_on_supply_air: Set it false when the mapped supply air does not pass the heating coil (a dual-duct unit whose supply_air_temp is the cold deck); map the heating coil's leaving air instead. Note: Heating coil only. False with no HEAT_COIL_LEAVING_TEMP declines the heating instance.
  • min_mat_span_f: Leave it; below it the baseline is a flat level that scores only current hours inside its mixed-air band (coil_leak_n_out_of_scope counts the rest).
  • slack_sigma: Leave it until labelled fault onsets exist to time against; then calibrate it with camber.driftvalidation.sweep.
  • limit_sigma: Leave it until labelled fault onsets exist to time against; then calibrate it with camber.driftvalidation.sweep.
  • clip_sigma: Leave it until labelled fault onsets exist to time against; then calibrate it with camber.driftvalidation.sweep.
  • min_consecutive: Leave it until labelled fault onsets exist to time against; then calibrate it with camber.driftvalidation.sweep.

Not thresholds: store (the injected baseline store); site (identity); run_id (identity); coil (identity: set by the family entry's coil_leak list); freeze_if_missing (set by the drift run (never from a config)).