5. Materials and Cross-Section Data

In OpenSn, a “material” is represented by assigning a multi-group cross-section object to one or more mesh block ids. The two main elements are:

  • a pyopensn.xs.MultiGroupXS object containing the macroscopic cross-section data, and

  • an xs_map entry that associates that object with a list of block_ids.

This section describes how to create and load cross-section data, how block id mapping works in transport inputs, how OpenMC and native OpenSn cross-section files are used, and how cross sections can be combined and inspected from Python.

5.1. Overview

The Python MuliGroupXS API has the following methods:

The most common workflow is:

  1. Create or load one or more pyopensn.xs.MultiGroupXS objects.

  2. Assign them to mesh block ids with xs_map.

  3. Pass that mapping directly to a transport problem, or update the problem

  4. later using pyopensn.solver.LBSProblem.SetXSMap().

Note

OpenSn’s Python interface works with macroscopic multi-group cross sections. If you combine two cross sections, the resulting object is another macroscopic cross section with density-weighted data.

5.2. Materials, Block IDs, and xs_map

Mesh cells in OpenSn carry integer block ids. The transport problem uses those block ids to decide which cross section applies in each cell.

Each entry in xs_map is a dictionary with:

Example:

from pyopensn.mesh import OrthogonalMeshGenerator
from pyopensn.xs import MultiGroupXS
from pyopensn.aquad import GLCProductQuadrature2DXY
from pyopensn.solver import DiscreteOrdinatesProblem

meshgen = OrthogonalMeshGenerator(node_sets=[[0.0, 1.0, 2.0], [0.0, 1.0]])
grid = meshgen.Execute()

# Sets the block id for each cell to 0
grid.SetUniformBlockID(0)

xs_fuel = MultiGroupXS()
xs_fuel.CreateSimpleOneGroup(sigma_t=1.0, c=0.7, velocity=1.0)

problem = DiscreteOrdinatesProblem(
    mesh=grid,
    num_groups=1,
    groupsets=[
        {
            "groups_from_to": (0, 0),
            "angular_quadrature": GLCProductQuadrature2DXY(
                n_polar=2,
                n_azimuthal=4,
                scattering_order=0,
            ),
        },
    ],
    xs_map=[
        {"block_ids": [0], "xs": xs_fuel},
    ],
)

If multiple block ids should use the same material, put them in the same block_ids list:

xs_map = [
    {"block_ids": [0, 3, 7], "xs": xs_fuel},
]

If you need to replace the material assignment after problem construction, use pyopensn.solver.LBSProblem.SetXSMap() with the same xs_map structure.

Note

block_ids are mesh labels, not material names. A common and simple pattern is to assign one block id per material region when building the mesh, then map each region to its corresponding pyopensn.xs.MultiGroupXS object.

5.3. Creating a Simple One-Group Cross Section

For small tests, manufactured problems, and simple transients, pyopensn.xs.MultiGroupXS.CreateSimpleOneGroup() is the quickest way to build a cross section.

from pyopensn.xs import MultiGroupXS

xs = MultiGroupXS()
xs.CreateSimpleOneGroup(sigma_t=1.0, c=0.8, velocity=2.0)

This creates a one-group material with:

  • sigma_t = 1.0

  • isotropic within-group scattering ratio c = 0.8

  • absorption implied by sigma_a = sigma_t * (1 - c)

  • inverse velocity populated from 1 / velocity when velocity > 0

This method is intentionally minimal. It is useful when you want a small, explicit test problem without maintaining a cross-section file.

5.4. Inspecting a MultiGroupXS Object

The following properties and methods are available from Python for inspecting cross section data:

  • num_groups

  • scattering_order

  • num_precursors

  • is_fissionable

  • GetScaleFactor()

  • sigma_t

  • sigma_a

  • sigma_f

  • chi

  • nu_sigma_f

  • nu_prompt_sigma_f

  • nu_delayed_sigma_f

  • inv_velocity

  • has_custom_xs(name)

  • get_custom_xs(name)

  • custom_xs_names()

Example:

print(xs.num_groups)
print(xs.sigma_t)
print(xs.inv_velocity)

Note

The Python getters expose the data that the solver will actually use. This makes them useful both for debugging file imports and for checking the results of operations such as pyopensn.xs.MultiGroupXS.Combine() and pyopensn.xs.MultiGroupXS.Scale().

5.5. Loading OpenSn Cross-Section Files

Use pyopensn.xs.MultiGroupXS.LoadFromOpenSn() to load OpenSn’s native text cross-section format:

xs = MultiGroupXS()
xs.LoadFromOpenSn("fuel.cxs")

