OpenMC Guide
Python API Basics
Getting Started with the API
OpenMC's Python API makes nuclear simulations much more accessible than traditional input-file approaches. Instead of learning complex syntax rules, you use familiar Python to build models, run simulations, and analyze results.
The API follows object-oriented principles. You create materials, surfaces, and cells as Python objects, then combine them into a complete model. This approach makes it easy to modify designs, run parameter studies, and integrate with other Python tools.
import openmc
import numpy as np
import matplotlib.pyplot as plt
# Basic workflow: create objects, combine them, run simulation
fuel = openmc.Material(name='UO2')
fuel.set_density('g/cm3', 10.4)
fuel.add_nuclide('U235', 0.04)
fuel.add_nuclide('U238', 0.96)
fuel.add_element('O', 2.0)
# OpenMC objects are Pythonic - they have useful methods
print(f"Fuel density: {fuel.density} g/cm³")
print(f"Fuel contains {len(fuel.nuclides)} nuclides")Essential Patterns
Here are the most important patterns you'll use repeatedly in OpenMC. Master these and you'll be able to build complex models efficiently.
1. Creating and Organizing Objects
# Use descriptive names for clarity
fuel = openmc.Material(name='UO2 Fuel 4.5%')
clad = openmc.Material(name='Zircaloy-4')
water = openmc.Material(name='Light Water')
# Group related objects into collections
materials = openmc.Materials([fuel, clad, water])2. Working with Geometry
# Surfaces define boundaries
fuel_outer = openmc.ZCylinder(r=0.4096, name='fuel outer')
clad_outer = openmc.ZCylinder(r=0.4750, name='clad outer')
# Regions use boolean operations (& = and, | = or, ~ = not)
fuel_region = -fuel_outer
clad_region = +fuel_outer & -clad_outer
water_region = +clad_outer
# Cells fill regions with materials
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)3. Building Complete Models
# Combine everything into a model
universe = openmc.Universe(cells=[fuel_cell, clad_cell, water_cell])
geometry = openmc.Geometry(universe)
settings = openmc.Settings()
settings.particles = 10000
settings.batches = 100
# The Model class ties everything together
model = openmc.Model(geometry, materials, settings)
# Run and get results (model.run() returns statepoint path)
sp = openmc.StatePoint(model.run())
keff = sp.keff
print(f"k-effective: {keff.nominal_value:.5f} ± {keff.std_dev:.5f}")Common Mistakes to Avoid
These are the most frequent errors we see from new OpenMC users. Avoiding them will save you debugging time and frustration.
Missing Material Properties
# Wrong - OpenMC will throw an error
fuel = openmc.Material()
fuel.add_nuclide('U235', 0.04) # No density set!
# Correct - always set density first
fuel = openmc.Material()
fuel.set_density('g/cm3', 10.4) # Density must be set
fuel.add_nuclide('U235', 0.04) # Now this worksAmbiguous Geometry
# Wrong - unclear what this region means
cell_region = +surf1 +surf2 # Addition, not boolean logic
# Correct - use explicit boolean operators
cell_region = +surf1 & +surf2 # Intersection (inside both)
# or
cell_region = +surf1 | +surf2 # Union (inside either)Forgetting Collections
# Wrong - individual exports are error-prone
materials.export_to_xml()
geometry.export_to_xml()
settings.export_to_xml()
tallies.export_to_xml()
# Better - use Model to handle everything
model = openmc.Model(geometry, materials, settings, tallies)
model.export_to_xml()Practical Example: Parameter Study
Here's how the Python API makes complex tasks simple. This example shows how to study the effect of fuel enrichment on reactivity - something that would be tedious with traditional input files.
import openmc
import numpy as np
import matplotlib.pyplot as plt
def create_pin_model(enrichment):
"""Create a pin cell model with specified enrichment."""
# Materials
fuel = openmc.Material()
fuel.set_density('g/cm3', 10.4)
fuel.add_nuclide('U235', enrichment)
fuel.add_nuclide('U238', 1.0 - enrichment)
fuel.add_element('O', 2.0)
water = openmc.Material()
water.set_density('g/cm3', 1.0)
water.add_nuclide('H1', 2.0)
water.add_element('O', 1.0)
water.add_s_alpha_beta('c_H_in_H2O')
# Simple infinite lattice geometry
fuel_boundary = openmc.ZCylinder(r=0.4)
pin_cell = openmc.Cell(fill=fuel, region=-fuel_boundary)
water_cell = openmc.Cell(fill=water, region=+fuel_boundary)
# Settings for k-eigenvalue calculation
settings = openmc.Settings()
settings.particles = 5000
settings.batches = 50
settings.inactive = 10
universe = openmc.Universe(cells=[pin_cell, water_cell])
return openmc.Model(
geometry=openmc.Geometry(universe),
materials=openmc.Materials([fuel, water]),
settings=settings
)
# Run parameter study
enrichments = np.linspace(0.02, 0.06, 5)
k_values = []
for enr in enrichments:
model = create_pin_model(enr)
sp = openmc.StatePoint(model.run())
keff = sp.keff
k_values.append(keff.nominal_value)
print(f"Enrichment {enr:.1%}: k = {k_values[-1]:.4f}")
# Plot results
plt.figure(figsize=(8, 6))
plt.plot(enrichments * 100, k_values, 'o-', linewidth=2, markersize=8)
plt.xlabel('Enrichment (%)')
plt.ylabel('k-effective')
plt.title('Reactivity vs. Fuel Enrichment')
plt.grid(True, alpha=0.3)
plt.show()Why this works: The Python API makes it easy to parameterize models, run multiple cases, and analyze results. Try doing this with traditional input files - you'd need dozens of separate files and manual result extraction.
Integration with Scientific Python
OpenMC works seamlessly with the scientific Python ecosystem. NumPy handles arrays and mathematical operations, Matplotlib creates plots, and Pandas manages data analysis. This integration is one of OpenMC's biggest advantages over traditional codes.
As you become more advanced, you can integrate OpenMC with optimization libraries (SciPy), machine learning tools (scikit-learn), and uncertainty quantification frameworks. The Python ecosystem makes sophisticated analysis workflows much more accessible.