Docs + simulations refresh; align spill-flow keys with new position

- wiki/functional-description.md: rename Overfill Protection → High-volume
  Safety; tighten basin-ordering chain; relocate level-based mode
  diagrams under wiki/diagrams/modes/level-based/; document the new
  flow.predicted.overflow.default position (replaces the previous
  child='overflow' under position 'out'); add underflowVolume +
  predictedUnderflowVolume entries.
- wiki/modes/{levelbased,powerbased}.md: paragraph cleanups.
- wiki/diagrams: move level-linear basin diagram under modes/level-based/
  alongside a new level-log variant.
- simulations/run.js: add max_demand_gt expectation.
- simulations/scenarios/*: minor fixture updates.
- test/basic/nodeClass-config.test.js: new config-shape coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rene De Ren
2026-05-06 17:23:20 +02:00
parent d8490aa949
commit 6ab585bcc2
14 changed files with 165 additions and 50 deletions

View File

@@ -7,7 +7,7 @@ updated: 2026-04-22
# Level-based mode
The simplest and most widely deployed control strategy. Demand is a direct, *static* piecewise-linear function of basin level — no feedback loop, no predictions beyond the level measurement itself. This page uses the [shared basin model](../functional-description.md#basin-model); see [`modes/README.md`](README.md) for the template other mode pages follow.
The simplest and most widely deployed control strategy. Demand is a direct, static function of basin level — no feedback loop, no predictions beyond the level measurement itself. This page uses the [shared basin model](../functional-description.md#basin-model); see [`modes/README.md`](README.md) for the template other mode pages follow.
## At a glance
@@ -20,9 +20,9 @@ The simplest and most widely deployed control strategy. Demand is a direct, *sta
## Diagram
![Level-linear basin mode — demand vs level transfer function](../diagrams/modes/basin-mode-level-linear.drawio.svg)
![Level-linear basin mode — demand vs level transfer function](../diagrams/modes/level-based/basin-mode-level-linear.drawio.svg)
*Editable source: [`../diagrams/modes/basin-mode-level-linear.drawio.svg`](../diagrams/modes/basin-mode-level-linear.drawio.svg) (drag into [draw.io](https://app.diagrams.net/) — it round-trips).*
*Editable sources: [`../diagrams/modes/level-based/basin-mode-level-linear.drawio.svg`](../diagrams/modes/level-based/basin-mode-level-linear.drawio.svg) and [`../diagrams/modes/level-based/basin-mode-level-log.drawio.svg`](../diagrams/modes/level-based/basin-mode-level-log.drawio.svg) (drag into [draw.io](https://app.diagrams.net/) — they round-trip).*
## Inputs
@@ -30,10 +30,11 @@ The simplest and most widely deployed control strategy. Demand is a direct, *sta
|---|---|---|
| current level | `measurement` child (`measured`) → predicted from volume integrator (fallback) | X-axis of the transfer function |
| `config.control.levelbased.minLevel` | editor, static | below → pumps hard OFF |
| `config.control.levelbased.startLevel` | editor, static | where demand-ramp starts |
| `config.control.levelbased.startLevel` | editor, static | falling ramp reaches 0 % here; rising demand holds 0 % until the inlet level |
| `config.control.levelbased.maxLevel` | editor, static | where demand saturates at 100 % |
| `config.control.levelbased.curveType` | editor, static | `linear` or `log`; log is fast early response |
The three control thresholds are the **only** mode-specific configuration. Nothing here is recomputed at runtime.
The three control thresholds plus curve type are the mode-specific configuration. Nothing here is recomputed at runtime.
## Threshold policy
@@ -42,6 +43,7 @@ The three control thresholds are the **only** mode-specific configuration. Nothi
| `minLevel` | `config.control.levelbased.minLevel` | No |
| `startLevel` | `config.control.levelbased.startLevel` | No |
| `maxLevel` | `config.control.levelbased.maxLevel` | No |
| `curveType` | `config.control.levelbased.curveType` | No |
That this policy is trivial (all static) is **the defining simplicity of this mode**. Modes like `powerBased` or future `weather-aware` variants will recompute these thresholds on the fly.
@@ -51,15 +53,15 @@ That this policy is trivial (all static) is **the defining simplicity of this mo
if level < minLevel:
demand = 0
MGC → turnOffAllMachines() # explicit shutdown, not just "0 %"
elif level < startLevel:
demand = <previous demand> # dead zone — hold last command (hysteresis)
elif level <= maxLevel:
demand = lerp(level, [startLevel, maxLevel], [0 %, 100 %])
elif direction == filling:
demand = curve(level, [inflowLevel, maxLevel], [0 %, 100 %])
elif direction == draining:
demand = curve(level, [startLevel, maxLevel], [0 %, 100 %])
else:
demand = 100 % # saturated; MGC clamps internally if overshoot
demand = previous demand
```
Where `lerp` is linear interpolation. The MGC is free to distribute the demand across its pumps however its own policy dictates (equal split, lead-lag, staging — that's the MGC's business).
Below the active lower ramp point, demand is 0 %. Above `maxLevel`, demand is 100 %. `curve` is either linear or logarithmic; the log variant rises faster early in the ramp. The MGC is free to distribute the demand across its pumps however its own policy dictates (equal split, lead-lag, staging — that's the MGC's business).
## Edge cases