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>
9.3 KiB
Reference — Limitations
Note
What
reactordoes not do, current rough edges, and open questions. Open items live in.agents/improvements/IMPROVEMENTS_BACKLOG.mdand.claude/refactor/OPEN_QUESTIONS.mdin the superproject.Pending full node review (2026-05). Content reflects
CONTRACT.md, the current source, and a partial walkthrough ofsrc/kinetics/— not a full audit.
When you would not use this node
| Scenario | Use instead |
|---|---|
| A passive equalisation tank (no biological reactions, just buffering) | A simple Node-RED buffer / function node — the kinetics engines assume reactions are happening. |
| A residence-time delay (plug-flow without ASM) | A delay node or custom buffer; the ASM3 13-species machinery + mathjs cold-start are overkill. |
| Aerobic-only contactors where you only need oxygen mass-transfer | An OTR-only model is lighter; ASM3 brings 13 species you'll ignore. |
| A clarifier / settler | settler — reactor has no settling, no sludge thickening, no underflow / overflow split. |
| A pump / blower / valve | rotatingMachine / valve — reactor is a process-tank model, not an actuator. |
| Anaerobic digestion | ASM3 is calibrated for activated sludge under aerobic / anoxic conditions. ADM1 (a separate model family) is the right tool for digesters. |
Known limitations
X_A initial default footgun
The HTML editor form's default for initial autotroph biomass is 0.001 mg/L (effectively zero). The JSON schema default is 200 mg/L. The HTML wins at deploy time. With X_A ≈ 0 nitrification never starts — S_NH stays at the influent value forever, no S_NO is produced.
Warning
Always open every deployed reactor node and confirm
Initial X_Ais≥ ~50mg/L before expecting nitrification. Tracked inCONTRACT.md## 14row 2 and in EVOLV memoryMEMORY.md"Key Integration Gotchas".
mathjs cold-start ~13 s
baseEngine.js requires mathjs. The first require('mathjs') in a Node.js process takes ~13 s wall-clock to initialise. This delays first data.clock advance after a fresh deploy, and can time out the wiki:datamodel autogen wrapper (60 s budget). Two remedies tracked:
- Tree-shake
mathjsto only the operations actually used (add,multiply,diag,resize,sum,divide). - Lazy-initialise / cache the instance.
Tracked in .claude/refactor/OPEN_QUESTIONS.md — "mathjs slow load".
timeStep unit mismatch
- HTML form label:
Time step [s]. - Schema (
generalFunctions/src/configs/reactor.jsonline ~144):unit: "h". baseEngine.jsline ~40 converts by÷ 86400(seconds → days) before using it.
The conversion suggests the true unit is seconds. Schema is wrong. Until reconciled, treat the form field as seconds. Tracked in CONTRACT.md ## 14 row 7 and in OPEN_QUESTIONS.md (Phase 5/6 cleanup list).
reactor_type enum casing
The JSON schema validator lowercases reactor_type (so 'PFR' → 'pfr'). Reactor._buildEngine calls .toUpperCase() to compensate. If that guard is ever removed prematurely (before the platform-wide canonical casing rule is decided in Phase 7), PFR configs silently fall back to the default branch — which constructs a CSTR. Tracked in OPEN_QUESTIONS.md.
getEffluent shape historically varied
Earlier versions of BaseReactorEngine.getEffluent returned either an envelope object or an array of envelopes (multi-outlet PFR). The current code emits a single {topic, payload, timestamp} envelope, but the downstream settler._connectReactor tolerates both shapes. Don't break this contract without coordinating with the settler node. EVOLV memory records a 2026-03-02 fix in settler for the array-vs-envelope assumption.
No FSM — no mode / setpoint / startup-shutdown sequencing
reactor has no startup, no shutdown, no e-stop, no mode, no setpoint. It runs continuous-state ODE / PDE integration unconditionally as long as data.clock advances (or the tick loop fires). A downstream consumer that expects a state field on Port 0 will get nothing of the sort. This is by design — biological reactors don't have meaningful FSM states — but it's a divergence from rotatingMachine / pumpingStation patterns that callers should know about.
No mode / source / action allow-list gating
All incoming topics are accepted as long as the payload validates. There is no parent / GUI / fysical source-gating, no auto / virtualControl / fysicalControl mode-gating. If you want to lock down a deployed reactor (e.g. ignore manual data.fluent injections while a real flow sensor is wired), you must do it externally.
additional_nodes/ legacy companions not refactored
additional_nodes/recirculation-pump.js and additional_nodes/settling-basin.js are sibling Node-RED nodes shipped from the reactor repo (because they share the same package context). They are not yet refactored to BaseDomain. Tracked as P6.5 follow-up.
reaction_modules/ legacy directory
src/reaction_modules/asm3_class.js is consumed by the current engines. src/reaction_modules/asm3_class Koch.js is a legacy plug-in variant not consumed by anything in the current codebase. Removal pending. Tracked as P6.5 follow-up.
Units don't follow EVOLV canonical-unit rule
The platform-wide MeasurementContainer canonical units are Pa / m³/s / W / K. reactor uses m³/d for flow, °C for temperature, mg/L (or mmol/L for alkalinity) for concentrations. No conversion at the system boundary. Calling code that expects canonical units must convert.
data.dispersion is silently a no-op on CSTR
specificClass.set setDispersion checks if (this.engine instanceof Reactor_PFR) before forwarding. On a CSTR the setter just drops the payload — no warn, no error. If you deploy a flow that injects data.dispersion and switch the reactor type to CSTR, the injection is silently ignored.
Single output-shape convention not documented per-key
The getOutput() implementation omits non-finite species values (Number.isFinite guard) rather than emitting them as null. Per .claude/rules/output-coverage.md, every node should pick one convention and document it. reactor's is "absent" — downstream consumers should treat a missing species key as "not produced this tick", never as zero.
output-coverage manifest not yet present
test/_output-manifest.md (required by the platform-wide output-coverage rule, 2026-05-14) is not yet checked in for reactor. The Port-0 envelope shape, Port-1 InfluxDB fields, and GridProfile payload all need enumeration with populated + degraded test coverage. Tracked in .agents/improvements/IMPROVEMENTS_BACKLOG.md.
Open questions (tracked)
| Question | Where it lives |
|---|---|
mathjs slow load — tree-shake or lazy-init |
.claude/refactor/OPEN_QUESTIONS.md — "mathjs slow load" |
reactor_type enum casing — platform-wide canonical |
.claude/refactor/OPEN_QUESTIONS.md — "reactor schema enum lowercases reactor_type" |
timeStep unit reconciliation (HTML s vs schema h vs engine d) |
OPEN_QUESTIONS.md Phase 5/6 cleanup list |
Removal of reaction_modules/asm3_class Koch.js and additional_nodes/* |
P6.5 follow-up |
| Output-coverage manifest + populated / degraded tests | .agents/improvements/IMPROVEMENTS_BACKLOG.md |
| Should reactor adopt a canonical-unit boundary like the rest of EVOLV? | Internal — not yet ticketed |
| Multi-outlet PFR (separate effluent streams per spatial point) | Internal — long-term |
Migration notes
From the pre-BaseDomain reactor
The current specificClass extends BaseDomain and uses ChildRouter to dispatch measurement / reactor registrations. Older flows that pre-date this refactor may have hand-wired child handlers; redeploying after npm install should pick up the new path automatically — no schema migration is required.
From legacy topic names
The five data.* topics replace the pre-canonical PascalCase aliases (Fluent, OTR, Temperature, Dispersion, clock). The aliases are still accepted and emit a one-time deprecation warning on first use, but will be removed in Phase 7. Migrate flows by renaming the topic string on each inject.
From hand-counted internal steps
Before the speedUpFactor field, simulation acceleration required adjusting timeStep. The current path is to leave timeStep at its physically-meaningful value (~1 s) and crank speedUpFactor to advance more process-time per wall-clock second. Old flows with abnormally large timeStep should be re-saved with the new field.
Related pages
| Page | Why |
|---|---|
| Home | Intuitive overview |
| Reference — Contracts | Topic + config + child filters |
| Reference — Architecture | Code map, kinetics engines, integration sequence |
| Reference — Examples | Shipped flows + debug recipes |
| settler — Limitations | The downstream Unit's quirks (incl. the historical getEffluent shape tolerance) |
| diffuser wiki | The Equipment node that pushes data.otr — not a registered child |