This is often the most convenient format when:

  • you want a small, readable input file under version control,

  • you are defining benchmark or regression materials by hand, or

  • you want direct control over prompt and delayed fission data.

Note

The native OpenSn format is the best choice when you want an input file that can be edited and reviewed directly. OpenMC MGXS files are better when the data already exists in an HDF5 library and should be imported as-is.

5.5.1. Minimal OpenSn Format

At minimum, an OpenSn cross-section file defines the number of groups and one or more group-dependent cross sections. A small one-group example looks like:

NUM_GROUPS 1
NUM_MOMENTS 1

SIGMA_T_BEGIN
0 1.0
SIGMA_T_END

SIGMA_A_BEGIN
0 0.2
SIGMA_A_END

TRANSFER_MOMENTS_BEGIN
M_GFROM_GTO_VAL 0 0 0 0.8
TRANSFER_MOMENTS_END

For most transport materials, the most important top-level sections are:

  • NUM_GROUPS

  • NUM_MOMENTS

  • GROUP_STRUCTURE_BEGINGROUP_STRUCTURE_END (optional)

  • SIGMA_T_BEGINSIGMA_T_END

  • SIGMA_A_BEGINSIGMA_A_END (optional)

  • TRANSFER_MOMENTS_BEGINTRANSFER_MOMENTS_END (optional)

  • INV_VELOCITY_BEGININV_VELOCITY_END or VELOCITY_BEGINVELOCITY_END (optional)

Important details:

  • NUM_MOMENTS is the number of scattering moments, not the maximum Legendre order. For example, NUM_MOMENTS 1 means isotropic scattering only.

  • If SIGMA_A is omitted, OpenSn will infer absorption from the total cross section and the zeroth transfer matrix when possible.

  • The transfer matrix entries use M_GFROM_GTO_VAL moment g_from g_to value.

Note

A practical rule is to keep the native file explicit when possible. Even if OpenSn can infer some quantities, files that provide the intended data directly are easier to review and less ambiguous.

5.5.2. Fission Data in OpenSn Files

For fissionable materials, OpenSn supports two main styles of input.

For prompt-only or steady-state fission data, the usual path is:

  • SIGMA_F

  • either NU with CHI, or NU_SIGMA_F together with enough data to infer the remaining quantities

For delayed-neutron problems, the file may additionally provide:

  • NUM_PRECURSORS

  • NU_PROMPT

  • NU_DELAYED

  • CHI_PROMPT

  • CHI_DELAYED

  • PRECURSOR_DECAY_CONSTANTS

  • PRECURSOR_FRACTIONAL_YIELDS

OpenSn also supports an alternative PRODUCTION_MATRIX representation for fission production. This is useful for advanced data preparation, but most user inputs are clearer when written in terms of SIGMA_F, NU or NU_PROMPT/NU_DELAYED, and the corresponding spectra.

5.6. Loading OpenMC MGXS Files

Use pyopensn.xs.MultiGroupXS.LoadFromOpenMC() to load cross sections from an OpenMC multi-group HDF5 library.

xs_uo2 = MultiGroupXS()
xs_uo2.LoadFromOpenMC(
    file_name="mgxs.h5",
    dataset_name="set1",
    temperature=294.0,
)

Parameters:

  • file_name: the OpenMC MGXS HDF5 file

  • dataset_name: the material or dataset group to load

  • temperature: the requested temperature in kelvin

  • extra_xs_names: an optional list of additional named one-dimensional datasets to import as custom XS

Example:

xs_hdpe = MultiGroupXS()
xs_hdpe.LoadFromOpenMC(
    file_name="HDPE.h5",
    dataset_name="set1",
    temperature=294.0,
    extra_xs_names=["absorption"],
)

Note

When importing from OpenMC, OpenSn recomputes absorption from the imported total cross section and transfer matrix rather than trusting the OpenMC absorption dataset directly. This makes the imported data less sensitive to small inconsistencies or statistical noise.

5.6.1. What OpenMC Import Expects

The Python interface expects an OpenMC MGXS HDF5 file with:

  • filetype = "mgxs"

  • a dataset group matching dataset_name

  • a temperature subgroup such as 294K

The imported object may include:

  • total cross section

  • transfer matrices

  • inverse velocity

  • fission cross section

  • fission production data

  • fission spectrum

  • any requested named custom one-dimensional XS in extra_xs_names

If the requested dataset or temperature is not present, the load fails with an error.

5.7. Custom Cross Sections

OpenSn’s Python API supports named custom one-dimensional cross sections through OpenMC import. These are useful when a dataset should be carried alongside the standard transport data for later inspection, combination, scaling, or use in derived field functions.

