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>
8.5 KiB
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.jsandgeneralFunctions/src/configs/diffuser.jsononly. Test scaffolding undertest/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
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.
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. Theexamples/README.mdis 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):
set.water-height = 5— sets a 5 m column above the diffuser. Static head pressure is recomputed;oPLossupdates.set.elements = 100— declares 100 elements in this zone. Per-element flow + specific flux now scale by this denominator.set.density = 15— bottom-coverage percentage; selects which curve family is interpolated.set.header-pressure = 600— gauge pressure above atmospheric (mbar). Air-density correction kicks in.set.alfa-factor = 0.7— the α correction used in the kg O₂/h calculation.data.flow = 200— push 200 Nm³/h into the model. Watch Port 0:oOtr,oKgo2H,oFluxPerM2,efficiency,slopeall populate. Settingdata.flow = 0flipsidleback 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 aftergifsicle -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):
{
"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-rby 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.
Need more?
| Page | What you'll find |
|---|---|
| Reference — Contracts | Full topic contract, config schema, parent registration handshake |
| Reference — Architecture | Code map, OTR / ΔP pipeline, idle behaviour, output ports |
| Reference — Examples | Shipped example flows + debug recipes |
| Reference — Limitations | When not to use, known limitations, open questions |