OpenMC Guide
Cell Creation Techniques
Understanding Cells
Cells connect geometric regions to physical materials. Each cell defines a region of space (via surface combinations) and specifies what fills it — a material, void, or another universe.
import openmc
# Basic cell creation pattern
fuel_region = -fuel_cylinder # Define the shape
fuel_cell = openmc.Cell( # Create the container
fill=fuel_material, # What goes inside
region=fuel_region # The shape
)Tutorial snippet — no separate file in examples repo
Fill Options
Cells can be filled with materials, other universes, lattices, or left void:
# Material fill - most common
fuel_cell = openmc.Cell(fill=fuel_material, region=fuel_region)
# Void fill - empty space
void_cell = openmc.Cell(fill=None, region=void_region)
# Universe fill - for nested structures
pin_cell = openmc.Cell(fill=pin_universe, region=pin_region)
# Lattice fill - for repeated arrays
assembly_cell = openmc.Cell(fill=pin_lattice, region=assembly_region)
# Temperature specification
hot_fuel_cell = openmc.Cell(
fill=fuel_material,
region=fuel_region,
temperature=900 # Kelvin
)Tutorial snippet — no separate file in examples repo
Complex Boolean Operations
For complex geometries, you'll need to combine multiple surfaces using boolean logic. The key is building up complexity step by step.
# Multiple cylinders example - fuel assembly with guide tubes
fuel_radius = 0.4096
clad_radius = 0.4750
guide_radius = 0.6120
pitch = 1.26
# Define surfaces
fuel_surf = openmc.ZCylinder(r=fuel_radius)
clad_surf = openmc.ZCylinder(r=clad_radius)
guide_surf = openmc.ZCylinder(r=guide_radius)
# Create regions step by step
fuel_region = -fuel_surf
clad_region = +fuel_surf & -clad_surf
guide_region = -guide_surf
water_region = +clad_surf & +guide_surf # Outside both cylinders
# Alternative: Use parentheses for clarity
complex_region = (+surf1 & -surf2) | (+surf3 & -surf4)
intersection_region = -surf1 & -surf2 & +surf3 # Inside 1&2, outside 3Tutorial snippet — no separate file in examples repo
Coordinate Transformations
When you need the same geometry in different locations or orientations, use transformations rather than creating new surfaces.
# Define base geometry once
base_pin = create_pin_universe() # Function returning a universe
# Place multiple copies with transformations
assembly_cells = []
for i in range(17):
for j in range(17):
x_pos = (i - 8) * pitch
y_pos = (j - 8) * pitch
cell = openmc.Cell(fill=base_pin, region=pin_boundary)
cell.translation = (x_pos, y_pos, 0)
assembly_cells.append(cell)
# Rotation example (angles in degrees)
rotated_cell = openmc.Cell(fill=fuel_material, region=fuel_region)
rotated_cell.rotation = (0, 0, 45) # 45° rotation around z-axisTutorial snippet — no separate file in examples repo
Different Materials in Different Regions
When you need different materials in different parts of your geometry, create separate cells — each with its own material fill and non-overlapping region.
# Define multiple fuel compositions
fuel_fresh = openmc.Material(name='Fresh UO2')
fuel_fresh.set_density('g/cm3', 10.4)
fuel_fresh.add_nuclide('U235', 0.045)
fuel_fresh.add_nuclide('U238', 0.955)
fuel_fresh.add_element('O', 2.0)
fuel_burned = openmc.Material(name='Burned UO2')
fuel_burned.set_density('g/cm3', 10.2)
fuel_burned.add_nuclide('U235', 0.020)
fuel_burned.add_nuclide('U238', 0.935)
fuel_burned.add_nuclide('Pu239', 0.008)
fuel_burned.add_element('O', 2.0)
# Use separate cells for each material
# For example, inner and outer fuel regions in a pellet
inner_radius = openmc.ZCylinder(r=0.25)
outer_radius = openmc.ZCylinder(r=0.41)
inner_fuel_cell = openmc.Cell(
fill=fuel_burned,
region=-inner_radius
)
outer_fuel_cell = openmc.Cell(
fill=fuel_fresh,
region=+inner_radius & -outer_radius
)
# Or use Material.mix_materials for a homogeneous mixture
mixed_fuel = openmc.Material.mix_materials(
materials=[fuel_fresh, fuel_burned],
fracs=[0.7, 0.3],
percent_type='vo'
)
mixed_fuel_cell = openmc.Cell(fill=mixed_fuel, region=-outer_radius)Tutorial snippet — no separate file in examples repo
Error Prevention
Avoid these common cell creation mistakes:
# Wrong: Overlapping cells
cell1 = openmc.Cell(fill=material1, region=-cylinder)
cell2 = openmc.Cell(fill=material2, region=-cylinder) # Same region!
# Correct: Non-overlapping regions
cell1 = openmc.Cell(fill=material1, region=-inner_cylinder)
cell2 = openmc.Cell(fill=material2, region=+inner_cylinder & -outer_cylinder)
# Wrong: Missing regions
incomplete_cell = openmc.Cell(fill=material) # No region specified
# Correct: Always specify region
complete_cell = openmc.Cell(fill=material, region=region)
# Wrong: Complex expressions without testing
complex_region = +s1 & -s2 | +s3 & -s4 & +s5 | -s6 # Hard to debug
# Better: Build incrementally and test
base_region = +s1 & -s2
add_region = +s3 & -s4 & +s5
final_region = base_region | add_region | -s6Tutorial snippet — no separate file in examples repo
Validation and Debugging
Always validate your cell definitions before running full simulations:
universe = openmc.Universe(cells=[cell1, cell2, cell3])
geometry = openmc.Geometry(universe)
# Use geometry plots to visually verify cells and detect overlaps
plot = openmc.Plot()
plot.basis = 'xy'
plot.origin = (0, 0, 0)
plot.width = (5, 5)
plot.pixels = (400, 400)
plot.color_by = 'material'
plots = openmc.Plots([plot])
plots.export_to_xml()
geometry.export_to_xml()
materials = openmc.Materials([cell.fill for cell in [cell1, cell2, cell3] if isinstance(cell.fill, openmc.Material)])
materials.export_to_xml()
openmc.plot_geometry()
# For overlap detection, run OpenMC with overlap checking enabled:
# openmc --overlap-check (command line)
# or set settings.check_overlaps = True in the Settings object
# Check cell fills
for cell in [cell1, cell2, cell3]:
if cell.fill is None:
print(f"Warning: Cell {cell.id} has no fill")
if cell.region is None:
print(f"Warning: Cell {cell.id} has no region")Tutorial snippet — no separate file in examples repo
Fix geometry issues before worrying about physics parameters — if the plot looks wrong, the geometry is wrong.