OpenMC Guide
Materials in OpenMC
What you'll learn
- Build a material from nuclides or elements, using atom or weight fractions.
- Add thermal scattering data, and explain when leaving it out changes the answer.
- Decide whether a temperature belongs on the cell, the material, or the global default.
Before you start
What a material needs
An openmc.Material is a composition plus a density. Composition is a list of nuclides or elements, each with a fraction; density is a single number with units. OpenMC uses the two together to build cross sections for every reaction the transport solver can score.
Fractions default to atom fractions. Pass 'wo' as the third argument to add_nuclide or add_element to give a weight fraction instead — useful for alloys and structural materials, which are usually specified by mass. Atom and weight fractions cannot be mixed within one material.
fuel = openmc.Material(name='UO2 Fuel')
fuel.set_density('g/cm3', 10.4)
fuel.add_nuclide('U235', 0.045) # atom fraction (default)
fuel.add_nuclide('U238', 0.955)
fuel.add_element('O', 2.0)
steel = openmc.Material(name='Stainless Steel 316')
steel.set_density('g/cm3', 8.0)
steel.add_element('Fe', 0.665, 'wo') # weight fraction — Fe is the balance
steel.add_element('Ni', 0.12, 'wo')
steel.add_element('Cr', 0.17, 'wo')
steel.add_element('Mo', 0.025, 'wo')
steel.add_element('Mn', 0.02, 'wo') # sums to exactly 1.0Tutorial snippet — no separate file in examples repo
Add thermal scattering data with add_s_alpha_beta whenever a material both contains a light nuclide — hydrogen in water, carbon in graphite — and moderates neutrons near room temperature. Free-atom cross sections assume the target nucleus is unbound, which breaks down once thermal neutron energies approach the chemical binding energy. Skip it and the moderator moderates less than it should.
Temperature is kept separate from composition. It can be set on the material, on the cell that the material fills, or as a global default in Settings.temperature, and the three form a precedence chain: cell wins over material, material wins over the global default.
settings.temperature = {'default': 293.6} # 1. global default, Kelvin
hot_fuel = openmc.Material(name='UO2 Fuel', temperature=900.0) # 2. material default
fuel_cell = openmc.Cell(fill=hot_fuel, region=-fuel_outer)
fuel_cell.temperature = 950.0 # 3. wins over both of the aboveTutorial snippet — no separate file in examples repo
You will see it claimed that temperature can only be set on cells. It cannot only be set there — OpenMC's own openmc.model.borated_water() helper builds its return value with openmc.Material(temperature=T, **kwargs). The useful version of that advice is narrower: one material object can fill many cells at different temperatures, so a temperature that varies between cells has to live on the cells, not the shared material.
Two more things a material can do, briefly. Material.mix_materials() blends several materials by volume or weight fraction into a homogeneous mixture — it does not support materials carrying S(α,β) data, so mix structural and absorber materials, not a moderator into fuel. Setting material.depletable = True plus a volume in cm³ enables burnup tracking, covered on the Depletion page.
The pin cell's three materials
The running example for this path is a PWR pin cell, and it needs exactly three materials: fuel, cladding, and moderator. This is the same fragment that opens the model on Example: Pin Cell.
import openmc
fuel = openmc.Material(name='UO2 Fuel')
fuel.set_density('g/cm3', 10.4)
fuel.add_nuclide('U235', 0.045) # 4.5% enriched
fuel.add_nuclide('U238', 0.955)
fuel.add_element('O', 2.0)
clad = openmc.Material(name='Zircaloy-4')
clad.set_density('g/cm3', 6.56)
clad.add_element('Zr', 0.982)
clad.add_element('Sn', 0.015)
clad.add_element('Fe', 0.002)
clad.add_element('Cr', 0.001)
water = openmc.Material(name='Light Water')
water.set_density('g/cm3', 0.998)
water.add_nuclide('H1', 2.0)
water.add_element('O', 1.0)
water.add_s_alpha_beta('c_H_in_H2O') # room-temperature light water needs this
materials = openmc.Materials([fuel, clad, water])Tutorial snippet — no separate file in examples repo
Nothing about the cladding needs thermal scattering data — Zircaloy is a solid structural alloy, not a moderator. Keep these three names and densities; the geometry pages build cells around this exact fuel, clad, and water.
One thing to try
The moderator above is cold and unborated. Try switching it to hot, pressurized, borated PWR conditions and rerunning the pin cell from Example: Pin Cell. Predict which way k-effective moves before you check — moderator density and dissolved boron each change the neutron balance, and confirming the sign yourself is worth more than taking it on faith.
water.set_density('g/cm3', 0.7) # hot, pressurized water instead of cold
# 1000 ppm natural boron by weight, written as atom fractions on the same
# scale as the H1 2.0 / O 1.0 above — this material stays all-atom-fraction
water.add_nuclide('B10', 0.00033)
water.add_nuclide('B11', 0.00134)Tutorial snippet — no separate file in examples repo
Check yourself
- Build a material from nuclides or elements, with atom or weight fractions?
- Add thermal scattering data, and say when leaving it out changes the answer?
- Decide whether temperature belongs on the cell, the material, or the global default?