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

FENDL-3.2b Retrofitting #42

Draft
wants to merge 59 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
dbef6ca
First commit for FENDL3.2B retrofitting
Jun 14, 2024
cdd7bcd
Replacing NJOY Bash script with subprocess execution in Python
Jun 17, 2024
f3d010f
Simplifying mass number formatting in tendl_download() function
Jun 17, 2024
6c5ac24
Simplifying endf_specs() function
Jun 17, 2024
03e3af3
Remove now-obsolete ENDFtk warning suppression
Jun 17, 2024
fa4f29e
Simplify tendl_download() function using data structures
Jun 17, 2024
0190096
Switching tendl_download() function over to urllib dependence
Jun 17, 2024
413ae46
Moving card deck formatting from Pandas DataFrame to dictionary
Jun 17, 2024
2eb9ffd
Separating out a write function for the GROUPR input from the input c…
Jun 17, 2024
1247db3
Removing now-obsolete Pandas dependence
Jun 17, 2024
1d35b79
Simplifying card writing for groupr_input_file_writer()
eitan-weinstein Jun 18, 2024
de3cbb4
Fixing indexing on groupr_input_file_writer()
Jun 18, 2024
d20eed8
Storing elements in a single dictionary to be referenced across both …
Jun 18, 2024
58a4ede
Removing now-obsolete ENDFtk warning supression from gend_tools.py an…
Jun 18, 2024
b83be55
Updating gendf_download() function -- notably switching away from wge…
Jun 18, 2024
e135311
Switching CSV reading from Pandas DataFrame to dictionary
Jun 18, 2024
29528dd
Moving away from direct input to argparse input/options
Jun 18, 2024
0abb51b
Expanding argparse usage
Jun 18, 2024
582424b
Moving away from print statements towards logging
Jun 18, 2024
77e9a65
Removed unnecessary file from file cleanup list
Jun 19, 2024
493a35c
Expanding logger to capture 'No bottleneck testing available' message
Jun 19, 2024
a29bd66
Improving readability of NJOY run message for logger
Jun 19, 2024
69fe5f0
Updating the logging to redirect ENDFtk messages to the logger and re…
Jun 21, 2024
d0f7d3b
Removing stand-alone groupr script -- unnecessary and not called indi…
Jun 21, 2024
4318ae4
Reorganizing folder structure -- separate GROUPR folder no longer see…
Jun 21, 2024
b1b63f9
Finalizing move out of GROUPR/
Jun 21, 2024
1edd251
Moving the rest of fendl3_gendf.py to the main() function
Jun 21, 2024
fb2d548
Forgot to include mt_table in main()
Jun 21, 2024
4065d00
Streamlining endf_specs usage and placement.
Jun 24, 2024
4250c44
Removing direct GENDF download function -- all downloads need to be p…
Jun 24, 2024
93d469f
Moving GROUPR parameters to global constants.
Jun 24, 2024
98dcc93
Logging error if NJOY run is unsuccessful.
Jun 24, 2024
2460d72
Cleaning up package imports
Jun 24, 2024
6fcf5e5
Removing unnecessary package imports on fendl3_gendf.py
Jun 24, 2024
5ec6bbf
Fixing KZA formatting.
Jun 26, 2024
f490d38
Addressing low-level comments from most recent review.
Jul 1, 2024
45df27f
Improving readability
Jul 1, 2024
b76634f
Beginning high-level overhaul and restructuring
Jul 1, 2024
121e57a
Improving readability for nuclear_decay()
Jul 1, 2024
fb1b796
Increasing readability of argparser
Jul 1, 2024
c8e6cea
Major overhaul of modularity and including functionality for iteratin…
Jul 9, 2024
4d99f41
Removing time package.
Jul 9, 2024
e0529dc
Removing specific example file from GENDF files.
Jul 9, 2024
cc064b6
Making the file saving more versatile.
Jul 9, 2024
14c5730
Responding to a majority of the high-level comments from Tuesday's re…
Jul 11, 2024
95815b2
Fixing docstring for ensure_gendf_markers() function.
Jul 11, 2024
a5997b5
Improving isotope identification methods.
Jul 12, 2024
f83a646
Improving isotope identification methods.
Jul 12, 2024
98f23c3
Simplifying logging method and usage.
Jul 12, 2024
498c824
One more logging fix.
Jul 12, 2024
a807f1e
Completing response to last review and making arg processing more mod…
Jul 16, 2024
76b9aa1
Improving ability to iterate over all elements.
Jul 16, 2024
eebeea3
Fixing minor bug in execution of handle_TENDL_downloads().
Jul 16, 2024
912530f
Small formatting change to fit in max line length.
Jul 16, 2024
c374494
More minor formatting adjustments and simplifying the line length set…
Jul 16, 2024
f50b617
Allowing for fendle_retrofit.py to be executed from DataLib.
Jul 16, 2024
4ba725e
Removing unnecessary print statement.
Jul 17, 2024
6257033
Ensuring that NJOY output is properly handled when program is execute…
Jul 17, 2024
4921dba
Small formatting changes before moving over to project individual PRs.
Jul 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions src/DataLib/fendl32B_retrofit/activation_analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# Import packages
from logging_config import logger, LoggerWriter
import pandas as pd
from tendl_preprocessing import extract_cross_sections

