Files
pumpingStation/wiki/modes/mpc.md
znetsixe 66fd3feff8 Add eval harness + Tier 2/3 mode template pages
### eval/ (scenario-based evaluation)

Complements the unit tests under test/basic. Scenarios fluctuate inputs
over simulated time, record every tick to JSONL, print a summary
table + event log, and check expectations. Complementary to unit
tests — these answer "how does the system respond to this input
profile" rather than "is this function correct".

- eval/run.js             — driver; monkey-patches Date.now so the
                            volume integrator ticks at 1 s/iter
                            regardless of wall-clock
- eval/scenarios/         — one file per scenario
  - levelbased-steady.js  — constant inflow, demand converges
  - levelbased-storm.js   — inflow surge, demand saturates
  - safety-dry-run-trip.js — manual mode, empty basin, safety trips
- eval/formatters/table.js — ASCII summary of sampled ticks
- eval/logs/              — per-scenario JSONL output (one line per tick)
- eval/README.md          — usage + scenario file shape + how to pipe
                            into InfluxDB/Grafana

All three starter scenarios PASS with their expectations.

### wiki/modes/ (tier template pages)

The levelbased page templated Tier-1 modes (static transfer function).
Added worked examples for the other two tiers so all mode pages share
a common skeleton and new modes have something concrete to imitate:

- flowbased.md   — Tier 2 (PID on measured outflow)
- powerbased.md  — Tier 2 (levelbased curve clipped by grid power budget)
- mpc.md         — Tier 3 (optimisation + forecast; block diagram +
                           scenario time-series instead of a fixed curve)

- modes/README.md — updated with the three-tier classification table
                    and diagram-type-per-tier guidance

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:49:41 +02:00

6.9 KiB
Raw Blame History

title, mode, tier, status, updated
title mode tier status updated
MPC (Model-Predictive Control) mpc 3 placeholder 2026-04-22

MPC mode — Tier 3 template

Status — not yet implemented. Not even in the schema today. This page reserves the shape for when the time comes.

Why this is Tier 3

The levelbased/flowbased/powerBased modes are all memoryless or near-memoryless transfer functions. You give them the current state; they give you a demand. You can draw them as 2D plots.

MPC is different. At each tick the controller solves an optimisation over a prediction horizon:

minimise   Σ  cost(state(t+k), command(t+k))         for k = 0 .. N
subject to forecast, physical limits, power budget, spill penalty, ...

The command that's emitted at time t is merely the first step of that plan; next tick the forecast shifts and the optimiser re-runs. There's no fixed demand = f(level) curve — the curve is remade every tick.

That's why Tier-3 modes get block diagrams + scenario time-series, not transfer functions.

At a glance

Item Value
Tier 3 — optimisation-based
Signal driving demand full state (level, flow, power) + forecasts (inflow, grid price, weather)
Secondary inputs cost weights, horizon length, solver config
Output demand + planned trajectory over horizon
Thresholds adjusted at runtime? Effectively yes — the optimiser treats them as soft constraints
Use when Available forecasts beat reactive control, or multi-objective optimisation is needed

Diagram 1 — signal flow (block diagram)

Placeholder image — replace with:
  diagrams/modes/mpc-block.drawio.svg

Blocks:

  [sensors]   [inflow forecast]   [grid price]   [weather API]
      │             │                  │              │
      └─────────────┴──────────────────┴──────────────┘
                             │
                       ┌─────▼──────┐
                       │ state +    │
                       │ forecast   │
                       │ bundle     │
                       └─────┬──────┘
                             │
                       ┌─────▼───────────────────┐
                       │ MPC solver              │
                       │  • horizon N            │
                       │  • cost weights w       │
                       │  • constraints C        │
                       │  • linearised model     │
                       └─────┬───────────────────┘
                             │
                       ┌─────▼───────┐
                       │ command[0]  │ ── the step we act on now
                       │ command[1]  │
                       │   ...       │
                       │ command[N]  │ ── re-planned next tick
                       └─────┬───────┘
                             │
                   ┌─────────▼─────────┐
                   │ safety layer clip │ ← dryRun / overflow always apply
                   └─────────┬─────────┘
                             │
                          demand  →  MGC

Diagram 2 — scenario time-series

A much more useful way to evaluate MPC is to plot what it did over a simulated scenario: level, planned vs actual demand, the cost function breakdown, the active constraints. The eval harness is built for exactly this — MPC will need a dedicated scenario like mpc-storm-with-forecast.js.

Placeholder — replace with:
  diagrams/modes/mpc-scenario.drawio.svg

Stacked time-series showing:
  1. basin level over time (with forecast shadow and horizon)
  2. demand over time (with the re-planning edges visible)
  3. cost breakdown: energy vs spill-penalty vs ramp-penalty
  4. active constraints over time (colored bands)

Inputs

Signal Where from Role
current state measurements container initial condition for optimiser
inflow forecast external — sewer model / weather API drives the cost integral
grid-price forecast external — market feed / schedule weights energy cost
cost weights w config trades off spill vs energy vs ramp
horizon N config 1560 minutes typical
model parameters config / learned basin dynamics, pump curves

Threshold policy

Levels appear in the optimiser as soft constraints (penalties in the cost function):

Threshold Role in MPC
dryRunLevel, overflowLevel hard constraints — if the optimiser's plan crosses them, safety layer clips
minLevel, maxLevel soft constraints — penalty weight w_level applied to excursions
startLevel advisory only — optimiser doesn't inherently care, but may be used in cost weights for rule-of-thumb alignment with human expectations

So unlike Tier-1/2 where thresholds directly gate the action, here they shape the objective.

Demand formula

Not a formula — an optimisation problem:

state, forecast, constraints = gather_inputs()
plan = mpc_solver.solve(
    state0         = state,
    forecast       = forecast,
    horizon        = N,
    model          = basin_dynamics + pump_curves,
    cost           = w_energy × Σ power(k)
                   + w_spill  × Σ max(0, level(k)  overflowLevel)²
                   + w_undercut × Σ max(0, minLevel  level(k))²
                   + w_ramp   × Σ (command(k)  command(k-1))²,
    constraints    = pump_limits + power_budget + rate_limits,
)
demand = plan.command[0]

Edge cases

  • Solver timeout. Fall back to the previous plan's step, or to a levelbased curve as a safe default. Log.
  • Bad forecast (persistent bias). Optimiser can chase a wrong prediction for many ticks. Adaptive forecast bias correction, or a watchdog comparing forecast-vs-realised, is essential.
  • Infeasibility. If constraints can't be satisfied (e.g. power budget and maxLevel simultaneously during a severe storm), relax soft constraints in priority order (ramp first, then maxLevel, then energy) — never relax dryRun/overflow.
  • Safety takeover. The safety layer still overrides. MPC should anticipate safety trips in its cost function (big penalty for trajectories that invoke them), not hit them.