Pump controller virtual commissioning
Executed the actual controller against a synthetic reservoir and motor-feedback model. Selected reference service is 1 L/s with one-pump standby capacity and an attended power-restoration procedure. Tests include normal cycles, capacity loss, invalid sensors and power interruption.
Methods and declared inputs
Reservoir capacity 200 L; dry stop below 20 L, start at 80 L, assist at 120 L and reported high level at 180 L. The mass balance is V[k+1] = V[k] + (inflow - delivered outflow) * dt, with spill recorded separately. These are reference inputs, not plant dimensions.
Each available pump delivers a constant 4 L/s once proved, with 0.2 s start delay and instantaneous stop. The controller uses its actual 3 s start, 1 s feedback loss and 10 s minimum-run timers. The standard scan is 100 ms.
The power-loss case intentionally remains inhibited after restoration because no quiet reset occurs. Its spill is a design consequence of the existing restart policy, not a successful capacity result. An operating process requiring unattended recovery needs a separately approved restart policy or independent storage/alarm response.
The selected attended recovery case isolates the inlet 50 s after the modeled power loss, drains externally at 10 L/s, resets with quiet inputs, then restores inflow. It clears inhibition without spill. The original unacknowledged-restart case remains a rejected unattended operating condition.
Checks pass when observed behavior matches the specified expectation. This includes expected adverse conditions: capacity overrun and process overflow while restart is inhibited.
Executed cases
| case | max_volume_l | overflow_l | lead_rotations | latched_fault_a | sensor_fault | restart_inhibited |
|---|---|---|---|---|---|---|
| normal_cycles | 80.29999999999997 | 0.0 | 7 | False | False | |
| assist_demand | 120.40000000000049 | 0.0 | 0 | False | False | |
| lead_start_failure | 83.29999999999954 | 0.0 | 2 | START_TIMEOUT | False | False |
| one_pump_unavailable | 80.29999999999997 | 0.0 | 4 | False | False | |
| capacity_exceeded | 200.0 | 144.39999999998736 | 0 | False | False | |
| power_loss_restart_inhibit | 200.0 | 39.99999999999065 | 0 | False | True | |
| attended_power_recovery | 119.99999999999746 | 0.0 | 2 | False | False | |
| invalid_level_switches | 111.19999999999749 | 0.0 | 0 | True | False | |
| running_feedback_loss | 80.29999999999984 | 0.0 | 2 | RUN_FEEDBACK_LOST | False | False |
| short_feedback_dropout | 80.29999999999984 | 0.0 | 2 | False | False | |
| manual_dry_interlock | 0.0 | 0.0 | 0 | False | False |
Acceptance checks
| id | description | expected | observed | passed | method |
|---|---|---|---|---|---|
| balance-normal_cycles | Reservoir volume balance | Absolute residual < 1e-8 L | 8.162714948412031e-11 | True | Integrate incoming, pumped, spilled and retained volume independently |
| dry-normal_cycles | Low level overrides both commands | False | False | True | Check every scan of the coupled reservoir/controller run |
| balance-assist_demand | Reservoir volume balance | Absolute residual < 1e-8 L | -6.93574975230149e-10 | True | Integrate incoming, pumped, spilled and retained volume independently |
| dry-assist_demand | Low level overrides both commands | False | False | True | Check every scan of the coupled reservoir/controller run |
| balance-lead_start_failure | Reservoir volume balance | Absolute residual < 1e-8 L | -1.0786038728838321e-11 | True | Integrate incoming, pumped, spilled and retained volume independently |
| dry-lead_start_failure | Low level overrides both commands | False | False | True | Check every scan of the coupled reservoir/controller run |
| balance-one_pump_unavailable | Reservoir volume balance | Absolute residual < 1e-8 L | 1.382716163789155e-11 | True | Integrate incoming, pumped, spilled and retained volume independently |
| dry-one_pump_unavailable | Low level overrides both commands | False | False | True | Check every scan of the coupled reservoir/controller run |
| balance-capacity_exceeded | Reservoir volume balance | Absolute residual < 1e-8 L | 1.780335878720507e-10 | True | Integrate incoming, pumped, spilled and retained volume independently |
| dry-capacity_exceeded | Low level overrides both commands | False | False | True | Check every scan of the coupled reservoir/controller run |
| balance-power_loss_restart_inhibit | Reservoir volume balance | Absolute residual < 1e-8 L | 0.0 | True | Integrate incoming, pumped, spilled and retained volume independently |
| dry-power_loss_restart_inhibit | Low level overrides both commands | False | False | True | Check every scan of the coupled reservoir/controller run |
| balance-attended_power_recovery | Reservoir volume balance | Absolute residual < 1e-8 L | 1.1382894626876805e-11 | True | Integrate incoming, pumped, spilled and retained volume independently |
| dry-attended_power_recovery | Low level overrides both commands | False | False | True | Check every scan of the coupled reservoir/controller run |
| balance-invalid_level_switches | Reservoir volume balance | Absolute residual < 1e-8 L | -1.6484591469634324e-12 | True | Integrate incoming, pumped, spilled and retained volume independently |
| dry-invalid_level_switches | Low level overrides both commands | False | False | True | Check every scan of the coupled reservoir/controller run |
| balance-running_feedback_loss | Reservoir volume balance | Absolute residual < 1e-8 L | -1.06439301816863e-11 | True | Integrate incoming, pumped, spilled and retained volume independently |
| dry-running_feedback_loss | Low level overrides both commands | False | False | True | Check every scan of the coupled reservoir/controller run |
| balance-short_feedback_dropout | Reservoir volume balance | Absolute residual < 1e-8 L | -1.06439301816863e-11 | True | Integrate incoming, pumped, spilled and retained volume independently |
| dry-short_feedback_dropout | Low level overrides both commands | False | False | True | Check every scan of the coupled reservoir/controller run |
| balance-manual_dry_interlock | Reservoir volume balance | Absolute residual < 1e-8 L | 0.0 | True | Integrate incoming, pumped, spilled and retained volume independently |
| dry-manual_dry_interlock | Low level overrides both commands | False | False | True | Check every scan of the coupled reservoir/controller run |
| normal-cycles | Automatic cycles alternate the lead | At least 5 rotations and no spill | {'rotations': 7, 'overflow_l': 0.0} | True | 600 s closed-loop run, 1 L/s inlet |
| assist-capacity | Two-pump demand stays within tank capacity | No spill at 6 L/s | 0.0 | True | Each proved pump removes 4 L/s |
| failed-lead | Failed start latches and standby clears demand | START_TIMEOUT and no spill | START_TIMEOUT | True | Suppress motor A start/feedback |
| maintenance | Available standby carries declared demand | No spill | 0.0 | True | Motor A unavailable, 1 L/s inlet |
| overload | Insufficient installed capacity is reported as adverse | Positive spill at 9 L/s | 144.39999999998736 | True | Demand exceeds both pumps combined; no automatic design acceptance |
| restart | Restored power does not cause an automatic restart | Inhibited with positive spill | {'inhibited': True, 'spill': 39.99999999999065} | True | 5 s interruption; no operator drain or reset follows |
| attended-recovery | Selected operating response restores service without spill | No spill and inhibit cleared | {'case': 'attended_power_recovery', 'scan_s': 0.1, 'max_volume_l': 119.99999999999746, 'overflow_l': 0.0, 'lead_rotations': 2, 'latched_fault_a': '', 'sensor_fault': False, 'restart_inhibited': False, 'dry_command': False, 'volume_balance_error_l': 1.1382894626876805e-11, 'high_level_reached': False} | True | At 120 s isolate inlet and drain externally, reset at 133 s, reopen inlet at 134 s |
| sensor-fault | Impossible level ordering latches alarm | True | True | True | Force assist true while start false at 90 s |
| feedback-latch | Sustained feedback loss trips motor | RUN_FEEDBACK_LOST | RUN_FEEDBACK_LOST | True | Remove feedback after 90 s while flow can persist until command removal |
| feedback-debounce | Short feedback interruption does not trip | No latched fault | True | 0.4 s drop vs 1 s timer | |
| timer-0.05 | Start timeout includes scan quantization | 3 <= delay <= 3 + scan | 3.0000000000000004 | True | Discrete scan-period sweep |
| timer-0.1 | Start timeout includes scan quantization | 3 <= delay <= 3 + scan | 3.0 | True | Discrete scan-period sweep |
| timer-0.25 | Start timeout includes scan quantization | 3 <= delay <= 3 + scan | 3.0 | True | Discrete scan-period sweep |
| refinement | Reservoir peak is stable when scan is halved | Peak difference <= 0.5 L | 0.05000000000238458 | True | Compare 50 and 100 ms scans; deterministic event model |
Scope of the evidence
This completes a virtual commissioning exercise. It does not assert PLC timing, switch debounce, contactor feedback, motor hydraulics, electrical protection, or field reliability.
Constant flow and instantaneous stopping omit pump curves, inertia, pressure transients and stuck-on physical outputs. Independent electrical and process protections are not implemented by this supervisory state machine.
High-level indication is recorded by the plant harness. It is not a new hardware alarm output or a claim of safe unattended operation.
Reproduce
python -m pip install -r requirements.txt python run.py
Acceptance matrix · Evidence record and source hashes · Original analysis
Selected plant envelope and restoration policy