Files
reactor/wiki/Reference-Limitations.md
znetsixe cb49bb8b4d 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:11 +02:00

9.3 KiB

Reference — Limitations

code-ref

Note

What reactor does not do, current rough edges, and open questions. Open items live in .agents/improvements/IMPROVEMENTS_BACKLOG.md and .claude/refactor/OPEN_QUESTIONS.md in the superproject.

Pending full node review (2026-05). Content reflects CONTRACT.md, the current source, and a partial walkthrough of src/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 &asymp; 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_A is &ge; ~50 mg/L before expecting nitrification. Tracked in CONTRACT.md ## 14 row 2 and in EVOLV memory MEMORY.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:

  1. Tree-shake mathjs to only the operations actually used (add, multiply, diag, resize, sum, divide).
  2. 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.json line ~144): unit: "h".
  • baseEngine.js line ~40 converts by &divide; 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.


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