Dit dashboard toont resultaten van het Wflow SBM hydrologisch model, gedraaid op een NVIDIA Jetson AGX Orin — een compact ARM-gebaseerd edge-apparaat, normaal gebruikt voor AI-inferentie aan de rand van het netwerk. Het doel was te testen of een volledige hydrologische modelleeropzet (invoerdata, simulatie, export, webdashboard) volledig autonoom op zo'n apparaat kan draaien, zonder cloud en zonder x86-werkstation.
Wflow SBM is een fysisch-gebaseerd neerslagregelmodel ontwikkeld door Deltares en beschikbaar als open-source Julia-pakket. Het modelleert bodemwater, grondwater, laterale afvoer en rivierrouting op een rastergrid — in dit geval het IJssel-stroomgebied op ~800 m resolutie.
Netwerktoegang vanuit de iPad gaat via Tailscale — een overlay-VPN dat directe communicatie mogelijk maakt ondanks WiFi AP-isolatie op het lokale netwerk. Het dashboard wordt geserveerd door FastAPI/uvicorn en toont rivierdebieten via deck.gl 3D-kolommen.
De volledige rapportage is beschikbaar in LESSONS_LEARNED.md.
Hieronder de meest impactvolle bevindingen voor Deltares en toekomstige gebruikers.
| # | Probleem | Oorzaak | Fix | Impact |
|---|---|---|---|---|
| 1 | JIT-compilatie 2–3 uur op ARM | CFTime.jl type-instabiliteit → LLVM-cascade op aarch64 | Type-stabiele Period-constructor; @nospecialize in Wflow |
Blokkerend |
| 2 | c_layer heeft 3 lagen, model verwacht 4 |
Wflow voegt intern een NaN-schildwacht laag toe: maxlayers = n+1 |
NetCDF hergebouwd met 4 lagen | Blokkerend |
| 3 | Beginstaat mist time-dimensie |
set_states! eist 3D/4D met time=1 |
NetCDF herbouwd met singleton tijdsdimensie | Blokkerend |
| 4 | 3 cellen buiten ERA5-dekking | Bounding box te krap; uitlaat IJsselmeer net buiten grid | Nearest-neighbour invulling | Blokkerend |
| 5 | Kampen-meetpunt niet op riviernetwerk | 800 m resolutie: geografische stad ≠ model-riviercel | Coördinaten gesnapped naar uitlaatcel (5.496, 53.221) | Blokkerend |
| 6 | iPad kon dashboard niet bereiken | WiFi AP-clientisolatie blokkeert peer-to-peer op poort 8000 | Tailscale overlay-netwerk | Blokkerend |
| 7 | Python cdsapi crasht op ARM |
attrs-namespace ontbreekt in Python 3.10 systeem-installatie |
Installatiedependencies in apart --target-pad |
Oplosbaar |
| 8 | Browser laadt oude JavaScript | Safari op iPad cached app.js agressief |
?v=N cache-busting + Cache-Control: no-store |
Oplosbaar |
Tijdsinvestering: ~12 uur voor de 1995-opzet (waarvan ~7 uur aan ARM-specifieke technische problemen), ~3 uur voor 2021 + dashboard-uitbreiding. Met deze kennis vooraf ingepast: ~2–3 uur totaal.
De juli 2021 Rijn-vloed was de zwaarste in Duitsland en Nederland in decennia.
Bij Lobith (meetpunt Pannerdense Kop) bereikte de Rijn een piek van
6 687 m³/s op 18 juli 2021 (bron: RWS Waterinfo, station
lobith.bovenrijn.tolkamer). De simulatie is twee keer uitgevoerd:
eerst met een synthetische inflow-schatting, daarna met de werkelijk gemeten debietreeks.
Bij de eerste opzet was de RWS Waterinfo REST API niet beschikbaar (het oude eindpunt gaf "Exceeded 30 redirects" doordat het in april 2026 was vervangen). De inflow bij Westervoort werd daarom synthetisch opgebouwd uit twee aannames:
| Aanname | Bron | Gebruikte waarde | Werkelijk (RWS) | Fout |
|---|---|---|---|---|
| Lobith piekdebiet | RWS waterstandsberichten jul 2021 (persbericht, niet gevalideerde meting) |
~8 900 m³/s op 15 jul | 6 687 m³/s op 18 jul | +33% |
| IJssel-aandeel bij hoog water | Vuistregel voor gemiddelde Rijnverdeling (langjarig gemiddelde ~18–25%) |
25% | 13,4% (899 / 6 687) | +87% |
| Resulterende inflow Westervoort | 8 900 × 25% = 2 225 m³/s | 2 200 m³/s | 899 m³/s | ×2,45 |
Waarom week het IJssel-aandeel zo sterk af? Bij extreme afvoer op de Rijn reguleert Rijkswaterstaat actief de verdeling bij de Pannerdense Kop. Bij de jul-2021 golf werd meer water naar de Waal geleid om benedenrivierafvoer te spreiden. Het langjarig gemiddelde van ~25% is dus géén betrouwbare schatting tijdens hoogwater — juist het moment dat het er toe doet.
rws-waterinfo Python-pakket, station westervoort.ijsselkop,
grootheid Q). Dit moet de eerste keuze zijn voor randcondities bij
operationele runs — niet de fallback.
Waterlab is een persoonlijk leerplatform, geen operationeel systeem. De zes proeven zijn opgezet om drie concrete dingen te leren — en elke keuze (model, dataset, AI-aanpak) is gemaakt om iets te begrijpen, niet om een product te bouwen.
De IJssel is geen zelfstandige rivier maar een zijtak van de Rijn. Al het water in de IJssel komt via de Rijn uit de Alpen en het Middenrijngebied. Bij Lobith (bij Zevenaar) passeert de Rijn de grens. Verderop, bij de Pannerdense Kop, splitst de stroom zich op in drie richtingen.
Als de Rijn hoog staat door smeltwater of extreme regenval in Duitsland stijgt ook de IJssel. Bij Kampen mondt de IJssel uit in het IJsselmeer. Als het water te hoog stijgt, moeten dijken het tegenhouden — of ze houden het niet meer.
De kaart toont het IJssel-stroomgebied van Westervoort (bij Arnhem) tot Kampen. Elke 3D-staaf is één rastercel van ~800 × 800 meter.
🧪 Waterlab — negen proeven
Elke proef is een afgebakend experiment met één concrete leervraag. Hieronder per POC: wat het functioneel doet, hoe het technisch werkt, en wat een logische opvolging zou zijn. Klap onderaan elke kaart Herkomst — hoe komt dit tot stand open om elke stap af te pellen: databron → randvoorwaarde → modelstap → output → AI-duiding, met bron-endpoints en een eerlijke markering waar een stap niet fysisch is.
Het dashboard toont een 14-daagse debietverwachting bij Kampen op basis van de actuele RWS Waterinfo-meting bij Lobith en de ECMWF-verwachting.
Een Claude-agent (Anthropic API) legt de verwachting uit als expert-hydroloog: niet "het debiet wordt X m³/s" maar "het peil nadert de waakdrempel — RWS en HHNK starten dan doorgaans Y protocol".
De meerwaarde: domeinkennis over stakeholders, peilregimes en de Pannerdense Kop-verdeling zit in de systeemprompt, niet in de modeloutput.
Integraal (nieuw): de interventie is cross-domein. Via de gekalibreerde IJssel→Veluwe-grondwaterkoppeling (Proef 9) vertaalt de agent de live peilverwachting óók naar drinkwaterwinning (Vitens), landbouw/beregening en natuur/kweldruk — niet alleen scheepvaart en peil.
Grondwater in de grafiek: de verwachtingsgrafiek toont nu naast debiet en peil ook de verwachte absolute grondwaterstand (Veluwe-flank, BRO) met onzekerheidsband, uit het reservoirmodel (Proef 9 / v2).
Model: lineaire regressie op ERA5-historiek (recessiecoëfficiënt + impulsrespons). Geen fysisch gekalibreerd model — bewust simpel om de AI-interventie centraal te stellen.
Data: RWS Waterinfo live via REST (debiet Lobith), ECMWF open API (14-daagse neerslag), ERA5-Land (trainingsdata).
AI: claude-haiku-4-5 via Anthropic API. Systeemprompt bevat gebiedsschematisatie: Pannerdense Kop-verdeling (~13% IJssel), peilregimes (waak/alarm), stakeholders (RWS-dist. Oost-Nederland, HHNK, Vitens Oost).
Grondwater (Proef 9-koppeling): de prompt krijgt live de recente BRO-grondwaterstand + de gekalibreerde koppeling (lag ~18 d, r≈0.9) én de reservoirmodel-voorspelling (nowcast/forecast met NSE) mee; de grafiek toont die absolute voorspelling + band. Endpoints /api/forecast/intervention en /api/grondwater/reservoir.
Stack: Python/FastAPI backend, Plotly.js chart, server-sent events voor live update.
RWS Waterinfo (debiet Lobith, live REST) · ECMWF / Open-Meteo (14-d neerslag) · ERA5-Land (training) · BRO GLD (grondwater).
Actuele Lobith-meting + Pannerdense Kop-verdeling (~13% naar de IJssel) als instroom-aanname.
Lineaire regressie (recessie + impulsrespons) + lineair reservoirmodel grondwater (v2/v2b). Niet fysisch gekalibreerd — bewust simpel.
14-d debietverwachting Kampen, P10/mean/P90-band + absolute grondwaterstand met band. /api/forecast · /api/grondwater/reservoir
claude-haiku-4-5 (Anthropic API), expert-hydroloog-systeemprompt. /api/forecast/intervention
Vijf parallelle wflow SBM-runs simuleren het droogtescenario zomer 2018 met neerslagmultiplicatoren van ×0.70 tot ×1.30. De spread tussen de runs visualiseert hydrologische onzekerheid.
Een lokaal LLM interpreteert de ensemble-output: "De P10–P90 band is smal — de droogte wordt gedomineerd door de bovenstrooms Rijn-afvoer, niet door lokale neerslag."
Leervraag: kan een lokaal 32B-model bruikbare hydrologische interpretaties geven op edge hardware?
Model: wflow SBM (Julia) met ERA5-Land forcing mei–aug 2018. Vijf runs met neerslagscaling: 0.70 / 0.85 / 1.00 / 1.15 / 1.30. Elke run: ~800×800m rastercellen, IJssel-bekken (~8.000 km²).
AI: Qwen2.5-32B lokaal via llama.cpp op Jetson AGX Orin (60W TDP). Geen cloud-latentie, maar ~45 s per interpretatie.
Output: P10, P25, P50, P75, P90 percentielband als tijdreeks. Plotly.js band-chart met shade-fill tussen P10 en P90.
Stack: Python orchestrator start Julia-subprocessen, verzamelt NetCDF-output, berekent percentielen.
Q.sim.P50, Q.sim.P90).ERA5-Land reanalyse (neerslag / temperatuur / verdamping), mei–aug 2018.
Neerslagscaling ×0.70–×1.30. Synthetische perturbatie — geen echte meteorologische onzekerheid.
wflow SBM ×5 (Julia), ~800×800 m raster, IJssel-bekken ~8.000 km².
P10/P25/P50/P75/P90 percentielband (NetCDF → tijdreeks). /api/ensemble
Qwen2.5-32B lokaal (llama.cpp, Jetson AGX Orin) — ~45 s per interpretatie.
Een drietrapspipeline koppelt hydraulische netwerkroutering, LLM-orkestratie en hydrologische detailsimulatie in één run:
Stap 1 — Ribasim: simuleert het Rijn/IJssel-netwerk (Pannerdense Kop, drie aftakkingen) en berekent waterdeficiet per tak.
Stap 2 — LLM: de orchestrator analyseert de Ribasim-output en identificeert de kritieke knoop (grootste waterdeficiet).
Stap 3 — wflow ×5: voert vijf droogtescenario's uit voor het geïdentificeerde deelbekken.
Ribasim: de officiële Linux-binary is x86-64 only. Op ARM64 Jetson: Python 3.13 bouwt het model (.gpkg + toml), Julia-solver draait de simulatie via het Ribasim.jl package. Geen binary nodig.
LLM orchestrator: Qwen2.5-32B via llama.cpp. Input: Ribasim waterbalans als JSON. Output: knoop-ID + motivatie. Geen function-calling — plain text parsing met regex fallback.
wflow: zelfde setup als Proef 2, maar alleen voor het geselecteerde deelbekken. Vijf perturbatiescenario's parallel als Julia-subprocessen.
Data: wflow_moselle dient als referentie-run voor de Moselle-tak (Roer/Maas systeem).
Ribasim-netwerkschematisatie (Rijn/IJssel, Pannerdense Kop, 3 takken) + ERA5-Land voor de wflow-stap.
Climatologisch gemiddelde Pannerdense Kop-verdeling als netwerk-instelling.
Ribasim (Julia-solver, ARM64, géén x86-binary) → LLM kiest kritieke knoop → wflow SBM ×5 op het deelbekken.
Kritieke knoop-ID + droogtescenario's voor het geselecteerde deelbekken. /api/multimodel
Qwen2.5-32B orchestrator (plain-text parsing + regex-fallback, geen function-calling).
Simulatie van de zwaarste Rijnvloed in decennia: januari 1995 bereikte Lobith 12.600 m³/s; 250.000 mensen werden preventief geëvacueerd. Het model simuleert 90 dagen (nov 1994–feb 1995).
De 3D-kaart toont dag voor dag hoe de hoogwatergolf door het IJssel-bekken trekt: instroom Westervoort → piekafvoer Kampen → uitzakken. De tijdreeks vergelijkt gesimuleerd debiet met de drempelwaarden voor waak en alarm.
Leervraag: reproduceert wflow SBM de grootteorde en timing van een extreme hoogwatergolf op ERA5-forcing zonder kalibratie?
Model: wflow SBM (kinematische golfrouting), ~800×800m raster, IJssel-bekken ~8.000 km². Cold start — geen spin-up voorafgaand aan de eventperiode.
Forcing: ERA5-Land reanalyse (neerslag, temperatuur, verdamping). Tijdstap: 1 dag. Dekking: nov 1994 t/m feb 1995 (90 tijdstappen).
Instroom: bovenstroomse randconditie bij Westervoort — gesynthetiseerd uit het RIZA/RWS-archief (piek ~3 120 m³/s; RWS Waterinfo heeft geen data vóór ~2000), niet uit ERA5. Naast de cold start de voornaamste onzekerheid. Zie docs/WL-PROV-2_schematisatie.md ↗.
Output: NetCDF → GeoJSON per dag (riviercellen + debiet). MapLibre GL JS rendert de 3D-extrusie met Plotly.js tijdreeks naast de kaart.
ERA5-Land reanalyse, nov 1994 – feb 1995 (90 dagstappen).
Instroom bij Westervoort gesynthetiseerd uit het RIZA/RWS-archief (piek ~3 120 m³/s) — geen RWS-data vóór ~2000. Meteo-forcing uit ERA5-Land.
wflow SBM, kinematische golfrouting, ~800 m raster. Cold start — geen spin-up vóór de event-periode.
NetCDF → GeoJSON per dag (riviercellen + debiet Kampen) voor de 3D-kaart. /fews … period=1995
Geen — historische simulatie zonder AI-stap.
De ernstigste droogte in Nederland in decennia: zomer 2018 zakte de Rijn bij Lobith naar ~600 m³/s — 70% onder het langjarig gemiddelde. De 3D-kaart toont de IJssel in extreme laagtestand.
Het model simuleert 4 maanden (mei–aug 2018). De tijdreeks toont het sluipende karakter van de droogte: geen dramatische piek maar een langzame daling, wekenlang onder de grens voor scheepvaartbeperkingen (~700 m³/s Lobith).
Leervraag: hoe snel signaleert wflow SBM een droogte-aanvang, en waar in het bekken worden de eerste effecten zichtbaar?
Model: zelfde setup als Proef 4 (wflow SBM, ~800m raster). Periode: 1 mei–31 aug 2018 (123 tijdstappen).
Forcing: ERA5-Land reanalyse mei–aug 2018. Droogte-signaal zit in de neerslagdeficieten: ERA5 reproduceert de periode nauwkeurig (RMSE neerslag <1 mm/dag t.o.v. KNMI-stations).
Beperking: wflow SBM is een regen-afvoermodel. Grondwateronttrekkingen (landbouw, Vitens) en laagwater-regulering via stuwen zitten niet in het model — daardoor overschatting van het debiet bij extreme droogte.
Ensemble-link: Proef 2 gebruikt dezelfde forcingperiode voor de ensemble-varianten (×0.70–×1.30).
ERA5-Land reanalyse, mei–aug 2018 (123 dagstappen). Neerslagdeficiet gevalideerd vs KNMI-stations (RMSE <1 mm/dag).
ERA5-forcing. Géén grondwateronttrekking (landbouw/Vitens) of stuwregulering → overschatting bij extreme droogte.
wflow SBM ~800 m raster (zelfde setup als Proef 4). Regen-afvoermodel.
Debiet Kampen (sluipend laagwaterverloop). /fews … period=2018
Geen — historische simulatie.
Extreme buien boven Eifel en Ardennen veroorzaakten in 48 uur catastrofale overstromingen langs Ahr, Roer en Maas. Nederland was minder zwaar getroffen dan Duitsland, maar de IJssel steeg snel.
Dit is de enige proef met een expliciete validatie: de bovenstrooms randconditie bij Westervoort is de gemeten RWS-inflow (niet ERA5). De gesimuleerde en gemeten afvoer bij Kampen zijn daardoor vergelijkbaar.
Leervraag: wat is het verschil in piekafvoer en timing tussen ERA5-gedreven en meting-gedreven simulatie bij een snel-opkomend hoogwater?
Model: wflow SBM, periode jun–aug 2021 (92 tijdstappen). Twee runs: (a) ERA5-forcing, (b) gemeten RWS-inflow bij Westervoort als bovenstrooms randconditie.
Validatie: RWS Waterinfo-debiet bij Kampen (daggemiddelde). Nash-Sutcliffe coëfficiënt voor run (b) is merkbaar hoger dan voor run (a) — de Eifel-piek wordt door ERA5 onderschat (te brede, te lage regenband).
Opmerking: de Maas-overstroming en Ahr-vallei catastrofe zijn buiten het IJssel-model. Wflow beslaat alleen het IJssel-bekken; de Roer-bijdrage loopt via de Maas, niet via de IJssel.
Data: RWS Waterinfo REST API voor zowel Lobith als Kampen (debiet + waterpeil).
RWS Waterinfo: gemeten inflow Westervoort + validatie-debiet Kampen (daggemiddeld). ERA5-Land voor run (a).
Beide op ERA5-Land-forcing; verschil = instroom Westervoort: (a) gesynthetiseerd (RWS-berichten, ~2 200 m³/s), (b) gemeten RWS-debiet.
wflow SBM, jun–aug 2021 (92 dagstappen). Alleen IJssel-bekken (Maas/Roer buiten het model).
Debiet Kampen + Nash-Sutcliffe vs gemeten — run (b) merkbaar hoger dan run (a). /fews … period=2021
Geen — validatie-experiment zonder AI-stap.
Waterlab publiceert wflow-tijdreeksen en RWS Waterinfo-metingen via een FEWS-compatibele PI REST API. Elke FEWS-instantie bij RWS of een waterschap kan Waterlab als externe databron aanroepen — zonder aanpassing aan FEWS.
De vier standaard-endpoints zijn live op localhost:8000/fews/rest/fewspiservice/v1/:
filters — beschikbare datafilterslocations — Kampen en Westervoort (coördinaten, metadata)parameters — Q.sim, Q.meting, H.metingtimeseries — PI JSON met header + events[] per dagImplementatie: 5 Python-bestanden (~200 regels), FastAPI router gemount op het hoofd-dashboard. Conform Deltares PI REST spec v1.25 (publiek beschikbaar).
Niet-standaard extensie: de period-parameter selecteert een historische wflow-simulatie (1995 / 2018 / 2021). Standaard PI REST heeft geen historisch-event-concept — dit is bewust toegevoegd als leerfunctie.
Data-adapter: pi_client.py haalt live RWS Waterinfo-data op en vertaalt die naar PI JSON-formaat. data_adapter.py leest wflow NetCDF-output en converteert naar PI timeseries.
Beperking: read-only, geen authenticatie, geen schrijf-operaties. Geen WebSocket push — polling per request.
wflow NetCDF-output (historische runs op schijf) + RWS Waterinfo live.
n.v.t. — interoplaag, geen eigen modelrun of randvoorwaarde.
data_adapter.py (wflow NetCDF) + pi_client.py (RWS) → PI JSON v1.25, FastAPI-router.
filters · locations · parameters · timeseries op /fews/rest/fewspiservice/v1/ (Deltares PI REST 1.25).
Geen — read-only databron-service.
Eén "verhaal" over een station — bijvoorbeeld Kampen tijdens juli 2021 — combineert nu stationmetadata, drempelregimes, gemeten RWS-debiet, gesimuleerd wflow-debiet én de 14-daagse verwachting met AI-interventie. Voorheen moest een consument die uit losse REST-calls zelf aan elkaar knopen.
Met de GraphQL-gateway haalt één query precies de gevraagde velden op, en wordt het schema de domeingraaf: station → thresholds / measured / simulated / forecast.
Bewijst de platformvisie: API-first, headless, elke consumer (dashboard, FEWS, app, webhook) is een verwisselbare client.
Stack: strawberry-graphql[fastapi] gemount via GraphQLRouter op de bestaande uvicorn-app (/graphql, GraphiQL aan). Read-only, geen mutations/subscriptions, geen auth (PoC, LAN/Tailscale).
Geen tweede datapad: de resolvers delegeren naar bestaande bronfuncties — build_forecast(), get_waterinfo_timeseries(), get_wflow_timeseries() en _build_intervention(). Geen gedupliceerde datalogica.
Voorbeeld-velden: measured = live RWS, simulated = wflow-output (peak 1627 m³/s op 2021-07-17), forecast.band = P10/mean/P90, forecast.intervention = Claude-duiding met graceful fallback.
Bestand: dashboard/graphql_api.py (~1 module, thin resolvers).
station → nearbyGroundwaterWells toe (WL-GQL-2) zodra de BRO-connector (Proef 9) bestaat — dit koppelt de platform-laag aan de nieuwe databron.Delegeert naar bestaande bronnen: build_forecast(), get_waterinfo_timeseries(), get_wflow_timeseries(), _build_intervention().
Geen tweede datapad — thin resolvers, geen gedupliceerde datalogica.
strawberry-graphql[fastapi] read-only gateway op /graphql (GraphiQL aan).
Domeingraaf station → thresholds / measured / simulated / forecast; één query haalt precies de gevraagde velden.
forecast.intervention = Claude-duiding via de gedelegeerde functie (graceful fallback).
Koppelt echte gemeten grondwaterstanden (BRO) langs de oostflank van de Veluwe aan het IJssel-peil bij Kampen. Toetst de hypothese kwelzone-omkering bij laagwater data-gedreven op het droogte-event zomer 2018 — zonder eerst een MODFLOW/iMOD-model te bouwen.
De analyse berekent de lag-correlatie (rivierpeil → grondwaterrespons, dagen–weken vertraging) en laat Qwen2.5-32B lokaal een hydrologische duiding genereren.
Vooruitblik + voorspelling: de tab projecteert de live IJssel-verwachting naar de verwachte grondwaterrespons, en een lineair reservoirmodel voorspelt de absolute grondwaterstand per put met onzekerheidsband — dat overbrugt de BRO-meetlatentie van maanden (nowcast → forecast).
Maakt de bestaande conceptuele backlog-POC "iMOD · Veluwe grondwater" voor het eerst uitvoerbaar met data.
Discovery + bridge: PDOK OGC API Features bro-gminsamenhang-karakteristieken, collectie gm_gld met bbox → per put: BRO-ID, locatie, GMW-koppeling, aantal observaties, datumbereik én CSV-reeks-URL. Eén call lost discovery + GMW↔GLD-koppeling op.
Reeksen: broservices GET /gm/gld/v1/seriesAsCsv/{broId}?asISO8601=true (CSV, m-waarden). Server-side requests (CORS-restrictie omzeild), 15-min cache.
Feasibility bevestigd (WL-BRO-0): bbox Veluwe-oostflank ↔ IJssel = 997 GMW-putten, 313 GLD's met zomer-2018 data. 5 kandidaatputten gecureerd.
AI: Qwen2.5-32B lokaal — zelfde patroon als de ensemble-interpretatie, met graceful fallback.
Voorspelling (v2 + v2b): lineair reservoir met twee termen — recharge (Open-Meteo neerslag−ET0) én de IJssel-stand (RWS WATHTE Kampen): GW = base + k_r·Σ recharge·e^(−i/τr) + k_q·Σ rivier·e^(−i/τq). Per put gekalibreerd op de BRO-historie (2D-grid τ + regressie); nowcast laatste-meting→vandaag + forecast +14 d, bias-correctie + NSE + band; graceful fallback naar recharge-only. Endpoints /api/grondwater/projection, /api/grondwater/reservoir. Zie docs/grondwater_voorspelling_voorstel.md.
bro_gld + overlay, lag-correlatie, projectie, reservoirmodel v2 (recharge) + v2b (river-term, RWS-stand) en GraphQL-koppeling nearbyGroundwaterWells (Proef 8). De river-term verbetert alle putten; GLD…8262 blijft moeilijk (NSE 0.22, band ±1 m).BRO GLD via PDOK OGC (gm_gld, bbox) + broservices seriesAsCsv · Open-Meteo (neerslag/ET0) · RWS WATHTE Kampen. Server-side, gecached.
Recharge (neerslag−ET0) + IJssel-stand (RWS WATHTE Kampen) als river-term; per put een eigen lag/τ, gekalibreerd op zomer 2018.
Lag-correlatie + lineair reservoir (v2 recharge, v2b + river-term). Data-gedreven — géén fysisch kwelmodel (geen MODFLOW/iMOD).
Absolute grondwaterstand per put + NSE + onzekerheidsband. /api/grondwater/projection · /api/grondwater/reservoir
Qwen2.5-32B lokaal (llama.cpp, Jetson) — hydrologische duiding, graceful fallback.
Hoe goed is het model écht? Per netwerkpunt leggen we simulatie en meting naast elkaar met een objectieve skill-score (NSE, KGE, Pearson r, bias). Eerlijk principe: een score alleen waar een onafhankelijke meting bestaat — de rest staat met reden in de matrix onderaan, geen verzonnen getallen. De scores zijn ook een API-veld: /api/validation.
Terugblik: voor elke dag in het venster reconstrueren we de toen uitgegeven Westervoort-verwachting (de recessie die de live verwachting gebruikt) en leggen die over de gerealiseerde RWS-meting — "leg de groene over de blauwe, wat was de fout?" Zo zie je de fout per lead-time (dag 1–14) en of de onzekerheidsband klopt. Realisatie = RWS Westervoort: dezelfde bron als hierboven, geen tweede waarheid.
| Punt / run | Status | Reden |
|---|
Skill-metrieken: dashboard/validation.py (getest, tests/test_validation.py). NSE/KGE: 1 = perfect, 0 = niet beter dan het gemiddelde, <0 = slechter. Bij waterpeil staat de simulatie op een ander referentievlak → alleen de dynamiek (geanomaliseerd) wordt getoetst, met het datum-offset apart.
De hindcast liet zien dat de Westervoort-verwachting systematisch overschat. Hier assimileren we de recente RWS-meting via een ensemble-Kalman-update (Ensemble Smoother, EnKF-familie) op de recessieparameters (recessiesnelheid τ + seizoensdoel), en corrigeren zo de verwachting. Onder de live-grafiek staat het bewijs: de fout per horizon vrij vs geassimileerd, gedraaid via dezelfde hindcast-machinerie. Getoetst bij Westervoort — dezelfde meetbron als de validatie, geen tweede datapad.
Het model schat hoe de rivierafvoer de komende twee weken langzaam wegzakt nadat de regen is gevallen. Maar het bleek dat het model dat steevast te hoog inschat — het denkt dat het water trager wegloopt dan in werkelijkheid.
Data-assimilatie lost dat op: we laten het model eerst de metingen van de afgelopen ~10 dagen bekijken en zijn aannames daarop bijstellen, vóórdat het vooruit voorspelt. Vergelijk het met een hardloper die zijn eindtijd bijstelt op basis van hoe snel hij de eerste kilometers écht liep — in plaats van vast te houden aan de gok vooraf.
In de grafiek hieronder: de groene lijn is wat er echt gemeten is, de rode stippellijn is de oude (ongecorrigeerde) verwachting die te hoog zit, en de blauwgroene lijn is de bijgestuurde verwachting — dichter bij de werkelijkheid. De schaduw eromheen is de eerlijke onzekerheid.
Onderaan staat het bewijs: als je dit voor elke dag in het verleden nadoet, is de fout van de bijgestuurde verwachting op elke voorspeldag kleiner. Kortom: door de meting mee te nemen wordt de verwachting betrouwbaarder — en het blijft eerlijk over hoe zeker het is.
Geanimeerd uit de live assimilatie-data; opnieuw te genereren met tools/make_assim_video.py.
Van de recessie-ODE via het ensemble-Kalman-filter tot de RMSE-toets — formules met MathJax, voorkennis 1e jaar TU. Deck: tools/math_slides.html, render: tools/make_math_video.py.
Dit draait exact het model uit de video — zie tools/enkf_from_scratch.py ↗, een ~50-regels-versie recht uit de formules die de productie-code tot machineprecisie reproduceert. Sleep aan de knoppen en zie het effect op de verwachting en de fout.
Laden…
RMSE = snelle schatting over de laatste ~24 uitgifte-dagen (voor responsiviteit). Meetfout klein ⇒ grotere gain ⇒ sterkere correctie — precies de 1-D-intuïtie uit de video.
Stel een vraag over Waterlab: de inrichting, de proeven, de herkomst van de data of de skill van de modellen. De assistent (Claude Haiku) antwoordt uitsluitend op basis van de eigen herkomst- en uitleg-bronnen (WL-PROV/GOV + handleiding) — staat het er niet in, dan zegt hij dat eerlijk. Achter login en met een budget per sessie i.v.m. tokenkosten.
Toegang vereist een wachtwoord (tokenkosten-beheersing).
Waterlab is een persoonlijk leerplatform rond hydrologische modellering en AI, draaiend op een NVIDIA Jetson AGX Orin. Drie leerdoelen: modellen leren (wflow, Ribasim), het vakgebied verkennen (IJssel–Rijn, droogte/hoogwater) en AI testen (Claude + lokale Qwen). Deze pagina legt uit hoe je het dashboard en de API's gebruikt.
| Tab | Wat je ziet |
|---|---|
| Waterlab | Introductie en de drie leerdoelen. |
| Rijn & IJssel | Uitleg van het stroomgebied: Pannerdense Kop, het traject Westervoort → Kampen, en de kaart. |
| POC's | De negen proeven, elk langs drie lijnen: functioneel · technisch · opvolging + een uitklapbare herkomst-keten (databron → … → AI-duiding). |
| Verwachting | Live 14-daagse IJssel-verwachting + de integrale AI-interventie (incl. grondwater-koppeling). |
| Ensemble AI · Multimodel | wflow-ensemble (5 neerslagscenario's) en de Ribasim→LLM→wflow pipeline. |
| Jan 1995 · Zomer 2018 · Jul 2021 | Historische wflow-simulaties (hoogwater/droogte-events). |
| Grondwater | Proef 9: gemeten grondwater (BRO) vs IJssel-peil + lag-correlatie + AI-duiding. |
| Validatie | Gesimuleerd vs gemeten per punt met skill-score (NSE/KGE/r/bias) + eerlijke "niet te valideren"-matrix. API: /api/validation. |
| Vraag het | Uitleg-chatbot (Claude Haiku), gegrond in de eigen herkomst-/uitleg-bronnen — geen vrije generatie. Achter login + budget per sessie. |
| FEWS | Waterlab als FEWS PI REST-service (API-explorer). |
| Platform Visie · Roadmap · Info | Architectuurvisie, backlog en analyse-achtergrond. |
Waterlab is API-first: elke consumer (dashboard, FEWS, app) is een verwisselbare client. Er zijn drie ingangen.
/fews/rest/fewspiservice/v1/filters, locations, parameters, timeseries.
/api/forecast · /api/grondwater · /api/ensemble · /api/multimodel — JSON voor de dashboardpanelen.
GraphQL voorbeeld — één query haalt stationmetadata, de live verwachting én de nabije grondwaterputten op:
{
station(id: "kampen") {
name
forecast(days: 14) {
band { date p10 mean p90 }
intervention { regime text source }
}
nearbyGroundwaterWells(radiusKm: 20, limit: 3) {
broId distanceKm nObs
series(period: "2018-06-01/2018-08-31") { events { date value } }
}
}
}
PI REST voorbeeld (vanaf de commandline):
curl "http://<host>:8000/fews/rest/fewspiservice/v1/timeseries?locationIds=KAMPEN¶meterIds=Q.sim&period=2021"
Limieten op GraphQL: read-only (geen mutations); query-diepte max 10, max 2000 tokens, max 15 aliassen per query; en max 60 verzoeken per minuut per IP (HTTP 429 bij overschrijding). Zware bulk-tijdreeksen blijven bewust op PI REST/JSON (HTTP-caching).
| Bron | Gebruik |
|---|---|
| RWS Waterinfo | Live debiet (Westervoort/Lobith) en waterpeil (Kampen) — voor de verwachting. |
| Open-Meteo / ECMWF | 14-daagse neerslagverwachting IJssel-stroomgebied. |
| wflow SBM (Julia) | Hydrologische simulaties (historische events + ensemble), output op schijf. |
| BRO GLD + PDOK OGC | Gemeten grondwaterstanden + putlocaties (Proef 9). Server-side opgehaald, gecached. |
| ERA5-Land | Historische forcing/training voor de modellen. |
Waterlab is een leer-/PoC-omgeving, geen operationeel systeem. De verwachting is indicatief (statistisch model, geen gekalibreerde nowcast). BRO-grondwaterdata heeft maanden latentie; de IJssel→grondwater-koppeling is gekalibreerd op één droogte-event (2018) en data-gedreven, niet fysisch (geen MODFLOW/iMOD). Voor operationele beslissingen: waterinfo.rws.nl.
Persoonlijk leer- en experimenteerplatform voor hydrologische modellering en AI-integratie in waterbeheer. Één persoon, één edge computer (NVIDIA Jetson AGX Orin), open data en open-source modellen — om te begrijpen wat er mogelijk is.
Achterliggende vraag: hoe zou een modern kennisinstrumentarium voor waterbeheer eruitzien als je het vandaag opnieuw ontwerpt — API-first, AI-ondersteund, zonder legacy GUI-afhankelijkheden? De uitkomsten zijn indicatief. Dit is geen operationeel systeem.
Kies een waardestroom. Je ziet welke informatiefuncties ze raakt — innemen, schematiseren, rekenen, duiden, samenstellen, ontsluiten, valideren — en komt dan bij de proef die het aantoont. De negen proeven blijven eronder staan als techniek; hier staat de waarde voorop.
| Waardestroom | innemen | schematiseren | rekenen | duiden | samenstellen | ontsluiten | valideren |
|---|---|---|---|---|---|---|---|
| W1Verwachting Proef 1 · live 14-daagse + AI bekijk →herkomstskill | |||||||
| W2Historisch & ensemble Proef 2 · 4 · 5 · 6 · wflow SBM bekijk →herkomstskill | |||||||
| W4Multimodel Proef 3 · Ribasim → AI → wflow bekijk →herkomst | |||||||
| W5Grondwater & validatie Proef 9 · BRO + reservoir bekijk →herkomstskill |
● = de waardestroom raakt deze informatiefunctie. De schil is een client op dezelfde API's; de proeven, FEWS en GraphQL blijven onveranderd bereikbaar via de tabs hierboven.
Positionering: het innovatie-/leersegment van de waardeketenring, niet de operationele keten. Uitkomsten zijn indicatief — voor operationele beslissingen: waterinfo.rws.nl. · volledige wél/niet-tekst ↗
Herontwerp van het complete waterinstrumentarium als een API-first platform zonder GUI-afhankelijkheden. Een generieke basislaag verzorgt data-ingestie, model-executie en AI-orkestratie; domeinmodules voegen gebiedsspecifieke schematisaties, drempelwaarden en expertkennis toe als configuratie — niet als code. Elke consumer (dashboard, FEWS, app, webhook) is een verwisselbare API-client. Het resultaat is een significante vermindering van koppelingen en een platform dat zonder aanpassingen een nieuw stroomgebied ondersteunt zodra de domein-config beschikbaar is.
domain.yaml
met schematisatie-pad, meetpuntlocaties, drempelwaarden en LLM-contextprompt.
| Component | Huidig (Waterlab) | Nieuw platform | Winst |
|---|---|---|---|
| Model-uitvoer | Per-event Julia scripts, hardcoded paden, handmatig starten | Model Execution API: POST /models/wflow-sbm/run → run-id |
containerized versioned |
| GUI / frontend | FastAPI serveert statische HTML + JS; tightly coupled met server | API retourneert JSON/GeoJSON; elke consumer is verwisselbaar | ontkoppeld headless |
| AI / LLM | Directe Anthropic API-call in server.py; lokale Qwen los beheerd | AI Orchestration Service: één interface, pluggable backends (cloud/lokaal) | unified switchable |
| Data-ingestie | Per-script: download_forcing.py, forecast.py, RWS-fetch los | Data Hub connectors: gestandaardiseerde adapter per bron, gecached | normalized cacheable |
| Configuratie | Hardcoded paden in TOML/YAML, per-event handmatig aanpassen | Domain config YAML: één bestand volledig beschrijft een domein | portable reproduceerbaar |
| Observability | Print-statements, handmatig logbestanden bekijken | Structured logs, distributed tracing (OpenTelemetry), audit trail | traceable alerting |
| Nieuw domein toevoegen | Fork + aanpassen van model-scripts, nieuwe export-scripts, nieuwe endpoints | Schrijf domain.yaml, registreer model-adapter → klaar |
0 code-wijziging |
POST /models/wflow-sbm-ijssel/run retourneert een run_id;
polling of webhook bij gereed. Bestaande scripts ongewijzigd — API is een thin adapter.
Resultaat: model-uitvoer is nu aanroepbaar door elke service of agent.
GET /data/{source}/{variable}/{bbox}/{period}.
Cache-laag (15 min) vervangt de huidige ad-hoc caching in server.py.
Open-Meteo, ERA5T, RWS Waterinfo en FEWS Web Services krijgen elk een adapter.
ijssel.yaml.
Voeg een Event Bus toe die observatie-triggers en drempeloverschrijdingen publiceert;
de AI-agent subscribet hierop. Nieuw domein = nieuw YAML-bestand.
Verkenning van uitbreidingen voor Waterlab: koppeling met het operationele Delft-FEWS-systeem van RWS, hydraulische modellen (SOBEK, D-FLOW FM), data-assimilatie via een Ensemble Kalman Filter, een live physics-based ensemble en klimaatprojecties met KNMI'23-scenario's. De zes uitgewerkte cases zijn concreet uitvoerbaar op de bestaande Jetson-hardware en sluiten direct aan op wat al draait.
De review was enthousiast maar legde één structureel risico bloot: voor een vakgenoot was de herkomst van data en schematisatie onzichtbaar en niets tegen metingen gevalideerd. De leidende as van de opvolging is daarom vertrouwen, niet cosmetica: eerst herkomst zichtbaar maken, dan skill tegen metingen, dan interpretatie aan de kaart. Volledige backlog: docs/BACKLOG_WaterLab_review ↗.
| ID | Actie | Status | Waar te zien |
|---|---|---|---|
| WL-VIS-2 | Eerlijke grafieklabels — elke serie gemeten / gesimuleerd / verwacht + eenheid, gescheiden assen | ✓ klaar | alle grafieken |
| WL-GOV-1 | Canonieke "wat dit wél/niet bewijst"-grens | ✓ klaar | landing · doc ↗ |
| WL-PROV-1 | Aftepelbare herkomst-keten per proef (databron → randvoorwaarde → modelstap → output → AI-duiding) | ✓ klaar | POC's-tab |
| WL-PROV-2 | Herkomst van schematisatie & instroom-randvoorwaarden per run | ✓ klaar | doc ↗ |
| WL-VAL-1 | Gesimuleerd vs gemeten per punt + skill-score (NSE/KGE/r/bias), eerlijke "niet te valideren"-matrix | ✓ klaar | Validatie-tab · /api/validation |
| WL-VAL-2 | Hindcast-terugblik — uitgegeven verwachting vs realisatie, fout per horizon | ✓ klaar | Validatie-tab |
| WL-VIS-1 | Elk netwerkpunt klikbaar → detail (debiet, rivierdiepte, hoger/lager dan normaal) + tooltip | ✓ klaar | kaart Jul 2021 |
| WL-COMM-1 | Site-herinrichting: redeneerlijn-schil (waardestromen × informatiefuncties) | ✓ klaar | landing |
| WL-CHAT-1 | Bevraagbare uitleg-chatbot, gegrond in de PROV-bronnen, achter login + budget | ✓ klaar | Vraag het-tab |
| WL-DEMO-1 | Rondleiding afgestemd op het herkomst- & validatieverhaal (~4 min) | ✓ klaar | DEMO.md |
| + extra | Labelcorrectie: wflow h_river = rivierdiepte, niet m+NAP (bijvangst VAL-1) · docs-viewer /docs/{naam} | ✓ klaar | grafieken · docs |
| WL-FC-1 | Statistisch model vervangen door wflow SBM nowcast | ⏳ open | geblokkeerd op voorverwarmde Julia-sysimage (build_sysimage.jl); bewuste PO-noot |
Waterlab publiceert wflow SBM output en RWS Waterinfo data via een FEWS-compatibele PI REST API. De vier standaard-endpoints zijn live op localhost beschikbaar.
Wil je zien hoe een héle proef via deze interface binnenkomt?
← klik een endpoint om de ruwe PI JSON response te zien
Waterlab implementeert versie 1.25 van de PI REST standaard (Deltares/RWS).
De parameter period is een niet-standaard extensie
om historische wflow simulaties op te vragen via dezelfde endpoint.
Hier draaien we een bestaande proef volledig via de FEWS PI REST-interface (/fews/rest/fewspiservice/v1/) — niet via de interne API. Elke waarde die je hieronder ziet is opgehaald zoals een échte Delft-FEWS-instantie dat zou doen: eerst de bron ontdekken (filters → locations → parameters), dan de tijdreeks (timeseries) binnenhalen als PI JSON. Zo bewijst Waterlab dat het een verwisselbare FEWS-databron is — geen tweede datapad.
Bron: alle waarden via /fews/rest/fewspiservice/v1/timeseries?parameterIds=Q.sim&period=… — dezelfde proef (wflow SBM 1995/2018/2021), maar binnengehaald als FEWS-client. De meet-parameters Q.meting/H.meting zijn via dezelfde interface beschikbaar, maar leveren live RWS-data (recent) — niet de event-periode — dus die overlayen we hier bewust niet.