AHU-0024 — Duct static pressure reset not functioning
| Status | verified — engine e2ff2f8, cxf:fnv1a128:0bc49a814c1f728a54e9a9a3ecff1244, 2026-08-17 |
| Severity | 3 |
| Method | statistical |
| Phase | 2 |
| Category | EXCESS_CONSUMPTION |
| Confidence | HIGH |
| Estimation | PROXY_ESTIMATION |
| G36 | — |
| Clusters | CLU-02 |
| Suppresses | — |
| Suppressed by | — |
| Related | AHU-0001, AHU-0023, AHU-0031, FPB-0002 |
| Playbooks | missing-reset |
| Source | HVAC FDD Reference v1.0 §9, AHU-0024; PNNL RetuningOpps A01; PNNL-25985 EEM-12 |
| Operating states | Occupied, fan running |
Preconditions (host-enforced): AHU serves multiple zones; zone damper feedback available and aggregated by the host into zone_dmpr_pos_max. When zone damper data is missing or stale, the verdict is NO_EVAL, not healthy.
Points: dsp_sp, zone_dmpr_pos_max
Outputs:
yFault— True while the DSP setpoint has stayed flat over the evaluation window despite all zone dampers staying well below fully open, for at least alarm_delay
Parameters:
| Name | Default | Unit | CXF path | Description |
|---|---|---|---|---|
evaluation_window | 259200.0 | s | spRef.samplePeriod, spFlatHeld.delayTime, dmprLowHeld.delayTime | Window over which setpoint flatness and low damper demand are assessed (3 days) |
sp_flat_tolerance | 25.0 | Pa | spFlat.t | Max deviation of DSP_SP from its sampled baseline to count as flat (half the reference’s 50 Pa min expected range) |
high_damper_threshold | 70.0 | % | dmprLow.t | Max zone damper position below which the setpoint could safely drop |
alarm_delay | 86400.0 | s | persist.delayTime | Fault persistence before alarm (24 h) |
Description
The duct static pressure setpoint remains fixed despite varying zone demand. A functioning DSP reset (G36 §5.16.1 trim-and-respond) lowers the setpoint when zone dampers are not widely open; because fan power scales with the cube of pressure, even small reductions produce outsized savings. The other half of the “74% problem” with AHU-0023 — absent in 74% of buildings (PNNL 151-building study) — and a member of cluster CLU-02 with the same $0 desk-only fix.
Detection Logic
baseline(dsp_sp) = dsp_sp sampled and held every evaluation_window (3 days)
sp_flat = |dsp_sp − baseline(dsp_sp)| < sp_flat_tolerance,
continuously for evaluation_window
low_demand = zone_dmpr_pos_max < high_damper_threshold,
continuously for evaluation_window
yFault = sp_flat AND low_demand, sustained for alarm_delay
Block graph (rule.cxf.jsonld):
The setpoint chain mirrors AHU-0023’s sampled-baseline flatness detector.
The demand condition needs no baseline at all: the reference’s
max(zone_dmpr_pos_all) < 70% over the window is exactly equivalent to
“the highest zone damper stays below 70% continuously,” which is one
LessThreshold plus a dwell TrueDelay on the host-aggregated maximum.
Worst-case time to alarm from cold start: evaluation_window + alarm_delay
(4 days).
Possible Diagnoses
- DSP reset never programmed in the BAS
- DSP reset disabled by an operator
- Trim-and-respond parameters misconfigured
- Zone damper feedback not connected to the AHU controller
Energy Impact
EXCESS_CONSUMPTION, HIGH confidence, PROXY_ESTIMATION (EEM-12). Fan energy scales with the cube of duct pressure: a 20% setpoint reduction yields ~49% fan energy savings. Savings 1–3% of site energy; climate-neutral. Prevalence: 74% of buildings.
Emissions Impact
Scope 2, PROXY_EMISSIONS, HIGH confidence; typical 400–3,000 kg CO₂e/yr (excess fan energy, cubic law). Avoided-emissions basis: MOER.
Deviations
zone_dmpr_pos_all(array) → host-derivedzone_dmpr_pos_max(scalar). The reference consumes every zone damper position; library v1 avoids array boundary points, so the host aggregates the maximum across the zones served and feeds one scalar (flaggedderivedin the point dictionary). The zone-side underlying points get their semantic tags when the VAV dictionary lands.- Windowed range → sampled-baseline flatness on the setpoint chain, as
AHU-0023 (
sp_flat_tolerance = min_expected_sp_range/2; sameMovingAveragering-capacity rationale). The damper condition is an exact transformation, not an approximation:max over window < t⇔continuously below t. delayOnInit = trueon allTrueDelays (startup conservatism per AHU-0016).
Notes
Evaluate together with AHU-0023: both fire → CLU-02 confirmed, one trim-and-respond programming visit fixes both. The reference’s PNNL-27338 AIRCx corollary — DSP > 0.2 in. w.g. during unoccupied hours — is covered separately by the after-hours rules (AHU-0018 family).
Test Vectors
5 scenarios, clock step 300 s over 432000 s.
| Scenario | Description |
|---|---|
reset_working | Reference vector: DSP_SP modulates 100 Pa with demand; no fault |
fixed_sp_low_demand | Reference vector: DSP_SP pinned while dampers well below 70%; fault after evaluation_window (3d) + alarm_delay (24h) |
fixed_sp_high_demand | Reference vector: DSP_SP pinned but dampers near max — setpoint cannot drop, no fault |
demand_rises_resets | Low demand for 2.3 days, then dampers open past threshold before the window completes; dwell resets, never alarms |
damper_at_threshold | Max damper exactly at high_damper_threshold is not low demand (strict < comparison) |
vectors.json
{
"schema": "cxf-library/vectors/v1",
"clock": {
"step_s": 300,
"horizon_s": 432000
},
"scenarios": [
{
"name": "reset_working",
"description": "Reference vector: DSP_SP modulates 100 Pa with demand; no fault",
"inputs": {
"dsp_sp": [
{
"t": 0,
"value": 350.0
},
{
"t": 21600,
"value": 450.0
},
{
"t": 43200,
"value": 350.0
},
{
"t": 64800,
"value": 450.0
},
{
"t": 86400,
"value": 350.0
},
{
"t": 108000,
"value": 450.0
},
{
"t": 129600,
"value": 350.0
},
{
"t": 151200,
"value": 450.0
},
{
"t": 172800,
"value": 350.0
},
{
"t": 194400,
"value": 450.0
},
{
"t": 216000,
"value": 350.0
},
{
"t": 237600,
"value": 450.0
},
{
"t": 259200,
"value": 350.0
},
{
"t": 280800,
"value": 450.0
},
{
"t": 302400,
"value": 350.0
},
{
"t": 324000,
"value": 450.0
},
{
"t": 345600,
"value": 350.0
},
{
"t": 367200,
"value": 450.0
},
{
"t": 388800,
"value": 350.0
},
{
"t": 410400,
"value": 450.0
},
{
"t": 432000,
"value": 350.0
}
],
"zone_dmpr_pos_max": 85.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 432000,
"equals": false
}
]
},
{
"name": "fixed_sp_low_demand",
"description": "Reference vector: DSP_SP pinned while dampers well below 70%; fault after evaluation_window (3d) + alarm_delay (24h)",
"inputs": {
"dsp_sp": 400.0,
"zone_dmpr_pos_max": 45.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 343200,
"equals": false
},
{
"output": "yFault",
"from_s": 348000,
"to_s": 432000,
"equals": true
}
]
},
{
"name": "fixed_sp_high_demand",
"description": "Reference vector: DSP_SP pinned but dampers near max \u2014 setpoint cannot drop, no fault",
"inputs": {
"dsp_sp": 400.0,
"zone_dmpr_pos_max": 95.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 432000,
"equals": false
}
]
},
{
"name": "demand_rises_resets",
"description": "Low demand for 2.3 days, then dampers open past threshold before the window completes; dwell resets, never alarms",
"inputs": {
"dsp_sp": 400.0,
"zone_dmpr_pos_max": [
{
"t": 0,
"value": 45.0
},
{
"t": 200000,
"value": 95.0
}
]
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 432000,
"equals": false
}
]
},
{
"name": "damper_at_threshold",
"description": "Max damper exactly at high_damper_threshold is not low demand (strict < comparison)",
"inputs": {
"dsp_sp": 400.0,
"zone_dmpr_pos_max": 70.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 432000,
"equals": false
}
]
}
]
}