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>
161 lines
9.1 KiB
Markdown
161 lines
9.1 KiB
Markdown
# Reference — Examples
|
|
|
|

|
|
|
|
> [!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` → `reactor` → `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 — 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 → Import → 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 — 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 — default is 20 °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 — 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 — `tickInterval = 1000` ms wall-clock). Each advance integrates `n_iter = floor(speedUpFactor · Δ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 — nitrification is proceeding.
|
|
|
|
> [!IMPORTANT]
|
|
> **GIF needed.** Demo recording of `S_NH` ↓ / `S_NO` ↑ over 30 simulated days. Save as `wiki/_partial-gifs/reactor/01-basic-cstr.gif`.
|
|
|
|
---
|
|
|
|
## Example — Reactor chain
|
|
|
|
Upstream → 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` — planned upgrade tracked in the EVOLV "Example Flows" TODO.
|
|
|
|
---
|
|
|
|
## Example — 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 − 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 · sum(Fs) / (D · A)` must be `< 2` — if you see `Local Peclet number ... is too high!`, either increase `resolution_L` (more cells, smaller `d_x`) or raise `D`.
|
|
- `Co_D = D · timeStep / d_x²` must be `< 0.5` for the explicit FD scheme — 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 — 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` ↔ `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` — 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 · (sat − 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 — 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 — Contracts](Reference-Contracts) | Topic + config + child filters |
|
|
| [Reference — Architecture](Reference-Architecture) | Code map, kinetics engines, integration sequence |
|
|
| [Reference — Limitations](Reference-Limitations) | Known issues and open questions |
|
|
| [settler — Examples](https://gitea.wbd-rd.nl/RnD/settler/wiki/Reference-Examples) | The typical downstream Unit |
|
|
| [EVOLV — Topology Patterns](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topology-Patterns) | Where reactor fits in a larger plant |
|