Waterschap Brabantse Delta · R&D-lab · uitleg
reactor is de biologische-procesmodule van EVOLV: een beluchte actiefslibtank
die de ASM3-kinetiek (Activated Sludge Model No. 3) over de tijd integreert.
Hij neemt een influentstroom (fluent) in, rekent per klok-tick de biokinetiek
en zuurstofoverdracht door, en levert een effluentstroom plus telemetrie. De fysica zit in
een verwisselbare engine — een volledig gemengde tank (CSTR) of een propstroomreactor
(PFR). De node is tick-gestuurd: hij vordert de integrator alleen als er een
klok-tick verstrijkt. Deze pagina volgt letterlijk wat de code doet aan de hand van één
klok-tick, een interactieve CSTR-respons met de échte ASM3-parameters, en een
node-voorberekend PFR-profiel.
RnD/EVOLV, submodule reactor
@ 18394c3b5ef2 (op 2026-07-17 gelijk aan origin/main; zie de
verantwoording onderaan).reactor is een S88-Unit (lane L4, kleur #50a8d9;
nodes/reactor/CLAUDE.md, S88-classificatietabel). De domeinklasse omschrijft
zichzelf als "biological reactor orchestrator (Unit-level)"
(src/specificClass.js:10-14). Het gemodelleerde proces is een beluchte
actiefslibtank: heterotrofe groei, opslag (storage), autotrofe nitrificatie, hydrolyse en
endogene ademhaling — geen SBR-/batch-fasering.
In de keten neemt de reactor een influent (fluent) in en levert een effluent
op poort 0. Reactoren kunnen in serie staan: een downstream reactor registreert
een upstream reactor als kind en trekt diens effluent binnen als influent
(src/kinetics/baseEngine.js:114-118,130). Nageschakelde eenheden in
additional_nodes/ — een nabezinktank (settling-basin) en een
recirculatiepomp (recirculation-pump) — zijn legacy losse nodes (zie de
aandachtspunten).
Rode draad door de hele pagina: één klok-tick van een
voorbeeld-CSTR — voorbeeldreactor volume 1000 m³,
timeStep 1 s, influent F = 100 m³/d met o.a.
S_S (opgelost substraat) en S_NH (ammonium). Alle live-getallen op
deze pagina zijn met node tegen de échte asm3_class.js +
cstr.js/pfr.js doorgerekend (parity exact; testcases als
commentaar in de pagina-JS).
Het I/O-contract in één oogopslag: kies een verb, stel de velden in,
kopieer het exacte injecteerbare msg, en klik Injecteer ▸ om te zien wat
er uit de drie poorten komt — met live doorgerekende effluentwaarden op de
voorbeeld-CSTR (V 1000 m³). De data-verbs dragen geen unit-descriptor,
dus de registry normaliseert niets: mg/L, m³/d en °C bereiken de engine rauw. Elke bewering
draagt een pad:regel-chip. Poortlabels
["process","dbase","parent"].
["process","dbase","parent"],
reactor.html:61-63). Emissie in nodeClass._emitOutputs()
(src/nodeClass.js:41-50). Domeineenheden: mg/L (mmol/L voor
S_HCO), m³/d, °C.| Poort | Label | Belangrijkste keys |
|---|---|---|
| 0 | process |
Bij PFR eerst {topic:'GridProfile', payload:{grid, n_x, d_x, length,
species, timestamp}} (src/kinetics/pfr.js:25-35); daarna het
effluent {topic:'Fluent', payload:{inlet:0, F, C}, timestamp} met
F = math.sum(Fs) en C = de laatste toestand (CSTR: hele
13-vector; PFR: laatste grid-slice via state.at(-1))
(src/kinetics/baseEngine.js:60-63). |
| 1 | dbase |
Zelfde snapshot via de influxdb-formatter uit
getOutput(): flow_total, temperature, en per
species — alleen als Number.isFinite — S_O, S_I, S_S,
S_NH, S_N2, S_NO, S_HCO, X_I, X_S, X_H, X_STO, X_A, X_TS
(src/specificClass.js:7-8,101-104). Omzetbaar naar
frost/json/csv. |
| 2 | parent |
In _emitOutputs altijd null
(src/nodeClass.js:46,49). De startup-child.register-
handshake naar de ouder zit in BaseNodeAdapter (generalFunctions), niet
in reactor-code (aanname — niet in dit submodule
geverifieerd). |
De parent staat boven, de kinderen onder.
Registratie en telemetrie lopen via draden; kind-measurements en de upstream-effluent
komen als in-process events binnen (op de engine-emitter). De nageschakelde
settling-basin + recirculation-pump zijn een legacy zijtak:
losse nodes op de oude node.on('input')/msg.topic-stijl, geen
3-lagen-patroon.
Kinderen worden via ChildRouter
aangesloten (src/specificClass.js:28-29). Een measurement-kind
abonneert op <type>.measured.<position> op zijn emitter; herkend
wordt temperature.measured.atEquipment → engine.temperature
(src/kinetics/baseEngine.js:100-126), en op PFR extra
quantity (oxygen).measured.<distance> → DO in de dichtstbijzijnde grid-cel
(src/kinetics/pfr.js:67-79). Een reactor-kind wordt de upstream
reactor: bij elke updateState wordt diens getEffluent als influent
ingetrokken (src/kinetics/baseEngine.js:114-118,130).
Klik een stap in de lijst (of gebruik ◀ ▶). Boven zie je waar in de pijplijn je bent; rechts wat er gebeurt, een concreet getal van de voorbeeld-CSTR op dat punt, en het codepad. De hele reis wordt in gang gezet door één klok-tick — zonder tick vordert de integrator niet.
Een volledig gemengde tank (CSTR, V = 1000 m³) krijgt vanaf de
editor-startconcentraties een influent F = 100 m³/d. Elke stap is
de Forward-Euler dC = (inflow + outflow + reaction + transfer)·Δt met
reaction = ASM3.compute_dC en de zuurstofoverdracht alléén op S_O.
Dit is de échte 13-species / 12-reactie ASM3 @20 °C-parameterset — geen
vereenvoudiging; de reken-kern is met node tegen asm3_class.js +
cstr.js gevalideerd (trajectorie-parity exact). Schuif het influent-substraat,
de kla en de temperatuur; zie hoe substraat wordt afgebroken en hoe de opgeloste
zuurstof reageert.
De propstroomreactor (PFR) mengt niet: substraat, ammonium en zuurstof variëren langs
de reactorlengte. Elke cel volgt advectie − dispersie − reactie − transfer, met een
gegeneraliseerde Danckwerts-inlaat (α) en een Neumann-uitlaat. Omdat de PFR-operatoren
(mathjs-diagonalen, in-place-aliasing in _makeDoperator) fragiel zijn om live in
de browser te herbouwen, is dit profiel node-voorberekend met de échte
Reactor_PFR (n_x = 10, length = 10 m,
α = 0,5, D = 5 m²/d, externe OTR = 50,
T = 20 °C, ~4 dagen ingelopen) en hier statisch ingebed — geen
live-plot.
cstr.js:12-24). PFR = een n_x × 13-grid met expliciete
finite-difference: advectie (−ΣF/(A·dx)·D_op·state) + dispersie
(D/dx²·D2_op·state) + reactie + transfer (pfr.js:48-65).
Randvoorwaarden: gegeneraliseerde Danckwerts aan de inlaat bij flow>0,
Neumann (no-flux) aan de uitlaat (pfr.js:83-92). De PFR waarschuwt bij
lokaal Péclet ≥ 2 en Courant ≥ 0,5
(pfr.js:41-44).S_O, S_I, S_S, S_NH, S_N2, S_NO, S_HCO, X_I, X_S,
X_H, X_STO, X_A, X_TS) en 12 reacties: hydrolyse; heterotrofe aerobe/anoxische
opslag & groei; endogene en opslag-ademhaling; autotrofe nitrificatie; autotrofe
ademhaling (asm3_class.js:171-197). Kinetiek via Monod c/(K+c) +
inverse-Monod K/(K+c), temperatuur via k·exp(θ·(T−20)). De
12×13-stoichiometrie is hard-gecodeerd en getransponeerd; compute_dC =
stoiᵀ·rates (asm3_class.js:101-120,205-208).OTR = kla·(S_O,sat − S_O), met de verzadiging als 3e-graads
polynoom in T (14.652 − 0.41022·T + 7.991e-3·T² + 7.7774e-5·T³)
(baseEngine.js:67-74). Is kla NaN, dan wordt de extern
gezette OTR-waarde gebruikt (cstr.js:17-19,
pfr.js:54-56). Na elke stap wordt S_O begrensd op
[0, verzadiging] (baseEngine.js:76-85).timeStep is seconden in de UI, maar de integrator werkt
in dagen: this.timeStep = (1/86400)·config.timeStep
(baseEngine.js:40). Het JSON-schema waarschuwt hier expliciet voor
(configs/reactor.json:138-145). Losstaand: kla staat in het
schema als 1/h terwijl de tijdbasis dagen is; er is geen h→d-omrekening in de
code gezien (CONTRACT.md:16-19). aanname mogelijke
inconsistentie — niet in runtime geverifieerd.src/reaction_modules/asm3_class Koch.js is een alternatieve
parameterset (Koch et al. 2000: k_H=9, k_STO=12,
nu_NO=0.5) — niet geïmporteerd; de engines laden
asm3_class.js (baseEngine.js:4). De bestandsnaam bevat een spatie
en wijkt van de conventie af. De additional_nodes/
(settling-basin, recirculation-pump) volgen het 3-lagen-patroon
niet: node.on('input') + switch(msg.topic) +
console.log bij een onbekende topic (additional_nodes/*.js:30-31).getOutput() filtert niet-eindige species eruit, maar
flow_total/temperature gaan via Number(...) en kunnen
NaN worden zonder effluent/temperatuur
(src/specificClass.js:97-100). aanname onzeker of
formatMsg die twee velden alsnog wegfiltert — niet in runtime geverifieerd.UI-defaults in reactor.html:17-58; schema/defaults in
nodes/generalFunctions/src/configs/reactor.json. De adapter mapt via
buildDomainConfig (src/nodeClass.js:19-36), het domein plat via
_flattenEngineConfig (src/specificClass.js:38-56).
volume is
0., terwijl het JSON-schema 1000 geeft — de schema-default vult
aan wat de editor leeg laat. Volledige defaults/enums:
configs/reactor.json.| Veld | Default (html / json) | Betekenis |
|---|---|---|
reactor_type | CSTR | keuze engine CSTR|PFR (configs/reactor.json:76-85; specificClass.js:58-68) |
volume (m³) | 0. / 1000 | reactorvolume (baseEngine.js:30) |
length (m, PFR) | 0. / 10 | reactorlengte (pfr.js:10) |
resolution_L (PFR) | 0. / 10 | aantal ruimtelijke cellen n_x (pfr.js:11) |
alpha (PFR) | 0 / 0.5 | inlaat-BC-parameter (α=0 Danckwerts, α=1 Dirichlet) (pfr.js:14,86) |
n_inlets | 1 / 1 | aantal inlaten (baseEngine.js:32-33) |
kla (1/h) | null / 0 | zuurstofoverdrachtscoëfficiënt; NaN ⇒ externe OTR (baseEngine.js:37, cstr.js:17-19) |
timeStep (s) | 1 / 1 | integratie-tijdstap; intern → dagen via (1/86400)·timeStep (baseEngine.js:40) |
speedUpFactor | 1 / 1 | versnelling t.o.v. realtime (baseEngine.js:41,131-134) |
S_*_init / X_*_init | zie reactor.html:27-39 | 13 startconcentraties (mg/L; mmol/L voor S_HCO) |
Resterende velden (asset/FROST-identiteit uuid/model/frostServer/geo,
output-formaten process|json|csv resp.
influxdb|frost|json|csv, logging,
positionVsParent) staan in reactor.html:41-58 +
configs/reactor.json.
Alleen formules die letterlijk zo in de code staan. De ASM3-reactieterm gaat in een
massabalans per engine-type; de zuurstofoverdracht drijft de S_O-transfer.
| symbool | betekenis | eenheid | herkomst |
|---|---|---|---|
stoi_matrix | 12×13 stoichiometrie (fracties, yields, N-/TSS-/ladingsparameters), getransponeerd | – | asm3_class.js:101-120 |
rates | 12 procesreactiesnelheden (Monod/inverse-Monod × Arrhenius) | mg/(L·d) | asm3_class.js:171-197 |
θ | temperatuurcoëfficiënt, uit 2 ijkpunten ln(k1/k2)/(T1−T2) | 1/°C | asm3_class.js:149-163 |
| symbool | betekenis | eenheid | herkomst |
|---|---|---|---|
Fi / V | inlaatdebiet / reactorvolume | m³/d · m³ | cstr.js:13-14 |
transfer | zuurstofoverdracht, alleen op index S_O | mg/(L·d) | cstr.js:16-19 |
Δt | tijdstap, intern in dagen | d | baseEngine.js:40 |
SO,sat | verzadigings-DO, 3e-graads polynoom in T | mg/L | baseEngine.js:72-74 |
https://lab.wbd-rd.nl/rnd-lab/projects/evolv-reactor.docs/contract.html, docs/functional.html,
docs/transport.html in de submodule.CONTRACT.md en de commando-registry
src/commands/index.js.RnD/EVOLV (super-repo 74c4089, 2026-07-03) — submodule
reactor geanalyseerd @ 18394c3b5ef2, plus het feitendossier van
deze analyse. Analysedatum 2026-07-17.git fetch gedraaid;
de geanalyseerde HEAD (18394c3) is gelijk aan
origin/main en er staan geen open fix-branches voor deze node open — de brief
is volledig code-geverifieerd.pad:regel-verwijzing. De CSTR-respons en de console-payloads zijn getrouwe
herimplementaties, vooraf met node tegen asm3_class.js +
cstr.js doorgerekend (ASM3-parity exact, CSTR-trajectorie-parity exact;
testcases als commentaar in de pagina-JS). Het PFR-profiel is node-voorberekend met de
échte Reactor_PFR en statisch ingebed. Illustratieve aannames (voorbeeldreactor,
influentsamenstelling) zijn expliciet gelabeld.