Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

FCU-0007 — Simultaneous heating and cooling commands

Statusverified — engine e2ff2f8, cxf:fnv1a128:4b59bb37f9d6537178e60d22896cd2d6, 2026-08-20
Severity2
Methodrule
Phase2
CategoryCRITICAL_WASTE
ConfidenceHIGH
EstimationQUALITATIVE_ONLY
G36
Clusters
Suppresses
Suppressed by
RelatedFCU-0004, FCU-0005
Playbookssimultaneous-hc, fcu-faults
SourceHVAC FDD Reference v1.0 ch.12 and library AHU-0016 — ordinary simultaneous heating/cooling command overlap is an actionable control-waste signature; Library-authored FCU command-level adaptation; no cited source publishes the shipped 10% thresholds or 300 s duration as portable values
Operating statesnormal automatic FCU heating or cooling when simultaneous cooling-plus-reheat is not an intended humidity-control sequence

Preconditions (host-enforced): htg_vlv_cmd and clg_vlv_cmd must be physical 0-100% valve commands for heating and cooling coils on the same FCU, with known scaling and fail positions. Exclude valve exercise, freeze protection, commissioning tests, and intentional dehumidification/cooling-plus-reheat. If simultaneous conditioning is a designed mode, provide a host mode gate or do not instantiate this rule. Inputs must be fresh and aligned; unmet obligations are NO_EVAL, not healthy.

Points: htg_vlv_cmd, clg_vlv_cmd

Outputs:

  • yFault — True after both same-FCU valve commands remain strictly above their active thresholds for sustained_duration
  • yHeatingActive — Diagnostic sub-condition flag; true while the heating command is strictly above threshold. False never means NO_EVAL
  • yCoolingActive — Diagnostic sub-condition flag; true while the cooling command is strictly above threshold. False never means NO_EVAL

Parameters:

NameDefaultUnitCXF pathDescription
heating_active_threshold10.0%heatingActive.tADOPTED_TUNABLE material heating-command threshold; equality is clear. Commission above leakage, actuator minimum, and command noise.
cooling_active_threshold10.0%coolingActive.tADOPTED_TUNABLE material cooling-command threshold; equality is clear. Commission above leakage, actuator minimum, and command noise.
sustained_duration300.0spersist.delayTimeLIBRARY_PRECEDENT continuous overlap duration; verify against normal FCU mode transitions and sampling.

Description

This rule detects a same-FCU heating-valve command and cooling-valve command materially open together. It reports a command conflict, not physical valve position or coil heat transfer. FCU-0004/0005 remain useful for distinguishing unintended thermal effect with a nominally closed command.

Detection Logic

heating_active = htg_vlv_cmd > heating_active_threshold
cooling_active = clg_vlv_cmd > cooling_active_threshold

yHeatingActive = heating_active
yCoolingActive = cooling_active
yFault = (heating_active AND cooling_active) sustained for sustained_duration

FCU-0007 block graph

Both comparisons are strict. TrueDelay(delayOnInit=true) starts only while both subconditions are true; either command clearing resets the timer and a mature alarm clears immediately.

Possible Diagnoses

  1. Heating and cooling PID loops overlap or lack an interlock
  2. Occupancy, mode, or setpoint transition leaves both outputs active
  3. BAS priority-array override holds one command open
  4. Wrong point scaling or commands bound from different FCUs
  5. Intentional dehumidification/reheat, freeze, or exercise mode not host-gated

Energy Impact

Simultaneous commands can add and remove heat at the same terminal, increasing plant and fan energy. Commands alone do not prove valve position, water flow, or thermal transfer, so this card makes no portable savings claim.

Emissions Impact

Scope 1 and/or 2 effects depend on the serving heating and cooling plants. Quantify only after measuring delivered heat and avoided plant input energy.

