VFD-0003 — At maximum speed with load unsatisfied
| Status | verified — engine e2ff2f8, cxf:fnv1a128:172305490314de625490108d7740b3b6, 2026-08-20 |
| Severity | 3 |
| Method | rule |
| Phase | 2 |
| Category | COMFORT_ENERGY |
| Confidence | MEDIUM |
| Estimation | QUALITATIVE_ONLY |
| G36 | — |
| Clusters | — |
| Suppresses | — |
| Suppressed by | VFD-0001, VFD-0005 |
| Related | VFD-0002, VFD-0004 |
| Playbooks | vfd-pump-faults |
| Source | PNNL Variable Speed Drives O&M Best Practice — output frequency/speed and the controlled process variable are core drive operating evidence; manufacturer and application limits remain site-specific; NIST, Automatically Detecting Faulty Regulation in HVAC Controls (2013) — generic failure-to-regulate and actuator-limit fault classes; Library-authored high-limit complement to VFD-0002, reusing its assembled speed boundary, symmetric process-error test, and 900 s persistence convention |
| Operating states | drive enabled, under remote automatic process control, and not intentionally capacity-limited |
Preconditions (host-enforced): The final drive enable and active-loop states are host gates; a stopped drive is NO_EVAL, not healthy. The drive must be accepting remote automatic control, with approved hand/bypass, commissioning, smoke/purge, lead/lag transition, and functional-test intervals excluded. VFD-0005 suppresses this rule while automatic control is absent. vfd_speed must be trustworthy normalized feedback; VFD-0001 suppresses this rule during command/feedback tracking failure. The host must exclude intentional torque, current, safety, demand, or equipment-capacity limits. vfd_process_value and vfd_process_sp must be the same loop in the same units, the setpoint must be settled for that loop’s time constant, and pv_error_threshold must be configured in those units. When any gate is unmet the verdict is NO_EVAL, not healthy.
Points: vfd_speed, vfd_process_value, vfd_process_sp
Outputs:
yFault— True while speed is strictly above max_speed minus speed_tolerance and either directional process error has persisted for sustained_durationyProcessHigh— Immediate raw diagnostic; true when process value is more than pv_error_threshold above setpoint, whether or not speed is at maximumyProcessLow— Immediate raw diagnostic; true when process value is more than pv_error_threshold below setpoint, whether or not speed is at maximum
Parameters:
| Name | Default | Unit | CXF path | Description |
|---|---|---|---|---|
max_speed | 100.0 | % | maxSpd.k | ADOPTED_TUNABLE: normalized maximum-speed reference. Configure to the drive/application limit when it is not 100%. |
speed_tolerance | 3.0 | % | tol.k | LIBRARY_PRECEDENT: margin below maximum that still counts as pinned at the high limit; inherited from VFD-0002’s assembled limit shape. |
pv_error_threshold | 10.0 | 1 | processHigh.t, processLow.t | NO_PORTABLE_DEFAULT: absolute directional error in the bound loop’s own units. The executable 10.0 is a placeholder; deployment is blocked until it is set for that loop. |
sustained_duration | 900.0 | s | persist.delayTime | LIBRARY_PRECEDENT: continuous high-limit and process-error conjunction required before alarm (15 min), reused from VFD-0002. |
Description
A process loop has spent its last increment of actuator authority: the drive is near its configured maximum, yet the quantity it regulates remains materially away from setpoint. That signature does not identify one component. It can mean an obstructed air or water path, undersized equipment, a drive held below its reported limit by current or torque protection, a bad process sensor, an incorrect control direction, or a maximum that was commissioned too low. What it does identify is the useful decision boundary: more ordinary loop output is no longer available, so waiting for the controller to recover is not a fix.
Detection Logic
speed_ceiling = max_speed - speed_tolerance
at_max = vfd_speed > speed_ceiling
process_high = vfd_process_value - vfd_process_sp > pv_error_threshold
process_low = vfd_process_sp - vfd_process_value > pv_error_threshold
yProcessHigh = process_high
yProcessLow = process_low
yFault = TrueDelay(at_max AND (process_high OR process_low),
sustained_duration)
Block graph (rule.cxf.jsonld):
The high and low branches are separate rather than an absolute-value block so
the graph can expose the error direction without pretending to know the loop’s
control sign. They are mutually exclusive at a positive threshold and remain
raw: either may be true while the drive is at mid-speed. Only their OR is gated
by atMax and delayed. Any release of the speed or process branch clears the
fault and resets the complete 900-second timer.
Possible Diagnoses
- Air- or water-side obstruction: dirty filter/coil, closed damper or valve, blocked strainer, or restricted duct/pipe.
- Equipment or distribution system undersized for the present load.
- Drive current, torque, safety, demand, or application limit below the normalized maximum assumed by the BAS.
- Process sensor bias, setpoint/unit mismatch, or reversed control action.
- Mechanical degradation in the driven equipment, belt, coupling, or impeller.
- Maximum-speed parameter or reset sequence commissioned incorrectly.
Energy Impact
COMFORT_ENERGY, MEDIUM confidence, QUALITATIVE_ONLY. A saturated drive can spend long periods near its largest electrical draw without meeting demand, but the three inputs cannot distinguish useful peak-load operation from wasted work behind an obstruction. Use the playbook to establish the cause before applying fan/pump laws or claiming savings.
Emissions Impact
Scope 2, qualitative. VFD-driven equipment is electric; any avoidable high-speed runtime becomes purchased-electricity emissions. No avoided-emissions number is defensible without measured power and a diagnosed correction.
Deviations
- This is a library-authored high-limit complement, not a transcribed PNNL or NIST equation. Those sources support monitoring drive output and regulated process behavior; the exact 100%, 3-point, 10-unit, and 900-second choices are classified above rather than presented as published universal limits.
pv_error_thresholdhas no portable default. The shipped 10.0 exists so the CXF graph is executable. A deployment that has not replaced it in the same units as the bound PV/setpoint is invalid.- Both comparisons are strict. Exactly 97% is clear at the defaults and exactly +/-10 loop units leaves both direction flags false. The vectors pin every boundary from both sides.
- Direction does not imply underdelivery.
yProcessHighcan be the bad side of one loop and the desired response of another. It is a diagnostic sign, not a cause label. - Automatic mode is suppressed explicitly. VFD-0005 suppresses this rule because a local/hand or bypassed drive invalidates the automatic-loop saturation inference; VFD-0001 does the same when feedback cannot establish where the drive actually is. Suppression must be instance-scoped to the same physical drive.
- The graph has no enable input. A stopped drive produces a raw process
direction but cannot satisfy
at_max; the host still reports NO_EVAL because the operating premise is absent. persist.delayOnInit = true, so a violation present at engine start waits the full 15 minutes.- No simulation validation is claimed. The current harness has no actual drive feedback paired with that same loop’s PV and setpoint; joining the pump-flow proxy to boiler temperature would be cross-loop fabrication.
Notes
Read the sign before visiting the field, then read the loop definition. A high PV at maximum speed may be correct action for a reverse-acting loop, evidence of overdelivery, or proof that the drive limit is unrelated to the bound process. The generic rule deliberately stops at that boundary. Resolve VFD-0005 and VFD-0001 first, verify loop units and direction, then check configured drive limits and the mechanical path.
Test Vectors
20 scenarios, clock step 60 s over 3600 s.
| Scenario | Description |
|---|---|
healthy_mid_speed_satisfied | At 60% speed with PV one loop unit above setpoint, neither saturation nor process-error direction is active. |
maximum_speed_satisfied | The drive can sit at 100% without fault when the controlled process is within its configured error band. |
maximum_speed_process_high | At 100% speed with PV 15 loop units above setpoint, the high diagnostic is immediate and the combined fault waits 15 minutes. |
maximum_speed_process_low | The symmetric direction: PV 15 loop units below setpoint activates only yProcessLow and reaches the same persisted fault. |
speed_exactly_at_ceiling | max_speed - speed_tolerance is 97%; the graph uses strict greater-than, so exactly 97% is not at maximum even though the raw high-error diagnostic is true. |
speed_just_beyond_ceiling | At 97.1% the speed term passes; a 10.1-unit PV miss brackets both assembled strict boundaries from the faulting side. |
pv_error_exactly_at_threshold_high | A positive error of exactly 10.0 loop units is not high under the strict comparator, even at 100% speed. |
pv_error_exactly_at_threshold_low | The negative-error boundary is symmetric: PV exactly 10.0 units below setpoint does not activate yProcessLow. |
violation_shorter_than_duration | A 14-minute high-process episode ends one sample before the 15-minute persistence can mature and never alarms. |
speed_release_resets_timer | Six hundred seconds at maximum, one sample below the ceiling, then a fresh violation proves the conjunction timer resets rather than accumulating disjoint episodes. |
process_recovery_resets_timer | The same reset through the process branch: a one-sample return inside the allowance band discards elapsed persistence before the miss returns. |
initialization_violation_waits | A violation present at engine initialization waits the full delay because persist.delayOnInit is true. |
stopped_drive_raw_behavior_host_gated | With the drive stopped, the raw process-low diagnostic remains true but the high-speed term is false. The required enabled/auto/active-loop exclusion is host-side. |
mid_speed_raw_direction_switch | At 50% speed the fault remains false while the raw process diagnostic switches from high to low at t=1800 s, proving the secondary outputs are not saturation-gated. |
both_just_beyond_low_boundaries | The low-error mirror brackets both strict thresholds from the active side: speed 97.1% and PV 10.1 units below setpoint. |
violation_ends_at_delay_boundary | The candidate releases at the staged 900-second boundary before that tick is evaluated. No assertion is made on the boundary tick, and no fault appears. |
two_short_opposite_direction_episodes | A short high-error episode and a separate low-error episode are divided by recovery. Direction changes do not let the shared persistence timer accumulate disjoint intervals. |
recovery_after_alarm | A matured high-direction fault clears immediately when PV returns to setpoint at t=1800 s. |
speed_threshold_chatter_resets_delay | Speed alternates on opposite sides of the 97% ceiling every minute while PV remains high. The candidate repeatedly resets and never survives 900 seconds. |
process_threshold_chatter_resets_delay | PV alternates just inside and outside the high-error threshold every minute at maximum speed; the strict candidate never persists long enough to fault. |
vectors.json
{
"schema": "cxf-library/vectors/v1",
"clock": {
"step_s": 60,
"horizon_s": 3600
},
"scenarios": [
{
"name": "healthy_mid_speed_satisfied",
"description": "At 60% speed with PV one loop unit above setpoint, neither saturation nor process-error direction is active.",
"inputs": {
"vfd_speed": 60.0,
"vfd_process_value": 101.0,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessHigh",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessLow",
"from_s": 0,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "maximum_speed_satisfied",
"description": "The drive can sit at 100% without fault when the controlled process is within its configured error band.",
"inputs": {
"vfd_speed": 100.0,
"vfd_process_value": 105.0,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessHigh",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessLow",
"from_s": 0,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "maximum_speed_process_high",
"description": "At 100% speed with PV 15 loop units above setpoint, the high diagnostic is immediate and the combined fault waits 15 minutes.",
"inputs": {
"vfd_speed": 100.0,
"vfd_process_value": 115.0,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 840,
"equals": false
},
{
"output": "yFault",
"from_s": 960,
"to_s": 3600,
"equals": true
},
{
"output": "yProcessHigh",
"from_s": 0,
"to_s": 3600,
"equals": true
},
{
"output": "yProcessLow",
"from_s": 0,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "maximum_speed_process_low",
"description": "The symmetric direction: PV 15 loop units below setpoint activates only yProcessLow and reaches the same persisted fault.",
"inputs": {
"vfd_speed": 100.0,
"vfd_process_value": 85.0,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 840,
"equals": false
},
{
"output": "yFault",
"from_s": 960,
"to_s": 3600,
"equals": true
},
{
"output": "yProcessHigh",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessLow",
"from_s": 0,
"to_s": 3600,
"equals": true
}
]
},
{
"name": "speed_exactly_at_ceiling",
"description": "max_speed - speed_tolerance is 97%; the graph uses strict greater-than, so exactly 97% is not at maximum even though the raw high-error diagnostic is true.",
"inputs": {
"vfd_speed": 97.0,
"vfd_process_value": 115.0,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessHigh",
"from_s": 0,
"to_s": 3600,
"equals": true
},
{
"output": "yProcessLow",
"from_s": 0,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "speed_just_beyond_ceiling",
"description": "At 97.1% the speed term passes; a 10.1-unit PV miss brackets both assembled strict boundaries from the faulting side.",
"inputs": {
"vfd_speed": 97.1,
"vfd_process_value": 110.1,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 840,
"equals": false
},
{
"output": "yFault",
"from_s": 960,
"to_s": 3600,
"equals": true
},
{
"output": "yProcessHigh",
"from_s": 0,
"to_s": 3600,
"equals": true
},
{
"output": "yProcessLow",
"from_s": 0,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "pv_error_exactly_at_threshold_high",
"description": "A positive error of exactly 10.0 loop units is not high under the strict comparator, even at 100% speed.",
"inputs": {
"vfd_speed": 100.0,
"vfd_process_value": 110.0,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessHigh",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessLow",
"from_s": 0,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "pv_error_exactly_at_threshold_low",
"description": "The negative-error boundary is symmetric: PV exactly 10.0 units below setpoint does not activate yProcessLow.",
"inputs": {
"vfd_speed": 100.0,
"vfd_process_value": 90.0,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessHigh",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessLow",
"from_s": 0,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "violation_shorter_than_duration",
"description": "A 14-minute high-process episode ends one sample before the 15-minute persistence can mature and never alarms.",
"inputs": {
"vfd_speed": 100.0,
"vfd_process_value": [
{
"t": 0,
"value": 115.0
},
{
"t": 840,
"value": 100.0
}
],
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 840,
"equals": false
},
{
"output": "yFault",
"from_s": 960,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "speed_release_resets_timer",
"description": "Six hundred seconds at maximum, one sample below the ceiling, then a fresh violation proves the conjunction timer resets rather than accumulating disjoint episodes.",
"inputs": {
"vfd_speed": [
{
"t": 0,
"value": 100.0
},
{
"t": 600,
"value": 90.0
},
{
"t": 660,
"value": 100.0
}
],
"vfd_process_value": 115.0,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 1500,
"equals": false
},
{
"output": "yFault",
"from_s": 1620,
"to_s": 3600,
"equals": true
}
]
},
{
"name": "process_recovery_resets_timer",
"description": "The same reset through the process branch: a one-sample return inside the allowance band discards elapsed persistence before the miss returns.",
"inputs": {
"vfd_speed": 100.0,
"vfd_process_value": [
{
"t": 0,
"value": 85.0
},
{
"t": 600,
"value": 100.0
},
{
"t": 660,
"value": 85.0
}
],
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 1500,
"equals": false
},
{
"output": "yFault",
"from_s": 1620,
"to_s": 3600,
"equals": true
}
]
},
{
"name": "initialization_violation_waits",
"description": "A violation present at engine initialization waits the full delay because persist.delayOnInit is true.",
"inputs": {
"vfd_speed": 98.0,
"vfd_process_value": 80.0,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 840,
"equals": false
},
{
"output": "yFault",
"from_s": 960,
"to_s": 3600,
"equals": true
}
]
},
{
"name": "stopped_drive_raw_behavior_host_gated",
"description": "With the drive stopped, the raw process-low diagnostic remains true but the high-speed term is false. The required enabled/auto/active-loop exclusion is host-side.",
"inputs": {
"vfd_speed": 0.0,
"vfd_process_value": 80.0,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessHigh",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessLow",
"from_s": 0,
"to_s": 3600,
"equals": true
}
]
},
{
"name": "mid_speed_raw_direction_switch",
"description": "At 50% speed the fault remains false while the raw process diagnostic switches from high to low at t=1800 s, proving the secondary outputs are not saturation-gated.",
"inputs": {
"vfd_speed": 50.0,
"vfd_process_value": [
{
"t": 0,
"value": 115.0
},
{
"t": 1800,
"value": 85.0
}
],
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessHigh",
"from_s": 0,
"to_s": 1740,
"equals": true
},
{
"output": "yProcessHigh",
"from_s": 1860,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessLow",
"from_s": 0,
"to_s": 1740,
"equals": false
},
{
"output": "yProcessLow",
"from_s": 1860,
"to_s": 3600,
"equals": true
}
]
},
{
"name": "both_just_beyond_low_boundaries",
"description": "The low-error mirror brackets both strict thresholds from the active side: speed 97.1% and PV 10.1 units below setpoint.",
"inputs": {
"vfd_speed": 97.1,
"vfd_process_value": 89.9,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 840,
"equals": false
},
{
"output": "yFault",
"from_s": 960,
"to_s": 3600,
"equals": true
},
{
"output": "yProcessHigh",
"from_s": 0,
"to_s": 3600,
"equals": false
},
{
"output": "yProcessLow",
"from_s": 0,
"to_s": 3600,
"equals": true
}
]
},
{
"name": "violation_ends_at_delay_boundary",
"description": "The candidate releases at the staged 900-second boundary before that tick is evaluated. No assertion is made on the boundary tick, and no fault appears.",
"inputs": {
"vfd_speed": 100.0,
"vfd_process_value": [
{
"t": 0,
"value": 115.0
},
{
"t": 900,
"value": 100.0
}
],
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 840,
"equals": false
},
{
"output": "yFault",
"from_s": 960,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "two_short_opposite_direction_episodes",
"description": "A short high-error episode and a separate low-error episode are divided by recovery. Direction changes do not let the shared persistence timer accumulate disjoint intervals.",
"inputs": {
"vfd_speed": 100.0,
"vfd_process_value": [
{
"t": 0,
"value": 115.0
},
{
"t": 600,
"value": 100.0
},
{
"t": 720,
"value": 85.0
},
{
"t": 1320,
"value": 100.0
}
],
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "recovery_after_alarm",
"description": "A matured high-direction fault clears immediately when PV returns to setpoint at t=1800 s.",
"inputs": {
"vfd_speed": 100.0,
"vfd_process_value": [
{
"t": 0,
"value": 115.0
},
{
"t": 1800,
"value": 100.0
}
],
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 840,
"equals": false
},
{
"output": "yFault",
"from_s": 960,
"to_s": 1740,
"equals": true
},
{
"output": "yFault",
"from_s": 1860,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "speed_threshold_chatter_resets_delay",
"description": "Speed alternates on opposite sides of the 97% ceiling every minute while PV remains high. The candidate repeatedly resets and never survives 900 seconds.",
"inputs": {
"vfd_speed": [
{
"t": 0,
"value": 97.1
},
{
"t": 60,
"value": 96.9
},
{
"t": 120,
"value": 97.1
},
{
"t": 180,
"value": 96.9
},
{
"t": 240,
"value": 97.1
},
{
"t": 300,
"value": 96.9
},
{
"t": 360,
"value": 97.1
},
{
"t": 420,
"value": 96.9
},
{
"t": 480,
"value": 97.1
},
{
"t": 540,
"value": 96.9
},
{
"t": 600,
"value": 97.1
},
{
"t": 660,
"value": 96.9
},
{
"t": 720,
"value": 97.1
},
{
"t": 780,
"value": 96.9
},
{
"t": 840,
"value": 97.1
},
{
"t": 900,
"value": 96.9
},
{
"t": 960,
"value": 97.1
},
{
"t": 1020,
"value": 96.9
},
{
"t": 1080,
"value": 97.1
},
{
"t": 1140,
"value": 96.9
},
{
"t": 1200,
"value": 97.1
},
{
"t": 1260,
"value": 96.9
},
{
"t": 1320,
"value": 97.1
},
{
"t": 1380,
"value": 96.9
},
{
"t": 1440,
"value": 97.1
},
{
"t": 1500,
"value": 96.9
},
{
"t": 1560,
"value": 97.1
},
{
"t": 1620,
"value": 96.9
},
{
"t": 1680,
"value": 97.1
},
{
"t": 1740,
"value": 96.9
},
{
"t": 1800,
"value": 97.1
},
{
"t": 1860,
"value": 96.9
},
{
"t": 1920,
"value": 97.1
},
{
"t": 1980,
"value": 96.9
},
{
"t": 2040,
"value": 97.1
},
{
"t": 2100,
"value": 96.9
},
{
"t": 2160,
"value": 97.1
},
{
"t": 2220,
"value": 96.9
},
{
"t": 2280,
"value": 97.1
},
{
"t": 2340,
"value": 96.9
},
{
"t": 2400,
"value": 97.1
},
{
"t": 2460,
"value": 96.9
},
{
"t": 2520,
"value": 97.1
},
{
"t": 2580,
"value": 96.9
},
{
"t": 2640,
"value": 97.1
},
{
"t": 2700,
"value": 96.9
},
{
"t": 2760,
"value": 97.1
},
{
"t": 2820,
"value": 96.9
},
{
"t": 2880,
"value": 97.1
},
{
"t": 2940,
"value": 96.9
},
{
"t": 3000,
"value": 97.1
},
{
"t": 3060,
"value": 96.9
},
{
"t": 3120,
"value": 97.1
},
{
"t": 3180,
"value": 96.9
},
{
"t": 3240,
"value": 97.1
},
{
"t": 3300,
"value": 96.9
},
{
"t": 3360,
"value": 97.1
},
{
"t": 3420,
"value": 96.9
},
{
"t": 3480,
"value": 97.1
},
{
"t": 3540,
"value": 96.9
}
],
"vfd_process_value": 115.0,
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 3600,
"equals": false
}
]
},
{
"name": "process_threshold_chatter_resets_delay",
"description": "PV alternates just inside and outside the high-error threshold every minute at maximum speed; the strict candidate never persists long enough to fault.",
"inputs": {
"vfd_speed": 100.0,
"vfd_process_value": [
{
"t": 0,
"value": 110.1
},
{
"t": 60,
"value": 109.9
},
{
"t": 120,
"value": 110.1
},
{
"t": 180,
"value": 109.9
},
{
"t": 240,
"value": 110.1
},
{
"t": 300,
"value": 109.9
},
{
"t": 360,
"value": 110.1
},
{
"t": 420,
"value": 109.9
},
{
"t": 480,
"value": 110.1
},
{
"t": 540,
"value": 109.9
},
{
"t": 600,
"value": 110.1
},
{
"t": 660,
"value": 109.9
},
{
"t": 720,
"value": 110.1
},
{
"t": 780,
"value": 109.9
},
{
"t": 840,
"value": 110.1
},
{
"t": 900,
"value": 109.9
},
{
"t": 960,
"value": 110.1
},
{
"t": 1020,
"value": 109.9
},
{
"t": 1080,
"value": 110.1
},
{
"t": 1140,
"value": 109.9
},
{
"t": 1200,
"value": 110.1
},
{
"t": 1260,
"value": 109.9
},
{
"t": 1320,
"value": 110.1
},
{
"t": 1380,
"value": 109.9
},
{
"t": 1440,
"value": 110.1
},
{
"t": 1500,
"value": 109.9
},
{
"t": 1560,
"value": 110.1
},
{
"t": 1620,
"value": 109.9
},
{
"t": 1680,
"value": 110.1
},
{
"t": 1740,
"value": 109.9
},
{
"t": 1800,
"value": 110.1
},
{
"t": 1860,
"value": 109.9
},
{
"t": 1920,
"value": 110.1
},
{
"t": 1980,
"value": 109.9
},
{
"t": 2040,
"value": 110.1
},
{
"t": 2100,
"value": 109.9
},
{
"t": 2160,
"value": 110.1
},
{
"t": 2220,
"value": 109.9
},
{
"t": 2280,
"value": 110.1
},
{
"t": 2340,
"value": 109.9
},
{
"t": 2400,
"value": 110.1
},
{
"t": 2460,
"value": 109.9
},
{
"t": 2520,
"value": 110.1
},
{
"t": 2580,
"value": 109.9
},
{
"t": 2640,
"value": 110.1
},
{
"t": 2700,
"value": 109.9
},
{
"t": 2760,
"value": 110.1
},
{
"t": 2820,
"value": 109.9
},
{
"t": 2880,
"value": 110.1
},
{
"t": 2940,
"value": 109.9
},
{
"t": 3000,
"value": 110.1
},
{
"t": 3060,
"value": 109.9
},
{
"t": 3120,
"value": 110.1
},
{
"t": 3180,
"value": 109.9
},
{
"t": 3240,
"value": 110.1
},
{
"t": 3300,
"value": 109.9
},
{
"t": 3360,
"value": 110.1
},
{
"t": 3420,
"value": 109.9
},
{
"t": 3480,
"value": 110.1
},
{
"t": 3540,
"value": 109.9
}
],
"vfd_process_sp": 100.0
},
"expect": [
{
"output": "yFault",
"from_s": 0,
"to_s": 3600,
"equals": false
}
]
}
]
}