handlePressureChange: mirror aggregate flow onto DOWNSTREAM

PS subscribes to MGC's flow.predicted.downstream and uses it as the
outflow estimate for net-flow computation. MGC was only writing to
DOWNSTREAM inside optimalControl (the optimizer's bestFlow TARGET, not
the achieved aggregate), and to AT_EQUIPMENT in handlePressureChange.

During transients — e.g. demand dropping to dead-band keep-alive while
pumps are still ramping down from full throttle — PS saw a stale 25 m³/h
target on DOWNSTREAM while pumps were physically delivering 500+ m³/h.
NetFlow looked small and stable when the basin was actually draining
fast.

flow.act = sum of every pump's current predicted output = achieved
aggregate. Mirror it onto DOWNSTREAM so PS gets a live signal on every
pump flow/pressure update, not just every MGC.handleInput.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rene De Ren
2026-05-08 17:20:21 +02:00
parent b7c40b0ddc
commit dc27a569d9

View File

@@ -283,6 +283,18 @@ class MachineGroup {
this.logger.debug(`Dynamic Totals after pressure change - Flow: Min ${flow.min}, Max ${flow.max}, Act ${flow.act} | Power: Min ${power.min}, Max ${power.max}, Act ${power.act}`);
this._writeMeasurement("flow", "predicted", POSITIONS.AT_EQUIPMENT, flow.act, this.unitPolicy.canonical.flow);
// Mirror the aggregate flow onto DOWNSTREAM as well. PS subscribes to
// flow.predicted.downstream from MGC and uses it as the outflow
// estimate for net-flow computation. Without this mirror, the only
// place DOWNSTREAM gets written is optimalControl's bestFlow (the
// optimizer's TARGET, not the achieved aggregate). During transients
// — e.g. demand dropping to dead-band keep-alive while pumps are
// still ramping down from full throttle — PS would see a stale
// 25 m³/h target while pumps are physically delivering 500+ m³/h,
// making netFlow look small and stable when the basin is actually
// draining fast. flow.act here is the sum of every pump's current
// predicted output, so it IS the achieved aggregate.
this._writeMeasurement("flow", "predicted", POSITIONS.DOWNSTREAM, flow.act, this.unitPolicy.canonical.flow);
this._writeMeasurement("power", "predicted", POSITIONS.AT_EQUIPMENT, power.act, this.unitPolicy.canonical.power);
const { maxEfficiency, lowestEfficiency } = this.calcGroupEfficiency(this.machines);