Update content & design (#75)
All checks were successful
Deploy to Pages / build (push) Successful in 2m58s

Reviewed-on: https://home.schmelczer.dev/git/git/andras/schmelczer-dev/pulls/75
This commit is contained in:
Andras Schmelczer 2026-05-28 16:20:12 +01:00
parent 0be50b6c24
commit b554e92e9f
83 changed files with 2995 additions and 723 deletions

View file

@ -1,6 +1,6 @@
---
title: Graph Models for a Real-Time Cooling Simulation
description: Simulating a nuclear facility cooling system with graph traversal, matrix solving, Flask, NumPy, and real-time monitoring clients.
title: 'Two Graphs Are Simpler Than One: A Cooling System Simulator'
description: A live cooling-system sim for a PLC cybersecurity event. Splitting flow and heat into two graph passes kept the calculation cheap and the behaviour believable.
date: 2026-05-04
projectPeriod: 'October-November 2018'
thumbnail:
@ -10,47 +10,49 @@ tags: ['simulation', 'systems', 'tools']
featuredOrder: 5
role: Simulation and UI author
stack: ['Python', 'Flask', 'NumPy', 'HTML canvas', 'JavaFX']
scale: Remote simulation server with multiple monitoring clients and a separate graph editor
outcome: A believable, extensible cooling-system simulation for a cybersecurity challenge context
scale: One remote sim server, many monitoring clients, separate JavaFX graph editor
outcome: A believable PLC simulation usable by non-specialists during a live cybersecurity challenge
audience: recruiter-relevant
links: []
media:
- type: image
src: ./_assets/process-simulator.jpg
alt: Screenshot of the cooling system simulator with pipes, pumps, coolers, and temperature values.
caption: The simulator calculated flow and temperature over graph-based process models.
caption: Flow ran first as a graph traversal, then heat solved as a matrix equation.
- type: image
src: ./_assets/process-simulator-input.jpg
alt: Screenshot of the JavaFX graph editor used to define simulator input.
caption: A separate JavaFX editor produced JSON inputs for the simulation backend.
caption: The JavaFX editor produced JSON that the simulator ate as input.
---
This project simulated the cooling system of a nuclear facility. It was built for a cybersecurity challenge about PLCs, where participants needed to see the consequences of changing a system state.
Trying to solve flow and heat as a coupled system would have been a real CFD problem and I had two weeks. A cybersecurity event in late 2018 needed a cooling-system simulator that contestants could poke at through PLCs over a weekend, and the deadline shaped every decision after it: cheap to compute, plausible to a non-specialist, runs all weekend on one server. The useful design move was modelling flow and heat as **two separate graph passes**, not one combined PDE.
The simulation did not try to be physically complete. It aimed to be cheaply calculated, believable to a non-specialist, scalable enough for the event context, and understandable through a clean GUI.
## What the event needed
## The Problem
The challenge was about PLCs. Contestants would change setpoints, valves, or pump speeds, and we needed them to see whether their action made the plant stable, wasted coolant, or melted something. That meant:
The simulated system needed reactors, coolers, pumps, heat exchangers, drains, sources, and pipes. Those elements had to be configurable, and multiple monitoring clients needed to update in real time from a remote server.
- Multiple monitoring clients had to update from one simulation server in near real time.
- The system had to be configurable enough that the event organisers could ship me a new plant on Friday night and have it running Saturday morning.
- It had to be obvious. A simulator nobody understands isn't a teaching tool, it's noise.
The key challenge was representing flow and temperature in a way that was simple enough to calculate repeatedly but structured enough to produce plausible behaviour.
## The split that made it cheap
## Design
Instead of the coupled solver:
The system used two graph models. First, water was distributed by traversing the graph of pipes according to pressures generated by pumps. Then, an adjacency matrix was populated from the relations between nodes based on water flow.
1. **Flow first, as graph traversal.** Walk the pipe graph from the pumps, accumulate pressure, distribute water to nodes.
2. **Heat second, as a linear system.** Build the adjacency matrix from the flow result, add boundary conditions (heaters, exchangers, base temperatures), solve for node temperatures with NumPy.
3. Repeat both passes per tick.
After accounting for base temperatures, heaters, and heat exchangers, the matrix was solved to calculate current node temperatures. Repeating that process advanced the simulation.
This is wrong as physics. It's right as a model. Flow doesn't react to instantaneous heat in any way contestants could perceive, and the cost of solving them separately was a tiny fraction of solving them together. The clean phase boundary also meant when "the heat is weird," I knew exactly which pass to look at.
Python handled the backend logic with Flask and NumPy. The monitoring frontend used an HTML5 canvas. A separate JavaFX graph editor let users move nodes, edit element parameters, export JSON, and upload inputs to the backend.
## Why the editor mattered
## What Worked
The simulator's most-used UI was the _input_ editor, a separate JavaFX tool where you laid out the plant, set parameters per element, and exported JSON the sim ate. I wrote up the editor's [own story here](/articles/graph-editor-javafx-simulation-input/), because in hindsight it deserved to be its own project.
The graph/matrix split was a useful modelling boundary. Flow and heat exchange are related, but treating them as separate calculation phases kept the implementation easier to reason about.
The lesson: a simulation is only as useful as its input pipeline. If editing the plant requires editing source, organisers won't use it.
The editor also mattered. A simulation is much more useful when its input is inspectable and editable by people who are not editing source files.
## What I'd change
## What I Would Change
Today I would formalise the model limitations more clearly. A convincing simulation can be useful, but it should say exactly what it does and does not claim.
I would also add recorded scenarios and regression tests. Simulation projects are vulnerable to accidental behaviour changes that still look plausible on screen.
- **State what the model claims.** A convincing sim needs an honest README about what it does and doesn't model. Mine didn't. Anyone who took the numbers seriously could have walked away believing more than the model deserved.
- **Recorded scenarios as regression tests.** Sim projects drift in ways that look plausible on screen. Storing "this input over 60 seconds produces these outputs" would have caught me when I broke the temperature solver on Saturday morning at the event.
- **Skip JavaFX.** Cross-platform packaging was painful and the desktop dependency made the editor harder to hand off than it should have been. A web-based editor in the same browser the monitors used would have meant one fewer install for the organisers.