openmc icon indicating copy to clipboard operation
openmc copied to clipboard

Python API not reading nuclear data ```interpolation``` and ```breakpoint``` correctly

Open shimwell opened this issue 1 month ago • 9 comments

Bug Description

Reading in the h5 nuclear data with openmc appears to result in default interpolation and breakpoint values such as [2]

Steps to Reproduce

change the dir in this script and run the script it should print out different interpolation and breakpoint values when the file is read with the openmc python api and h5py directly.

import openmc.data
import h5py

# Load a sample HDF5 file and product
filename = "/home/jon/nuclear_data/tendl-2021-hdf5/tendl-2021-hdf5/Cr52.h5"
lib = openmc.data.IncidentNeutron.from_hdf5(filename)

# Pick a product with tabulated distributions
for rx in lib.reactions.values():
    for prod in rx.products:
        # Look for Tabulated1D in applicability or yield
        for app in getattr(prod, "applicability", []):
            if hasattr(app, "breakpoints") and hasattr(app, "interpolation"):
                print("App breakpoints:", app.breakpoints)
                print("App interpolation:", app.interpolation)
        y = getattr(prod, "yield_", None)
        if hasattr(y, "breakpoints") and hasattr(y, "interpolation"):
            print("Yield breakpoints:", y.breakpoints)
            print("Yield interpolation:", y.interpolation)
        # Look for Tabulated1D in distributions
        for dist in getattr(prod, "distribution", []):
            if hasattr(dist, "breakpoints") and hasattr(dist, "interpolation"):
                print("Dist breakpoints:", dist.breakpoints)
                print("Dist interpolation:", dist.interpolation)

# Now print the same info using h5py
print("\n--- h5py direct read ---")
with h5py.File(filename, "r") as f:
    cr52 = f["Cr52"]
    reactions = cr52["reactions"]
    for rx_name in reactions:
        rx_group = reactions[rx_name]
        for prod_name in rx_group:
            if not prod_name.startswith("product_"):
                continue
            prod_group = rx_group[prod_name]
            # Yield
            if "yield" in prod_group:
                y_ds = prod_group["yield"]
                print(f"{rx_name} {prod_name} yield breakpoints:", y_ds.attrs.get("breakpoints", None))
                print(f"{rx_name} {prod_name} yield interpolation:", y_ds.attrs.get("interpolation", None))
            # Distribution(s)
            for dist_key in prod_group:
                if dist_key.startswith("distribution_"):
                    dist_group = prod_group[dist_key]
                    # Applicability
                    if "applicability" in dist_group:
                        app_ds = dist_group["applicability"]
                        print(f"{rx_name} {prod_name} {dist_key} applicability breakpoints:", app_ds.attrs.get("breakpoints", None))
                        print(f"{rx_name} {prod_name} {dist_key} applicability interpolation:", app_ds.attrs.get("interpolation", None))
                    # Angle/mu distribution
                    if "angle" in dist_group and "mu" in dist_group["angle"]:
                        mu_ds = dist_group["angle"]["mu"]
                        print(f"{rx_name} {prod_name} {dist_key} angle/mu interpolation:", mu_ds.attrs.get("interpolation", None))

Environment

ubuntu , python 3.13, openmc develop branch

shimwell avatar Dec 06 '25 21:12 shimwell

This script print a large amount of numbers. Could you provide a specific reaction and reaction product where there is a problem. It will help if you could also provide the script output for that reaction and reaction product combination using python api and h5py.

GuySten avatar Dec 07 '25 03:12 GuySten

Thanks GuySten

MT 70 products appear to have different break points, others are also different but this script focuses on mt 70

import openmc.data
import h5py

# Load a sample HDF5 file and product
filename = "/home/jon/nuclear_data/tendl-2021-hdf5/tendl-2021-hdf5/Cr52.h5"
lib = openmc.data.IncidentNeutron.from_hdf5(filename)

reaction_mt = 70

# Pick a product with tabulated distributions
for rx in lib.reactions.values():
    for prod in rx.products:
        if rx.mt == reaction_mt:
            # Look for Tabulated1D in applicability or yield
            for app in getattr(prod, "applicability", []):
                if hasattr(app, "breakpoints") and hasattr(app, "interpolation"):
                    print("App breakpoints:", app.breakpoints)
                    print("App interpolation:", app.interpolation)
            y = getattr(prod, "yield_", None)
            if hasattr(y, "breakpoints") and hasattr(y, "interpolation"):
                print("Yield breakpoints:", y.breakpoints)
                print("Yield interpolation:", y.interpolation)
            # Look for Tabulated1D in distributions
            for dist in getattr(prod, "distribution", []):
                if hasattr(dist, "breakpoints") and hasattr(dist, "interpolation"):
                    print("Dist breakpoints:", dist.breakpoints)
                    print("Dist interpolation:", dist.interpolation)

