From 7bd5e617bcd3c25610e0c2036be7f75db2478f7e Mon Sep 17 00:00:00 2001 From: znetsixe Date: Wed, 22 Apr 2026 12:08:27 +0200 Subject: [PATCH] Fix basin + control-zone diagram accuracy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three corrections after review: 1. Overfill behaviour — replaced "overfill trip (upstream stops)" with "spill over weir (measure & log)" and added a Known Limitation box in the Safety section. The code's execSequence:shutdown on upstream children only makes sense in a cascaded-station layout; for the gravity-sewer case the inflow can't be stopped (toilets back up). Correct response is spill measurement + alarm. 2. Demand-ramp annotations — removed "100% / 0% demand" and the RUN arrow from the basin cross-section. Those are levelbased-mode specifics; the basin model should describe physical geometry only. Demand annotations remain in the Control logic thermometer. 3. heightInlet placement — moved below startLevel in both diagrams. Matches physical reality: pumps start before water rises to the gravity-inlet pipe. Flags a contradictory comment in specificClass.js for cleanup next time that file is touched. Co-Authored-By: Claude Opus 4.7 (1M context) --- Functional-Description.md | 50 ++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/Functional-Description.md b/Functional-Description.md index 2890481..6360233 100644 --- a/Functional-Description.md +++ b/Functional-Description.md @@ -173,27 +173,31 @@ The basin is modelled as a rectangular prism with constant cross-section. Everyt ┌─────────────────┐ ◄─ heightBasin (rim) │ │ │ freeboard │ - ├─ ─ ─ ─ ─ ─ ─ ─ ─┤ ◄─ heightOverflow ═══► overfill trip - │≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ (upstream stops) + ├─ ─ ─ ─ ─ ─ ─ ─ ─┤ ◄─ heightOverflow ═══► spill over weir + │≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ (measure & log — + │≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ see Safety note) + ├╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌┤ ◄─ maxFlowLevel │≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ - ├╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌┤ ◄─ maxFlowLevel ═══► 100 % demand - │≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ ▲ - │≈≈≈≈≈ RUN ≈≈≈≈≈≈≈│ │ ramp - │≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ │ linearly - INFLOW ═══════►╣≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ ◄─ heightInlet │ - │≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ │ - ├╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌┤ ◄─ startLevel ═══► 0 % demand - │≈≈ DEAD ZONE ≈≈≈≈│ ─── hysteresis - │≈≈ (keep cmd) ≈≈≈│ - ├╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌┤ ◄─ stopLevel ═══► unconditional STOP + │≈≈≈ SCALING ≈≈≈≈≈│ (levelbased: demand + │≈≈≈ RANGE ≈≈≈≈≈│ ramps 0 → 100 % + │≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ across this band) + ├╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌┤ ◄─ startLevel + │≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ + │≈≈≈ DEAD ZONE ≈≈≈│ (hysteresis — + │≈≈ (keep cmd) ≈≈│ keep last command) + INFLOW ═══════►╣≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ ◄─ heightInlet + │≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ + ├╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌ ╌┤ ◄─ stopLevel ═══► unconditional STOP │≈≈≈≈≈ BUFFER ≈≈≈≈│ │≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ - OUTFLOW ◄══════╣≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ ◄─ heightOutlet ═══► dry-run trip - │░░░ DEAD VOLUME ░│ (downstream stops) + OUTFLOW ◄══════╣≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈│ ◄─ heightOutlet ═══► dry-run trip + │░░░ DEAD VOLUME ░│ (downstream stops) └─────────────────┘ ◄─ 0 (floor) ``` -**Typical ordering** (bottom → top): `stopLevel < startLevel = minFlowLevel ≤ heightInlet < maxFlowLevel ≈ heightOverflow`. +**Typical ordering** (bottom → top): `stopLevel < heightInlet < startLevel = minFlowLevel < maxFlowLevel ≤ heightOverflow`. + +> ⚠️ The comment block in `specificClass.js` currently says `startLevel ≤ heightInlet` (inlet above startLevel). The physical convention is the opposite: pumps start *before* the water reaches the gravity inlet, so `heightInlet < startLevel`. Worth fixing in the code comment next time that file is touched. **minHeightBasedOn** — which pipe defines `minVol`, the operational floor used for the initial seed, the dry-run trigger, and the 0 % point of the fill percentage: @@ -246,7 +250,7 @@ flowPositions = { inflow: ['in', 'upstream'], outflow: ['out', 'downstream'] } level ▲ │ - ┼── heightOverflow ═══ overfill trip ─► upstream STOP + ┼── heightOverflow ─── weir crest (spill → measure) │ │ ┐ │ │ RUN @@ -254,17 +258,17 @@ flowPositions = { inflow: ['in', 'upstream'], outflow: ['out', 'downstream'] } │ │ ┼── maxFlowLevel ═══ ┴ 100 % demand │ - ┼── heightInlet ─── inflow pipe - │ - ┼── startLevel ═══ 0 % demand (ramp starts) + │ ┐ + │ │ (scaling range) + │ │ + ┼── startLevel ═══ 0 % demand ── ramp starts │ ┐ │ │ DEAD ZONE - │ │ hysteresis — keep last cmd + ┼── heightInlet ─── │ hysteresis — keep last cmd │ │ ┼── stopLevel ═══ ┴ unconditional STOP │ - ┼── heightOutlet ─── outflow pipe - │ ═══ dry-run trip ─► downstream STOP + ┼── heightOutlet ─── outflow pipe (dry-run trip here) │ ┴── 0 (floor) ``` @@ -310,6 +314,8 @@ flowPositions = { inflow: ['in', 'upstream'], outflow: ['out', 'downstream'] } During overfill, level-based control naturally commands ≥100 % on the downstream MGC because the level is above `maxFlowLevel`. +> ⚠️ **Known limitation — gravity-sewer context.** The "upstream STOP" action only makes sense in a **cascaded** station layout where the upstream equipment is an EVOLV-controllable pump or station. In a conventional wastewater wet-well the inflow is gravity-fed from the municipal sewer and **cannot be stopped** — attempting to would back up toilets. For that case the correct response to an overfill event is to **measure and log the spill over the weir** (for compliance reporting) and raise an alarm, while keeping downstream pumps at maximum demand. The current code fires `execSequence: shutdown` on upstream children regardless of what they are; that should be gated on "is the upstream actually controllable?" and supplemented with overflow-rate tracking. Tracked as follow-up work. + A missing volume reading is treated as a hard fault: every direct machine is sent `execSequence: shutdown` and `safetyControllerActive` latches. Calibrate predicted volume (`calibratePredictedVolume`) or wire a level measurement to recover. ## Registration — which children count as flow?