OpenMC Guide
Building a Reactor Model in OpenMC
Overview
This guide walks through creating a simple reactor model in OpenMC. We'll build the model step by step, from basic components to a complete core. Each step builds on concepts from previous tutorials, focusing on practical implementation.
Step 1: Materials
First, define the basic materials needed for the reactor: fuel and moderator. This example uses UO2 fuel and light water moderator with appropriate thermal scattering treatment.
# Basic reactor model setup
import openmc
import numpy as np
# Define materials
fuel = openmc.Material(name='UO2 fuel')
fuel.add_nuclide('U235', 0.04)
fuel.add_nuclide('U238', 0.96)
fuel.add_nuclide('O16', 2.0)
fuel.set_density('g/cm3', 10.4)
water = openmc.Material(name='Water')
water.add_nuclide('H1', 2)
water.add_nuclide('O16', 1)
water.set_density('g/cm3', 1.0)
water.add_s_alpha_beta('c_H_in_H2O')
materials = openmc.Materials([fuel, water])
materials.export_to_xml()For more complex material definitions, including burnable absorbers or control materials, see the Materials page.
Step 2: Pin Cell
Next, create a basic fuel pin cell. This forms the fundamental building block of our reactor model, consisting of a fuel cylinder surrounded by moderator.
# Define pin cell geometry
fuel_radius = 0.4
pin_pitch = 1.25
# Create cylinder and reflective square boundary
fuel_surf = openmc.ZCylinder(r=fuel_radius)
square_region = openmc.rectangular_prism(
pin_pitch,
pin_pitch,
boundary_type='reflective'
)
# Create cells
fuel_cell = openmc.Cell(fill=fuel, region=-fuel_surf)
water_cell = openmc.Cell(fill=water, region=+fuel_surf & square_region)
# Create universe
pin_universe = openmc.Universe(cells=[fuel_cell, water_cell])For detailed pin cell modeling, including gap and cladding, see thePin Cell Example.
Step 3: Fuel Assembly
Create a 17×17 fuel assembly using a lattice of pin cells. This example uses a simple uniform loading pattern.
# Create 17x17 assembly
assembly_pitch = 1.25
lattice = openmc.RectLattice()
lattice.lower_left = (-10.71, -10.71)
lattice.pitch = (assembly_pitch, assembly_pitch)
lattice.universes = [[pin_universe] * 17] * 17
# Reflective boundary around assembly
half_span = 17 * assembly_pitch / 2
xmin = openmc.XPlane(-half_span, boundary_type='reflective')
xmax = openmc.XPlane(half_span, boundary_type='reflective')
ymin = openmc.YPlane(-half_span, boundary_type='reflective')
ymax = openmc.YPlane(half_span, boundary_type='reflective')
zmin = openmc.ZPlane(-200.0)
zmax = openmc.ZPlane(200.0)
assembly_region = +xmin & -xmax & +ymin & -ymax & +zmin & -zmax
# Create assembly cell and universe
assembly_cell = openmc.Cell(fill=lattice, region=assembly_region)
assembly_universe = openmc.Universe(cells=[assembly_cell])For advanced assembly configurations, including guide tubes and varying enrichments, see theAssembly Example.
Step 4: Core Configuration
Arrange assemblies into a 3×3 core layout. This demonstrates core construction while keeping the model computationally manageable.
# Create 3x3 core
core_lattice = openmc.RectLattice()
core_lattice.lower_left = (-32.13, -32.13)
core_lattice.pitch = (21.42, 21.42)
core_lattice.universes = [[assembly_universe] * 3] * 3
# Vacuum boundary around core
core_half_span = 3 * 21.42 / 2
core_xmin = openmc.XPlane(-core_half_span, boundary_type='vacuum')
core_xmax = openmc.XPlane(core_half_span, boundary_type='vacuum')
core_ymin = openmc.YPlane(-core_half_span, boundary_type='vacuum')
core_ymax = openmc.YPlane(core_half_span, boundary_type='vacuum')
zmin = openmc.ZPlane(-200.0, boundary_type='vacuum')
zmax = openmc.ZPlane(200.0, boundary_type='vacuum')
core_region = +core_xmin & -core_xmax & +core_ymin & -core_ymax & +zmin & -zmax
# Create core cell and root universe
core_cell = openmc.Cell(fill=core_lattice, region=core_region)
root_universe = openmc.Universe(cells=[core_cell])
# Create and export geometry
geometry = openmc.Geometry(root_universe)
geometry.export_to_xml()Step 5: Simulation Settings
Configure basic simulation parameters including particle count, batches, and initial source distribution.
# Define simulation settings
settings = openmc.Settings()
settings.batches = 100
settings.inactive = 20
settings.particles = 10000
# Set initial fission source near the core center
lower_left = (-32.13, -32.13, -50.0)
upper_right = (32.13, 32.13, 50.0)
source = openmc.Source(space=openmc.stats.Box(lower_left, upper_right))
settings.source = source
settings.export_to_xml()Step 6: Tallies
Set up basic tallies to score flux and fission rate distributions across the core.
# Define tallies
mesh = openmc.RegularMesh()
mesh.dimension = [34, 34]
mesh.lower_left = [-32.13, -32.13]
mesh.upper_right = [32.13, 32.13]
tally = openmc.Tally(name='core_mesh_flux')
tally.filters = [openmc.MeshFilter(mesh)]
tally.scores = ['flux', 'fission']
tallies = openmc.Tallies([tally])
tallies.export_to_xml()For more advanced tally options, see theTallies page.
Next Steps
This example demonstrates basic reactor modeling. For more advanced topics, explore:
Step 7: Run the Model
Combine the geometry, materials, settings, and tallies into a complete model and run OpenMC to obtain k-effective.
# Build full model and run
model = openmc.Model(geometry, materials, settings, tallies)
sp = openmc.StatePoint(model.run())
print(f"k-effective: {sp.keff.nominal_value:.5f} ± {sp.keff.std_dev:.5f}")