Replaces the prior stub/partial wiki with a Home + Reference-{Architecture,
Contracts,Examples,Limitations} + _Sidebar structure. Topic-contract and
data-model sections wrapped in AUTOGEN markers for the future wiki-gen tool.
Source-vs-spec contradictions surfaced and flagged inline (not silently
fixed). Pending-review notes mark sections that need a full node review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
147 lines
8.5 KiB
Markdown
147 lines
8.5 KiB
Markdown
# diffuser
|
||
|
||
  
|
||
|
||
A `diffuser` models a single aeration-diffuser zone — the gas-side dynamics of a fine-bubble grid sitting under a reactor's water column. Given a header pressure, water height, alpha factor, element count and incoming air flow, it normalises the air to Nm³/h, interpolates a supplier OTR curve (oxygen transfer rate vs specific flux) plus a ΔP curve, and emits oxygen-transfer power (kg O₂/h), efficiency, per-element flow, and a reactor-zone OTR. Used as a leaf Equipment Module under a `reactor` (or any aeration train).
|
||
|
||
> [!NOTE]
|
||
> Pending full node review (2026-05). Content reflects `CONTRACT.md`, `src/commands/index.js`, `src/specificClass.js` and `generalFunctions/src/configs/diffuser.json` only. Test scaffolding under `test/basic/`, `test/integration/`, `test/edge/` is still placeholder-level — a domain-test pass remains TODO.
|
||
|
||
---
|
||
|
||
## At a glance
|
||
|
||
| Thing | Value |
|
||
|:---|:---|
|
||
| What it represents | One aeration-diffuser zone — the gas-side OTR + ΔP model of a fine-bubble grid |
|
||
| S88 level | Equipment Module |
|
||
| Use it when | You have a supplier OTR / ΔP curve and need oxygen-transfer + head-loss telemetry from a measured air flow |
|
||
| Don't use it for | Coarse-bubble / jet aeration without a fine-bubble curve, or when the blower already publishes OTR (you'd duplicate the calc) |
|
||
| Children it accepts | None — diffuser is a leaf |
|
||
| Parents it talks to | `reactor` (typical), or any node that consumes `child.register` from Port 2 |
|
||
|
||
---
|
||
|
||
## How it fits
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
blower[blower / MGC /<br/>dashboard slider]:::unit -->|data.flow<br/>Nm³/h| diff[diffuser<br/>Equipment]:::equip
|
||
setters[dashboard setters /<br/>setup tab]:::ctrl -->|set.density<br/>set.water-height<br/>set.header-pressure<br/>set.elements<br/>set.alfa-factor| diff
|
||
diff -->|child.register<br/>positionVsParent=atEquipment| reactor[reactor / parent<br/>Unit]:::unit
|
||
diff -.->|Port 0: oOtr,<br/>oKgo2H, oZoneOtr,<br/>efficiency, slope| reactor
|
||
classDef unit fill:#50a8d9,color:#000
|
||
classDef equip fill:#86bbdd,color:#000
|
||
classDef ctrl fill:#a9daee,color:#000
|
||
```
|
||
|
||
S88 colours are anchored in `.claude/rules/node-red-flow-layout.md`. Per the layout rule, diffuser lives on lane **L3** of the Process Plant tab, wrapped in a `#86bbdd` Node-RED group.
|
||
|
||
---
|
||
|
||
## Try it — 3-minute demo
|
||
|
||
Import the basic example flow, deploy, and drive a single diffuser zone through the OTR curve.
|
||
|
||
```bash
|
||
curl -X POST -H 'Content-Type: application/json' \
|
||
--data @nodes/diffuser/examples/basic.flow.json \
|
||
http://localhost:1880/flow
|
||
```
|
||
|
||
> [!NOTE]
|
||
> Example flows currently shipped: `examples/basic.flow.json`, `examples/integration.flow.json`, `examples/edge.flow.json`. The `examples/README.md` is a one-line placeholder ("Placeholder structure"); a proper per-tier README following the rotatingMachine template is TODO.
|
||
|
||
What to send after deploy (each inject maps one-to-one to a topic in [Reference — Contracts](Reference-Contracts#topic-contract)):
|
||
|
||
1. `set.water-height = 5` — sets a 5 m column above the diffuser. Static head pressure is recomputed; `oPLoss` updates.
|
||
2. `set.elements = 100` — declares 100 elements in this zone. Per-element flow + specific flux now scale by this denominator.
|
||
3. `set.density = 15` — bottom-coverage percentage; selects which curve family is interpolated.
|
||
4. `set.header-pressure = 600` — gauge pressure above atmospheric (mbar). Air-density correction kicks in.
|
||
5. `set.alfa-factor = 0.7` — the α correction used in the kg O₂/h calculation.
|
||
6. `data.flow = 200` — push 200 Nm³/h into the model. Watch Port 0: `oOtr`, `oKgo2H`, `oFluxPerM2`, `efficiency`, `slope` all populate. Setting `data.flow = 0` flips `idle` back to true and resets the derived outputs.
|
||
|
||
> [!IMPORTANT]
|
||
> **GIF needed.** Demo recording of steps 1–6 with the live status badge. Save as `wiki/_partial-gifs/diffuser/01-basic-demo.gif`, target ≤ 1 MB after `gifsicle -O3 --lossy=80`.
|
||
|
||
---
|
||
|
||
## The six things you'll send
|
||
|
||
| Topic | Aliases | Payload | What it does |
|
||
|:---|:---|:---|:---|
|
||
| `data.flow` | `air_flow` | `number` — Nm³/h | Pushes the measured air flow into the model. Clamped to ≥ 0. Triggers a full recompute. |
|
||
| `set.density` | `density` | `number` — bottom-coverage % | Selects the curve family used for OTR interpolation. Curves multi-keyed by coverage get linearly interpolated; single-key curves are clamped. |
|
||
| `set.water-height` | `height_water` | `number` — m | Sets the water column above the diffuser. Recomputes static head + total ΔP. Clamped to ≥ 0. |
|
||
| `set.header-pressure` | `header_pressure` | `number` — mbar gauge | Header (supply) pressure above atmospheric. Feeds air-density correction. |
|
||
| `set.elements` | `elements` | `number` — integer > 0 | Active element count. Drives per-element flow + total membrane area used for specific flux. |
|
||
| `set.alfa-factor` | `alfaFactor` | `number` (typically 0–1) | Alpha correction used in the kg O₂/h calculation. |
|
||
|
||
Aliases log a one-time deprecation warning the first time they fire. There are no query topics today (the entire state is on Port 0).
|
||
|
||
---
|
||
|
||
## What you'll see come out
|
||
|
||
Sample Port 0 message (delta-compressed, while operational with flow = 200 Nm³/h, 100 elements, water height 5 m):
|
||
|
||
```json
|
||
{
|
||
"topic": "diffuser_1",
|
||
"payload": {
|
||
"iFlow": 200,
|
||
"iPressure": 600,
|
||
"iMWater": 5,
|
||
"nFlow": 218.42,
|
||
"oFlowElement": 2.18,
|
||
"oFluxPerM2": 12.13,
|
||
"oOtr": 16.4,
|
||
"oPLoss": 540.7,
|
||
"oKgo2H": 12.51,
|
||
"oZoneOtr": 6.0,
|
||
"efficiency": 73.2,
|
||
"slope": 0.044,
|
||
"idle": false,
|
||
"warning": [],
|
||
"alarm": []
|
||
}
|
||
}
|
||
```
|
||
|
||
> [!NOTE]
|
||
> The numbers above are illustrative — the exact values depend on the loaded supplier curve (`gva-elastox-r` by default). Run the basic example flow to see real values for your asset.
|
||
|
||
| Field | Meaning |
|
||
|:---|:---|
|
||
| `iFlow` / `iPressure` / `iMWater` | Echo of the current setter inputs (Nm³/h, mbar, m). |
|
||
| `nFlow` | Air flow normalised to standard conditions (Nm³/h, T=20 °C, p=1.01325 bar, RH=0). |
|
||
| `oFlowElement` | `nFlow / elements` — flow per element. |
|
||
| `oFluxPerM2` | Specific flux through the membrane (Nm³/(h·m²)) — the canonical x-axis of every supplier curve. |
|
||
| `oOtr` | Interpolated oxygen transfer rate (g O₂ / Nm³). |
|
||
| `oPLoss` | Total head loss in mbar — static head from the water column plus diffuser ΔP. |
|
||
| `oKgo2H` | kg O₂ per hour at the current operating point. Uses α-factor and water height. |
|
||
| `oZoneOtr` | Reactor-zone OTR in kg O₂ / m³ / day. Computed from `oKgo2H` against `diffuser.zoneVolume`; zero when zone volume is unset. |
|
||
| `efficiency` | Combined OTR / ΔP efficiency (0–100). High OTR + low ΔP → high score. |
|
||
| `slope` | Local OTR-vs-flux slope (g O₂/Nm³ per Nm³/(h·m²)). Useful as a "we're near the curve knee" indicator. |
|
||
| `idle` | `true` when `iFlow ≤ 0` — derived predicates only, no FSM. |
|
||
| `warning` / `alarm` | String arrays describing flow-per-element band excursions (`±2 %` warn / `±10 %` alarm hysteresis, hardcoded). |
|
||
|
||
---
|
||
|
||
## Not the same shape as rotatingMachine / measurement
|
||
|
||
Where rotatingMachine emits per-measurement keys (`<type>.<variant>.<position>.<childId>`), diffuser uses a flat camelCase schema (`oOtr`, `oKgo2H`, `oFlowElement`, …). The diffuser does **not** currently publish typed `MeasurementContainer` series — parents must consume Port 0 directly. Promoting `oOtr` / `oZoneOtr` to typed measurement series is tracked in [Reference — Limitations](Reference-Limitations#known-limitations).
|
||
|
||
---
|
||
|
||
## Need more?
|
||
|
||
| Page | What you'll find |
|
||
|:---|:---|
|
||
| [Reference — Contracts](Reference-Contracts) | Full topic contract, config schema, parent registration handshake |
|
||
| [Reference — Architecture](Reference-Architecture) | Code map, OTR / ΔP pipeline, idle behaviour, output ports |
|
||
| [Reference — Examples](Reference-Examples) | Shipped example flows + debug recipes |
|
||
| [Reference — Limitations](Reference-Limitations) | When not to use, known limitations, open questions |
|
||
|
||
[EVOLV master wiki](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Home) · [Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) · [Topic Conventions](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topic-Conventions)
|