---
title: 'Two Graphs Are Simpler Than One: A Cooling System Simulator'
description: Live cooling-system sim for a PLC cybersecurity event. Splitting flow and heat into two graph passes kept it cheap and the behaviour believable.
date: 2026-05-04
period: 'October-November 2018'
thumbnail:
  src: ./_assets/process-simulator.jpg
  alt: Cooling system simulator interface with pipes, pumps, and temperature values.
article:
  featuredOrder: 5
  tags: ['simulation', 'systems', 'tools']
  stack: ['Python', 'Flask', 'NumPy', 'HTML canvas', 'JavaFX']
  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
  media:
    - type: image
      src: ./_assets/process-simulator.jpg
      alt: Screenshot of the cooling system simulator with pipes, pumps, coolers, and temperature values.
      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: The JavaFX editor produced JSON that the simulator ate as input.
project:
  title: Cooling System Simulation
  selected: true
  thumbnail:
    src: ./_assets/nuclear-simulation.jpg
---

Solving flow and heat as one coupled system would have been a genuine CFD problem, and I had two weeks. A cybersecurity event in late 2018 needed a cooling-plant simulator that contestants could poke at through PLCs over a weekend, and that deadline made every decision for me: cheap to compute, plausible to a non-specialist, able to run unattended on one server from Friday night to Sunday. The design move I'd keep from it is modelling flow and heat as two separate graph passes instead of one PDE.

## What the event needed

The challenge was about PLCs. Contestants changed setpoints, valves, and pump speeds, and they needed to see whether their action kept the plant stable, wasted coolant, or melted something. Concretely that meant a single simulation server feeding many monitoring clients in near real time, a plant configurable enough that the organisers could hand me a new layout on Friday night and have it running Saturday morning, and behaviour legible enough to teach. A simulator nobody understands isn't a teaching tool; it's noise with units on it.

## The split that made it cheap

Each tick runs two passes:

1. **Flow first, as a graph traversal.** Walk the pipe network outward from the pumps, accumulate pressures, distribute water across the nodes.
2. **Heat second, as a linear system.** Use the flow result to build an adjacency matrix, add the boundary conditions (heaters, exchangers, ambient temperatures), and let NumPy solve for the node temperatures.

As physics, this is wrong; flow and heat are coupled, and a real plant knows it. As a model, it's right, because nothing a contestant could do made the coupling perceptible on a dashboard, and the decoupled version cost a tiny fraction of the real solve. It bought one more thing I didn't anticipate valuing so much: a clean seam for debugging. When the temperatures looked weird, I knew which of the two passes to suspect before I'd opened the editor.

## The editor mattered more than the solver

The brief for the editor was one sentence: non-technical organisers needed to rewire the plant, live, without me hovering behind them. That quietly ruled out every interface I'd have enjoyed building, namely config files, a DSL, anything with a curly brace in it. So the most-used piece of the whole system ended up being a separate JavaFX desktop tool where organisers laid the plant out as a graph, dragged pipes between pumps and coolers, tuned each element's parameters in a side panel, and exported the JSON the sim consumed, or uploaded it straight to the backend.

It was the smallest piece of the project and the one the event quietly depended on. A simulator only its author can reconfigure is a demo; the editor is what made it a tool other people could own for a weekend. The simulator decided what was possible, but the editor decided who could use it.

## What I'd change

- **State what the model claims.** A convincing simulator owes its audience an honest README about what it does and doesn't capture. Mine didn't have one, and anyone who took the numbers seriously could have walked away believing more than the model deserved.
- **Recorded scenarios as regression tests.** Simulations drift in ways that still look plausible on screen. A few stored "this input over 60 seconds produces these outputs" runs would have caught me when I broke the temperature solver, which I did on the Saturday morning of the event, naturally.
- **Skip JavaFX.** Cross-platform desktop packaging was a fight, and every install I asked of the organisers was one more reason to call me over, which is the exact thing the brief said to prevent. A web editor living next to the monitoring clients would have meant one fewer install for everyone.