def count_emitted_particles(particle, emitted_particle_string):
"""
Count emitted particles from a reaction given a target particle
and the particles produced in a neutron activation reaction.

Arguments:
particle (str): Name of the target particle produced in the reaction.
Options include n, p, alpha, d, t, and 3He, corresponding to
neutrons, protons, alpha particles, deuterons, tritons, and helium-3 nuclides.
emitted_particle_string (str): Particle product(s) of the neutron activation,
of the format 'p' for a single proton for example or '2n' for two neutrons etc.

Returns:
number_str (int or None): Count of the target particle present in the product.
For particles not present, returns None rather than 0.
"""

particle_index = emitted_particle_string.find(particle)
number_str = ''
for i in range(particle_index - 1, -1, -1):
if emitted_particle_string[i].isdigit():
number_str = emitted_particle_string[i] + number_str
else:
break

if number_str:
number_str = int(number_str)
elif particle in emitted_particle_string:
number_str = 1
else:
number_str = None

return number_str

def isomer_check(emitted_particle_string):
"""
Check the isomeric status of a neutron-activated nucleus.
By the formatting conventions of ENDF reaction types,
if the string of a reaction product ends with a digit,
that signifies the excitation state of the nucleus, so
this function looks for and stores these values.

Arguments:
emitted_particle_string (str): Particle product(s) of the neutron activation.

Returns:
isomeric_state (int): Nuclear excitation level of the activated nucleus.
For a nucleus in the ground state, isomeric_state = 0.
"""

last_digits_str = ''
for char in reversed(emitted_particle_string):
if char.isdigit():
last_digits_str = char + last_digits_str
else:
break
isomeric_value = int(last_digits_str) if last_digits_str else 0
return isomeric_value

def nuclear_decay(A, nucleus_protons, emission_tuples):
"""
Reconfigure nucleus for nuclear decay during neutron activation
by adding in a single neutron and then subtracting the total number
of neutrons and protons (if any) emitted during the reaction from
the nucleus.

Arguments:
A (int): Mass number for target isotope.
nucleus_protons (int): Atomic number for the target isotope,
namely the number of protons in the nucleus.
emission_tuples (list): List of all emitted particles for a given reaction,
in the form of tuples with the particle count as the first value
and the particle symbol as the second value. For example, a reaction that
emits one neutron and one proton will have
emission_tuples = [(1, 'n'), (1, 'p')].

Returns:
nucleus_neutrons (int): Updated count of neutrons in the residual nucleus.
nucleus_protons (int): Updated count of protons in the residual nucleus.
"""

# Neutron capture
nucleus_neutrons = A - nucleus_protons + 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    #.            emitted   delta N   delta P
    delta_NP = { 'n' : np.array([-1, 0]), # neutron emission, N = 1, P = 0
                 'p' : np.array([0,-1]),
                 't' : np.array([-2, -1]), 
....
}

N_P = np.array([A - Z + 1, Z])

for particle, num_particles in emission_tuples.items(): # if changed to dictionary
    N_P += num_particle * change_N_P[particle]


for num_particle, particle in emission_tuples:
# Neutron emission
if particle == 'n':
nucleus_neutrons -= num_particle
# Proton emission
if particle == 'p':
nucleus_protons -= num_particle
# Deuteron (1 proton, 1 neutron) emission
if particle == 'd':
nucleus_neutrons -= num_particle
nucleus_protons -= num_particle
# Triton (1 proton, 2 neutrons) emission
if particle == 't':
nucleus_neutrons -= 2 * num_particle
nucleus_protons -= num_particle
# Helium-3 (2 protons, 1 neutron) emission
if particle == '3He':
nucleus_neutrons -= num_particle
nucleus_protons -= 2 * num_particle
# Alpha particle (2 protons, 2 neutrons) emission
if particle == 'α':
nucleus_neutrons -= 2 * num_particle
nucleus_protons -= 2 * num_particle

