Files
diffuser/wiki/Home.md
znetsixe 6372bdc926 P11.6 diffuser wiki: banner hash + section 9 form completeness + section 14 category/colour notes
- Bump banner hash from 15cfb228cc02ee (latest HEAD).
- Section 9: add missing `number` form field; mark localAtmPressure /
  waterDensity as hidden defaults with no form row; update mermaid diagram.
- Section 14: add issue #5 (category 'wbd typical' vs 'EVOLV') and issue
  #6 (S88 colour already set — §16 note now stale).
- npm run wiki:all run prior to edit; AUTOGEN markers untouched.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:04:57 +02:00

11 KiB
Raw Blame History

diffuser

Reflects code as of 8cc02ee · regenerated 2026-05-11 via npm run wiki:all If this banner is stale, the page may be out of date. Treat as informative, not authoritative.

1. What this node is

diffuser models an aeration-diffuser zone. Given header pressure, water-column height, alpha factor, element count and airflow, it interpolates a supplier OTR curve, normalises airflow to Nm³/h, and emits oxygen-transfer rate plus a reactor-zone OTR for the downstream parent. Used as a leaf Equipment Module under a reactor.

2. Position in the platform

flowchart LR
    src[blower / MGC / dashboard]:::unit -->|data.flow| diff[diffuser<br/>Equipment]:::equip
    setters[dashboard setters]:::ctrl -->|set.density / set.water-height /<br/>set.elements / set.alfa-factor /<br/>set.header-pressure| diff
    diff -->|child.register| reactor[reactor<br/>Unit]:::unit
    classDef unit fill:#50a8d9,color:#000
    classDef equip fill:#86bbdd,color:#000
    classDef ctrl fill:#a9daee,color:#000

S88 colours: Unit #50a8d9, Equipment #86bbdd, Control Module #a9daee. Source of truth: .claude/rules/node-red-flow-layout.md.

3. Capability matrix

Capability Status Notes
Supplier OTR curve interpolation Density-keyed; falls back to single key when only one available.
Air-density correction (header + atm) _calcAirDensityMbar per ideal-gas mix.
Per-element flow tracking o_flowElement = nFlow / nElements.
Static head loss from water column _heightToPressureMbar.
Warning / alarm bands on flow-per-element Hysteresis 2 % (warn) / 10 % (alarm).
Reactor-zone OTR for parent oZoneOtr derived from diffuser.zoneVolume.
Idle handling at flow ≤ 0 Resets all derived outputs to zero.
Typed MeasurementContainer emission All output flows via getOutput(); no typed series yet.

4. Code map

flowchart TB
    subgraph nodeRED["nodeClass.js — adapter (BaseNodeAdapter)"]
        nc["buildDomainConfig()<br/>static DomainClass, commands"]
    end
    subgraph domain["specificClass.js — orchestrator (BaseDomain)"]
        sc["Diffuser.configure()<br/>loads supplier specs<br/>setters → _recalculate()"]
    end
    subgraph concerns["src/ concern modules"]
        cmds["commands/<br/>topic registry + handlers"]
    end
    nc --> sc
    nc --> cmds
Module Owns Read first if you're changing…
commands/ Input-topic registry + per-topic handlers Topic naming, payload validation.
specificClass.js (single file) Setters, OTR/ΔP curve interpolation, alarms, output composition Anything domain-side.

The diffuser was a small node so the P6.4 refactor did not split it into per-concern directories. Future work may extract curves/ and alarms/ if the file grows past ~250 lines.

5. Topic contract

Auto-generated from src/commands/index.js. Do NOT hand-edit between the markers. Re-run npm run wiki:contract.

Canonical topic Aliases Payload Unit Effect
data.flow air_flow number volumeFlowRate (default m3/h) Push the measured air flow into the diffuser model.
set.density density number Update the air density used in OTR / SOTR calculations.
set.water-height height_water number Update the water column height above the diffusers (m).
set.header-pressure header_pressure number Update the header (supply) pressure feeding the diffusers (mbar).
set.elements elements number Update the count of active diffuser elements.
set.alfa-factor alfaFactor number Update the alfa factor used in oxygen-transfer correction.

6. Child registration

diffuser is a leaf node — it accepts no children. It registers itself with its upstream parent (typically a reactor) at startup via the standard Port-2 handshake.

flowchart LR
    diff[diffuser]:::equip -->|child.register payload=node.id<br/>positionVsParent=atEquipment| reactor[reactor / parent]:::unit
    classDef equip fill:#86bbdd,color:#000
    classDef unit fill:#50a8d9,color:#000
Direction Counterparty Side-effect
outbound at startup upstream reactor sends child.register on Port 2 with positionVsParent default atEquipment
inbound none accepted

7. Lifecycle — what one event does

sequenceDiagram
    participant src as upstream source
    participant diff as diffuser
    participant curve as supplier specs
    participant out as Port-0

    src->>diff: data.flow (Nm³/h)
    diff->>diff: setFlow → _recalculate
    alt flow ≤ 0
        diff->>diff: idle = true, reset derived outputs
    else flow > 0
        diff->>diff: air-density correction (atm + header)
        diff->>curve: interpolate OTR by density + flow/element
        diff->>curve: interpolate ΔP curve by flow/element
        diff->>diff: kg O₂/h, combined efficiency, slope
        diff->>diff: _checkLimits (warn / alarm bands)
    end
    diff->>diff: notifyOutputChanged()
    diff->>out: msg{topic, payload (delta-compressed)}

