OpenMC Guide
Cell Creation Techniques
Understanding Cells
Building on geometry basics, cells are the fundamental building blocks that connect your geometric regions to physical materials. Every cell defines a region of space and specifies what fills it - whether material, void, or another universe.
Think of cells as containers: the region property defines the container's shape using surface combinations, and the fill property determines what goes inside.
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
)Fill Options
Cells can be filled with materials, other universes, lattices, or left void. Understanding when to use each fill type is crucial for efficient modeling.
# 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
)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 3Coordinate 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,
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,
rotation=(0, 0, 45) # 45° rotation around z-axis
)Working with Distributed Materials
Sometimes you need different materials in the same geometric region. OpenMC supports distributed material fills for this purpose.
# 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)
# Create a distributed material (fractions sum to 1.0)
distributed_fuel = openmc.DistributedMaterial(
materials=[fuel_fresh, fuel_burned],
fractions=[0.7, 0.3]
)
fuel_cell = openmc.Cell(
fill=distributed_fuel,
region=fuel_region
)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 | -s6Validation and Debugging
Always validate your cell definitions before running full simulations:
# Check for overlaps
universe = openmc.Universe(cells=[cell1, cell2, cell3])
geometry = openmc.Geometry(universe)
geometry.check_for_overlaps()
# Plot a slice to verify visually (requires XML export)
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()
# 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")Visual verification is your best tool. If the geometry looks wrong in plots, it probably is wrong. Fix geometry issues before worrying about physics parameters.