This activity allows you to put you neutronics knowledge to the test

We are going to simulate a tokamak where you get to decide the materials.

The goal is to:

  • maximize the Tritium Breeding Ratio

  • minimize the heating on the center colum

  • maximize the heat in the blanket

I recommend plotting the cross sections before you make your choices.

The example for plotting cross sections might be useful.

First import OpenMC and configure the nuclear data path

import openmc
from pathlib import Path
# Setting the cross section path to the correct location in the docker image.
# If you are running this outside the docker image you will have to change this path to your local cross section path.
openmc.config['cross_sections'] = Path.home() / 'nuclear_data' / 'cross_sections.xml'

Task 1 - enter the names of materials#

# options for coolant

water = openmc.Material(name='water')
water.add_element('H', 2)
water.add_element('O', 1)
water.set_density('g/cm3', 1)

helium = openmc.Material(name='helium')
helium.add_element('He', 1)
helium.set_density('g/cm3', 0.0014)

super_critical_co2 = openmc.Material(name='super critical co2')
super_critical_co2.add_element('C', 1)
super_critical_co2.add_element('O', 2)
super_critical_co2.set_density('g/cm3', 0.001)


# options for first wall

tungsten = openmc.Material(name="your code here")
tungsten.add_element('W', 1, percent_type='wo')
tungsten.set_density('g/cm3', 19.2)

steel = openmc.Material(name="your code here")
steel.add_element('Fe', 0.95, percent_type='wo')
steel.add_element('C', 0.95, percent_type='wo')
steel.set_density('g/cm3', 1)

silicon_carbide = openmc.Material(name="your code here")
silicon_carbide.add_element('Si', 1)
silicon_carbide.add_element('C', 1)
silicon_carbide.set_density('g/cm3', 3.21)

zirconium = openmc.Material(name="your code here")
zirconium.add_element('Zr', 1, percent_type='wo')
zirconium.set_density('g/cm3', 6.49)

# options for reflector

beryllium = openmc.Material(name='mat_reflector')
beryllium.add_element('Be', 1, percent_type='wo')
beryllium.set_density('g/cm3', 1.85)

tungsten = openmc.Material(name='mat_reflector')
tungsten.add_element('W', 1, percent_type='wo')
tungsten.set_density('g/cm3', 19.2)

graphite = openmc.Material(name='mat_reflector')
graphite.add_element('C', 1, percent_type='wo')
graphite.set_density('g/cm3', 2.2)

# options for breeder

# note you can change the enrichment
lithium_lead = openmc.Material(name="your code here")
lithium_lead.add_element('Pb', 84.2, percent_type='ao')
lithium_lead.add_element('Li', 15.8, percent_type='ao', enrichment=90.0, enrichment_target='Li6', enrichment_type='ao')
lithium_lead.set_density('g/cm3', 9.5)

# note you can change the enrichment
lithium_orthosilicate = openmc.Material(name="your code here")
lithium_orthosilicate.add_element('Si', 4, percent_type='ao')
lithium_orthosilicate.add_element('O', 1, percent_type='ao')
lithium_orthosilicate.add_element('Li', 4, percent_type='ao', enrichment=50.0, enrichment_target='Li6', enrichment_type='ao')
lithium_orthosilicate.set_density('g/cm3', 2.2)

# note you can change the enrichment
lithium_titanate = openmc.Material(name="your code here")
lithium_titanate.add_element('O', 12, percent_type='ao')
lithium_titanate.add_element('Ti', 5, percent_type='ao')
lithium_titanate.add_element('Li', 4, percent_type='ao', enrichment=40.0, enrichment_target='Li6', enrichment_type='ao')
lithium_titanate.set_density('g/cm3', 2.4)

# note you can change the enrichment
liquid_lithium = openmc.Material(name="your code here")
liquid_lithium.add_element('Li', 1, percent_type='ao', enrichment=7.0, enrichment_target='Li6', enrichment_type='ao')
liquid_lithium.set_density('g/cm3', 0.5)

# conductor material

mat_conductor = openmc.Material(name='mat_conductor')
mat_conductor.add_element('Nb', 1, percent_type='ao')
mat_conductor.add_element('Sn', 3, percent_type='ao')
mat_conductor.set_density('g/cm3', 8.96)

Task 2 - plot the tritium production cross sections of all the breeder options#

# your code goes here

Task 3 - plot the absorption of all the coolants and first wall options#

# your code goes here

Task 4 - select your materials#

# put in your chosen material option
my_materials = openmc.Materials([mat_conductor]) # extend this list with your code

Task 3 - Fill the cells with the materials#

# surfaces
central_column_surface_outer = openmc.ZCylinder(r=100)
central_column_surface_mid = openmc.ZCylinder(r=95)
central_column_surface_inner = openmc.ZCylinder(r=90)

