Waterschap Brabantse Delta · R&D-lab · uitleg

EVOLV · valve — één klepstand: van Kv tot Δp

eigenaar R&D-lab laatst getest 2026-07-17 versie 2.0 concept
valve is de digitale twin én besturingsmodule voor één regelklep of afsluiter. Uit de klepstand (opening %) voorspelt hij via een leverancierscurve de klepcapaciteit Kv, en berekent daaruit met een hydraulisch model de drukval Δp over de klep bij de actuele — gemeten of voorspelde — flow. Het model is bewust één-richting: flow is een input, wordt niet samen met de klepstand opgelost — vandaar een Δp-plafond (100 bar) tegen blow-up bij een bijna-dichte klep. De node is event-driven: hij rekent op state- en measurement-events, niet op een vaste tick. Deze pagina volgt letterlijk wat de code doet aan de hand van één voorbeeld en een paar interactieve figuren die de kernfuncties getrouw naspelen.

Doel & rol in de keten

valve is een S88-Equipment Module (lane L3, kleur #86bbdd; nodes/valve/CLAUDE.md:14-16; src/specificClass.js:3-6). De kern is één functie: opening % → Kv via de curve → Δp bij de actuele flow (src/measurement/measurementRouter.js:107-124). Bijzonder aan deze node is de dubbele familierol: als child registreert hij zich bij startup via de Port-2 child.register-handshake bij een groepscontroller (valveGroupControl, machineGroupControl of pumpingStation); als parent is hij zélf eigenaar van upstream-bronnen (pomp/pompgroep) — niet voor telemetrie, maar voor fluid-contract-bewaking (gas vs. vloeistof), waarvoor registerChild is overschreven zodat registratie in FluidCompatibility landt i.p.v. de standaard ChildRouter (src/specificClass.js:97-100).

De node is event-drivenstatic tickInterval = null: state- en measurement-events triggeren de hercompute, alleen de status-badge wordt elke 1000 ms gepolld (src/nodeClass.js:10-11). Output verschijnt op het domein-event 'output-changed' via notifyOutputChanged() (BaseNodeAdapter.js:134-154; measurementRouter.js:104,123).

Rode draad door de hele pagina: een BL2BA-RIG-klep (as-installed rig-curve, echte catalogusdata; klep⊕leiding, DN 50, dichtheid ρ = 997 kg/m³). Eén commando beweegt de klep naar opening 58,76 %; bij een flow Q = 10 m³/h vloeistof volgt Δp ≈ 1,497 bar. Alle getallen in de rode draad zijn met node tegen het echte curvebestand (BL2BA-RIG.json) en de echte ValveHydraulicModel doorgerekend; illustratieve aannames zijn expliciet zo gelabeld.

Praat met deze node

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 Kv en Δp op de BL2BA-RIG-curve. Elke bewering draagt een pad:regel-chip. Poortlabels ["process","dbase","parent"].

Stuurinput · 1 poort
Bericht
Poortenoutput · 3 poorten
Volledige poort-referentie
Poortcontract (labels ["process","dbase","parent"], valve.html:55-57). Port 0 is delta-gecomprimeerd: alleen gewijzigde velden gaan mee (outputUtils.js:17-41). Output-units: mbar, m³/h, °C.
PoortLabelBelangrijkste keys
0process Delta-gecomprimeerde snapshot (src/io/output.js:20-36): dynamische measurement-keys <position>_<variant>_<type> voor elke gevulde meetreeks (o.a. delta_predicted_pressure, downstream_measured_flow, downstream_predicted_flow); scalars state, percentageOpen, moveTimeleft, mode. Alléén na een curve-commando ook de synchrone reply {topic:'Showing curve', payload:{model, serviceType, expectedServiceType, gasChokedRatioLimit, selectedDensity, selectedDiameter, curve, hydraulics}} (specificClass.js:183-192).
1dbase Zelfde getOutput()-velden via de influxdb-formatter (default; ook frost/json/csv). Tags = geflatteerde config-subset: id, softwareType, role, positionVsParent, uuid, tagcode, geoLocation_*, category, type, model, unit (null/leeg gefilterd; outputUtils.js:87-133).
2parent Eenmalig, 100 ms na start: {topic:'child.register', payload:node.id, positionVsParent, distance} (default 'atEquipment') naar de ouder (BaseNodeAdapter.js:117-132).
Naast de poorten publiceert de node events voor parents (geen draad): deltaPChange (mbar) bij elke eindige Δp-herberekening — geconsumeerd door valveGroupControl voor de groeps-max-Δp (measurementRouter.js:100-101) — plus fluidCompatibilityChange/ fluidContractChange en de state-events positionChange, stateChange, movementComplete, movementAborted.

Wie praat met wie

De klep is tegelijk child (van een groepscontroller, boven) én parent (van upstream-bronnen, onder). Registratie omhoog en telemetrie lopen via draden; de deltaPChange naar de VGC is een apart in-process event. Upstream-bronnen registreren naar boven, in de klep — puur voor het fluid-contract (gas vs. vloeistof), niet voor telemetrie.

VGC / MGC / pumpingStation parent valve deze node · S88 Equipment proces / Influx poort 0 / 1 pomp / rotatingMachine upstream-bron · child pompgroep / MGC upstream-bron · child p2 registratie deltaPChange (event) p0/p1 registerChild → FluidCompatibility (gas vs. vloeistof)
registratie · via draad / in-process (poort 2 resp. fluid-contract) deltaPChange · in-process event naar VGC telemetrie · via draad (poort 0/1)

Het fluid-contract van elk kind komt uit getFluidContract() → direct serviceType-veld → default per softwareType (machine/rotatingmachine/machinegroup/pumpingstation ⇒ liquid). Statussen: pending/unknown/match/inferred/mismatch/conflict; mismatch of conflict kleurt de status-badge geel-ring met een ⚠-melding (src/fluid/fluidCompatibility.js:4-10,92-137; io/output.js:62-65).

De reis van één positie-tick

Klik een stap in de lijst (of gebruik ◀ ▶). Boven zie je waar in de pijplijn je bent; rechts wat er gebeurt, de waarde van het rode-draadvoorbeeld op dat punt (met node tegen BL2BA-RIG.json en de echte ValveHydraulicModel doorgerekend), en het codepad.

De BL2BA-RIG Kv-curve & Δp, interactief

Boven: de as-installed effectieve Kv(opening %) van de rig-klep — de 12 echte catalogus-punten met de monotone-cubic-spline die de node gebruikt (de kale klep BL2BA DN 25 staat als context op de rechter as: veel hogere Kv, want zonder leidingverlies). Onder: de Δp bij een flow Q, doorgerekend met beide hydraulische modellen zodat de service-type-gotcha zichtbaar wordt. Schuif de klepstand en Q, en wissel het service-type — let op hoe ver de gas- en vloeistoflijn uit elkaar liggen, en hoe bij bijna-dichte klep de choked-cap (gas) en het 100-bar-plafond (vloeistof) inzetten.

58,76 %
10,0 m³/h
as-installed = klep ⊕ leiding
De rig-curve is niet de kale klep. Een matrix-sweep (2026-06-24, 1580 stabiele punten, Kv = Q/√(Δp/SG)) leverde een serieweerstand-fit 1/Kv_eff² = 1/Kv_klep² + 1/Kv_loop² met een vaste loop-restrictie Kv_loop ≈ 10.3. Daarom plafonneert de effectieve Kv rond 10,21 terwijl de kale DN 25 tot 60 loopt (catalog/valve.json:242-246; docs/research/README.md:14-29).
rode draad — node-geverifieerd
Kv @ 58,76 %8,161
Δp vloeistof @ Q 101,497 bar
Δp gas @ Q 10 (p₂=0)0,0021 bar
Zelfde klep, zelfde flow, drie decades verschil in Δp — puur door het gekozen model. Een uit de editor geconfigureerde klep pakt altijd de gas-tak (zie de kaart hieronder).

De service-type-gotcha, één-richting & de vloeren

⚠ service-type-gotcha (kern)
normalizeServiceType geeft 'liquid' alléén bij exact 'liquid', anders 'gas' (hydraulicModel.js:29-35). En serviceType (net als fluidDensity, valveDiameter, gasChokedRatioLimit) staat niet in de editor-defaults van valve.html:18-51 — wél geconsumeerd in nodeClass.js:21-32,42. Gevolg: een uit de editor geconfigureerde klep draait feitelijk altijd het gas-model met ρn = 1.293, ook voor water. aanname: mogelijk achterstallig — het schema-veld valveDiameter bestaat wél (configs/valve.json:205-212).
één-richting + 100-bar-clamp
Δp volgt uit de opgelegde flow; klepstand wordt niet mede-opgelost. Bij Kv→0 divergeert (Q/Kv)²·SG terwijl een upstream node flow blijft opleggen — daarom een absoluut plafond maxDeltaPBar (default 100 bar, ver boven elke afvalwaterdienst): warn + clampedToMax:true. Een bewuste pleister, geen fysica (hydraulicModel.js:20-27,79-99).
choked-gas-cap
Op de gas-tak wordt Δp begrensd op gasChokedRatioLimit × p2_abs (default ratio 0.7 → choke-onset bij Δp/p1 ≈ 0.41, conservatief t.o.v. xT≈0.5). Houdt de Samson-relatie binnen haar subkritieke geldigheid (hydraulicModel.js:123-128).
geen leklloor bij dicht
predictKvForPosition geeft Math.max(0, kv) — de eerdere 0.1-vloer fabriceerde een permanente lek zodat de twin nooit een gesloten klep toonde. Bij kv≤0 (of flow=0, of niet-eindig) geeft het Δp-model null: geen update (supplierCurve.js:113-128; hydraulicModel.js:63-65).
Δp-staleness bij flow=0
Retourneert het model null, dan blijft de laatst geschreven pressure.predicted.delta in de meetcontainer staan; de delta-compressie op Port 0 verstuurt hem niet opnieuw. Of downstream hiermee rekent is niet geverifieerd (hydraulicModel.js:63-65; measurementRouter.js:94).
geen maintenance-FSM
Mode maintenance heeft géén allowedSources-entry en weigert dus alle bronnen — maar er is geen maintenance-sequence of -state; de mode blokkeert alleen commandobronnen (flowController.js:13-16; CONTRACT.md:76-87).

Toestanden & configuratie

De state-machine komt uit de gedeelde state-klasse (generalFunctions/src/state/state.js). Toestanden: idle, starting, warmingup, operational, accelerating, decelerating, stopping, coolingdown, off, maintenance (+ transient emergencystop). De klep start direct in operational (src/specificClass.js:48). "Operationeel" voor de Δp-hercompute = operational | accelerating | decelerating — alleen dán herberekent updatePositionDependent (measurementRouter.js:110). Sequences (config r.304-360): startup = starting→warmingup→operational; shutdown = stopping→coolingdown→idle (rijdt bij operational eerst naar stand 0, flowController.js:57-60); emergencystop = emergencystop→off. Beweging loopt in [0, 100] % met staticspeed (lineair) of dynspeed (default; ease-in/out), snelheid in %/s van de volle slag; elke bewegingstick emit positionChange (state/movementManager.js). Modes (los van states) met bron-gating: auto (default; parent/GUI/fysical), virtualControl (GUI/fysical), fysicalControl (fysical), maintenance (alle bronnen geweigerd).

Belangrijkste editor-velden (valve.html:18-51). Volledige defaults/enums: nodes/generalFunctions/src/configs/valve.json.
VeldDefaultBetekenis
name""wordt msg.topic / measurement-naam (fallback <softwareType>_<id>).
speed1 (required)"Reaction Speed", %/s van de volle slag.
modelnullVerplicht bij startup: bepaalt de curve via assetResolver.resolve('curves', model). Klepmodellen met curve: ECDV, BL2BA, BL2BA-RIG.
processOutputFormat / dbaseOutputFormatprocess / influxdbPort-0- resp. Port-1-formaat (process/json/csv resp. influxdb/frost/json/csv).
unit""Ongeldige flow-unit valt terug op m3/h (nodeClass.js:63-72).
serviceType, fluidDensity, valveDiameter, gasChokedRatioLimit, fasetijden— (editor-gat) geconsumeerd in nodeClass.js:21-32,42 maar niet in de editor-defaults → niet instelbaar via de editor, vallen altijd terug op defaults (serviceType → gas; diameter → nearest-125). Legacy supplier/category/assetType op een opgeslagen node → harde deploy-error (nodeClass.js:49-61).

Aandachtspunten uit de code-analyse

Alle punten zijn code-observaties d.d. 2026-07-17 (statisch, geen runtime-verificatie); elk draagt een pad:regel. De submodule valve stond op peildatum op 14ef7a4c8712, gelijk aan origin/main (na git fetch); er zijn geen open fix-branches voor deze node aangetroffen.

Verder lezen

Bron statische code-analyse van de werkkopie RnD/EVOLV (super-repo 74c4089, 2026-07-03) — submodule valve geanalyseerd @ 14ef7a4c8712, plus het feitendossier van deze analyse. Analysedatum 2026-07-17.
Versheids-herverificatie op 2026-07-17 is git fetch gedraaid: de submodule-tip 14ef7a4c8712 is gelijk aan origin/main; er zijn geen open fix-branches voor deze node.
Methode statische code-analyse (geen runtime-verificatie); elk feit draagt een pad:regel-verwijzing. De interactieve figuren en de console zijn getrouwe herimplementaties, vooraf met node tegen het echte curvebestand BL2BA-RIG.json en de echte ValveHydraulicModel doorgerekend (testgevallen als commentaar in de pagina-JS); illustratieve aannames zijn expliciet zo gelabeld.
Beperking beschrijft de geanalyseerde revisie; gedrag op andere branches of deployments kan afwijken.
Contact R&D-lab · lab.wbd-rd.nl