Submodule pointers - pumpingStation: realistic basin defaults, ramp-foot visual fix, manual-mode observability, new 02-Dashboard.json (charts + raw-output table), wiki Home/Reference-Examples with screenshots + demo GIF. - generalFunctions: pumpingStation config schema defaults aligned with the new editor drag-in values; startLevel description corrected (ramp foot is inflowLevel, not startLevel). - monster: examples cleanup — drop pre-refactor flows, ship single 02-integrated-e2e.json. Wiki - New wiki/Functional-Overview.md: companion to Architecture covering the process side — what each node physically represents and which control objective it serves. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
627 lines
25 KiB
Markdown
627 lines
25 KiB
Markdown
# Functional Overview
|
|
|
|

|
|

|
|
|
|
> [!NOTE]
|
|
> What each EVOLV node physically represents and what control objective it serves. Companion to [Architecture](Architecture), which describes the **code** shape; this page describes the **process** the code models. Read this when you want to know "what is actually happening in the water", not "what is happening in the JavaScript".
|
|
|
|
---
|
|
|
|
## Plant-level process flow
|
|
|
|
Real water flows left to right through real equipment. EVOLV models a subset of that equipment as Node-RED nodes. The coloured nodes below are modelled by EVOLV; the grey ones are upstream / downstream of the EVOLV-modelled section.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
inf["Influent"]:::extern
|
|
scr["Coarse screens"]:::extern
|
|
grit["Grit chamber"]:::extern
|
|
ps1["Inlet lift station"]:::pc
|
|
primary["Primary settler"]:::extern
|
|
reactor["Aerobic reactor"]:::unit
|
|
secondary["Secondary settler"]:::unit
|
|
disinf["Disinfection / UV"]:::extern
|
|
eff["Effluent"]:::extern
|
|
sludge["Sludge handling"]:::extern
|
|
|
|
inf --> scr --> grit --> ps1 --> primary --> reactor --> secondary --> disinf --> eff
|
|
secondary -. RAS .-> reactor
|
|
secondary -. WAS .-> sludge
|
|
|
|
classDef pc fill:#0c99d9,color:#fff,stroke:#075a82,stroke-width:2px
|
|
classDef unit fill:#50a8d9,color:#000,stroke:#2c7ba8,stroke-width:2px
|
|
classDef extern fill:#f0f0f0,color:#666,stroke:#bbb,stroke-width:1px,stroke-dasharray:4 4
|
|
```
|
|
|
|
### Plant step to EVOLV node mapping
|
|
|
|
| Plant step | What happens physically | EVOLV node(s) |
|
|
|:---|:---|:---|
|
|
| Inlet lift station | Wet-well buffer; pumps raise water to plant elevation | pumpingStation + machineGroupControl + rotatingMachine |
|
|
| Aerobic reactor | Bacteria consume NH4, COD under aeration; O2 supplied by diffusers | reactor + diffuser + measurement |
|
|
| Secondary settler | Biomass settles by gravity; clean water overflows; sludge returns or is wasted | settler + rotatingMachine (RAS pump) |
|
|
| Flow distribution | Multi-valve manifold for effluent split, airflow distribution, RAS proportioning | valveGroupControl + valve |
|
|
| Composite sampling | Flow-proportional sample buildup for lab analysis | monster + measurement |
|
|
| Telemetry to UI | Time-series storage + dashboard provisioning | dashboardAPI (Grafana) + Port 1 to InfluxDB |
|
|
|
|
---
|
|
|
|
## pumpingStation — wet-well lift station
|
|
|
|
### Physical view
|
|
|
|
A wet-well buffer at low elevation. Inflow arrives by gravity from the upstream sewer; pumps lift water to plant elevation. Level is the controlled variable.
|
|
|
|
```
|
|
inflow
|
|
|
|
|
v
|
|
+---------------------+
|
|
| | <-- overflow weir (safety, alarm)
|
|
| |
|
|
| ~ ~ ~ ~ ~ ~ ~ ~ ~ | <-- startLevel (e.g. 80% of basin)
|
|
| |
|
|
| | <-- band where 1 pump runs
|
|
| |
|
|
| ~ ~ ~ ~ ~ ~ ~ ~ ~ | <-- stopLevel (e.g. 30%)
|
|
| |
|
|
| | <-- band where pumps idle
|
|
| |
|
|
| ___________________| <-- dryRunLevel (cutoff, alarm)
|
|
| | | | | |
|
|
| v v v v |
|
|
| P1 P2 P3 P4 |
|
|
+--|----|----|----|---+
|
|
v v v v
|
|
outflow (to next stage)
|
|
```
|
|
|
|
### Process variables
|
|
|
|
| Variable | Typical range | Unit | Source |
|
|
|:---|:---|:---|:---|
|
|
| Inflow Q_in | 0 to 2000 | m3/h | gravity sewer; measured by inlet flow meter |
|
|
| Outflow Q_out | 0 to 2000 | m3/h | sum of running pump flows |
|
|
| Level | 0 to 5 | m | level sensor (radar, ultrasonic, hydrostatic) |
|
|
| Volume | 0 to V_max | m3 | basin geometry × level |
|
|
| Predicted ETA full / empty | seconds | s | (V_max − V) / (Q_in − Q_out) |
|
|
|
|
### Control objective
|
|
|
|
| Goal | Mechanism |
|
|
|:---|:---|
|
|
| Keep level in operating band | Schmitt-trigger hysteresis: start pumps at `startLevel`, stop at `stopLevel` |
|
|
| Avoid rapid pump cycling | `stopLevel` strictly below `startLevel` (deadband) |
|
|
| Avoid overflow | High-level alarm; safety controller can override |
|
|
| Avoid running dry | Low-level cutoff stops all pumps |
|
|
| Stay near pump BEP | Demand is shared by machineGroupControl using each pump's curve |
|
|
|
|
### EVOLV implementation
|
|
|
|
- `pumpingStation.configure()` builds a `BasinGeometry` from `config.basin` plus a `FlowAggregator` that integrates volume from inflow / outflow measurements per tick.
|
|
- Level + inflow + outflow arrive as `measurement` children with positions `inflow`, `outflow`, `atequipment` for the level.
|
|
- The `control` strategy module picks one of several modes (level-based, flow-based, time-based). The level-based strategy implements the Schmitt trigger and shifted-ramp behaviour.
|
|
- `SafetyController` guards against panic / dry-run / overfill conditions and can block dispatch.
|
|
|
|
---
|
|
|
|
## rotatingMachine — single pump or compressor
|
|
|
|
### Physical view
|
|
|
|
A centrifugal pump (or compressor) characterised by its supplier curves. Speed sets where on the curve the machine operates. The operating point is the intersection of pump curve and system curve.
|
|
|
|
```
|
|
Q-H curve (head vs flow) Q-P curve (power vs flow)
|
|
^ ^
|
|
H | * P | *
|
|
| * | *
|
|
| * <-- BEP | *
|
|
| * | * <-- BEP region
|
|
| * | *
|
|
|________________ Q |____________________ Q
|
|
flow [m3/h] flow [m3/h]
|
|
|
|
Operating point = (system curve) intersect (pump curve at running speed)
|
|
```
|
|
|
|
### Process variables
|
|
|
|
| Variable | Typical range | Unit |
|
|
|:---|:---|:---|
|
|
| Flow Q | 1 to 1000 | m3/h |
|
|
| Head H (or differential pressure) | 1 to 100 | m (or bar) |
|
|
| Power P | 1 to 500 | kW |
|
|
| Efficiency η | 0.3 to 0.85 | — |
|
|
| Speed N | 0 to 100 | % of rated |
|
|
|
|
### Physical state machine (water-side)
|
|
|
|
```mermaid
|
|
stateDiagram-v2
|
|
[*] --> off
|
|
off --> warmingup: cmd.startup
|
|
warmingup --> operational: warmup time elapsed
|
|
operational --> accelerating: setpoint changes
|
|
accelerating --> operational: target reached
|
|
operational --> decelerating: setpoint reduced
|
|
decelerating --> operational: target reached
|
|
operational --> coolingdown: cmd.shutdown
|
|
coolingdown --> off: cooldown time elapsed
|
|
operational --> emergencystop: cmd.estop
|
|
warmingup --> emergencystop: cmd.estop
|
|
emergencystop --> off: cmd.reset
|
|
```
|
|
|
|
### Control objective
|
|
|
|
| Goal | Mechanism |
|
|
|:---|:---|
|
|
| Deliver commanded flow / speed | Internal FSM ramps speed up / down within configured rates |
|
|
| Predict outputs before sensors react | Q + P + η predicted from speed + measured pressure differential via characteristic curves |
|
|
| Detect drift | `drift/` module compares predicted vs measured; fires HealthStatus levels 0..3 |
|
|
| Protect during warmup / cooldown | Some transitions are non-interruptible (configurable per machine) |
|
|
|
|
### EVOLV implementation
|
|
|
|
- `curves/` module loads supplier characteristic curves (Q-H, Q-P, Q-η); supports multi-dataset assets.
|
|
- `prediction/` module interpolates curve values at current operating point.
|
|
- `state/` module owns the FSM with configurable warmup / cooldown / ramp times.
|
|
- `drift/` module assesses prediction quality and emits `HealthStatus`.
|
|
|
|
---
|
|
|
|
## machineGroupControl — multi-pump load sharing
|
|
|
|
### Physical view
|
|
|
|
Multiple pumps share a common discharge header. The group operating point is where the **sum of pump curves** intersects the system curve. The load-sharing problem is: which pumps run at what speed to deliver demand at the lowest combined power.
|
|
|
|
```
|
|
Pump A curve Pump B curve Pump C curve
|
|
^ ^ ^
|
|
| | |
|
|
+-> share Q_A -->+-> share Q_B -->+-> share Q_C
|
|
|
|
|
sum = Q_demand
|
|
v
|
|
total power = P_A + P_B + P_C
|
|
minimize subject to per-pump curve constraints
|
|
```
|
|
|
|
### Process variables
|
|
|
|
| Variable | Source |
|
|
|:---|:---|
|
|
| Group demand Q_d | Inbound from parent (pumpingStation, UI, scheduler) |
|
|
| Per-pump setpoint | Computed each tick |
|
|
| Pressure (downstream) | Measurement child, position `downstream` |
|
|
| Group totals (flow, power, efficiency) | Sum / weighted average of per-pump predictions |
|
|
|
|
### Control objective
|
|
|
|
| Goal | Mechanism |
|
|
|:---|:---|
|
|
| Deliver Q_d at lowest total power | `GroupOperatingPoint` solver picks per-pump shares |
|
|
| Avoid frequent pump start / stop | Hysteresis on pump count + `NCog` switching metric |
|
|
| Stay near per-pump BEP | Penalise operating points far from BEP in the solver |
|
|
| Settle latest demand if upstream fires faster than children absorb | `DemandDispatcher` (LatestWinsGate) keeps only the freshest dispatch in flight |
|
|
|
|
### EVOLV implementation
|
|
|
|
- `dispatch/DemandDispatcher` wraps a `LatestWinsGate` so a rapid stream of demands collapses to the most recent.
|
|
- `efficiency/groupEfficiency` computes mean group efficiency at the current shares.
|
|
- `totals/TotalsCalculator` aggregates flow / power across active machines.
|
|
|
|
---
|
|
|
|
## valveGroupControl — multi-valve flow distribution
|
|
|
|
### Physical view
|
|
|
|
Multiple valves on a distribution manifold. Each valve has a flow coefficient K_v that varies with position. The group must split a total available flow between branches.
|
|
|
|
```
|
|
upstream pressure P_up
|
|
|
|
|
v
|
|
+---+---+---+
|
|
| | | |
|
|
v v v v
|
|
Kv1 Kv2 Kv3 Kv4 <-- per-valve K_v
|
|
| | | |
|
|
Q1 Q2 Q3 Q4 <-- per-branch flow
|
|
|
|
Q_i = K_v_i * sqrt(P_up - P_branch_i)
|
|
sum(Q_i) = Q_available (from upstream flow source)
|
|
```
|
|
|
|
### Process variables
|
|
|
|
| Variable | Typical range | Unit |
|
|
|:---|:---|:---|
|
|
| Valve position | 0 to 100 | % |
|
|
| K_v at full open | 1 to 1000 | m3/h / sqrt(bar) |
|
|
| Differential pressure across valve | 0.1 to 5 | bar |
|
|
| Per-branch flow split | percentage of total | % |
|
|
|
|
### Control objective
|
|
|
|
| Goal | Mechanism |
|
|
|:---|:---|
|
|
| Achieve target per-branch flow split | Solve per-valve K_v from inverse characteristic |
|
|
| Respect upstream availability | Read total flow from registered flow source (pumpingStation, MGC, etc.) |
|
|
| Honour valve position limits | Clamp K_v to physical valve range |
|
|
|
|
### Note on softwareType registration
|
|
|
|
`valveGroupControl.configure()` registers five softwareTypes — `valve`, `machine`, `machinegroup`, `pumpingstation`, `valvegroupcontrol`. Only `valve` is a true S88 child. The other four are flow-source registrations: VGC reads their flow output to know how much it has to distribute.
|
|
|
|
---
|
|
|
|
## reactor — bioreactor (ASM kinetics)
|
|
|
|
### Physical view
|
|
|
|
A tank where bacteria consume substrate under aeration. Continuous-flow operation (CSTR or PFR). The state of the reactor is described by 13 ASM state variables (soluble + particulate fractions of organic matter, ammonia, nitrate, biomass, alkalinity, oxygen).
|
|
|
|
```
|
|
air from diffuser
|
|
|
|
|
v bubbles
|
|
Q_in +-------+-------+ Q_out
|
|
---->| ~ ~ ~ ~ ~ ~ |---->
|
|
| ~ ~ ~ ~ ~ ~ |
|
|
C_in | ~ ~ ~ ~ ~ | C_out
|
|
| ~ ~ ~ ~ ~ ~ |
|
|
| ~ ~ ~ ~ ~ ~ |
|
|
+----------------------+
|
|
|
|
Mass balance per ASM component i:
|
|
V * dC_i/dt = Q_in * C_in_i - Q_out * C_out_i + V * r_i(C, T, DO, ...)
|
|
inflow term outflow term reaction term
|
|
```
|
|
|
|
### Process variables
|
|
|
|
| Variable | Typical range | Unit |
|
|
|:---|:---|:---|
|
|
| Volume V | 100 to 10000 | m3 |
|
|
| Hydraulic retention time HRT | 4 to 24 | h |
|
|
| Sludge retention time SRT | 5 to 30 | d |
|
|
| MLSS | 2000 to 5000 | mg/L |
|
|
| Temperature | 5 to 30 | degC |
|
|
| DO setpoint | 1 to 3 | mg/L |
|
|
| NH4 effluent target | < 1 | mg/L |
|
|
| NO3 effluent | 0 to 15 | mg/L |
|
|
|
|
### Control objective
|
|
|
|
| Goal | Mechanism |
|
|
|:---|:---|
|
|
| Maintain DO setpoint | DO measurement → diffuser airflow loop (closed externally) |
|
|
| Achieve effluent quality | Manage HRT via reactor inflow, SRT via WAS rate |
|
|
| Operate stably across temperature | Kinetics are temperature-corrected via Arrhenius factors |
|
|
|
|
### Engine choice
|
|
|
|
| Engine | When to use |
|
|
|:---|:---|
|
|
| CSTR | Single well-mixed tank or short reactor |
|
|
| PFR | Long, narrow plug-flow reactor; discretised into grid cells along the flow path |
|
|
|
|
Set via `config.reactor_type`.
|
|
|
|
### EVOLV implementation
|
|
|
|
- `kinetics/baseEngine.js` — common state vector + boundary-condition handling.
|
|
- `kinetics/cstr.js` — single-zone integrator.
|
|
- `kinetics/pfr.js` — multi-zone PFR with a grid.
|
|
- Diffuser fires `data.otr` on its emitter; reactor subscribes and treats OTR as an O2 source term.
|
|
- Downstream `settler` subscribes to reactor `stateChange`; the settler reads effluent composition each tick.
|
|
|
|
---
|
|
|
|
## settler — secondary clarifier
|
|
|
|
### Physical view
|
|
|
|
A wide, shallow tank where biomass settles by gravity. A sludge blanket forms at the bottom; clean water overflows the rim at the top. A return-pump sucks settled sludge back to the reactor; a smaller waste stream removes excess (WAS).
|
|
|
|
```
|
|
Q_in (from reactor)
|
|
+ biomass C_in
|
|
|
|
|
v
|
|
+------------------------------+
|
|
| clean water |---> overflow Q_out
|
|
| | low TSS
|
|
| - - - - - - - - - - - - - | <-- top of sludge blanket
|
|
| (settling zone) |
|
|
| = = = = = = = = = = = = = |
|
|
| compacting biomass |
|
|
| ########################### | <-- sludge blanket
|
|
+-----------+---+--------------+
|
|
| |
|
|
v v
|
|
underflow to reactor (RAS)
|
|
high TSS small bleed (WAS)
|
|
```
|
|
|
|
### Process variables
|
|
|
|
| Variable | Typical range | Unit |
|
|
|:---|:---|:---|
|
|
| Surface area | 50 to 2000 | m2 |
|
|
| Depth | 3 to 5 | m |
|
|
| Surface loading rate (SLR) | 0.5 to 2 | m/h |
|
|
| RAS flow | 50 to 150 | % of inflow |
|
|
| WAS flow | 1 to 5 | % of inflow |
|
|
| Effluent TSS | < 30 | mg/L |
|
|
| Underflow TSS | 6000 to 12000 | mg/L |
|
|
|
|
### Control objective
|
|
|
|
| Goal | Mechanism |
|
|
|:---|:---|
|
|
| Keep sludge blanket below overflow weir | RAS pump rate adjusted to inflow |
|
|
| Maintain reactor MLSS target | WAS rate set as fraction of inflow |
|
|
| Avoid blanket carryover | Limit SLR; alarm on rising blanket level |
|
|
|
|
### EVOLV implementation
|
|
|
|
- `settler._connectReactor` attaches `emitter.on('stateChange', ...)` to the upstream reactor, pulling effluent composition each tick.
|
|
- `settler._connectMachine` registers the RAS pump (a `rotatingMachine`) as a child.
|
|
- Settling-velocity model (Takács or Vesilind) is in the settler's `src/`; see [Settling Models](Concept-Settling-Models).
|
|
|
|
---
|
|
|
|
## diffuser — aeration device
|
|
|
|
### Physical view
|
|
|
|
A perforated panel or membrane at the bottom of the reactor that releases fine bubbles. Bubbles rise through the water column; oxygen dissolves into the water across the gas-liquid interface.
|
|
|
|
```
|
|
reactor side
|
|
~ ~ ~ ~ ~ dissolved O2 enters water
|
|
~ ~ ~ ~ ~
|
|
o o o o o o <-- bubbles rising
|
|
o o o o o o
|
|
o o o o o
|
|
ooo ooo
|
|
ooo oo
|
|
+----[========]----+ <-- diffuser membrane / panel
|
|
| | | | | | | | | compressed air manifold
|
|
+-+--+-+--+-+--+-+-+
|
|
^
|
|
air inflow
|
|
(from blower upstream)
|
|
```
|
|
|
|
### Process variables
|
|
|
|
| Variable | Typical range | Unit |
|
|
|:---|:---|:---|
|
|
| Airflow per diffuser | 1 to 10 | Nm3/h |
|
|
| Header pressure | 0.3 to 0.7 | bar |
|
|
| Water depth (above diffuser) | 4 to 6 | m |
|
|
| K_La (volumetric mass-transfer coefficient) | 1 to 20 | 1/h |
|
|
| Alpha factor (wastewater vs clean water) | 0.5 to 0.9 | — |
|
|
| OTR (oxygen transfer rate) | 1 to 5 | kg-O2/h per diffuser |
|
|
|
|
### Control objective
|
|
|
|
| Goal | Mechanism |
|
|
|:---|:---|
|
|
| Deliver enough OTR to meet reactor DO setpoint | Modulate airflow via upstream blower / valve |
|
|
| Distribute air evenly across panels | Manifold sizing; sometimes a valveGroupControl on the air side |
|
|
| Avoid over-aeration (energy waste) | DO feedback loop |
|
|
|
|
### EVOLV implementation
|
|
|
|
- `diffuser` reads `headerPressure`, water depth, airflow as inputs.
|
|
- Computes OTR from K_La (configurable, supplier-specific), alpha factor, water properties.
|
|
- Emits `data.otr` on its emitter. Reactor subscribes via `emitter.on('otr', ...)` — this is **not** a child-register handshake.
|
|
|
|
---
|
|
|
|
## valve — single valve actuator
|
|
|
|
### Physical view
|
|
|
|
A control valve in a pipe. Position 0..100% maps to K_v via the valve's characteristic curve (linear, equal-percentage, or quick-opening). Flow through the valve obeys `Q = K_v * sqrt(dP)`.
|
|
|
|
```
|
|
flow +---+ flow
|
|
----------| |----------->
|
|
| ===== <-- closure element (plug, ball, disc, gate)
|
|
| |
|
|
+---+
|
|
^
|
|
position 0..100%
|
|
|
|
position --(characteristic curve)--> K_v
|
|
Q = K_v * sqrt(P_up - P_down)
|
|
```
|
|
|
|
### Process variables
|
|
|
|
| Variable | Typical range | Unit |
|
|
|:---|:---|:---|
|
|
| Position | 0 to 100 | % |
|
|
| K_v at full open | 1 to 1000 | m3/h / sqrt(bar) |
|
|
| Differential pressure | 0.1 to 5 | bar |
|
|
| Stroke time (close to open) | 10 to 60 | s |
|
|
|
|
### Physical state machine
|
|
|
|
valve shares the rotatingMachine state model. States: `off`, `idle`, `warmingup`, `operational`, `accelerating` (opening / closing), `decelerating`, `coolingdown`, `emergencystop`, `maintenance`. `warmingup` and `coolingdown` are protected (cannot be aborted).
|
|
|
|
### Control objective
|
|
|
|
| Goal | Mechanism |
|
|
|:---|:---|
|
|
| Reach commanded position | Move at the configured `reactionSpeed` rate |
|
|
| Avoid water hammer | Limit how fast position changes |
|
|
| Provide flow feedback to upstream | Computed Q from current K_v and measured dP |
|
|
|
|
### EVOLV implementation
|
|
|
|
- `valve` registers `measurement` children for position / pressure feedback.
|
|
- Inherits the shared FSM (`generalFunctions` state config) with rotatingMachine.
|
|
- Characteristic curve is supplier-specific and loaded similarly to pump curves.
|
|
|
|
---
|
|
|
|
## measurement — sensor signal conditioning
|
|
|
|
### Physical view
|
|
|
|
A field sensor (level meter, flow meter, pressure transducer, DO probe, ...) outputs a raw signal. EVOLV's `measurement` node wraps that signal: scales it from instrument units (mA, mV, raw counts) to engineering units, smooths it, rejects outliers, and publishes the result to a parent process node.
|
|
|
|
```
|
|
Sensor 4-20 mA (or digital, or MQTT)
|
|
in the pipe -----------------+
|
|
|
|
|
v
|
|
+--------------------------------+
|
|
| measurement node |
|
|
| |
|
|
| raw input |
|
|
| | |
|
|
| v scaling (mA -> EU) |
|
|
| v smoothing |
|
|
| v outlier rejection |
|
|
| v calibration offset |
|
|
| |
|
|
+-------+------------------------+
|
|
|
|
|
v data.<type>
|
|
parent process node
|
|
(pumpingStation, reactor, ...)
|
|
```
|
|
|
|
### Three input modes
|
|
|
|
| Mode | Source | When to use |
|
|
|:---|:---|:---|
|
|
| analog | 4-20 mA, 0-10 V, raw scaled value | Direct PLC / IO-card analog input |
|
|
| digital | Boolean (on / off, ok / fault) | Limit switches, status contacts |
|
|
| MQTT | External MQTT broker topic | Field bus, sensor with its own gateway |
|
|
|
|
### Process variables (examples)
|
|
|
|
| Type | Typical range | Unit | Example sensor |
|
|
|:---|:---|:---|:---|
|
|
| level | 0 to 5 | m | radar level meter |
|
|
| flow | 0 to 2000 | m3/h | electromagnetic flowmeter |
|
|
| pressure | 0 to 10 | bar | piezo pressure transmitter |
|
|
| temperature | 5 to 40 | degC | PT100 RTD |
|
|
| DO | 0 to 10 | mg/L | optical dissolved-O2 probe |
|
|
| NH4 | 0 to 50 | mg/L | ion-selective electrode |
|
|
| TSS | 0 to 5000 | mg/L | optical turbidity sensor |
|
|
|
|
### Control objective
|
|
|
|
| Goal | Mechanism |
|
|
|:---|:---|
|
|
| Deliver trustworthy values to parent | Pipeline: scaling → smoothing → outlier → publish |
|
|
| Survive sensor faults | Outlier rejection + sticky-last-good behaviour |
|
|
| Calibrate against reference | `cmd.calibrate` triggers a calibration cycle |
|
|
|
|
---
|
|
|
|
## monster — composite sampling
|
|
|
|
### Physical view
|
|
|
|
A virtual composite sampler: imagine a small bucket beside the pipe. Every time a unit volume of water flows past, a unit dose of that water is added to the bucket. After a sampling period (e.g. 24 h) the bucket contains a flow-proportional composite of every concentration over that period.
|
|
|
|
```
|
|
____________________
|
|
| sampling bucket | <-- accumulated sample
|
|
| ~~~~~~ |
|
|
| ~~~~~ |
|
|
| ~ |
|
|
|_____________________|
|
|
|
|
|
| sampling dose dV at every flow increment
|
|
v
|
|
-----++------------------------+-----> main pipe
|
|
| (flow Q, conc C) flow
|
|
v
|
|
dV = (Q * dt / total_flow_target) * sample_volume
|
|
composite_C(t) = integral( C(s) * dV(s) ) / total_dV
|
|
```
|
|
|
|
### Process variables
|
|
|
|
| Variable | Typical range | Unit |
|
|
|:---|:---|:---|
|
|
| Sampling period | 1 to 24 | h |
|
|
| Bucket volume target | 2 to 10 | L |
|
|
| Sample doses per period | 24 to 96 | — |
|
|
| Output composite concentration | as configured per parameter | mg/L, NTU, … |
|
|
|
|
### Control objective
|
|
|
|
| Goal | Mechanism |
|
|
|:---|:---|
|
|
| Build a representative composite sample over the period | Flow-proportional dosing |
|
|
| Produce reportable averages | Each tick, accumulate flow-weighted concentration |
|
|
| Reset for next period | At end of period, emit composite and reset bucket |
|
|
|
|
### Gotchas
|
|
|
|
| Gotcha | Detail |
|
|
|:---|:---|
|
|
| `assetType` must be `"flow"` exactly | Sub-types like `"flow-electromagnetic"` are silently ignored by monster's child router |
|
|
| `constraints.flowmeter` not forwarded | Toggling proportional-vs-time mode has no runtime effect in current code. Tracked in `.claude/refactor/OPEN_QUESTIONS.md` |
|
|
|
|
---
|
|
|
|
## dashboardAPI — Grafana provisioning
|
|
|
|
### Physical view
|
|
|
|
There is none. `dashboardAPI` does not model any piece of water-treatment equipment. It is a utility that auto-generates Grafana dashboards from a node's softwareType + measurements, so operators see the right panels per equipment without hand-building dashboard JSON.
|
|
|
|
### Operational role
|
|
|
|
| Trigger | Effect |
|
|
|:---|:---|
|
|
| Any EVOLV node sends `child.register` to dashboardAPI | dashboardAPI composes a dashboard JSON from the template for that softwareType and POSTs an upsert to Grafana |
|
|
| Telemetry arrives in InfluxDB (Port 1 of process nodes) | Grafana panels query InfluxDB and render the trends |
|
|
|
|
dashboardAPI is the one node in the platform that does not extend `BaseDomain` (it is a passive HTTP bridge). See `.claude/refactor/OPEN_QUESTIONS.md`.
|
|
|
|
---
|
|
|
|
## Where it all fits
|
|
|
|
If you imagine a wastewater plant from inlet to outlet, every EVOLV node is a piece of equipment you would see on a P&ID. The software's job is to model that equipment well enough that:
|
|
|
|
1. Operators can run the plant without watching the water directly (predictions + telemetry).
|
|
2. New plants can be commissioned by composing standard nodes (no bespoke control code).
|
|
3. Anomalies surface as `HealthStatus` flags before they become operator problems.
|
|
|
|
See [Topology Patterns](Topology-Patterns) for how to assemble these nodes into a working plant.
|
|
|
|
---
|
|
|
|
## Related pages
|
|
|
|
| Page | Why |
|
|
|:---|:---|
|
|
| [Home](Home) | Top-level node map and S88 hierarchy |
|
|
| [Topology Patterns](Topology-Patterns) | Standard assemblies of these nodes |
|
|
| [Architecture](Architecture) | The **code** counterpart of this page |
|
|
| [Topic Conventions](Topic-Conventions) | How process variables travel between nodes |
|
|
| [Telemetry](Telemetry) | How process variables land in InfluxDB and Grafana |
|
|
| [ASM Models](Concept-ASM-Models) | The reactor's biological kinetics in detail |
|
|
| [Pump Affinity Laws](Concept-Pump-Affinity-Laws) | Pump curve physics |
|
|
| [Settling Models](Concept-Settling-Models) | Settler physics |
|
|
| [Signal Processing — Sensors](Concept-Signal-Processing-Sensors) | Measurement node pipeline |
|