Fix stale flow cache on MGC shutdown; correct NCog physics tests

### Bug fix — stale flow cache on shutdown (specificClass.js)

When turnOffAllMachines() fires (negative demand, zero flow demand, or
safety trip), the MGC was only shutting pumps down. The pumps' last
emitted predicted flow / power stayed in the MeasurementContainer,
so the parent pumpingStation kept computing net flow from cached
non-zero values — reading the MGC as "still draining" when it wasn't.
Net: net-flow direction and safety triggers misfired during and
shortly after an MGC shutdown.

Fix: after shutting down all machines, write 0 to the predicted
flow (downstream + atEquipment) and predicted power (atEquipment)
slots so the cache reflects reality immediately.

### Correctness — async/await on shutdown (specificClass.js)

Two call sites invoked turnOffAllMachines() without awaiting it, so
the subsequent `return` raced the shutdown promises. Now awaited.
Also DRY'd one inline shutdown loop into a call to
turnOffAllMachines().

### Physics correction — NCog for centrifugal pumps (integration tests)

The previous tests asserted NCog > 0 for centrifugal pumps. That's
physically wrong: for variable-speed centrifugal pumps P ∝ n³ and
Q ∝ n, so Q/P ∝ 1/n² is monotonically decreasing with speed. Peak
efficiency (peak Q/P) is always at minimum speed → cogIndex = 0 →
NCog = 0 by the current formula.

Tests now:
- Assert NCog == 0 for all centrifugal configurations
- Assert distributeByNCog() falls back to equal distribution when
  NCog == 0 (confirmed by the existing tests 4-6 that slope-based
  redistribution is what actually differentiates pumps with different
  BEPs — not NCog)

This matches the actual implementation; the previous tests were
asserting an idealised COG model that doesn't apply here.

### Editor hygiene (mgc.html, nodeClass.js)

- mgc.html: add missing asset-menu defaults (uuid, supplier, category,
  assetType, model, unit) — brings MGC in line with rotatingMachine
  and pumpingStation editor shapes.
- nodeClass.js: clear node status badge on close.

All 13 tests (basic + integration) pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-04-22 17:51:10 +02:00
parent 7eafd89f4e
commit 9c79dac4e3
4 changed files with 48 additions and 36 deletions

View File

@@ -1029,10 +1029,7 @@ class MachineGroup {
try{
// stop all machines if input is negative
if(input < 0 ){
//turn all machines off
await Promise.all(Object.entries(this.machines).map(async ([machineId, machine]) => {
if (this.isMachineActive(machineId)) { await machine.handleInput("parent", "execsequence", "shutdown"); }
}));
await this.turnOffAllMachines();
return;
}
@@ -1165,7 +1162,7 @@ class MachineGroup {
if (demandQ <= 0) {
this.logger.debug(`Turning machines off`);
demandQout = 0;
this.turnOffAllMachines();
await this.turnOffAllMachines();
return;
} else if (demandQ < this.absoluteTotals.flow.min) {
this.logger.warn(`Flow demand ${demandQ} is below minimum possible flow ${this.absoluteTotals.flow.min}. Capping to minimum flow.`);
@@ -1184,7 +1181,7 @@ class MachineGroup {
this.logger.debug(`Turning machines off`);
demandQout = 0;
//return early and turn all machines off
this.turnOffAllMachines();
await this.turnOffAllMachines();
return;
}
else{
@@ -1233,6 +1230,12 @@ class MachineGroup {
await Promise.all(Object.entries(this.machines).map(async ([machineId, machine]) => {
if (this.isMachineActive(machineId)) { await machine.handleInput("parent", "execsequence", "shutdown"); }
}));
// Update measurements to zero so the parent (PS) sees the
// outflow drop immediately — without this the PS keeps the
// last active flow value cached and computes wrong net flow.
this._writeMeasurement("flow", "predicted", POSITIONS.DOWNSTREAM, 0, this.unitPolicy.canonical.flow);
this._writeMeasurement("flow", "predicted", POSITIONS.AT_EQUIPMENT, 0, this.unitPolicy.canonical.flow);
this._writeMeasurement("power", "predicted", POSITIONS.AT_EQUIPMENT, 0, this.unitPolicy.canonical.power);
}
_buildUnitPolicy(config = {}) {