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>
This commit is contained in:
znetsixe
2026-05-19 09:42:11 +02:00
parent 0e34403c5d
commit cb49bb8b4d
6 changed files with 964 additions and 250 deletions

160
wiki/Reference-Examples.md Normal file
View File

@@ -0,0 +1,160 @@
# Reference &mdash; Examples
![code-ref](https://img.shields.io/badge/code--ref-0e34403-blue)
> [!NOTE]
> Every example flow shipped under `nodes/reactor/examples/`, plus how to load them, what they show, and the debug recipes that go with them. Live source: `nodes/reactor/examples/`.
>
> Pending full node review (2026-05). The current flows predate the standard 3-tier example-flow rework that `rotatingMachine` has completed; planned upgrade is tracked in the EVOLV superproject memory ("Example Flows" TODO).
---
## Shipped examples
| File | Tier | Dependencies | What it shows |
|:---|:---:|:---|:---|
| `basic.flow.json` | 1 | EVOLV only | Single CSTR with one inlet. Inject `data.fluent` to set influent, `data.clock` to advance the integrator; watch `Fluent` effluent on Port 0 and InfluxDB scalars on Port 1. |
| `integration.flow.json` | 2 | EVOLV only | Upstream `reactor` &rarr; `reactor` &rarr; `settler` chain. The downstream reactor registers the upstream via `child.register positionVsParent=upstream`; on each upstream `stateChange` the downstream pulls effluent and advances. |
| `edge.flow.json` | 3 | EVOLV only | PFR with axial dispersion (`data.dispersion`) and multi-inlet (`n_inlets > 1`). Emits both `GridProfile` and `Fluent` per advance. |
> [!IMPORTANT]
> **Screenshots needed.** Editor capture of each example flow. Save as `wiki/_partial-screenshots/reactor/{01-basic-cstr,02-chain,03-pfr-edge}.png`. Replace these callouts with image links once captured.
The legacy `additional_nodes/recirculation-pump` and `additional_nodes/settling-basin` Node-RED nodes are shipped from this repo but are not yet refactored to BaseDomain &mdash; they aren't part of these examples.
---
## Loading a flow
### Via the editor
1. Open the Node-RED editor at `http://localhost:1880`.
2. Menu &rarr; Import &rarr; drag the JSON file.
3. Click Deploy.
### Via the Admin API
```bash
curl -X POST -H 'Content-Type: application/json' \
--data @nodes/reactor/examples/basic.flow.json \
http://localhost:1880/flows
```
---
## Example &mdash; Basic CSTR
Single-reactor flow with one inlet and the minimum set of inputs needed to drive nitrification.
### What to do after deploy
1. Inject `data.temperature` with `payload: 15` (or whatever process T you want). Optional &mdash; default is 20 &deg;C.
2. Inject `data.fluent` with:
```json
{
"topic": "data.fluent",
"payload": {
"inlet": 0,
"F": 1000,
"C": [0, 30, 70, 25, 0, 0, 5, 1000, 100, 2000, 0, 200, 3500]
}
}
```
Note `C[11] = 200` (X_A &mdash; autotroph biomass). If you copy the HTML default of `0.001`, nitrification never starts.
3. If `kla > 0` is configured, you can skip OTR injection; the engine aerates internally. Otherwise inject `data.otr` with a positive scalar.
4. Inject `data.clock` repeatedly (or rely on the periodic tick &mdash; `tickInterval = 1000` ms wall-clock). Each advance integrates `n_iter = floor(speedUpFactor &middot; &Delta;t / timeStep_days)` internal steps.
5. Watch the debug tap on Port 0: `Fluent` envelopes with the 13-species effluent. `S_NH` should fall, `S_NO` should rise &mdash; nitrification is proceeding.
> [!IMPORTANT]
> **GIF needed.** Demo recording of `S_NH` &darr; / `S_NO` &uarr; over 30 simulated days. Save as `wiki/_partial-gifs/reactor/01-basic-cstr.gif`.
---
## Example &mdash; Reactor chain
Upstream &rarr; downstream coupling demo. The downstream reactor registers the upstream via:
```json
{
"topic": "child.register",
"payload": "<upstream-reactor-node-id>",
"positionVsParent": "upstream"
}
```
On every upstream `stateChange`, `engine._connectReactor` triggers downstream `updateState`. That call first reads `upstream.getEffluent` into the downstream's `Fs[0]` / `Cs_in[0]`, then integrates. So one `data.clock` to the upstream advances the whole chain.
> [!NOTE]
> Pending full node review (2026-05). The flow currently in `integration.flow.json` may not yet conform to the multi-tab layout standard (Process Plant / Dashboard UI / Demo Drivers / Setup) described in `.claude/rules/node-red-flow-layout.md` &mdash; planned upgrade tracked in the EVOLV "Example Flows" TODO.
---
## Example &mdash; PFR edge
Plug-flow reactor with axial discretization. After deploy:
1. Inject `data.dispersion` with `payload: <m²/d>` to set the axial dispersion coefficient `D`.
2. Inject one or more `data.fluent` messages with distinct `inlet` indices (0..`n_inlets &minus; 1`).
3. Drive with `data.clock` as usual.
4. Watch Port 0: each advance emits a `GridProfile` **before** the `Fluent`. The grid has `n_x` rows, 13 columns each.
5. Add a `measurement` child with `asset.type = 'quantity (oxygen)'` and a numeric `positionVsParent` (e.g. `5` for 5 m from the inlet). On each measurement event the PFR engine writes the value into the nearest grid cell's `S_O`.
Stability tips:
- `Pe_local = d_x &middot; sum(Fs) / (D &middot; A)` must be `< 2` &mdash; if you see `Local Peclet number ... is too high!`, either increase `resolution_L` (more cells, smaller `d_x`) or raise `D`.
- `Co_D = D &middot; timeStep / d_x²` must be `< 0.5` for the explicit FD scheme &mdash; if you see `Courant number ... is too high!`, decrease `timeStep`.
---
## Debug recipes
| Symptom | First thing to check | Where to look |
|:---|:---|:---|
| `S_NH` stays at its initial value &mdash; nitrification not proceeding | `initialState.X_A` is effectively zero (HTML default is `0.001` mg/L). Set to `~50` or higher to seed autotrophs. | `reactor.html` &harr; `generalFunctions/src/configs/reactor.json` `initialState.X_A` |
| `Fluent` payload `F = 0` | No `data.fluent` arrived, or `Fs[0]` is still 0 (no inlet flow). Check the message payload shape: `{inlet, F, C}`. | `src/commands/handlers.js` `dataFluent`, engine `setInfluent` |
| `Fluent` payload appears, but `C` array is all zeros / unchanged | `data.clock` not arriving, or `n_iter = 0` (timestamp delta too small for the configured `timeStep`). Bump `speedUpFactor` or check that clock injects are firing. | `engine.updateState` in `baseEngine.js` |
| PFR `GridProfile` not emitted | `reactor.reactor_type` is `CSTR` &mdash; only PFR has a grid profile. | `nodeClass._emitOutputs`, `pfr.getGridProfile` |
| `temperature` ignored | Payload is non-numeric, or wrapped as `{value: ...}` with `value` non-finite. Look for `Invalid temperature input: <raw>` in the log. | `baseEngine.js` `setTemperature` setter |
| Temperature child measurement not reconciling | The child's `asset.type` must be exactly `'temperature'` and `positionVsParent = atEquipment`. Anything else logs `Type '<x>' not recognized for measured update.` | `baseEngine.js` `_updateMeasurement` |
| `Local Peclet number ... is too high!` warning on every PFR `updateState` | Either `D` is too small, or `d_x` is too large. Increase `resolution_L` or set a larger dispersion. | `pfr.updateState` Peclet guard |
| `Courant number ... is too high!` warning | `timeStep` is too large for the configured `D`. Reduce it. | `pfr.updateState` Courant guard |
| Settler downstream not updating | Settler must subscribe to the **reactor's `emitter`**, not `reactor.measurements.emitter`. Historical bug in `settler/src/specificClass.js` `_connectReactor` (fixed 2026-03-02). | upstream chain wiring, `settler._connectReactor` |
| `wiki:datamodel` autogen script slow / timing out | `mathjs` cold-start is ~13 s. The current 60 s wrapper sometimes times out. | known limitation; fall back to the hand-curated Concrete sample in `CONTRACT.md` `Home.md` |
| `reactor_type: 'pfr'` (lowercase) silently runs CSTR | Schema validator lowercases the enum; `_buildEngine` calls `.toUpperCase()` to compensate. If you stripped that guard, lowercase `pfr` falls through to the default branch (CSTR). | `src/specificClass.js` `_buildEngine` |
| `data.otr` value ignored | `reactor.kla > 0`. The engine prefers internal `kla &middot; (sat &minus; S_O)` over external OTR. Set `kla = NaN` to enable external OTR. | `cstr.tick` / `pfr.tick` `klaIsNaN` branch |
> Never ship `enableLog: 'debug'` in a demo &mdash; the kinetics engines log per-step on debug, which fills the container log within seconds.
---
## Docker compose snippet
To bring up Node-RED + InfluxDB with EVOLV nodes pre-loaded:
```yaml
# docker-compose.yml (extract)
services:
nodered:
build: ./docker/nodered
ports: ['1880:1880']
volumes:
- ./docker/nodered/data:/data/evolv
influxdb:
image: influxdb:2.7
ports: ['8086:8086']
```
Full file: [EVOLV/docker-compose.yml](https://gitea.wbd-rd.nl/RnD/EVOLV/src/branch/development/docker-compose.yml).
---
## 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; Limitations](Reference-Limitations) | Known issues and open questions |
| [settler &mdash; Examples](https://gitea.wbd-rd.nl/RnD/settler/wiki/Reference-Examples) | The typical downstream Unit |
| [EVOLV &mdash; Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) | Where reactor fits in a larger plant |