Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add more advanced nuclear data processing examples #272

Open
shimwell opened this issue Feb 14, 2024 · 3 comments
Open

add more advanced nuclear data processing examples #272

shimwell opened this issue Feb 14, 2024 · 3 comments

Comments

@shimwell
Copy link
Member

shimwell commented Feb 14, 2024

we have cross section processing examples but no chain file examples

something like this could help find reactions only available via threshold reactions

from pathlib import Path
import os
import openmc
import openmc.deplete
import matplotlib.pyplot as plt
from openmc.data import NATURAL_ABUNDANCE
from openmc.deplete import REACTIONS
import pandas as pd 
import numpy as np
import tqdm 
# PR to change this to DADZ
from openmc.deplete import REACTIONS
from openmc.data import REACTION_MT
from openmc.data import REACTION_NAME

chain_file = Path(os.environ['OPENMC_CHAIN_FILE'])
chain = openmc.deplete.Chain.from_xml(chain_file)

cross_section_file = Path(os.environ['OPENMC_CROSS_SECTIONS'])
cross_section = openmc.data.DataLibrary().from_xml(cross_section_file)


def find_threshold_energy(cross_sections, mt):
    if mt not in cross_sections.reactions.keys():
        print(f'{cross_sections.name} mt {mt} not found')
        return False
    xs = cross_sections.reactions[mt].xs['1200K']
    energy = xs.x
    barns = xs.y
    index_of_threshold = np.where(barns > 0)[0]
    if len(index_of_threshold) > 0:
        first_index_over_threshold = index_of_threshold[0]
        energy_threshold = energy[first_index_over_threshold]
        return energy_threshold
    else:
        return 0.0

transmutation_reaction_names = list(REACTIONS.keys())

nuclide_names = []
reaction_types = []
reaction_targets = []
threshold_energies_needed = []

for nuclide in tqdm.tqdm(chain.nuclides): # 3820 nuclides
    if nuclide.name in NATURAL_ABUNDANCE.keys(): # 289 nuclides
        filename = cross_section.get_by_material(nuclide.name)['path']
        cross_sections = openmc.data.IncidentNeutron.from_hdf5(filename)
        for reaction in nuclide.reactions:
            # check if reaction results in a change to the proton or neutorn numbers or meta stable
            # this looks like is a duplicate check as we already check that a stable nuc is the target and unstable is the product. However it removes elastic heating dpa and other scores
            if reaction.type in transmutation_reaction_names:
                # check if target is unstable
                if reaction.target not in NATURAL_ABUNDANCE.keys():
                    # ruling out fission as they produce a wide range of products
                    if reaction.type != 'fission':
                        mt= REACTION_MT[reaction.type]
                        threshold_energy_needed = find_threshold_energy(cross_sections, mt)
                        if threshold_energy_needed: # can be false if mt not found
                            print(nuclide.name, reaction.type, reaction.target, threshold_energy_needed)
                            nuclide_names.append(nuclide.name)
                            reaction_types.append(reaction.type)
                            reaction_targets.append(reaction.target)
                            threshold_energies_needed.append(threshold_energy_needed)


dict = {'nuclide_names': nuclide_names, 'reaction_types': reaction_types, 'reaction_targets': reaction_targets, 'threshold_energies_needed': threshold_energies_needed} 
df = pd.DataFrame(dict)


all_products=df['reaction_targets'].unique()

threshold_energy = 5e6

options = []
unique_reactions = {}
for product in all_products[:40]:
    tmp_df = df.loc[df['reaction_targets'] == product]
    options_below_threshold = tmp_df.loc[tmp_df['threshold_energies_needed'] < threshold_energy]
    options_above_threshold = tmp_df.loc[tmp_df['threshold_energies_needed'] > threshold_energy]
    if len(options_above_threshold) >= 1 and len(options_below_threshold) == 0:
        options.append(tmp_df['reaction_targets'].values[0])
        all_reaction_paths = []
        for index, row in options_above_threshold.iterrows():
            all_reaction_paths.append(row.reaction_types)
        unique_reactions[row.nuclide_names] = all_reaction_paths

import matplotlib.pyplot as plt
openmc.plotter.plot_xs(
    reactions = unique_reactions
)
plt.xscale('linear')
plt.title('Isotopes that can only be produced with 5MeV neutrons')
plt.ylim(10e-9, 10e0)
plt.savefig(f'isotopes_{threshold_energy}.png')
@shimwell
Copy link
Member Author

this can help find pathways to a specific isotope. It shows the isotopes and reactions that result in the target

import os
from pathlib import Path

import openmc
import openmc.deplete
import openmc_depletion_plotter
from openmc.data import DADZ
 

chain_file = Path(os.environ['OPENMC_CHAIN_FILE'])

chain = openmc.deplete.Chain.from_xml(chain_file)

pathways_to_target = {}
for nuclide in chain.nuclides:
    atomics_number, mass_number, metastable_state = openmc.data.zam(nuclide.name)
    if len(nuclide.reactions)>2:
        print(nuclide.reactions)
        reactions=nuclide.reactions
        for reaction in reactions:
            if reaction.type in DADZ.keys():
                delta_mass_number, delta_atomics_number = DADZ[reaction.type]
                target_mass_number = mass_number + delta_mass_number
                target_atomics_number = atomics_number + delta_atomics_number
                target = openmc.data.gnds_name(
                    Z=target_atomics_number,
                    A=target_mass_number
                )
                print(f'{nuclide.name}{reaction.type}{target}')
                pathways_to_target.setdefault(target, []).append((nuclide.name, reaction.type))

@shimwell
Copy link
Member Author

shimwell commented Feb 26, 2024

These code above can be improved to account for targets that are metastable and made more concise

import os
from pathlib import Path

import openmc
import openmc.deplete
from openmc.data import DADZ
 

chain_file = Path(os.environ['OPENMC_CHAIN_FILE'])

chain = openmc.deplete.Chain.from_xml(chain_file)

pathways_to_target = {}
for nuclide in chain.nuclides:
    if len(nuclide.reactions)>0:
        for reaction in nuclide.reactions:
            # if the reaction changes the A or Z number
            if reaction.type in DADZ.keys():
                pathways_to_target.setdefault(reaction.target, []).append((nuclide.name, reaction.type))

@shimwell
Copy link
Member Author

shimwell commented Jul 28, 2024

This example searches the chain file and gets the energy release for each reaction (Q value) then sorts them from high to low

from pathlib import Path
import openmc
import openmc.deplete
import pandas as pd
chain_file = Path('chain_endf_b8.0_sfr.xml')
chain = openmc.deplete.Chain.from_xml(chain_file)
q_vals = []
targets = []
reactions = []
for nuclide in chain.nuclides:
    if len(nuclide.reactions)>0:
        for reaction in nuclide.reactions:
            
            targets.append(nuclide.name)
            reactions.append(reaction.type)
            q_vals.append(reaction.Q/1e6)
df=pd.DataFrame.from_dict({'targets':targets, 'reactions':reactions, 'Q [MeV]':q_vals})
df=df.sort_values(by=['Q [MeV]'], ascending=False)

the resulting Q values are available here

q_values.csv

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant