OpenMC Guide
Geometry Basics
What you'll learn
- Read the half-space operators + and - and combine regions with &, | and ~.
- Choose a boundary type, and recognise that the default lets particles escape.
- Spot the two failure modes OpenMC will not silently forgive: overlapping cells and undefined space.
Before you start
Half-spaces, and the default that lets particles escape
OpenMC builds geometry the way you would describe it out loud rather than the way a card deck does: a surface — a cylinder, a plane, a sphere — is an infinite mathematical object that splits all of 3D space into two halves. Writing the surface by itself gives you that object, not a region. You always have to pick a side with + or -, and then combine sides with & (and), | (or), and ~ (not) to build up the shape you actually want.
import openmc
cylinder = openmc.ZCylinder(r=0.5)
inside_cylinder = -cylinder # negative side
outside_cylinder = +cylinder # positive side
# 'cylinder' alone is a Surface, not a Region — & and | only work on the
# half-spaces above, not on the surface object itself.
annulus = +cylinder & -openmc.ZCylinder(r=0.75) # between two cylindersTutorial snippet — no separate file in examples repo
Every surface also takes a boundary_type, and its default value is the one rule worth memorizing: 'transmission', meaning the surface is invisible to particles unless it also happens to be the edge between two cells. Set boundary_type='vacuum' on an outer boundary you want particles to leak out of, or 'reflective' on one you want to bounce them back — the usual choice for turning a single pin cell into a stand-in for an infinite lattice. Forget to set either one on the surface that is supposed to be your model's edge, and OpenMC will not warn you; it just continues tracking the particle past it.
There are two geometry mistakes OpenMC will not silently forgive, because both leave a particle with nowhere well-defined to go: overlapping cells, where two cells claim the same point, and undefined space, where no cell claims it at all. Both show up as a fatal error the first time a particle actually reaches the bad region, and both are easiest to catch before that happens by plotting the geometry and checking that every color meets its neighbor with no gaps and no double coverage.
The pin cell's surfaces and boundary
The running pin cell needs exactly two cylinders and a square boundary: a ZCylinder at the fuel radius, a second one at the clad outer radius, and four reflective planes forming the 1.26 cm lattice pitch. This is the same fragment that opens the geometry on Example: Pin Cell.
import openmc
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, reflective on all four sides
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')
fuel_region = -fuel_outer
clad_region = +fuel_outer & -clad_outer
water_region = +clad_outer & +left & -right & +bottom & -topTutorial snippet — no separate file in examples repo
These three regions carry no material yet — that is the next page, Cell Creation. Keep this exact cylinder-cylinder-square shape; every later page builds on it.
Try It Yourself
Composite surfaces like the square boundary of a pin cell used to be built by helper functions. Those became classes in OpenMC 0.14, and the old factory functions are the single most common thing to carry over from an older script or a language model's memory. There is a catch that the rename alone does not tell you: rectangular_prism() returned a region, already negated for you, while RectangularPrism is a surface. Rename the call and stop there and the intersection fails with ValueError: Intersection operands must be of type Region, because a composite surface is not a region until you negate it.
rectangular_prism() factory with the RectangularPrism class, then negate it where it is used — -box rather than box — because you now hold a surface rather than a region.Check yourself
- Read the half-space operators
+and-, and combine regions with&,|, and~? - Choose a boundary type, and recognise that the default lets particles escape?
- Name the two failure modes OpenMC will not silently forgive: overlapping cells and undefined space?