Load them by name with extra_xs_names:

xs = MultiGroupXS()
xs.LoadFromOpenMC(
    "HDPE.h5",
    "set1",
    294.0,
    extra_xs_names=["absorption", "heating"],
)

Inspect them with:

print(xs.custom_xs_names())
print(xs.has_custom_xs("heating"))
print(xs.get_custom_xs("heating"))

Important behavior:

Note

The Python API currently supports loading and reading custom XS, but it does not expose a direct Python method for creating or modifying custom XS entries by hand. In practice, custom XS are an import-and-use feature rather than a full custom-data authoring interface.

5.8. Combining Cross Sections

Use pyopensn.xs.MultiGroupXS.Combine() to build a new cross section by combining existing ones with density weights.

xs_1 = MultiGroupXS()
xs_1.CreateSimpleOneGroup(sigma_t=1.0, c=0.5)

xs_2 = MultiGroupXS()
xs_2.CreateSimpleOneGroup(sigma_t=2.0, c=1.0 / 3.0)

xs_mix = MultiGroupXS.Combine([
    (xs_1, 0.5),
    (xs_2, 3.0),
])

Combine returns a new pyopensn.xs.MultiGroupXS object and does not modify its inputs.

Combination semantics are:

  • standard one-dimensional XS such as total, absorption, and fission are added with the supplied densities as linear weights

  • transfer matrices are combined with the same density weighting

  • custom one-dimensional XS are combined with the same density weighting

  • fission spectra and precursor fractional yields are weighted so that they remain normalized in the combined material

Important restrictions:

  • all inputs must have the same number of groups

  • if inverse velocity is present, all inputs must have the same group-wise inverse velocity

Note

Combine is intended for macroscopic mixing. The density values are weights in the macroscopic combination formula, not a request to renormalize the final material back to unit density.

Combining is often useful in transient tests where a composite material needs to be built from two pre-existing macroscopic states.

5.9. Scaling Cross Sections

Use pyopensn.xs.MultiGroupXS.Scale() to scale the current cross section in place:

xs.Scale(2.5)

Important behavior:

  • scaling does not compound

  • each call rescales from the original baseline data

  • named custom XS are scaled together with the standard one-dimensional XS

The current factor can be queried with GetScaleFactor().

Note

Non-compounding scaling is deliberate. It makes repeated parameter studies easier because Scale(0.5) followed later by Scale(2.0) means “scale relative to the original material” each time, not “multiply the current state again.”

5.10. Choosing Between OpenSn and OpenMC Inputs

As a practical guideline:

5.11. Practical Example

The following example shows a common pattern with two materials and two mesh regions:

from pyopensn.xs import MultiGroupXS
from pyopensn.aquad import GLCProductQuadrature2DXY
from pyopensn.solver import DiscreteOrdinatesProblem

# Assume the mesh already has block ids 0 and 1 on its cells.
grid = ...

xs_air = MultiGroupXS()
xs_air.LoadFromOpenSn("Air.cxs")

xs_poly = MultiGroupXS()
xs_poly.LoadFromOpenMC("HDPE.h5", "set1", 294.0, extra_xs_names=["absorption"])

problem = DiscreteOrdinatesProblem(
    mesh=grid,
    num_groups=xs_air.num_groups,
    groupsets=[
        {
            "groups_from_to": (0, xs_air.num_groups - 1),
            "angular_quadrature": GLCProductQuadrature2DXY(
                n_polar=2,
                n_azimuthal=4,
                scattering_order=1,
            ),
        },
    ],
    xs_map=[
        {"block_ids": [0], "xs": xs_air},
        {"block_ids": [1], "xs": xs_poly},
    ],
)

This pattern covers most transport inputs:

  • each material region gets a block id

  • each block id is mapped to a pyopensn.xs.MultiGroupXS

  • the cross sections can come from either OpenSn files, OpenMC libraries, or a combined/simple source

5.12. Cautions and Best Practices

  • Reuse the same pyopensn.xs.MultiGroupXS object for multiple block ids when the material is truly identical.

  • Use distinct objects when materials will be changed independently later with pyopensn.solver.LBSProblem.SetXSMap().

  • Keep the number of groups, scattering order, and fission model consistent with the solver setup.

  • When combining cross sections, ensure that group-wise inverse velocities match exactly if they are present.

  • When importing from OpenMC, request only the extra named datasets that you actually need as custom XS.

Note

MultiGroupXS objects are mutable Python handles to shared C++ objects. If the same object is reused in multiple places, later mutation of that object affects every place that shares it.