Clone
3
Home
vps1_gitea_admin edited this page 2026-05-11 18:30:16 +00:00
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.

settler

Reflects code as of 7bf464b · 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

settler is an S88 Unit that models a secondary clarifier. It takes the upstream reactor's effluent stream, performs a 13-species TSS mass balance, and splits it into three Fluent envelopes: clarified effluent, surplus sludge, and return sludge. A downstream return pump (rotatingMachine child) draws the return-sludge flow.

2. Position in the platform

flowchart LR
    upstream[reactor<br/>upstream<br/>Unit]:::unit
    settler[settler<br/>Unit]:::unit
    downstream[reactor<br/>downstream<br/>Unit]:::unit
    return[rotatingMachine<br/>return pump<br/>Equipment]:::equip
    tss[measurement<br/>type=quantity (tss)<br/>position=atequipment]:::ctrl

    upstream -.stateChange.-> settler
    settler -->|Fluent inlet=0,1,2| downstream
    return -->|child.register downstream| settler
    settler -.F_sr.-> return
    tss -->|quantity (tss).measured.atequipment| settler
    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
TSS mass-balance split (3 streams) Effluent / surplus / return derived from F_in * Cs[12] / C_TS.
Particulate zeroing in effluent Species 712 set to 0 in effluent when F_s > 0.
Particulate concentration in sludge Species 712 scaled by F_in / F_s in surplus + return.
Return-pump flow draw F_sr = min(pump flow, F_s). Surplus = F_s F_sr.
F_s clamp to F_in Prevents negative effluent when X_TS_in > C_TS.
Manual influent override data.influent lets ops supply { F, C } directly.
Multiple reactor upstreams Only one upstreamReactor slot; last registration wins.
Stateful FSM Stateless transform — recomputes on every push.

4. Code map

flowchart TB
    subgraph nodeRED["nodeClass.js — adapter (BaseNodeAdapter)"]
        nc["buildDomainConfig()<br/>static DomainClass = Settler<br/>static commands"]
    end
    subgraph domain["specificClass.js — orchestrator (BaseDomain)"]
        sc["Settler.configure()<br/>ChildRouter rules<br/>getEffluent — TSS split<br/>_connectReactor (manual listener)"]
    end
    subgraph commands["src/commands/"]
        cmds["index.js + handlers.js<br/>data.influent + aliases"]
    end
    nc --> sc
    nc --> cmds
Module Owns Read first if you're changing…
specificClass.js All domain logic: getEffluent split, reactor + machine + measurement wiring, getOutput, getStatusBadge. Mass-balance math, child wiring, telemetry shape.
commands/ Single command (data.influent) + aliases + payload validation. Manual-influent topic, new aliases.

Settler is small enough (~140 LOC) that no concern-split was needed (per P6.6).

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.influent influent, setInfluent any Push the influent stream (payload: {F: flow m3/h, C: [concentrations mg/L]}).
child.register registerChild string Register a child node (typically a measurement) with this settler.

6. Child registration

flowchart LR
    subgraph kids["accepted children (softwareType)"]
        m["measurement"]:::ctrl
        r["reactor<br/>upstream"]:::unit
        mach["machine<br/>downstream"]:::equip
    end
    m -->|"&lt;type&gt;.measured.&lt;position&gt;"| h_m[_connectMeasurement]
    r -.stateChange.-> h_r[_connectReactor<br/>manual listener]
    mach -->|registered| h_mach[_connectMachine<br/>sets returnPump]
    h_r --> pull[upstreamReactor.getEffluent]
    pull --> emit[notifyOutputChanged]
    classDef ctrl fill:#a9daee,color:#000
    classDef unit fill:#50a8d9,color:#000
    classDef equip fill:#86bbdd,color:#000
softwareType filter wired to side-effect
measurement any _connectMeasurement Re-emits on settler's measurements; quantity (tss) updates C_TS.
reactor positionVsParent=upstream (warns otherwise) _connectReactor Stores as upstreamReactor; subscribes to its own emitter (NOT measurements.emitter) for 'stateChange'.
machine positionVsParent=downstream _connectMachine Stores as returnPump; sets machine.upstreamSource = settler.

6.1 Reactor ↔ settler wiring (the load-bearing bit)

The reactor pushes its stateChange event on reactor.emitter, not reactor.measurements.emitter. The standard router.onMeasurement path can't subscribe — so settler attaches the listener manually inside _connectReactor. On each fire, settler pulls the upstream effluent via reactor.getEffluent and copies it into this.F_in + this.Cs_in.

reactor.getEffluent historically returned either an array (3-stream) or a single envelope — the 2026-03-02 _connectReactor fix preserves both shapes:

const raw = this.upstreamReactor.getEffluent;
const effluent = Array.isArray(raw) ? raw[0] : raw;
this.F_in = effluent.payload.F;
this.Cs_in = effluent.payload.C;
this.notifyOutputChanged();

