Curious behaviour when transferring elements towards reservoirs in series

I am currently running some tests in order to check the correct transfer of mass from one area to another.
I have a sphere of 239Pu used solely for transport, that won’t be of interest here.

The main point is a cloud made of 4 gases out of the geometry, followed by 4 reservoirs.
The 4 gases are extracted from the cloud towards the 1st reservoir, then 3 are extracted from reservoir 1 towards the next etc.

When I extract nucleide masses and plot them, I observe that some gas masses are reset to their original value in the cloud, with no consequence on the correct distribution in the reservoirs.

Has anyone else seen such behaviour ?

Input and post-processing python files are provided below.

I asked one collegue to do the same with his different config and he observed such things as well. I also tried with other fuels and other gases and the same behaviour occurs, always with the first material, whether it is included in the geometry or not.

My thanks in advance to any one contributing to this post :slight_smile:

The input :

import openmc
import pandas as pd 
import numpy as np 
import openmc.deplete


chain_file = "" #a chain file
xs_file = ""  #a xs file, I used data from ENDF-B7.1 for this test

openmc.config["cross_sections"] = xs_file
openmc.config["chain_file"] = chain_file

temperature = 900

# Geometry
radius_core = 4.95
core = openmc.Sphere(r=radius_core, boundary_type="vacuum")

# Cells
incore = openmc.Cell(name="incore", region=-core)
outcore = openmc.Cell(name="outcore", region=+core)

mat_fuel = openmc.Material(name="fuel", temperature=temperature)
mat_fuel.add_components({
    "Pu239": 1.0,
})

mat_fuel.set_density("g/cm3", 19.8)
mat_fuel.volume = (4/3)*np.pi*(radius_core**3)
mat_fuel.depletable = True
incore.fill = mat_fuel


mat_cloud = openmc.Material(name="cloud", temperature=temperature)
mat_cloud.add_components({
    "He4": 0.25,
    "Ar40": 0.25,
    "Kr84": 0.25,
    "Xe132": 0.25
})

radius_cloud = 10
mat_cloud.set_density("g/cm3", 1e-3)
mat_cloud.volume = (4/3)*np.pi*(radius_cloud**3)
mat_cloud.depletable = True

# Universe
root_universe = openmc.Universe(cells=(incore, outcore))

geometry = openmc.Geometry()
geometry.root_universe = root_universe


# Settings
# Create a point source
point = openmc.stats.Point((0, 0, 0))
source = openmc.Source(space=point)

settings = openmc.Settings()
settings.source = source
settings.batches = 400
settings.inactive = 100
settings.particles = 5000
settings.output = {"path":"."}


# Depletion
Reservoir1 = openmc.Material(name='Reservoir1')
Reservoir1.add_nuclide("Fe56", 1) # random stable element to avoid empty mat
Reservoir1.set_density("g/cm3", 1e-32)
Reservoir1.volume = 1e6
Reservoir1.depletable = True

Reservoir2 = openmc.Material(name='Reservoir2')
Reservoir2.add_nuclide("Fe56", 1)
Reservoir2.set_density("g/cm3", 1e-32)
Reservoir2.volume = 1e6
Reservoir2.depletable = True

Reservoir3 = openmc.Material(name='Reservoir3')
Reservoir3.add_nuclide("Fe56", 1)
Reservoir3.set_density("g/cm3", 1e-32)
Reservoir3.volume = 1e6
Reservoir3.depletable = True

Reservoir4 = openmc.Material(name='Reservoir4')
Reservoir4.add_nuclide("Fe56", 1)
Reservoir4.set_density("g/cm3", 1e-32)
Reservoir4.volume = 1e6
Reservoir4.depletable = True

materials = openmc.Materials([mat_fuel, mat_cloud, Reservoir1, Reservoir2, Reservoir3, Reservoir4])

materials.export_to_xml()
geometry.export_to_xml()
settings.export_to_xml()

times = np.array([1/24, 3/24, 6/24, 12/24, 1, 2, 4, 8, 16, 32, 64, 128, 256, 384, 512, 640, 768, 896, 1024])
times2 = times*1
for i in range(1, len(times2)):
    times2[i] = times[i] - times[i-1]


