HP-0008 — Auxiliary heat active above lockout with compressor running
| Status | verified — engine e2ff2f8, cxf:fnv1a128:8917cda230eb3df1fff05a46442b7cf9, 2026-08-20 |
| Severity | 2 |
| Method | rule |
| Phase | 2 |
| Category | EXCESS_CONSUMPTION |
| Confidence | MEDIUM |
| Estimation | PROXY_ESTIMATION |
| G36 | — |
| Clusters | — |
| Suppresses | — |
| Suppressed by | — |
| Related | HP-0001, HP-0002, HP-0007 |
| Playbooks | heat-pump-faults |
| Source | Winkler and Ramaraj, Field Validation of Air-Source Heat Pumps for Cold Climates, NREL/TP-5500-84745 (2023), Table 5 and note b, p.15 — auxiliary lockout is the maximum OAT for auxiliary operation; observed site values vary; NREL HPXML Data Dictionary v4.0, BackupHeatingLockoutTemperature — backup is disabled above the configured temperature and dual-fuel uses BackupHeatingSwitchoverTemperature; Library-authored executable combination of heating mode, configured OAT lockout, concurrent compressor/auxiliary proof, defrost exclusion, and persistence |
| Operating states | normal automatic heat-pump heating when the configured strategy prohibits auxiliary heat above its site-specific lockout while the compressor runs |
Preconditions (host-enforced): aux_heat_status must independently prove active heat production by an explicitly classified auxiliary electric, gas, or other source; do not bind demand, command echo, availability, crankcase heat, base-pan heat, or defrost heat. The 5 degC placeholder is adoption-blocking: replace it with the installed balance/lockout strategy for climate, tariff, and equipment. mode_command code 1 must mean heating; comp_status must prove the same applicable compressor scope. OAT must be valid and representative. Exclude defrost, emergency heat, compressor failure/lockout, demand response, commissioning, and explicit high-capacity recovery. On dual-fuel equipment instantiate only when simultaneous compressor/fuel operation above the configured switchover is prohibited. Unmet obligations are NO_EVAL, not healthy.
Points: aux_heat_status, oat, mode_command, defrost_status, comp_status
Outputs:
yFault— True after heating mode, above-lockout OAT, concurrent auxiliary/compressor proof, and no defrost persist for sustained_durationyHeatingMode— Diagnostic sub-condition flag; true when mode_command equals heating_mode_code. False never means NO_EVALyAboveLockout— Diagnostic sub-condition flag; true when OAT is strictly above aux_heat_lockout_oat. False never means NO_EVALyConcurrentHeat— Diagnostic sub-condition flag; true when auxiliary heat and compressor proofs are both active. False never means NO_EVAL
Parameters:
| Name | Default | Unit | CXF path | Description |
|---|---|---|---|---|
heating_mode_code | 1 | 1 | kHeat.k | LIBRARY_PRECEDENT host-mapped heating-mode integer. Rebind when the site enumeration differs. |
aux_heat_lockout_oat | 5.0 | °C | aboveLockout.t | NO_PORTABLE_DEFAULT and adoption-blocking placeholder. Replace with the installed unit’s commissioned auxiliary lockout or dual-fuel switchover strategy; equality is clear. |
sustained_duration | 300.0 | s | persist.delayTime | ADOPTED_TUNABLE continuous signature duration intended to reject brief staging transitions; commission against the OEM sequence. |
Description
This rule detects a proven auxiliary heat source operating concurrently with the heat-pump compressor above the configured outdoor lockout in heating mode, outside defrost. It targets prohibited concurrent operation, not emergency heat, required low-temperature supplementation, or balance-point optimization.
Detection Logic
heating_mode = mode_command == heating_mode_code
above_lockout = oat > aux_heat_lockout_oat
concurrent_heat = aux_heat_status AND comp_status
candidate = heating_mode AND above_lockout AND concurrent_heat
AND NOT defrost_status
yHeatingMode = heating_mode
yAboveLockout = above_lockout
yConcurrentHeat = concurrent_heat
yFault = candidate sustained for sustained_duration
The OAT comparison is strict. Diagnostic outputs are immediate;
TrueDelay(delayOnInit=true) applies only to the complete signature. Any
false candidate subcondition resets timing and clears a mature alarm.
Possible Diagnoses
- Auxiliary lockout or dual-fuel switchover misconfigured or disabled
- Staging/thermostat sequence energizes backup heat too early
- OAT sensor bias, bad location, stale value, or wrong unit conversion
- Auxiliary proof bound to command, availability, or a non-space-heating load
- Compressor-capacity fault causing an authorized recovery mode not host-gated
- Defrost, emergency, demand-response, or commissioning state omitted from gating
Energy Impact
Unnecessary resistance or fuel backup can displace more efficient compressor heating. Estimate only from verified auxiliary stage input during fault hours, after proving that the installed sequence did not require the extra capacity.
Emissions Impact
Scope 2 applies to electric auxiliary heat and compressor power; Scope 1 can apply to fuel backup. Use measured or nameplate stage input and appropriate time-varying electricity or fuel emissions factors.
Deviations
- 5 °C is not a portable default. It is an adoption-blocking placeholder; the applicable value is the installed balance/lockout or switchover strategy.
- Confidence is MEDIUM rather than the brief’s proposed HIGH. Auxiliary role/proof, dual-fuel behavior, site setpoint, and emergency/recovery modes must be established before concurrent operation is avoidable.
- Auxiliary semantics are provisional. Brick and 223 provide generic heating/status patterns but no exact auxiliary role; topology and independent proof must identify the real supplemental source.
- Defrost is encoded; other exceptions are host gates. Emergency, failure, demand-response, and recovery state semantics are site-specific.
- No automatic suppression is added. HP-0007 direction should lead diagnosis, but unexpected compressor proof can leave this concurrent-energy signature valid.
- No empirical FPR or TPR is claimed. Current datasets do not expose aligned auxiliary-production proof, defrost state, and the configured site lockout.
Test Vectors
14 scenarios, clock step 60 s over 720 s.
| Scenario | Description |
|---|---|
cooling_mode | Concurrent heat in the cooling code is diagnostically visible but outside the heating-mode candidate. |
heating_below_lockout | Auxiliary support below the configured lockout is permitted by this rule. |
heating_above_lockout_without_auxiliary | Compressor heating above lockout is healthy when the auxiliary source is inactive. |
auxiliary_above_lockout_compressor_off | Auxiliary heat without compressor proof may be emergency operation and is not this concurrent-energy signature. |
concurrent_heat_above_lockout_sustained | The complete avoidable-energy signature matures only after persistence. |
concurrent_heat_during_defrost | Defrost explicitly removes the alarm candidate while leaving diagnostic subconditions visible. |
exact_oat_lockout_is_clear | The exact configured OAT threshold is clear under strict greater-than semantics. |
unsupported_mode_code | An unmapped integer mode cannot accidentally satisfy the heating-mode gate. |
auxiliary_stage_ends_one_step_before_persistence | An auxiliary stage ending one evaluator step before 300 s never matures. |
mature_fault_recovers_when_auxiliary_clears | A mature alarm clears immediately when the auxiliary source stops producing heat. |
two_short_auxiliary_episodes_reset | Two short stages separated by a reset do not accumulate timer age. |
initialization_violation_serves_full_delay | delayOnInit prevents an immediate alarm when evaluation starts inside the complete signature. |
defrost_interval_resets_candidate | A defrost interval between two auxiliary stages resets persistence even though the other subconditions remain true. |
emergency_or_recovery_raw_case | The raw graph alarms on sustained concurrent recovery/emergency heat; the host must exclude an authorized mode. |
vectors.json
{
"schema": "cxf-library/vectors/v1",
"clock": {
"step_s": 60,
"horizon_s": 720
},
"scenarios": [
{
"name": "cooling_mode",
"description": "Concurrent heat in the cooling code is diagnostically visible but outside the heating-mode candidate.",
"inputs": {
"aux_heat_status": true,
"oat": 6.0,
"mode_command": 2,
"defrost_status": false,
"comp_status": true
},
"expect": [
{
"output": "yHeatingMode",
"from_s": 0,
"to_s": 720,
"equals": false
},
{
"output": "yAboveLockout",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yFault",
"from_s": 0,
"to_s": 720,
"equals": false
}
]
},
{
"name": "heating_below_lockout",
"description": "Auxiliary support below the configured lockout is permitted by this rule.",
"inputs": {
"aux_heat_status": true,
"oat": 4.9,
"mode_command": 1,
"defrost_status": false,
"comp_status": true
},
"expect": [
{
"output": "yHeatingMode",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yAboveLockout",
"from_s": 0,
"to_s": 720,
"equals": false
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yFault",
"from_s": 0,
"to_s": 720,
"equals": false
}
]
},
{
"name": "heating_above_lockout_without_auxiliary",
"description": "Compressor heating above lockout is healthy when the auxiliary source is inactive.",
"inputs": {
"aux_heat_status": false,
"oat": 6.0,
"mode_command": 1,
"defrost_status": false,
"comp_status": true
},
"expect": [
{
"output": "yHeatingMode",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yAboveLockout",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 720,
"equals": false
},
{
"output": "yFault",
"from_s": 0,
"to_s": 720,
"equals": false
}
]
},
{
"name": "auxiliary_above_lockout_compressor_off",
"description": "Auxiliary heat without compressor proof may be emergency operation and is not this concurrent-energy signature.",
"inputs": {
"aux_heat_status": true,
"oat": 6.0,
"mode_command": 1,
"defrost_status": false,
"comp_status": false
},
"expect": [
{
"output": "yHeatingMode",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yAboveLockout",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 720,
"equals": false
},
{
"output": "yFault",
"from_s": 0,
"to_s": 720,
"equals": false
}
]
},
{
"name": "concurrent_heat_above_lockout_sustained",
"description": "The complete avoidable-energy signature matures only after persistence.",
"inputs": {
"aux_heat_status": true,
"oat": 6.0,
"mode_command": 1,
"defrost_status": false,
"comp_status": true
},
"expect": [
{
"output": "yHeatingMode",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yAboveLockout",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yFault",
"from_s": 0,
"to_s": 240,
"equals": false
},
{
"output": "yFault",
"from_s": 360,
"to_s": 720,
"equals": true
}
]
},
{
"name": "concurrent_heat_during_defrost",
"description": "Defrost explicitly removes the alarm candidate while leaving diagnostic subconditions visible.",
"inputs": {
"aux_heat_status": true,
"oat": 6.0,
"mode_command": 1,
"defrost_status": true,
"comp_status": true
},
"expect": [
{
"output": "yHeatingMode",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yAboveLockout",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yFault",
"from_s": 0,
"to_s": 720,
"equals": false
}
]
},
{
"name": "exact_oat_lockout_is_clear",
"description": "The exact configured OAT threshold is clear under strict greater-than semantics.",
"inputs": {
"aux_heat_status": true,
"oat": 5.0,
"mode_command": 1,
"defrost_status": false,
"comp_status": true
},
"expect": [
{
"output": "yHeatingMode",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yAboveLockout",
"from_s": 0,
"to_s": 720,
"equals": false
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yFault",
"from_s": 0,
"to_s": 720,
"equals": false
}
]
},
{
"name": "unsupported_mode_code",
"description": "An unmapped integer mode cannot accidentally satisfy the heating-mode gate.",
"inputs": {
"aux_heat_status": true,
"oat": 6.0,
"mode_command": 0,
"defrost_status": false,
"comp_status": true
},
"expect": [
{
"output": "yHeatingMode",
"from_s": 0,
"to_s": 720,
"equals": false
},
{
"output": "yAboveLockout",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yFault",
"from_s": 0,
"to_s": 720,
"equals": false
}
]
},
{
"name": "auxiliary_stage_ends_one_step_before_persistence",
"description": "An auxiliary stage ending one evaluator step before 300 s never matures.",
"inputs": {
"aux_heat_status": [
{
"t": 0,
"value": true
},
{
"t": 240,
"value": false
}
],
"oat": 6.0,
"mode_command": 1,
"defrost_status": false,
"comp_status": true
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 720,
"equals": false
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 180,
"equals": true
},
{
"output": "yConcurrentHeat",
"from_s": 240,
"to_s": 720,
"equals": false
}
]
},
{
"name": "mature_fault_recovers_when_auxiliary_clears",
"description": "A mature alarm clears immediately when the auxiliary source stops producing heat.",
"inputs": {
"aux_heat_status": [
{
"t": 0,
"value": true
},
{
"t": 420,
"value": false
}
],
"oat": 6.0,
"mode_command": 1,
"defrost_status": false,
"comp_status": true
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 240,
"equals": false
},
{
"output": "yFault",
"from_s": 360,
"to_s": 360,
"equals": true
},
{
"output": "yFault",
"from_s": 420,
"to_s": 720,
"equals": false
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 360,
"equals": true
},
{
"output": "yConcurrentHeat",
"from_s": 420,
"to_s": 720,
"equals": false
}
]
},
{
"name": "two_short_auxiliary_episodes_reset",
"description": "Two short stages separated by a reset do not accumulate timer age.",
"inputs": {
"aux_heat_status": [
{
"t": 0,
"value": true
},
{
"t": 180,
"value": false
},
{
"t": 300,
"value": true
},
{
"t": 480,
"value": false
}
],
"oat": 6.0,
"mode_command": 1,
"defrost_status": false,
"comp_status": true
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 720,
"equals": false
}
]
},
{
"name": "initialization_violation_serves_full_delay",
"description": "delayOnInit prevents an immediate alarm when evaluation starts inside the complete signature.",
"inputs": {
"aux_heat_status": true,
"oat": 6.0,
"mode_command": 1,
"defrost_status": false,
"comp_status": true
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 240,
"equals": false
},
{
"output": "yFault",
"from_s": 360,
"to_s": 720,
"equals": true
},
{
"output": "yHeatingMode",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yAboveLockout",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 720,
"equals": true
}
]
},
{
"name": "defrost_interval_resets_candidate",
"description": "A defrost interval between two auxiliary stages resets persistence even though the other subconditions remain true.",
"inputs": {
"aux_heat_status": true,
"oat": 6.0,
"mode_command": 1,
"defrost_status": [
{
"t": 0,
"value": false
},
{
"t": 180,
"value": true
},
{
"t": 300,
"value": false
},
{
"t": 480,
"value": true
}
],
"comp_status": true
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 720,
"equals": false
}
]
},
{
"name": "emergency_or_recovery_raw_case",
"description": "The raw graph alarms on sustained concurrent recovery/emergency heat; the host must exclude an authorized mode.",
"inputs": {
"aux_heat_status": true,
"oat": 6.0,
"mode_command": 1,
"defrost_status": false,
"comp_status": true
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 240,
"equals": false
},
{
"output": "yFault",
"from_s": 360,
"to_s": 720,
"equals": true
},
{
"output": "yHeatingMode",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yAboveLockout",
"from_s": 0,
"to_s": 720,
"equals": true
},
{
"output": "yConcurrentHeat",
"from_s": 0,
"to_s": 720,
"equals": true
}
]
}
]
}