8. Data model — getOutput()

What lands on Port 0. Composed in Diffuser.getOutput(), then delta-compressed by outputUtils.formatMsg.

Key Type Unit Sample
alarm array […]
efficiency number 0
iFlow number 0
iMWater number 0
iPressure number 0
idle boolean true
nFlow number 0
oFlowElement number 0
oKgo2H number 0
oOtr number 0
oPLoss number 0
oZoneOtr number 0
slope number 0
warning array […]

oZoneOtr is kg O₂ / m³ / day; it is 0 when diffuser.zoneVolume is unset.

9. Configuration — editor form ↔ config keys

flowchart TB
    subgraph editor["Node-RED editor form"]
        f0[Zone number]
        f1[Element count]
        f2[Diffuser density]
        f3[Water height]
        f4[Header pressure]
        f5[Alpha factor]
        f6[Zone volume]
    end
    subgraph config["Domain config slice"]
        c0[diffuser.number]
        c1[diffuser.elements]
        c2[diffuser.density]
        c3[diffuser.waterHeight]
        c4[diffuser.headerPressure]
        c5[diffuser.alfaFactor]
        c6[diffuser.zoneVolume]
        c7["diffuser.localAtmPressure (hidden)"]
        c8["diffuser.waterDensity (hidden)"]
    end
    f0 --> c0
    f1 --> c1
    f2 --> c2
    f3 --> c3
    f4 --> c4
    f5 --> c5
    f6 --> c6
Form field Config key Default Range Where used
Zone number diffuser.number 1 int ≥ 1 node label (name_N)
Element count diffuser.elements 1 int ≥ 1 per-element flow
Diffuser density diffuser.density 2.4 > 0 OTR curve key
Water height diffuser.waterHeight 0 ≥ 0 (m) static head + kg O₂/h
Header pressure diffuser.headerPressure 0 ≥ 0 (mbar) air density correction
Alpha factor diffuser.alfaFactor 0.7 01 oxygen-transfer correction
(hidden default) diffuser.localAtmPressure 1013.25 > 0 (mbar) density baseline
(hidden default) diffuser.waterDensity 997 > 0 (kg/m³) static head
Zone volume diffuser.zoneVolume 0 ≥ 0 (m³) oZoneOtr

10. State chart

Skipped — diffuser is stateless. Every input setter recomputes the full output snapshot; there are no transitions to track. The idle flag is a derived predicate (i_flow ≤ 0), not a state.

11. Examples

Tier File What it shows Mandatory?
Basic examples/01-Basic.flow.json Inject data.flow + dashboard, no parent
Integration examples/02-Integration.flow.json diffuser registered under a reactor zone
Dashboard examples/03-Dashboard.flow.json Live FlowFuse charts (OTR, kg O₂/h, efficiency)

Screenshots under wiki/_partial-screenshots/diffuser/ when produced. Docker compose snippet under examples/README.md.

12. Debug recipes

Symptom First thing to check Where to look
oOtr stuck at zero i_flow is zero or negative → idle = true. _recalculate early-return
Warning / alarm always firing Flow-per-element outside curve minX / maxX ± hysteresis. _checkLimits
oZoneOtr is zero despite valid OTR diffuser.zoneVolume is unset or non-positive. getReactorOtr
nFlow differs from iFlow at non-zero flow Air-density correction — header pressure or atm differ from reference. _calcAirDensityMbar
efficiency flat at 0 OTR or ΔP curve span is zero in the operating band. _combineEff

Never ship enableLog: 'debug' in a demo — fills the container log within seconds and obscures real errors. Use only for live debugging.

13. When you would NOT use this node

  • Use diffuser for a fine-bubble aeration zone with a supplier OTR curve. For coarse-bubble or jet aeration, model OTR externally.
  • Don't use diffuser when the upstream blower already publishes oxygen-transfer telemetry — diffuser duplicates the calculation.
  • Skip diffuser if you only need flow-per-element warning bands without OTR — a measurement node with bands is lighter.

14. Known limitations / current issues

# Issue Tracked in
1 Port-count change (Phase 6): pre-refactor the diffuser exposed 4 outputs (process, dbase, reactor control with topic: 'OTR', parent registration). The reactor-control message merged into Port 0 as oZoneOtr; consumers reading the dedicated control port must migrate to payload.oZoneOtr. No alias is provided — the shape differs (single value vs full process payload). CONTRACT.md ## Port-count change
2 Supplier specs are hard-coded inside _loadSpecs() (GVA / ELASTOX-R). A configurable supplier registry is pending. specificClass.js _loadSpecs
3 No typed MeasurementContainer emission — oOtr / oZoneOtr cannot be subscribed via the generic ChildRouter handshake. Parents must read Port 0 messages. CONTRACT.md ## Events emitted
4 Warning / alarm thresholds are fixed (2 % / 10 % hysteresis); not yet config-driven. configure() literals
5 Node category mismatch: diffuser.html registers under category: 'wbd typical' instead of 'EVOLV'. All other platform nodes target 'EVOLV' for consistent palette grouping in the editor. diffuser.html line 3
6 S88 colour — resolved: diffuser.html already declares color: '#86bbdd' (Equipment Module). The WIKI_TEMPLATE §16 note listing diffuser as having "no colour set" is stale and can be removed in the next template refresh. diffuser.html line 4