Deviations

  • Thresholds and duration are library defaults. The source supports the signature, not universal 10% or 300 s values; commission all three.
  • Intentional cooling-plus-reheat is host-excluded. The graph has no mode input and intentionally alarms on that raw overlap when the host gate is absent.
  • CLU-01 is unchanged. Its AHU-0016 trigger cannot causally clear a local FCU command conflict under the cluster contract; shared workflow is represented by the simultaneous-hc playbook instead.
  • Leak rules are not suppressed. A command conflict and physical heat transfer with a closed command are different evidence and can co-occur.
  • No empirical FPR or TPR is claimed. No current harness exposes both physical same-FCU valve commands without inference; validation is synthetic only.

Test Vectors

12 scenarios, clock step 60 s over 720 s.

ScenarioDescription
both_closedOrdinary idle operation keeps both direction flags and the alarm clear.
heating_onlyA material heating command without cooling is valid single-mode operation.
cooling_onlyA material cooling command without heating is valid single-mode operation.
both_exactly_at_thresholdStrict comparisons keep equality clear on both lanes.
one_just_above_one_at_thresholdOne active lane cannot mature a simultaneous-command fault.
both_just_above_sustainedOne representable increment above both thresholds matures only after persistence.
overlap_ends_one_step_before_persistenceA conflict ending one evaluator step before 300 s never matures.
mature_overlap_recovers_when_cooling_clearsA mature fault clears immediately when either material command clears.
two_short_overlaps_resetTwo sub-duration conflicts separated by either valve clearing do not accumulate time.
initialization_overlap_serves_full_delaydelayOnInit prevents an immediate alarm when the evaluator starts inside a conflict.
heating_reset_then_cooling_overlapClearing the heating lane resets the timer before a later overlap.
intentional_dehumidification_raw_caseThe raw graph alarms on intentional cooling-plus-reheat; the host must exclude that mode or not instantiate this rule.
vectors.json
{
  "schema": "cxf-library/vectors/v1",
  "clock": {
    "step_s": 60,
    "horizon_s": 720
  },
  "scenarios": [
    {
      "name": "both_closed",
      "description": "Ordinary idle operation keeps both direction flags and the alarm clear.",
      "inputs": {
        "htg_vlv_cmd": 0.0,
        "clg_vlv_cmd": 0.0
      },
      "expect": [
        {
          "output": "yHeatingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        },
        {
          "output": "yCoolingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        },
        {
          "output": "yFault",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        }
      ]
    },
    {
      "name": "heating_only",
      "description": "A material heating command without cooling is valid single-mode operation.",
      "inputs": {
        "htg_vlv_cmd": 40.0,
        "clg_vlv_cmd": 0.0
      },
      "expect": [
        {
          "output": "yHeatingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": true
        },
        {
          "output": "yCoolingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        },
        {
          "output": "yFault",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        }
      ]
    },
    {
      "name": "cooling_only",
      "description": "A material cooling command without heating is valid single-mode operation.",
      "inputs": {
        "htg_vlv_cmd": 0.0,
        "clg_vlv_cmd": 40.0
      },
      "expect": [
        {
          "output": "yHeatingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        },
        {
          "output": "yCoolingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": true
        },
        {
          "output": "yFault",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        }
      ]
    },
    {
      "name": "both_exactly_at_threshold",
      "description": "Strict comparisons keep equality clear on both lanes.",
      "inputs": {
        "htg_vlv_cmd": 10.0,
        "clg_vlv_cmd": 10.0
      },
      "expect": [
        {
          "output": "yHeatingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        },
        {
          "output": "yCoolingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        },
        {
          "output": "yFault",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        }
      ]
    },
    {
      "name": "one_just_above_one_at_threshold",
      "description": "One active lane cannot mature a simultaneous-command fault.",
      "inputs": {
        "htg_vlv_cmd": 10.1,
        "clg_vlv_cmd": 10.0
      },
      "expect": [
        {
          "output": "yHeatingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": true
        },
        {
          "output": "yCoolingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        },
        {
          "output": "yFault",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        }
      ]
    },
    {
      "name": "both_just_above_sustained",
      "description": "One representable increment above both thresholds matures only after persistence.",
      "inputs": {
        "htg_vlv_cmd": 10.1,
        "clg_vlv_cmd": 10.1
      },
      "expect": [
        {
          "output": "yHeatingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": true
        },
        {
          "output": "yCoolingActive",
          "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": "overlap_ends_one_step_before_persistence",
      "description": "A conflict ending one evaluator step before 300 s never matures.",
      "inputs": {
        "htg_vlv_cmd": 25.0,
        "clg_vlv_cmd": [
          {
            "t": 0,
            "value": 25.0
          },
          {
            "t": 240,
            "value": 0.0
          }
        ]
      },
      "expect": [
        {
          "output": "yFault",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        },
        {
          "output": "yHeatingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": true
        },
        {
          "output": "yCoolingActive",
          "from_s": 0,
          "to_s": 180,
          "equals": true
        },
        {
          "output": "yCoolingActive",
          "from_s": 240,
          "to_s": 720,
          "equals": false
        }
      ]
    },
    {
      "name": "mature_overlap_recovers_when_cooling_clears",
      "description": "A mature fault clears immediately when either material command clears.",
      "inputs": {
        "htg_vlv_cmd": 25.0,
        "clg_vlv_cmd": [
          {
            "t": 0,
            "value": 25.0
          },
          {
            "t": 420,
            "value": 0.0
          }
        ]
      },
      "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": "yHeatingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": true
        },
        {
          "output": "yCoolingActive",
          "from_s": 0,
          "to_s": 360,
          "equals": true
        },
        {
          "output": "yCoolingActive",
          "from_s": 420,
          "to_s": 720,
          "equals": false
        }
      ]
    },
    {
      "name": "two_short_overlaps_reset",
      "description": "Two sub-duration conflicts separated by either valve clearing do not accumulate time.",
      "inputs": {
        "htg_vlv_cmd": 25.0,
        "clg_vlv_cmd": [
          {
            "t": 0,
            "value": 25.0
          },
          {
            "t": 180,
            "value": 0.0
          },
          {
            "t": 300,
            "value": 25.0
          },
          {
            "t": 480,
            "value": 0.0
          }
        ]
      },
      "expect": [
        {
          "output": "yFault",
          "from_s": 0,
          "to_s": 720,
          "equals": false
        }
      ]
    },
    {
      "name": "initialization_overlap_serves_full_delay",
      "description": "delayOnInit prevents an immediate alarm when the evaluator starts inside a conflict.",
      "inputs": {
        "htg_vlv_cmd": 30.0,
        "clg_vlv_cmd": 30.0
      },
      "expect": [
        {
          "output": "yFault",
          "from_s": 0,
          "to_s": 240,
          "equals": false
        },
        {
          "output": "yFault",
          "from_s": 360,
          "to_s": 720,
          "equals": true
        },
        {
          "output": "yHeatingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": true
        },
        {
          "output": "yCoolingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": true
        }
      ]
    },
    {
      "name": "heating_reset_then_cooling_overlap",
      "description": "Clearing the heating lane resets the timer before a later overlap.",
      "inputs": {
        "htg_vlv_cmd": [
          {
            "t": 0,
            "value": 25.0
          },
          {
            "t": 180,
            "value": 0.0
          },
          {
            "t": 360,
            "value": 25.0
          }
        ],
        "clg_vlv_cmd": 25.0
      },
      "expect": [
        {
          "output": "yFault",
          "from_s": 0,
          "to_s": 600,
          "equals": false
        },
        {
          "output": "yFault",
          "from_s": 720,
          "to_s": 720,
          "equals": true
        }
      ]
    },
    {
      "name": "intentional_dehumidification_raw_case",
      "description": "The raw graph alarms on intentional cooling-plus-reheat; the host must exclude that mode or not instantiate this rule.",
      "inputs": {
        "htg_vlv_cmd": 35.0,
        "clg_vlv_cmd": 60.0
      },
      "expect": [
        {
          "output": "yFault",
          "from_s": 0,
          "to_s": 240,
          "equals": false
        },
        {
          "output": "yFault",
          "from_s": 360,
          "to_s": 720,
          "equals": true
        },
        {
          "output": "yHeatingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": true
        },
        {
          "output": "yCoolingActive",
          "from_s": 0,
          "to_s": 720,
          "equals": true
        }
      ]
    }
  ]
}