return nucleus_neutrons, nucleus_protons

def reaction_calculator(MT, mt_dict, pKZA):
"""
Calculate the products of a neutron activation reaction given
the parent nuclide's KZA and the selected reaction type (MT).
This calculation determines both the residual nucleus, as described by the
daughter KZA value (dKZA) and the emitted particle(s).

Arguments:
MT (int): Unique identifier for the reaction type corresponding to a specific
reaction tabulated in the mt_table dictionary.
mt_dict (dict): Reference dictionary containing reaction information
for each MT number pre-defined in the ENDF manual.
(https://www.oecd-nea.org/dbdata/data/manual-endf/endf102_MT.pdf)
pKZA (int or str): Parent KZA identifier of the format ZZAAAM,
where ZZ is the isotope's atomic number, AAA is the mass number,
and M is the isomeric state (0 if non-isomeric).

Returns:
dKZA (str): KZA of the residual (daughter) nucleus.
emitted_particles (str): Name of the particles emitted from the reaction,
given as a single string. If multiple particles are emitted from the reaction,
then the emitted_particles would be written in the form "np", corresponding
to the emission of a neutorn and a proton.
"""

try:
# Extract the parent nuclide properties from the pKZA
nucleus_protons = int(str(pKZA)[:2])
A = int(str(pKZA)[2:5])

# Identify the particles emitted from the given reaction
reaction = mt_dict[str(MT)]
emitted_particles = reaction.split(',')[1][:-1]

# Tally the counts of each emitted particle from the reaction
particle_types = ['n', 'd', 'α', 'p', 't', '3He', 'gamma']
emission_tuples = [
(
count_emitted_particles(particle, emitted_particles),
particle
)
for particle in particle_types
if count_emitted_particles(particle, emitted_particles)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if count_emitted_particles(particle, emitted_particles)
if particle in emitted_particles)

]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
emission_tuples = [
(
count_emitted_particles(particle, emitted_particles),
particle
)
for particle in particle_types
if count_emitted_particles(particle, emitted_particles)
]
emission_tuples = {
particle : count_emitted_particles(particle, emitted_particles)
for particle in particle_types
if particle in emitted particles
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And build this as soon as you load the MT CSV data


# Reconfigure nucleus to account for changing nucleon counts
nucleus_neutrons, nucleus_protons = nuclear_decay(A,
nucleus_protons,
emission_tuples)
residual_A = str(nucleus_neutrons + nucleus_protons).zfill(3)
nucleus_protons = str(nucleus_protons).zfill(2)
M = isomer_check(emitted_particles)
if M != 0:
emitted_particles = emitted_particles[:-len(str(M))]
dKZA = f"{str(nucleus_protons)}{residual_A}{str(M)}"
return dKZA, emitted_particles

except Exception as e:
logger.error(f"Error in reaction calculation for MT {MT}: {e}")
return None, None

def iterate_MTs(MTs, file_obj, mt_dict, pKZA):
# Initialize lists
cross_sections_by_MT = []
emitted_particles_list = []
dKZAs = []
groups = []

# Extract data for each MT
for MT in MTs:
try:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using a try block, check beforehand to make sure that the MT is in the mt_dict and only proceed if True

sigma_list = extract_cross_sections(file_obj, MT)
if not sigma_list:
continue
dKZA, emitted_particles = reaction_calculator(MT, mt_dict, pKZA)
if dKZA is None:
continue
cross_sections_by_MT.append(sigma_list)
dKZAs.append(dKZA)
emitted_particles_list.append(emitted_particles)
groups.append(len(sigma_list))
except Exception as e:
logger.error(f"Error processing MT {MT}: {e}")
continue

# Store data in a Pandas DataFrame
gendf_data = pd.DataFrame({
'Parent KZA': [pKZA] * len(dKZAs),
'Daughter KZA': dKZAs,
'Emitted Particles': emitted_particles_list,
'Non-Zero Groups' : groups,
'Cross Sections': cross_sections_by_MT
})

return gendf_data
Loading