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
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
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¶
| File | What it covers | OpenMC API |
|---|---|---|
0_0_set_up_nuclear_data.ipynb | Install 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.py | Install 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.ipynb | Explore 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.ipynb | Build a reference DataFrame cataloging material, MT numbers, and nuclear-data libraries. | — |
0_3_revisit_HDF5_extract_temperatures.ipynb | Explore 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.cpp | Read 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.cpp | Inspect reaction groups in an OpenMC neutron HDF5 file. | — |
Cross sections¶
| File | What it covers | OpenMC API |
|---|---|---|
1_0_ways_to_plot.ipynb | Survey 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.ipynb | Generate 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¶
| File | What it covers | OpenMC API |
|---|---|---|
2_0_ways_to_define_materials.ipynb | Explore four ways to define multi-element materials in OpenMC. | Material(), Material.add_element(), Material.set_density(), Materials(), plot_xs() |
Geometry¶
| File | What it covers | OpenMC API |
|---|---|---|
3_0_ways_to_build_pacman_maze.ipynb | Build 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¶
| File | What it covers | OpenMC API |
|---|---|---|
4_0_breeder_tritium_neutron_maps.ipynb | Map tritium production and neutron spectra in a concentric-spheres model. | Cell(), CellFilter(), EnergyFilter(), Geometry(), IndependentSource(), Material(), Material.add_element(), Material.get_nuclides(), Material.mean_free_path(), Material.set_density(), Materials(), MeshFilter(), Region.from_expression(), Settings(), Sphere(), StatePoint(), Tallies(), Tally(), Model(), Model.plot(), Model.run(), plot_xs(), plotter.calculate_cexs(), Discrete(), Isotropic(), Point() |
4_1_ways_to_run_and_post_process.ipynb | Explore different ways to run an OpenMC model, continue simulations, and retrieve tally results. | StatePoint(), Model.from_model_xml() |
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:
Set the data location. Copy
data_root.example.txttodata_root.txt, then edit it to specify the path to your nuclear-data directory.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-workbenchSet 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_B81Step 3 generates openmc_workbench_config.hpp, providing the same paths to C++ programs through:
#include "openmc_workbench_config.hpp"Configuration files¶
| File | Purpose |
|---|---|
data_root.txt | Created and edited by the user; specifies the nuclear-data directory. |
openmc_workbench_config.py | Provided with the repository; discovers nuclear-data directories and makes their paths available to Python. |
openmc_workbench_config.hpp | Generated 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.