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

133 lines
9.3 KiB
Markdown

# Reference &mdash; Limitations
![code-ref](https://img.shields.io/badge/code--ref-0e34403-blue)
> [!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/` &mdash; 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 &mdash; 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` &mdash; reactor has no settling, no sludge thickening, no underflow / overflow split. |
| A pump / blower / valve | `rotatingMachine` / `valve` &mdash; 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 &mdash; `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` &mdash; "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 &rarr; 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'` &rarr; `'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 &mdash; 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 &mdash; 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 &mdash; biological reactors don't have meaningful FSM states &mdash; 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, &deg;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 &mdash; 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" &mdash; 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 &mdash; tree-shake or lazy-init | `.claude/refactor/OPEN_QUESTIONS.md` &mdash; "mathjs slow load" |
| `reactor_type` enum casing &mdash; platform-wide canonical | `.claude/refactor/OPEN_QUESTIONS.md` &mdash; "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 &mdash; not yet ticketed |
| Multi-outlet PFR (separate effluent streams per spatial point) | Internal &mdash; 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 &mdash; 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](Home) | Intuitive overview |
| [Reference &mdash; Contracts](Reference-Contracts) | Topic + config + child filters |
| [Reference &mdash; Architecture](Reference-Architecture) | Code map, kinetics engines, integration sequence |
| [Reference &mdash; Examples](Reference-Examples) | Shipped flows + debug recipes |
| [settler &mdash; Limitations](https://gitea.wbd-rd.nl/RnD/settler/wiki/Reference-Limitations) | The downstream Unit's quirks (incl. the historical `getEffluent` shape tolerance) |
| [diffuser wiki](https://gitea.wbd-rd.nl/RnD/diffuser/wiki/Home) | The Equipment node that pushes `data.otr` &mdash; not a registered child |