Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

OpenMC Workbench

Ongoing work exploring OpenMC, building on an extensive background in Monte Carlo radiation transport.

This workbench explores OpenMC from the perspective of an experienced Monte Carlo radiation-transport physicist. It emphasizes hands-on investigation, computational workflows, and practical understanding.

By Mary P. W. Chin

Source repository →

Status

This is an active, evolving workbench. Code, documentation, and analyses will be added and revised as the work progresses.

The repository is public in the spirit of open-source collaboration and scientific exchange. Efforts are made to document the behind-the-scenes work, including data preparation, configuration, and computational setup, for transparency and reproducibility.

The workbench is a record of ongoing explorations rather than an end-user tutorial. Comments, corrections, and peer review are welcome.

Nuts and bolts

Python and C++

OpenMC’s transport kernel is written in C++, but much of its user-facing API is in Python. Most explorations here therefore use Python, particularly for model construction, calculations, and analysis. C++ is used selectively, where it is useful to examine data formats, underlying structures, or aspects of OpenMC’s implementation more directly.

Programmatic approach

Throughout the Workbench, I draw on practices from data science where practical. I organize nuclear data, model specifications, and simulation results in pandas DataFrames, and process them programmatically.

For instance, an OpenMC material is typically defined by calling openmc.Material.add_element() separately for each element, passing numerical values directly as arguments:

breeder_material = openmc.Material()
breeder_material.add_element('Pb', 84.2, percent_type='ao')
breeder_material.add_element('Li', 15.8, percent_type='ao', enrichment=90., enrichment_target='Li6', enrichment_type='ao')

In contrast, 2_0_ways_to_define_materials.ipynb organizes material compositions, including isotopic enrichment specifications, in a DataFrame:

mats_df.loc[len(mats_df)] = ['PbLi_manual', 'Pb', 84.2, 'ao'] + 3 * [None]
mats_df.loc[len(mats_df)] = ['PbLi_manual', 'Li', 15.8, 'ao', 90., 'Li6', 'ao']

A common loop then constructs the OpenMC materials:

mats = {}
for name, dat in mats_df.groupby('name'):
    mats[name] = openmc.Material(name=name)

    for _, row in dat.drop(columns='name').iterrows():
        mats[name].add_element(**row.dropna().to_dict())

This separation of input data from model-construction logic minimizes repetitive code, reduces the risk of inconsistencies, and makes the examples easier to explore, modify, and validate.

Explorations

Start with the setup notebook if you wish to reproduce the examples locally. Otherwise, go directly to the ENDF/B-VIII.1 exploration notebook.

Nuclear data

FileWhat it coversOpenMC API
0_0_set_up_nuclear_data.ipynbInstall nuclear-data libraries, windowed multipole data, and depletion chains; check the resulting file layout.DataLibrary.export_to_xml(), DataLibrary.register_file(), IncidentNeutron.export_to_hdf5(), IncidentNeutron.from_ace()
0_0_set_up_nuclear_data.pyInstall nuclear-data libraries, windowed multipole data, and depletion chains; check the resulting file layout.DataLibrary.export_to_xml(), DataLibrary.register_file(), IncidentNeutron.export_to_hdf5(), IncidentNeutron.from_ace()
0_1_explore_endf-b8.1.ipynbExplore cross_section.xml and HDF5 data from ENDF/B-VIII.1, comparing direct XML/HDF5 inspection with the OpenMC Python API.DataLibrary.from_xml(), DataLibrary.get_by_material(), IncidentNeutron.from_hdf5()
0_2_catalog_nuclear_data_libraries.ipynbBuild a reference DataFrame cataloging material, MT numbers, and nuclear-data libraries.—
0_3_revisit_HDF5_extract_temperatures.ipynbExplore the structure of OpenMC neutron HDF5 files, inspect groups and attributes, extract temperatures, and compare temperature availability across nuclear-data libraries.—
0_4_cross_sections_xml_pugixml.cppRead and explore cross_sections.xml in C++ using pugixml. Display the XML elements and their attributes, with an option to choose a nuclear-data library other than the default ENDF/B-VIII.1.—
0_5_inspect_hdf5_reactions.cppInspect reaction groups in an OpenMC neutron HDF5 file.—

Cross sections

FileWhat it coversOpenMC API
1_0_ways_to_plot.ipynbSurvey three ways to plot continuous-energy cross sections in OpenMC: plot_xs(), calculate_cexs(), and direct access to HDF5 data.DataLibrary.from_xml(), DataLibrary.get_by_material(), IncidentNeutron.from_hdf5(), plot_xs(), plotter.calculate_cexs()
1_1_ways_to_convert_to_multigroup.ipynbGenerate and compare OpenMC multigroup cross sections (MGXS) with material_wise, stochastic_slab, and infinite_medium, as preparation for a later Pac-Man FW-CADIS simulation.Geometry.get_all_cells(), IndependentSource(), Material(), Material.add_element(), Material.set_density(), Materials(), MeshFilter(), RegularMesh(), Settings(), Tallies(), Tally(), Model(), plot_xs(), Discrete(), Isotropic(), Point()

Materials

FileWhat it coversOpenMC API
2_0_ways_to_define_materials.ipynbExplore four ways to define multi-element materials in OpenMC.Material(), Material.add_element(), Material.set_density(), Materials(), plot_xs()

Geometry

FileWhat it coversOpenMC API
3_0_ways_to_build_pacman_maze.ipynbBuild the classic Pac-Man maze from a simple 28 × 31 character map, using CSG and RectLattice. Export both geometries for reuse in future variance reduction notebooks.Cell(), Geometry(), Geometry.export_to_xml(), Geometry.get_all_cells(), Materials(), Plots(), Universe(), XPlane(), YPlane(), ZPlane(), Model()

Run

Nuclear-data paths

Scripts can break when files are not found at their expected locations. To minimize this problem, OpenMC Workbench keeps nuclear-data paths in one place, rather than requiring them to be specified in each notebook or program.

The nuclear-data libraries reside outside the repository, and the location needs to be specified only once.

Optional: Install nuclear-data libraries

If you already have the required nuclear-data libraries, you can skip their installation.

Otherwise, see README.md.

Set up nuclear-data paths

From the repository root:

  1. Set the data location. Copy data_root.example.txt to data_root.txt, then edit it to specify the path to your nuclear-data directory.

  2. Set up Python. In the Python environment used by your Jupyter kernel, run:

    python -m pip install -e .

    Alternatively, run the following directly in a notebook, using the absolute path to your repository:

    %pip install -e /absolute/path/to/openmc-workbench
  3. Set up C++. From the repository root, run:

    python openmc_workbench_config.py

Step 2 makes openmc_workbench_config importable in Python. The notebooks can then access nuclear-data paths through statements such as:

from openmc_workbench_config import ENDF_B81

Step 3 generates openmc_workbench_config.hpp, providing the same paths to C++ programs through:

#include "openmc_workbench_config.hpp"

Configuration files

FilePurpose
data_root.txtCreated and edited by the user; specifies the nuclear-data directory.
openmc_workbench_config.pyProvided with the repository; discovers nuclear-data directories and makes their paths available to Python.
openmc_workbench_config.hppGenerated by Step 3; makes the same paths available to C++.

Library names such as ENDF_B80, ENDF_B81, and FENDL_32C are generated automatically from existing directory names. If new library directories are added, rerun Step 3 to update the C++ header.

This arrangement allows Python notebooks and C++ programs to use the same nuclear-data paths without maintaining separate path settings.

Compilers and libraries

See Compilers and libraries.