# Now print the same info using h5py
print("\n--- h5py direct read ---")
with h5py.File(filename, "r") as f:
    cr52 = f["Cr52"]
    reactions = cr52["reactions"]
    for rx_name in reactions:
        if rx_name == f"reaction_0{reaction_mt}":
            rx_group = reactions[rx_name]
            for prod_name in rx_group:
                if not prod_name.startswith("product_"):
                    continue
                prod_group = rx_group[prod_name]
                # Yield
                if "yield" in prod_group:
                    y_ds = prod_group["yield"]
                    # print(f"{rx_name} {prod_name} yield breakpoints:", y_ds.attrs.get("breakpoints", None))
                    print(f"{rx_name} {prod_name} yield interpolation:", y_ds.attrs.get("interpolation", None))
                # Distribution(s)
                for dist_key in prod_group:
                    if dist_key.startswith("distribution_"):
                        dist_group = prod_group[dist_key]
                        # Applicability
                        if "applicability" in dist_group:
                            app_ds = dist_group["applicability"]
                            print(f"{rx_name} {prod_name} {dist_key} applicability breakpoints:", app_ds.attrs.get("breakpoints", None))
                            print(f"{rx_name} {prod_name} {dist_key} applicability interpolation:", app_ds.attrs.get("interpolation", None))
                        # Angle/mu distribution
                        if "angle" in dist_group and "mu" in dist_group["angle"]:
                            mu_ds = dist_group["angle"]["mu"]
                            print(f"{rx_name} {prod_name} {dist_key} angle/mu interpolation:", mu_ds.attrs.get("interpolation", None))

for Cr52.h5 Tendl 2021 outputs this

App breakpoints: [2]
App interpolation: [2]
Yield breakpoints: [2]
Yield interpolation: [2]
Yield breakpoints: [2]
Yield interpolation: [2]
Yield breakpoints: [2]
Yield interpolation: [2]

--- h5py direct read ---
reaction_070 product_0 yield interpolation: None
reaction_070 product_0 distribution_0 applicability breakpoints: [2]
reaction_070 product_0 distribution_0 applicability interpolation: [2]
reaction_070 product_0 distribution_0 angle/mu interpolation: [2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2]
reaction_070 product_1 yield interpolation: [2]
reaction_070 product_1 distribution_0 angle/mu interpolation: [1 1]
reaction_070 product_2 yield interpolation: [2]
reaction_070 product_2 distribution_0 angle/mu interpolation: [1 1]
reaction_070 product_3 yield interpolation: [2]
reaction_070 product_3 distribution_0 angle/mu interpolation: [1 1]

the python api shows lin lin [2] in all cases where the hdf5 data contains histogram [1, 1] in some cases

shimwell avatar Dec 07 '25 15:12 shimwell

I think the problem is in your script: The script does not know how to parse angle interpolation.

You can see that in the output there are no lines with either Dist breakpoints or Dist interpolation.

GuySten avatar Dec 07 '25 15:12 GuySten

Sorry my script is a mess, I think I've found something in the openmc source code

the Reation.from_hdf5 function calls Tabulated1D with no arguments for breakpoints or interpolation

https://github.com/openmc-dev/openmc/blob/f70febb05f5fc32925d08707db48ff2c33dfe493/openmc/data/reaction.py#L975C21-L975C78

So both breakpoints or interpolation default to None and then we get default of [2] linear linear interpolation

https://github.com/openmc-dev/openmc/blob/f70febb05f5fc32925d08707db48ff2c33dfe493/openmc/data/function.py#L147-L148

I think this is not correct to allow defaults here

shimwell avatar Dec 07 '25 17:12 shimwell

I think it is okay because the cross sections that are stored in openmc hdf5 libraries always have linear-linear interpolation.

EDIT: One of the responsibilities of NJOY2016 is to convert cross sections to linear-linear representation.

GuySten avatar Dec 07 '25 18:12 GuySten

I agree on the cross sections. However this is for the reaction products that have their own distributions.

I think the reaction products are missing their interpolation values when being read from hdf5 in the python api. When writing products in the python api that looks fine and when reading products in the c++ code that also looks fine.

shimwell avatar Dec 07 '25 18:12 shimwell

I think the reaction products are missing their interpolation values when being read from hdf5 in the python api. When writing products in the python api that looks fine and when reading products in the c++ code that also looks fine.

I did not find evidence to support that. I could look into it if you find some evidence.

GuySten avatar Dec 07 '25 18:12 GuySten

Thanks for your help with this, much appreachiated

I include a screenshot of h5dump of Cr52.h5 where we can see the interpolation is [1,1] for reaction 70.

When reading in this nuclear data with the openmc python api it gives [2]

Image

This is the same reaction as my earlier script was printing out interpolation values for.

shimwell avatar Dec 07 '25 20:12 shimwell

But how do you know that the python api does not read this correctly?

GuySten avatar Dec 07 '25 20:12 GuySten