inner_sphere_surface = openmc.Sphere(r=495)
middle_sphere_surface = openmc.Sphere(r=500) 
outer_sphere_surface = openmc.Sphere(r=505)
outer_outer_sphere_surface = openmc.Sphere(r=600)
edge_of_simulation_surface = openmc.Sphere(r=700, boundary_type='vacuum')

# regions
central_column_region = -central_column_surface_inner & -edge_of_simulation_surface
central_column_coolant_region = +central_column_surface_inner & -central_column_surface_mid & -edge_of_simulation_surface
central_column_fw_region = +central_column_surface_mid & -central_column_surface_outer & -edge_of_simulation_surface

inner_vessel_region = +central_column_surface_outer & -inner_sphere_surface

blanket_fw_region = -middle_sphere_surface & +inner_sphere_surface & +central_column_surface_outer
blanket_coolant_region = +middle_sphere_surface & -outer_sphere_surface & +central_column_surface_outer
blanket_breeder_region = +outer_sphere_surface & -outer_outer_sphere_surface & +central_column_surface_outer
blanket_reflector_region = +outer_outer_sphere_surface & -edge_of_simulation_surface & +central_column_surface_outer

# cells
central_column_cell = openmc.Cell(region=central_column_region, fill=mat_conductor)
central_column_coolant_cell = openmc.Cell(region=central_column_coolant_region) # add the fill argument with your selected material)
central_column_fw_cell = openmc.Cell(region=central_column_fw_region) # add the fill argument with your selected material)
inner_vessel_cell = openmc.Cell(region=inner_vessel_region)
blanket_fw_cell = openmc.Cell(region=blanket_fw_region) # add the fill argument with your selected material)
blanket_coolant_cell = openmc.Cell(region=blanket_coolant_region) # add the fill argument with your selected material)
blanket_breeder_cell  = openmc.Cell(region=blanket_breeder_region) # add the fill argument with your selected material)
blanket_reflector_cell = openmc.Cell(region=blanket_reflector_region) # add the fill argument with your selected material)

my_geometry = openmc.Geometry([
    central_column_cell,
    central_column_coolant_cell,
    central_column_fw_cell,
    inner_vessel_cell,
    blanket_fw_cell,
    blanket_coolant_cell,
    blanket_breeder_cell,
    blanket_reflector_cell,
])

task 4 - plot the geometry#

# your code here

Task 5 - plot the source location and energy#

# initialises a new source object
my_source = openmc.IndependentSource()
# the distribution of radius is just a single value
radius = openmc.stats.Discrete([300], [1])
# the distribution of source z values is just a single value
z_values = openmc.stats.Discrete([0], [1])
# the distribution of source azimuthal angles values is a uniform distribution between 0 and 2 Pi
angle = openmc.stats.Uniform(a=0., b=2* 3.14159265359)
# this makes the ring source using the three distributions and a radius
my_source.space = openmc.stats.CylindricalIndependent(r=radius, phi=angle, z=z_values, origin=(0.0, 0.0, 0.0))
# sets the direction to isotropic
my_source.angle = openmc.stats.Isotropic()
# sets the energy distribution to a Muir distribution neutrons
my_source.energy = openmc.stats.muir(e0=14080000.0, m_rat=5.0, kt=20000.0)

my_settings = openmc.Settings()
my_settings.batches = 10
my_settings.inactive = 0
my_settings.particles = 500
my_settings.run_mode = 'fixed source'
my_settings.source = my_source

# plot the source term
# your code goes here

Task 7 - complete the tally scores and filters#

cell_filter_breeder = openmc.CellFilter(blanket_breeder_cell)
tbr_tally = openmc.Tally(name='TBR')
tbr_tally.filters = [cell_filter_breeder]
# tbr_tally.scores = [# your code here]

cell_filter_blanket_fw_coolant = openmc.CellFilter([
    blanket_breeder_cell,
    blanket_coolant_cell,
    blanket_fw_cell,
    central_column_coolant_cell,
    central_column_fw_cell,
])
blanket_heating_tally = openmc.Tally(name='heating')
blanket_heating_tally.filters = [cell_filter_blanket_fw_coolant]
blanket_heating_tally.scores = ['heating']

# material_filter_conductor = openmc.MaterialFilter() # add the material to the material filter
conductor_damage_tally = openmc.Tally(name='conductor_damage')
# conductor_damage_tally.filters = [material_filter_conductor]
# conductor_damage_tally.scores = [] # set the score of the tally

my_tallies = openmc.Tallies([tbr_tally, blanket_heating_tally, conductor_damage_tally])
model = openmc.model.Model(my_geometry, my_materials, my_settings) # add the tallies in here

# removes the old output files
!rm *.h5 *.xml

