OpenMC Guide
Geometry Basics
The Building Block Approach
OpenMC uses constructive solid geometry (CSG) where you build complex shapes by combining simple surfaces with boolean operations. Think of it like building with digital blocks - surfaces divide space into regions, and you select which regions to fill with materials.
Every surface splits 3D space into two half-spaces: positive (outside) and negative (inside). Cells are defined by combining these half-spaces with boolean operators.
import openmc
# Surface divides space into two regions
cylinder = openmc.ZCylinder(r=0.5)
# Boolean operators combine regions
inside_cylinder = -cylinder # Negative side (inside)
outside_cylinder = +cylinder # Positive side (outside)
# Note: you must always write +cylinder or -cylinder to get a half-space region.
# Using 'cylinder' alone gives a Surface object, not a Region.Tutorial snippet — no separate file in examples repo
Essential Surface Types
Start with these fundamental surfaces that cover most nuclear engineering applications:
# Cylinders (for pins, rods, pipes)
fuel_surface = openmc.ZCylinder(r=0.4096) # Cylinder along z-axis
pipe = openmc.XCylinder(r=2.5) # Cylinder along x-axis
# Planes (for boundaries, flat surfaces)
centerline = openmc.XPlane(0.0) # Plane at x=0
top = openmc.ZPlane(365.76) # Plane at z=365.76 cm
# Spheres (for particles, pellets)
pellet = openmc.Sphere(r=0.1) # Sphere at origin
# Rectangular regions (for assemblies, lattices)
box = openmc.model.RectangularPrism(2.0, 2.0) # 2×2 cm square (helper class)Tutorial snippet — no separate file in examples repo
For box-like CSG regions, current OpenMC uses the openmc.model.RectangularPrism class. Older examples that call a rectangular_prism() factory should be updated to this pattern.
Creating Cells with Boolean Logic
Cells are regions of space filled with material or left void. You define them by combining surface regions with logical operators.
# Example: Fuel pin with cladding
fuel_outer = openmc.ZCylinder(r=0.4096)
clad_outer = openmc.ZCylinder(r=0.4750)
# Define regions using boolean operations
fuel_region = -fuel_outer # Inside fuel cylinder
clad_region = +fuel_outer & -clad_outer # Between fuel and clad
water_region = +clad_outer # Outside clad
# Create cells by filling regions
fuel_cell = openmc.Cell(fill=fuel_material, region=fuel_region)
clad_cell = openmc.Cell(fill=clad_material, region=clad_region)
water_cell = openmc.Cell(fill=water_material, region=water_region)Tutorial snippet — no separate file in examples repo
The logical operators work intuitively: & means "and" (intersection), | means "or" (union), and ~ means "not" (complement).
Boundary Conditions
Surface boundary conditions control what happens when particles reach model edges:
# Common boundary types
vacuum_boundary = openmc.ZPlane(100, boundary_type='vacuum') # Particles escape
reflect_boundary = openmc.ZPlane(-100, boundary_type='reflective') # Particles reflect
periodic_left = openmc.XPlane(-10, boundary_type='periodic') # Particles wrap around
periodic_right = openmc.XPlane(10, boundary_type='periodic')
# For infinite lattice problems, use reflective boundaries
# For shielding problems, use vacuum boundaries
# For reactor cores, mix reflective (sides) and vacuum (top/bottom)Tutorial snippet — no separate file in examples repo
Working Example: Pin Cell
Here's a complete pin cell geometry that demonstrates these concepts:
import openmc
# Define surfaces
fuel_radius = 0.4096
clad_radius = 0.4750
pitch = 1.26
fuel_outer = openmc.ZCylinder(r=fuel_radius)
clad_outer = openmc.ZCylinder(r=clad_radius)
# Square boundary with reflective conditions
left = openmc.XPlane(-pitch/2, boundary_type='reflective')
right = openmc.XPlane(pitch/2, boundary_type='reflective')
bottom = openmc.YPlane(-pitch/2, boundary_type='reflective')
top = openmc.YPlane(pitch/2, boundary_type='reflective')
# Define regions
fuel_region = -fuel_outer
clad_region = +fuel_outer & -clad_outer
water_region = +clad_outer & +left & -right & +bottom & -top
# Create cells (materials defined elsewhere)
fuel_cell = openmc.Cell(fill=fuel, region=fuel_region)
clad_cell = openmc.Cell(fill=clad, region=clad_region)
water_cell = openmc.Cell(fill=water, region=water_region)
# Combine into universe and geometry
universe = openmc.Universe(cells=[fuel_cell, clad_cell, water_cell])
geometry = openmc.Geometry(universe)Tutorial snippet — no separate file in examples repo
Common Mistakes
Avoid these frequent geometry errors:
# Wrong: Missing boolean operators
region = surf1 surf2 # This doesn't work
# Correct: Explicit operators
region = +surf1 & +surf2 # Intersection
region = +surf1 | +surf2 # Union
# Wrong: Forgetting boundary conditions for finite geometry
boundary = openmc.ZPlane(100) # Default is 'transmission' — particles pass through!
# Correct: Specify boundary behavior to stop particles
boundary = openmc.ZPlane(100, boundary_type='vacuum')
# Wrong: Overlapping regions (OpenMC will detect this)
cell1_region = -cylinder
cell2_region = -cylinder # Same region used twice!
# Correct: Non-overlapping regions
cell1_region = -inner_cylinder
cell2_region = +inner_cylinder & -outer_cylinderTutorial snippet — no separate file in examples repo
Next Steps
For repeated structures like fuel assemblies, see the lattices and universes section. For complex shapes, see additional surface types and advanced boolean operations.