If you change the reactor's effluent shape, this is the line to update.

7. Lifecycle — what one stateChange does

sequenceDiagram
    participant reactor as upstream reactor
    participant settler as settler
    participant pump as return pump child
    participant downstream as downstream consumer
    participant out as Port-0 output

    reactor->>settler: emitter.emit('stateChange')
    settler->>reactor: pull getEffluent
    reactor-->>settler: { F, C[13] }
    settler->>settler: F_in = F, Cs_in = C
    settler->>pump: read measurements.flow.measured.atequipment
    pump-->>settler: returnFlow
    settler->>settler: getEffluent — split into 3 inlets
    settler->>out: [Fluent inlet=0, Fluent inlet=1, Fluent inlet=2]
    out->>downstream: 3 msgs on Port 0

The split runs lazily inside getEffluent: each call recomputes from current F_in, Cs_in, C_TS, and the pump's reported flow.measured.atequipment.

8. Data model — getOutput()

Port 0 carries the 3-envelope Fluent stream directly; Port 1 (this snapshot) is the scalar dashboard view.

Key Type Unit Sample
C_TS number 2500
F_eff number 0
F_in number 0
F_return number 0
F_surplus number 0

Concrete sample (typical operating point):

{
  "F_in": 1000,
  "C_TS": 2500,
  "F_eff": 850.0,
  "F_surplus": 50.0,
  "F_return": 100.0
}

F_eff + F_surplus + F_return = F_in always holds (modulo float). Particulates concentrate by F_in / F_s in the surplus + return streams.

9. Configuration — editor form ↔ config keys

flowchart TB
    subgraph editor["Node-RED editor form"]
        f1[Name]
        f2[Position vs parent]
        f3[Logging level]
    end
    subgraph config["Domain config slice"]
        c1[general.name]
        c2[functionality.positionVsParent]
        c3[general.logging.logLevel]
    end
    f1 --> c1
    f2 --> c2
    f3 --> c3
Form field Config key Default Range Where used
Name general.name Settler string display + Port-1 topic
ID general.id null nullable string child registration key
Software type functionality.softwareType settler string parent-side router filter
Position vs parent functionality.positionVsParent downstream enum: upstream / atEquipment / downstream parent-side routing
Logging level general.logging.logLevel info enum logger threshold

Settler has no operational config of its own — all behaviour is driven by the runtime state (F_in, Cs_in, C_TS). Tune behaviour by feeding it different reactor effluents or C_TS measurements.

10. State chart

Skipped — settler is stateless. Each stateChange (or data.influent, or quantity (tss) update) recomputes the 3 Fluent streams from scratch.

11. Examples

Tier File What it shows Status
Basic examples/basic.flow.json Inject data.influent, watch 3-stream split in repo
Integration examples/integration.flow.json reactor (upstream) + settler + return pump in repo
Edge examples/edge.flow.json F_s clamp + zero-influent fallback in repo

One screenshot per tier where helpful. PNG ≤ 200 KB under wiki/_partial-screenshots/settler/.

12. Debug recipes

Symptom First thing to check Where to look
F_eff negative or NaN C_TS zero or Cs_in[12] huge. F_s clamp should prevent — confirm clamp present. specificClass.js → getEffluent
Settler never updates after reactor changes Reactor child not on 'upstream' position, or listener attached to wrong emitter. _connectReactor — listens on reactor.emitter, NOT measurements.emitter.
Return-sludge flow = 0 returnPump.measurements.type('flow').variant('measured').position('atEquipment') empty. Wire a flow measurement on the pump. _connectMachine, pump measurement chain.
3 Fluent envelopes not arriving downstream payload.inlet selector on the downstream reactor mismatches (0=eff, 1=surplus, 2=return). downstream reactor's data.fluent handler.
quantity (tss) updates don't change C_TS Measurement child's asset.type not quantity (tss) exactly. _updateMeasurement switch.

13. When you would NOT use this node

  • Use settler for secondary clarification downstream of a biological reactor. For primary sedimentation (raw sewage), the species-7-12 zeroing is wrong — model that as a separate process.
  • Don't use settler as a generic mass-balance node — the 13-species ASM3 vector is hard-coded.
  • Skip settler when the downstream reactor doesn't need a 3-stream split (e.g. single-tank SBR). A direct reactor → reactor wire is lighter.

14. Known limitations / current issues

# Issue Tracked in
1 Only one upstreamReactor slot — multi-reactor settlers not supported (last registration wins). _connectReactor
2 TSS mass balance uses index 12 (X_TS) hard-coded — coupled tightly to ASM3 species ordering. getEffluent, _updateMeasurement
3 Settler depends on mathjs (~14 MB install) but only uses it transitively via reactor; no direct mathjs call in settler code. package.json
4 No flow-balance check at runtime — if particulate concentration drives F_s above F_in, the clamp masks an upstream bug rather than warning. getEffluent