sp_filename = model.run()
                                %%%%%%%%%%%%%%%
                           %%%%%%%%%%%%%%%%%%%%%%%%
                        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                                    %%%%%%%%%%%%%%%%%%%%%%%%
                                     %%%%%%%%%%%%%%%%%%%%%%%%
                 ###############      %%%%%%%%%%%%%%%%%%%%%%%%
                ##################     %%%%%%%%%%%%%%%%%%%%%%%
                ###################     %%%%%%%%%%%%%%%%%%%%%%%
                ####################     %%%%%%%%%%%%%%%%%%%%%%
                #####################     %%%%%%%%%%%%%%%%%%%%%
                ######################     %%%%%%%%%%%%%%%%%%%%
                #######################     %%%%%%%%%%%%%%%%%%
                 #######################     %%%%%%%%%%%%%%%%%
                 ######################     %%%%%%%%%%%%%%%%%
                  ####################     %%%%%%%%%%%%%%%%%
                    #################     %%%%%%%%%%%%%%%%%
                     ###############     %%%%%%%%%%%%%%%%
                       ############     %%%%%%%%%%%%%%%
                          ########     %%%%%%%%%%%%%%
                                      %%%%%%%%%%%

                 | The OpenMC Monte Carlo Code
       Copyright | 2011-2025 MIT, UChicago Argonne LLC, and contributors
         License | https://docs.openmc.org/en/latest/license.html
         Version | 0.15.3-dev144
     Commit Hash | 14ab4d3b6f3d77b73e8bb480782bbe1bdd56483d
       Date/Time | 2025-05-14 14:07:12
  OpenMP Threads | 16

 Reading model XML file 'model.xml' ...
 Reading cross sections XML file...
 Reading Nb93 from /home/jon/nuclear_data/neutron/Nb93.h5
 Reading Sn112 from /home/jon/nuclear_data/neutron/Sn112.h5
 Reading Sn114 from /home/jon/nuclear_data/neutron/Sn114.h5
 Reading Sn115 from /home/jon/nuclear_data/neutron/Sn115.h5
 Reading Sn116 from /home/jon/nuclear_data/neutron/Sn116.h5
 Reading Sn117 from /home/jon/nuclear_data/neutron/Sn117.h5
 Reading Sn118 from /home/jon/nuclear_data/neutron/Sn118.h5
 Reading Sn119 from /home/jon/nuclear_data/neutron/Sn119.h5
 Reading Sn120 from /home/jon/nuclear_data/neutron/Sn120.h5
 Reading Sn122 from /home/jon/nuclear_data/neutron/Sn122.h5
 Reading Sn124 from /home/jon/nuclear_data/neutron/Sn124.h5
 Minimum neutron data temperature: 294.0 K
 Maximum neutron data temperature: 294.0 K
 Preparing distributed cell instances...
 Writing summary.h5 file...
 Maximum neutron transport energy: 20000000.0 eV for Sn112

 ===============>     FIXED SOURCE TRANSPORT SIMULATION     <===============

 Simulating batch 1
 Simulating batch 2
 Simulating batch 3
 Simulating batch 4
 Simulating batch 5
 Simulating batch 6
 Simulating batch 7
 Simulating batch 8
 Simulating batch 9
 Simulating batch 10
 Creating state point statepoint.10.h5...

 =======================>     TIMING STATISTICS     <=======================

 Total time for initialization     = 2.5735e-01 seconds
   Reading cross sections          = 2.4667e-01 seconds
 Total time in simulation          = 1.5212e-02 seconds
   Time in transport only          = 1.2982e-02 seconds
   Time in active batches          = 1.5212e-02 seconds
   Time accumulating tallies       = 2.4440e-06 seconds
   Time writing statepoints        = 2.0069e-03 seconds
 Total time for finalization       = 1.2130e-06 seconds
 Total time elapsed                = 2.7629e-01 seconds
 Calculation Rate (active)         = 328681.0 particles/second

 ============================>     RESULTS     <============================

 Leakage Fraction            = 1.02800 +/- 0.00251

Task 8 - complete the code to get the correct tally names#

sp = openmc.StatePoint(sp_filename)

# tbr_tally = sp.get_tally() # add the name of the tally to help find the tally

# print(f'TBR={tbr_tally.mean.flatten()[0]} with standard deviation of {tbr_tally.std_dev.flatten()[0]}')

# heating_tally = sp.get_tally() # add the name of the tally to help find the tally
# print(f'Heating={heating_tally.mean.flatten()[0]/1e6}MeV per source neutron with standard deviation of {heating_tally.std_dev.flatten()[0]}')

# damage_tally = sp.get_tally() # add the name of the tally to help find the tally
# print(f'damage={damage_tally.mean.flatten()[0]} with standard deviation of {damage_tally.std_dev.flatten()[0]}')