power = 10e6

towards_Res1 = ["He", "Ar", "Kr", "Xe"]
towards_Res2 = ["He", "Ar", "Kr"]
towards_Res3 = ["He", "Ar"]
towards_Res4 = ["He"]


model = openmc.model.Model(geometry, materials, settings)
operator = openmc.deplete.CoupledOperator(model, chain_file=chain_file, normalization_mode='fission-q', fission_yield_opts={"energy":500e3})
integ = openmc.deplete.PredictorIntegrator(operator, times2, power, timestep_units='d')

integ.add_transfer_rate(mat_cloud, towards_Res1, 1/(1*3600), destination_material=Reservoir1)
integ.add_transfer_rate(Reservoir1, towards_Res2, 1/(2*3600), destination_material=Reservoir2)
integ.add_transfer_rate(Reservoir2, towards_Res3, 1/(3*3600), destination_material=Reservoir3)
integ.add_transfer_rate(Reservoir3, towards_Res4, 1/(4*3600), destination_material=Reservoir4)
integ.integrate()

The post-processing :

import openmc 
import openmc.deplete 
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec 

chain_file = "" # same file as input
xs_file = "" # same file as input

openmc.config["cross_sections"] = xs_file
openmc.config["chain_file"] = chain_file

f_dep = "depletion_results.h5"
results = openmc.deplete.Results(f_dep)

nuclei = ["He4", "Ar40", "Kr84", "Xe132"]
colors = ["red", "orange", "cyan", "blue"]
areas = ["Cloud", "Reservoir 1", "Reservoir 2", "Reservoir 3", "Reservoir 4"]

times = np.array([0, 1/24, 3/24, 6/24, 12/24, 1, 2, 4, 8, 16, 32, 64, 128 , 256, 384, 512, 640, 768, 896, 1024])


dico_results = {}
for area in areas:
    dico_results[area] = {}
    for nuc in nuclei:
        dico_results[area][nuc] = np.zeros((len(times)))

    
for id_mat in range(1, len(areas)+1): #core is 0
    area = areas[id_mat-1]

    for i in range(len(times)):
        mat = results.export_to_materials(i)[id_mat]
        nucl = mat.get_nuclides()
        # print(dico_results[area].keys())
        # print(nucl)
        for n in range(len(nucl)):
            try: dico_results[area][nucl[n]][i] = mat.get_mass(nucl[n])
            except: print(f"{nucl[n]} : {mat.get_mass(nucl[n])}") # ignore any element that can appear with superlow value, including the very low amount of iron

# print(dico_results.keys())
for area in areas:
    for nuc in nuclei:
        print(dico_results[area][nuc])

fig = plt.figure(figsize=(4*len(areas), 4))
gs = GridSpec(1, len(areas), fig)
axs=[]
for i in range(len(areas)):
    axs.append(fig.add_subplot(gs[i]))

for i in range(len(areas)):
    for j in range(len(nuclei)):
        vect = dico_results[areas[i]][nuclei[j]]
        axs[i].scatter(times, vect, label=nuclei[j], color=colors[j])

    axs[i].set_xscale("log")
    axs[i].set_xlim([times[1]*0.5, times[-1]*1.2])
    axs[i].set_xlabel("Time [d]")
    axs[i].set_ylabel("Mass [g]")
    axs[i].set_title(areas[i])

axs[0].legend()
fig.tight_layout()
fig.savefig("simple_distribution_in_areas.png", dpi=300)

Hi @hpitois
Don’t know if it’s a TransferRates bug yet, but before digging into the main could you make me a favor and try re-running you post-processing using the results.get_atoms() method, instead of export_to_materials and get_mass() , to get the total number of atoms by nuclide per each material and than convert it manually to mass.
Thanks.

Hello @lorenzo
I followed your suggestion and tested with results.get_atoms, then with results.get_mass.
Both are giving the expected results.

It seems they are some shenanigans with the loop containing the export_to_materials function as you identified. I don’t understand so far why this strange result occurs.

Thank you very much for your answer, I will let admins decide weither to mark this as a solution and/or if they wish to investigate it further.

1 Like