Reactor Kinetics Lab
A reactor-physics sandbox I built to connect OpenMC, Python solvers, a React dashboard, and Modelica thermal hydraulics in one interactive simulation.
I developed a theoretical heavy-water-moderated, natural-uranium reactor model and wrapped it in a web application so I could operate it instead of only producing plots from a notebook.
The simulator has one main operator input—control-rod insertion—and shows how reactivity, neutron flux, thermal power, and coolant conditions change with time. I can pause or reset the transient, trigger a SCRAM, and inspect the core flux and power distributions. The project is still a simplified educational model, but it gave me a place to connect reactor physics, numerical methods, thermal hydraulics, and software into one working system.
The main challenge
Getting four different tools to work together
The most interesting part of this project was not any single solver. It was deciding what each tool should be responsible for and building the interfaces between them.
OpenMC
I began with a homogeneous one-group annular model, but its limitations pushed me toward Monte Carlo transport. I explored an FRM-II-inspired involute core before settling on a simpler concentric geometry. I then wrote the workflows that turn OpenMC runs into multigroup cross sections, delayed-neutron data, rod-worth tables, reactivity coefficients, and reference power shapes.
Python
I wrote a FastAPI backend to own the simulation state and advance the physics. It runs six-group point kinetics for the transient and a resolved four-group finite-volume diffusion model for the Core view. Keeping the physics on the server also meant the browser never became a second, conflicting simulation engine.
Modelica
I modeled the primary D₂O loop, fuel heat structure, pump, and heat exchanger in Modelica, exported the model as a co-simulation FMU, and connected it to Python with FMPy. Python sends reactor power and an axial power shape; Modelica returns temperatures, moderator density, mass flow, and pressure drop. The effective fuel and moderator states then feed back into reactivity.
Web app
I built the operator interface with Bun, React, and TypeScript. It sends rod, run, reset, and SCRAM commands to FastAPI, polls the current state, and turns the results into an HMI, trend plots, and core maps. This made debugging the coupled model much more intuitive than working through disconnected scripts.
Some reactor physics
What sits behind the interface
The live transient uses an implicit point-kinetics solver with six delayed-neutron groups and a nominal 20 MWth operating point. Total reactivity combines the clean-core excess, the control-rod contribution, SCRAM worth, and temperature and density feedback from the Modelica FMU.
The separate Core view uses OpenMC-generated four-group data in an r-z finite-volume diffusion solve. The clean-core mesh has 13,500 cells and gives keff = 1.001429. I use the continuous-energy OpenMC result to correct the displayed fission-power shape, while keeping OpenMC itself outside the live request path.
Side experiment
Taking the same data into GeN-Foam
I also experimented with feeding the same OpenMC multigroup data into GeN-Foam. I wrote a conversion path for the nuclear data and initial flux fields, generated an axisymmetric wedge with Gmsh, and automated the OpenFOAM mesh and region checks. It is not part of the live web app, but it was a useful test that my OpenMC exports were explicit enough to drive a second deterministic solver rather than being tailored only to my Python implementation.
What I have intentionally left simplified
- One effective rod bank stands in for a full control system.
- Rodded four-group maps use an equivalent absorber and remain provisional.
- The FMU axial power shape and absolute flux scale still inherit pieces of the original one-group model.
- Xenon, burnup, and two-phase void behavior are not yet included.