Files
diffuser/wiki/Home.md
znetsixe 8c03fe774c docs(wiki): full 5-page wiki matching the rotatingMachine reference format
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>
2026-05-19 09:42:13 +02:00

147 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# diffuser
![code-ref](https://img.shields.io/badge/code--ref-4973a8b-blue) ![s88](https://img.shields.io/badge/S88-Equipment_Module-86bbdd) ![status](https://img.shields.io/badge/status-pending--review-yellow)
A `diffuser` models a single aeration-diffuser zone &mdash; 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 &mdash; a domain-test pass remains TODO.
---
## At a glance
| Thing | Value |
|:---|:---|
| What it represents | One aeration-diffuser zone &mdash; 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 &mdash; 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 &mdash; 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 &mdash; Contracts](Reference-Contracts#topic-contract)):
1. `set.water-height = 5` &mdash; sets a 5 m column above the diffuser. Static head pressure is recomputed; `oPLoss` updates.
2. `set.elements = 100` &mdash; declares 100 elements in this zone. Per-element flow + specific flux now scale by this denominator.
3. `set.density = 15` &mdash; bottom-coverage percentage; selects which curve family is interpolated.
4. `set.header-pressure = 600` &mdash; gauge pressure above atmospheric (mbar). Air-density correction kicks in.
5. `set.alfa-factor = 0.7` &mdash; the α correction used in the kg O₂/h calculation.
6. `data.flow = 200` &mdash; 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&ndash;6 with the live status badge. Save as `wiki/_partial-gifs/diffuser/01-basic-demo.gif`, target &le; 1&nbsp;MB after `gifsicle -O3 --lossy=80`.
---
## The six things you'll send
| Topic | Aliases | Payload | What it does |
|:---|:---|:---|:---|
| `data.flow` | `air_flow` | `number` &mdash; Nm³/h | Pushes the measured air flow into the model. Clamped to &ge; 0. Triggers a full recompute. |
| `set.density` | `density` | `number` &mdash; 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` &mdash; m | Sets the water column above the diffuser. Recomputes static head + total ΔP. Clamped to &ge; 0. |
| `set.header-pressure` | `header_pressure` | `number` &mdash; mbar gauge | Header (supply) pressure above atmospheric. Feeds air-density correction. |
| `set.elements` | `elements` | `number` &mdash; integer > 0 | Active element count. Drives per-element flow + total membrane area used for specific flux. |
| `set.alfa-factor` | `alfaFactor` | `number` (typically 0&ndash;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 &mdash; 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` &mdash; flow per element. |
| `oFluxPerM2` | Specific flux through the membrane (Nm³/(h·m²)) &mdash; the canonical x-axis of every supplier curve. |
| `oOtr` | Interpolated oxygen transfer rate (g O₂ / Nm³). |
| `oPLoss` | Total head loss in mbar &mdash; 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&ndash;100). High OTR + low ΔP &rarr; 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` &mdash; 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`, &hellip;). The diffuser does **not** currently publish typed `MeasurementContainer` series &mdash; parents must consume Port 0 directly. Promoting `oOtr` / `oZoneOtr` to typed measurement series is tracked in [Reference &mdash; Limitations](Reference-Limitations#known-limitations).
---
## Need more?
| Page | What you'll find |
|:---|:---|
| [Reference &mdash; Contracts](Reference-Contracts) | Full topic contract, config schema, parent registration handshake |
| [Reference &mdash; Architecture](Reference-Architecture) | Code map, OTR / ΔP pipeline, idle behaviour, output ports |
| [Reference &mdash; Examples](Reference-Examples) | Shipped example flows + debug recipes |
| [Reference &mdash; Limitations](Reference-Limitations) | When not to use, known limitations, open questions |
[EVOLV master wiki](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Home) &middot; [Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) &middot; [Topic Conventions](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topic-Conventions)