Files
EVOLV/wiki/Topology-Patterns.md
znetsixe 5ae8788fd7 wiki: crisp overhaul — no decoration emoji, all 9 master pages refactored
Source-tree mirror of EVOLV.wiki.git refactor (27a42ee on wiki.git):

- 7 master pages rewritten with clean design (Home, Architecture,
  Topology-Patterns, Topic-Conventions, Telemetry, Getting-Started,
  Glossary). Tables and Mermaid for visuals, gitea alert callouts for
  warnings, shields badges for metadata only. No emoji as decoration.
- Archive.md becomes a removal-changelog pointing readers to git
  history and to the successor pages.
- _Sidebar.md updated to navigate the new flat-name layout.
- Concept / finding / manual pages: uniform mini-header (badges +
  "reference page" callout) added without rewriting domain content.
- Every internal link now uses the flat naming that resolves on the
  live gitea wiki (Concept-ASM-Models, Finding-BEP-..., etc.).

On wiki.git: 29 Archive-* pages hard-deleted (the git history
preserves them; Archive.md documents the removal).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 22:24:51 +02:00

340 lines
12 KiB
Markdown

# Topology Patterns
![code-ref](https://img.shields.io/badge/code--ref-9ab9f6b-blue)
![verified](https://img.shields.io/badge/edges-verified_against_configure()-brightgreen)
> [!NOTE]
> Five canonical plant configurations and one worked example that combines them. Every edge in every diagram was checked against the parent's `configure()` declaration in source. Use these as templates when wiring your own plant.
---
## Pattern index
| Pattern | When to use it |
|:---|:---|
| [1. Pumping station with grouped pumps](#1-pumping-station-with-grouped-pumps) | Lift station, single basin, N pumps load-shared |
| [2. Reactor + diffuser + settler train](#2-reactor--diffuser--settler-train) | Biological treatment line |
| [3. Valve group on a distribution manifold](#3-valve-group-on-a-distribution-manifold) | Multi-valve flow split with upstream flow context |
| [4. Composite sampling](#4-composite-sampling) | Flow-proportional grab samples for lab analysis |
| [5. Dashboard provisioning](#5-dashboard-provisioning) | Auto-generated Grafana dashboards |
| [Worked example &mdash; small WWTP](#worked-example--small-wwtp) | All five patterns combined |
---
## 1. Pumping station with grouped pumps
The canonical wet-well lift station: one basin model, one demand controller (`pumpingStation`), one load-sharing coordinator (`machineGroupControl`), N pumps (`rotatingMachine` &times; N), measurements for level + flow + per-pump pressure.
```mermaid
flowchart TB
subgraph PC["Process Cell"]
ps[pumpingStation]
end
subgraph UN["Unit"]
mgc[machineGroupControl]
end
subgraph EM["Equipment Module"]
rmA[rotatingMachine A]
rmB[rotatingMachine B]
rmC[rotatingMachine C]
end
subgraph CM["Control Module"]
ml["measurement &mdash; level"]
mfin["measurement &mdash; inflow"]
mpA["measurement &mdash; pressure A"]
mpB["measurement &mdash; pressure B"]
mpC["measurement &mdash; pressure C"]
end
ps --> mgc
mgc --> rmA
mgc --> rmB
mgc --> rmC
ml -. data .-> ps
mfin -. data .-> ps
mpA -. data .-> rmA
mpB -. data .-> rmB
mpC -. data .-> rmC
class ps pc
class mgc unit
class rmA,rmB,rmC equip
class ml,mfin,mpA,mpB,mpC ctrl
classDef pc fill:#0c99d9,color:#fff,stroke:#075a82,stroke-width:2px
classDef unit fill:#50a8d9,color:#000,stroke:#2c7ba8,stroke-width:2px
classDef equip fill:#86bbdd,color:#000,stroke:#5a90b2,stroke-width:2px
classDef ctrl fill:#a9daee,color:#000,stroke:#76b7d4,stroke-width:2px
```
### Data flow
| Stage | What happens |
|:---|:---|
| Basin integration | `pumpingStation` integrates basin volume from inflow / outflow rates |
| Demand computation | `pumpingStation` computes a demand setpoint and dispatches it to `machineGroupControl` |
| Per-pump operating point | `machineGroupControl` solves a per-pump operating point using each pump's characteristic curve plus measured upstream pressure |
| Pump dispatch | Each `rotatingMachine` runs its own FSM (`idle` &rarr; `warmingup` &rarr; `operational` &rarr; `coolingdown` &rarr; `emergencystop`, plus `accelerating` / `decelerating`) and predicts flow + power from speed + pressure |
### Variants
| Variant | How to wire |
|:---|:---|
| Single pump (no MGC) | `pumpingStation.configure()` accepts `machine` directly &mdash; skip the MGC and parent the `rotatingMachine` under `pumpingStation` |
| Cascaded stations | `pumpingStation.configure()` accepts `pumpingstation` as a child &mdash; downstream PS registers upstream PS to read its predicted outflow |
---
## 2. Reactor + diffuser + settler train
Biological treatment line. `reactor` runs ASM kinetics (CSTR or PFR engine, set via `config.reactor_type`). `diffuser` injects OTR. `settler` clarifies the effluent and drives a return pump.
```mermaid
flowchart TB
subgraph UN["Unit"]
reactor[reactor]
settler[settler]
end
subgraph EM["Equipment Module"]
diff[diffuser]
rp["rotatingMachine &mdash; return pump"]
end
subgraph CM["Control Module"]
mt["measurement &mdash; temperature"]
mdo["measurement &mdash; dissolved O2"]
mts["measurement &mdash; TSS"]
end
reactor ==stateChange==> settler
diff -. OTR data .-> reactor
settler -->|return pump| rp
mt -. data .-> reactor
mdo -. data .-> reactor
mts -. data .-> settler
mdo -. data .-> diff
class reactor,settler unit
class diff,rp equip
class mt,mdo,mts ctrl
classDef unit fill:#50a8d9,color:#000,stroke:#2c7ba8,stroke-width:2px
classDef equip fill:#86bbdd,color:#000,stroke:#5a90b2,stroke-width:2px
classDef ctrl fill:#a9daee,color:#000,stroke:#76b7d4,stroke-width:2px
```
### Two non-standard wirings
> [!IMPORTANT]
> `diffuser` &rarr; `reactor` is data-only. Diffuser fires `data.otr` on its emitter; reactor subscribes via `emitter.on('otr', ...)`. There is no `child.register` handshake between them. See `nodes/reactor/src/specificClass.js` `configure()`.
> [!IMPORTANT]
> `reactor` &rarr; `settler` is a `stateChange` subscription, not a parent / child edge. Settler's `_connectReactor` attaches `emitter.on('stateChange', ...)` to pull effluent composition from the upstream reactor. The `reactor` softwareType is registered as a child of settler even though the reactor is semantically upstream.
> [!CAUTION]
> DO setpoint feedback is not automatic. A measured-DO &rarr; diffuser-airflow loop must be closed externally (a function node) or via a `valveGroupControl` upstream of an airflow valve.
---
## 3. Valve group on a distribution manifold
Multi-valve flow distribution. `valveGroupControl` computes per-valve K_v shares to satisfy a target split while respecting upstream flow availability.
```mermaid
flowchart TB
subgraph PC["Process Cell"]
ps["pumpingStation &mdash; upstream flow source"]
end
subgraph UN["Unit"]
vgc[valveGroupControl]
end
subgraph EM["Equipment Module"]
vA[valve A]
vB[valve B]
vC[valve C]
end
ps -. flow source .-> vgc
vgc --> vA
vgc --> vB
vgc --> vC
class ps pc
class vgc unit
class vA,vB,vC equip
classDef pc fill:#0c99d9,color:#fff,stroke:#075a82,stroke-width:2px
classDef unit fill:#50a8d9,color:#000,stroke:#2c7ba8,stroke-width:2px
classDef equip fill:#86bbdd,color:#000,stroke:#5a90b2,stroke-width:2px
```
> [!IMPORTANT]
> VGC's child types are unusual. `valveGroupControl.configure()` registers five softwareTypes:
> - `valve` &mdash; actual S88 child relationship (VGC controls these)
> - `machine`, `machinegroup`, `pumpingstation`, `valvegroupcontrol` &mdash; flow sources. VGC reads upstream flow availability when computing splits. Semantic is "VGC knows about this flow producer", not "VGC controls it".
>
> See `nodes/valveGroupControl/src/specificClass.js` lines 13&ndash;49.
---
## 4. Composite sampling
Virtual sensor for downstream lab analysis. `monster` accumulates samples in a bucket based on integrated flow &mdash; a flow-proportional grab sample.
```mermaid
flowchart TB
subgraph UN["Unit"]
monster[monster]
end
subgraph CM["Control Module"]
mflow["measurement &mdash; flow (assetType MUST be 'flow')"]
mq["measurement &mdash; any quality (e.g. NH4, COD)"]
end
mflow -. data .-> monster
mq -. data .-> monster
class monster unit
class mflow,mq ctrl
classDef unit fill:#50a8d9,color:#000,stroke:#2c7ba8,stroke-width:2px
classDef ctrl fill:#a9daee,color:#000,stroke:#76b7d4,stroke-width:2px
```
> [!WARNING]
> Two gotchas:
> 1. `measurement.config.asset.type` must be exactly `"flow"`. A value like `"flow-electromagnetic"` is silently ignored by monster's child router.
> 2. `monster.config.constraints.flowmeter` exists in the schema but is not forwarded by `buildDomainConfig`. Toggling proportional-vs-time mode has no runtime effect. Tracked in `.claude/refactor/OPEN_QUESTIONS.md`.
---
## 5. Dashboard provisioning
`dashboardAPI` doesn't operate on data &mdash; it generates Grafana dashboards. Any node registers via `child.register`; dashboardAPI composes a dashboard JSON from softwareType plus measurements and POSTs to Grafana's HTTP API.
```mermaid
flowchart LR
subgraph EVOLV["EVOLV process nodes (any softwareType)"]
direction TB
ps[pumpingStation]
mgc[machineGroupControl]
rm[rotatingMachine]
end
subgraph UT["Utility"]
dash[dashboardAPI]
end
grafana[("Grafana HTTP API")]
ps -. child.register .-> dash
mgc -. child.register .-> dash
rm -. child.register .-> dash
dash ==>|POST /api/dashboards/db| grafana
class ps pc
class mgc unit
class rm equip
class dash util
class grafana ext
classDef pc fill:#0c99d9,color:#fff,stroke:#075a82,stroke-width:2px
classDef unit fill:#50a8d9,color:#000,stroke:#2c7ba8,stroke-width:2px
classDef equip fill:#86bbdd,color:#000,stroke:#5a90b2,stroke-width:2px
classDef util fill:#dddddd,color:#000,stroke:#a8a8a8,stroke-width:2px
classDef ext fill:#fff2cc,color:#000,stroke:#aa8400,stroke-width:2px
```
| Behaviour | Detail |
|:---|:---|
| What it accepts | Any softwareType on `child.register` |
| What it emits | One HTTP POST per registered child, payload from `nodes/dashboardAPI/src/config/templates/<softwareType>.json` |
| Auth | Bearer token in `config.grafanaConnector.bearerToken` (when set) |
| `meta` envelope | `{nodeId, softwareType, uid, title}` for correlating responses |
| Architecture variance | The one node in the platform that does not extend `BaseDomain`. Documented in `.claude/refactor/OPEN_QUESTIONS.md` |
---
## Worked example &mdash; small WWTP
All five patterns combined.
```mermaid
flowchart TB
subgraph PC["Process Cell"]
ps1["pumpingStation &mdash; inlet lift"]
ps2["pumpingStation &mdash; RAS pumping"]
end
subgraph UN["Unit"]
mgc1["MGC inlet"]
mgc2["MGC RAS"]
vgc["VGC effluent split"]
r1["reactor aerobic"]
s1["settler"]
mon["monster &mdash; composite sampler"]
end
subgraph EM["Equipment Module"]
rm1["pump A"]
rm2["pump B"]
rm3["RAS pump"]
d1["diffuser"]
v1["valve 1"]
v2["valve 2"]
end
ps1 --> mgc1
mgc1 --> rm1
mgc1 --> rm2
ps2 --> mgc2
mgc2 --> rm3
r1 ==stateChange==> s1
s1 -->|return pump| rm3
d1 -. OTR .-> r1
ps2 -. flow source .-> vgc
vgc --> v1
vgc --> v2
class ps1,ps2 pc
class mgc1,mgc2,vgc,r1,s1,mon unit
class rm1,rm2,rm3,d1,v1,v2 equip
classDef pc fill:#0c99d9,color:#fff,stroke:#075a82,stroke-width:2px
classDef unit fill:#50a8d9,color:#000,stroke:#2c7ba8,stroke-width:2px
classDef equip fill:#86bbdd,color:#000,stroke:#5a90b2,stroke-width:2px
```
| Sub-pattern | Recognise it |
|:---|:---|
| Pumping station with grouped pumps (&times;2) | `ps1 -> mgc1 -> {rm1, rm2}` and `ps2 -> mgc2 -> rm3` |
| Reactor + settler train | `r1 ==stateChange==> s1` plus `d1 -. OTR .-> r1` |
| Valve group on flow source | `ps2 -. flow source .-> vgc -> {v1, v2}` |
| Settler return pump | `s1 -> rm3` |
| Composite sampling | `mon` (would be wired to inflow + quality measurements not drawn) |
Every edge here is reproducible from one of the patterns above.
---
## Anti-patterns
> [!CAUTION]
> `pumpingStation` &rarr; `valveGroupControl` as a parent / child edge. PS does not register VGC. VGC registers PS as a flow source &mdash; the edge goes the other way semantically.
> [!CAUTION]
> `diffuser` &rarr; `reactor` as a child registration. Diffuser emits OTR via its emitter; reactor subscribes via `emitter.on`. No `child.register` handshake.
> [!CAUTION]
> `measurement` parented under `dashboardAPI`. dashboardAPI accepts any node for Grafana provisioning, but `measurement` should register with the process node it is monitoring, not with dashboardAPI.
---
## Related pages
| Page | Why |
|:---|:---|
| [Home](Home) | Top-level node map |
| [Architecture](Architecture) | Three-tier code + generalFunctions API |
| [Topic Conventions](Topic-Conventions) | What topics flow on each edge |
| [Telemetry](Telemetry) | Port 0 / 1 / 2 InfluxDB layout |