--- title: Flow-based mode mode: flowbased tier: 2 status: placeholder updated: 2026-04-22 --- # Flow-based mode — *Tier 2 template* > **Status — not yet implemented.** The `flowbased` entry is a placeholder in `_controlLogic`. This page reserves the shape and documents the intended design so all Tier-2 modes share the same layout. ## At a glance | Item | Value | |---|---| | Tier | 2 — parameterised transfer function | | Signal driving demand | measured outflow (actual pumps) | | Secondary inputs | integrator + derivative state (for PID) | | Output | demand 0–100 % via PID correction | | Thresholds adjusted at runtime? | No (but the demand can move independently of level) | | Use when | The station has a flow sensor on the outlet and you want to hold a target outflow rate regardless of basin level | ## Diagram **Primary plot.** Demand vs *outflow-error* (not level!) is the meaningful transfer function for flow-based control. The curve is a classic PID surface — proportional slope times error, plus integral + derivative terms. **Secondary plot.** Level still enters as gates (STOP below `minLevel`, don't overfill above `maxLevel`) — same thresholds as levelbased, but the mode doesn't *use* level to pick demand. ``` Placeholder image — replace with: diagrams/modes/flowbased.drawio.svg (demand vs outflow-error, showing Kp slope) ``` ## Inputs | Signal | Where from | Role | |---|---|---| | measured outflow | sum of `flow.measured.*` at outflow positions | error = (flowSetpoint − measuredOutflow) | | `config.control.flowBased.flowSetpoint` | editor, static | target outflow in m³/h | | `config.control.flowBased.flowDeadband` | editor, static | zone around setpoint where PID output holds | | `config.control.flowBased.pid.{kp, ki, kd, ...}` | editor / schema | PID gains + rate limits | | current level | fallback → threshold gates | only used for `minLevel`/`maxLevel` bounds | ## Threshold policy The **control** thresholds (`minLevel`, `startLevel`, `maxLevel`) are still enforced but for different reasons than levelbased: | Threshold | Role in flowbased | |---|---| | `minLevel` | If level drops below, force demand=0 regardless of PID output (prevents pump undercut) | | `startLevel` | unused — demand is driven by error, not level | | `maxLevel` | If level climbs above, force demand=100 regardless of PID output (prevents spill) | ## Demand formula ```text error = flowSetpoint − measuredOutflow if level < minLevel: demand = 0 # pump-undercut guard elif level > maxLevel: demand = 100 # anti-spill guard else: # normal PID branch P = Kp × error I += Ki × error × dt # with anti-windup clamp D = Kd × d(error)/dt # with low-pass filter demand = clamp(P + I + D, 0, 100) # with rate limits Δup/Δdown ``` ## Edge cases - **Cold start, no prior outflow measurement.** PID state starts at 0; first error is `flowSetpoint`. Integral term will build up — rate-limit the demand ramp to avoid over-shoot. - **Sensor dropout on the outflow meter.** Fall back to predicted outflow (sum of pump curve predictions). Log a warning — PID on predicted-only is unreliable. - **Setpoint step change.** PID with derivative filter + rate limits handles this gracefully; without filter, the D-kick would saturate output. - **Safety layer interaction.** Same as levelbased — `dryRunLevel` and `overflowLevel` override the PID output. See [functional description § Safety](../functional-description.md#safety-controller). ## Related - [Functional description](../functional-description.md) — basin model + shared safety layer - [modes/README.md](README.md) — mode index + page template - [modes/levelbased.md](levelbased.md) — Tier 1 reference implementation