Python API Basics

Getting Started with the API

The API is object-oriented: you create materials, surfaces, and cells as Python objects, then combine them into a complete model.

python
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")

Tutorial snippet — no separate file in examples repo

Essential Patterns

1. Creating and Organizing Objects

python
# 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])

Tutorial snippet — no separate file in examples repo

2. Working with Geometry

python
# 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)

Tutorial snippet — no separate file in examples repo

3. Building Complete Models

python
# 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}")

Tutorial snippet — no separate file in examples repo

Common Mistakes to Avoid

Missing Material Properties

python
# Wrong - density is missing, so export_to_xml() will fail
fuel = openmc.Material()
fuel.add_nuclide('U235', 0.04)
fuel.add_nuclide('U238', 0.96)
# fuel.export_to_xml()  # Error: density not set

# Correct - density must be set before exporting the model
fuel = openmc.Material()
fuel.add_nuclide('U235', 0.04)
fuel.add_nuclide('U238', 0.96)
fuel.set_density('g/cm3', 10.4)  # Order of Python calls doesn't matter
# fuel.export_to_xml()           # Now this works

Tutorial snippet — no separate file in examples repo

Ambiguous Geometry

python
# Wrong - this is a Python syntax error, not a valid region
cell_region = +surf1 +surf2  # Python cannot combine half-spaces this way

# Correct - use explicit boolean operators
cell_region = +surf1 & +surf2  # Intersection (inside both)
# or
cell_region = +surf1 | +surf2  # Union (inside either)
# or
cell_region = +surf1 & ~surf2  # Inside surf1 but outside surf2

Tutorial snippet — no separate file in examples repo

Forgetting Collections

python
# 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()

Tutorial snippet — no separate file in examples repo

Practical Example: Parameter Study

This example studies the effect of fuel enrichment on reactivity by parameterizing a pin cell model:

python
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()

Tutorial snippet — no separate file in examples repo

Why this works: With traditional input files, this would require dozens of separate files and manual result extraction. The Python API handles parameterization, execution, and post-processing in a single script.

Integration with Scientific Python

OpenMC integrates directly with NumPy, Matplotlib, Pandas, SciPy, scikit-learn, and other scientific Python libraries. This makes sophisticated analysis workflows — optimization, machine learning, uncertainty quantification — straightforward to build.