Game design documentSystem architectureReference core v0.1

CANDUsim

Run a CANDU unit for a shift. Reactor Shift is the arcade version: one screen, six buttons, keep the city lit, keep the zones in the green, and handle whatever the plant throws at you in eight minutes. Underneath the buttons is a real CANDU reactor model — it does what a CANDU does, so the game teaches you the plant without a textbook.

Unit 1 · live from the core on this page
Reactor power
%FP
Avg zone level
%
Xenon load
mk
Generator
MW

Choose how the game looks

Four art directions, one scene: the reactor face with FM-1 clamped on channel L12, the reactor annunciator panel, and the liquid zone bar graph. Each artboard keeps its own look regardless of the page setting — pick one with Select and the whole site (including the live panel below) restyles to it. Your choice is remembered in this browser.

Live control panel

This is not a mock-up. The panel is driven by assets/candu-core.js — 14-zone coupled point kinetics with an implicit solve, precursors, photoneutrons, I/Xe, RRS bulk + spatial control, setback/stepback, lumped HTS and secondary side, SDS1/SDS2 trip logic, ECC staging and containment. Pick a scenario, then operate.

What this is / isn't. It is the same reference core the game engine will call every tick, with representative public CANDU 6 / Bruce-class numbers in one editable PLANT table. The heat transport and secondary side are lumped (one loop, one boiler pressure, one pressuriser); the containment is a single volume with PRV relief and dousing. It is not safety-analysis grade, it carries no station-specific data, and the restart overshoot is a known tuning item. Time acceleration (×10–×600) uses larger implicit steps when nothing fast is happening and drops back to 20 ms whenever log rate exceeds 2 %/s or rods are moving.

Physics validation

Numbers from the reference core's regression run (the same scenarios you can load above). The whole run takes about two seconds of wall-clock time.

Steady state

Holds exactly

100.00 %FP, zones at 50 %, generator 915 MW, zero drift over 10 min with photoneutron precursors included in βeff.

+1 mk ramp / 100 s

No trip

Peak 100.1 %FP; the zones absorb the whole insertion (average level 50 → 64 %), no coarse device moves.

Refuelled channel · Z4

Spatial control works

One channel refuelled (+0.3 mk) → 5 % tilt; the affected compartment fills to 77 % while the other thirteen sit near 53 %.

Spatial control disabled

Divergent xenon oscillation

Same perturbation, no spatial term: the tilt grows from 1 % to 16 % over 20 h and keeps growing — a divergent xenon spatial oscillation. This is the reason the 14 liquid zones exist.

Loss of regulation

SDS1 catches it

Zones draining with RRS off → power rises; SDS1 trips on high neutron power at ~1.9 min.

Grid load rejection

Stepback to 60 %

MCA drop to 56 %, re-clutched with MCAs 26 % in, holding 60 % from 3 min; the MCAs then withdraw as xenon builds. Return to 100 % clean, zones ending ~84 %.

Large LOCA

Full safety sequence

Trip, void and depressurisation; RB pressure held at +7 kPa(g) by the PRVs; ECC high-pressure injection at ~3.5 min; inventory restored.

Trip xenon transient

Poison-out

Peak −125 mk at 10.3 h; −12 mk relative at 30 min; post-trip growth ≈0.45 mk/min; poison-override window ≈40 min.

Careful restart at 3 min

Critical at ~9 min

SDS1 reset, bank A out, bank B out with 5 rate holds; 60 %FP at ~17 min with no overshoot peak — documented as a tuning item.

Late restart at 90 min

Poisoned out

Adjusters fully out, zones empty, still ~−5 mk. Correct physics: the window closed.

How it is built

Physics core

Engine-agnostic, no DOM, SI units, one editable plant table. 14 coupled zones solved implicitly each step (14×14 dense solve), six delayed groups + nine photoneutron groups semi-implicit, I/Xe per zone, lumped fuel/coolant thermal lags, feedback coefficients incl. positive void. GDD Part 1 →

Tick architecture

Kinetics at 20 ms when anything is fast, up to 2 s implicit steps when accelerated; RRS, HTS and safety logic run every substep; annunciator state derived once per frame. The game engine calls unit.advance(dt) and reads state — no engine code in the model.

Fault engine

unit.inject(id) plus per-step tickFaults: load rejection, turbine trip, pump trip, loss of regulation, small/large LOCA, loss of feedwater/condenser, channel blockage, feed-and-bleed failure, moderator cooling loss. Observable only through instruments and field cues. GDD Part 3 →

Roadmap

M0 reference core + this site · M1 engine port, MCR panels, RRS/annunciator UI · M2 field operator, fuelling machine, refuelling loop · M3 fault engine + SSS scenarios · M4 tutorial campaign + scoring · M5 multi-unit station, vacuum building, shift handover.

Repository layout

site/                      this page (static, hosted at candusim.nomanmaheri.ca)
site/assets/candu-core.js  engine-agnostic reference core — port target for Unity (C#) / Godot / Unreal
site/assets/panel.js       live panel wiring (rAF loop, presets, chart, event log)
site/assets/scene.js       art-direction mock scene renderer
site/gdd.html              Game Design Document (Parts 1–3 + architecture)
site/tutorial.html         tutorial campaign