Geometry Basics

What you'll learn

First pin cell · 5 / 1112 min read
  • 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.

python
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 cylinders

Tutorial 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.

python
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 & -top

Tutorial 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.

Try it yourself — pin_boundary.py
Two edits. Replace the deprecated 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.
1 warningChecked by the OWEN rule set

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?