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>
133 lines
9.3 KiB
Markdown
133 lines
9.3 KiB
Markdown
# Reference — Limitations
|
|
|
|

|
|
|
|
> [!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 ≈ 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 `≥ ~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 `÷ 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](Home) | Intuitive overview |
|
|
| [Reference — Contracts](Reference-Contracts) | Topic + config + child filters |
|
|
| [Reference — Architecture](Reference-Architecture) | Code map, kinetics engines, integration sequence |
|
|
| [Reference — Examples](Reference-Examples) | Shipped flows + debug recipes |
|
|
| [settler — 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` — not a registered child |
|