From 8053111f2d0b120962fbdbcc81b5066e37dac874 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 10 Feb 2026 02:08:25 +0000 Subject: [PATCH] making use of endf-python package more (#3786) Co-authored-by: GuySten --- openmc/data/ace.py | 2 +- openmc/data/endf.py | 294 ++++---------------------------------------- 2 files changed, 24 insertions(+), 272 deletions(-) diff --git a/openmc/data/ace.py b/openmc/data/ace.py index 6ccb76c92..d3de6a192 100644 --- a/openmc/data/ace.py +++ b/openmc/data/ace.py @@ -24,7 +24,7 @@ import numpy as np import openmc.checkvalue as cv from openmc.mixin import EqualityMixin from .data import ATOMIC_SYMBOL, gnds_name, EV_PER_MEV, K_BOLTZMANN -from .endf import ENDF_FLOAT_RE +from endf.records import ENDF_FLOAT_RE def get_metadata(zaid, metastable_scheme='nndc'): diff --git a/openmc/data/endf.py b/openmc/data/endf.py index eca374469..bccd6b36b 100644 --- a/openmc/data/endf.py +++ b/openmc/data/endf.py @@ -10,11 +10,31 @@ import io from pathlib import PurePath import re -import numpy as np - from .data import gnds_name from .function import Tabulated1D -from endf.records import float_endf +from endf.records import ( + float_endf, + py_float_endf, + int_endf, + get_text_record, + get_cont_record, + get_head_record, + get_list_record, + get_tab1_record as _get_tab1_record, + get_tab2_record, + get_intg_record, +) + + +def get_tab1_record(file_obj): + """Return data from a TAB1 record in an ENDF-6 file. + + This wraps the endf package's get_tab1_record to return an + openmc.data.Tabulated1D (which has HDF5 support and is a Function1D) + instead of endf.Tabulated1D. + """ + params, tab = _get_tab1_record(file_obj) + return params, Tabulated1D(tab.x, tab.y, tab.breakpoints, tab.interpolation) _LIBRARY = {0: 'ENDF/B', 1: 'ENDF/A', 2: 'JEFF', 3: 'EFF', @@ -61,274 +81,6 @@ SUM_RULES = {1: [2, 3], 106: list(range(750, 800)), 107: list(range(800, 850))} -ENDF_FLOAT_RE = re.compile(r'([\s\-\+]?\d*\.\d+)([\+\-]) ?(\d+)') - - -def py_float_endf(s): - """Convert string of floating point number in ENDF to float. - - The ENDF-6 format uses an 'e-less' floating point number format, - e.g. -1.23481+10. Trying to convert using the float built-in won't work - because of the lack of an 'e'. This function allows such strings to be - converted while still allowing numbers that are not in exponential notation - to be converted as well. - - Parameters - ---------- - s : str - Floating-point number from an ENDF file - - Returns - ------- - float - The number - - """ - return float(ENDF_FLOAT_RE.sub(r'\1e\2\3', s)) - - -def int_endf(s): - """Convert string of integer number in ENDF to int. - - The ENDF-6 format technically allows integers to be represented by a field - of all blanks. This function acts like int(s) except when s is a string of - all whitespace, in which case zero is returned. - - Parameters - ---------- - s : str - Integer or spaces - - Returns - ------- - integer - The number or 0 - """ - return 0 if s.isspace() else int(s) - - -def get_text_record(file_obj): - """Return data from a TEXT record in an ENDF-6 file. - - Parameters - ---------- - file_obj : file-like object - ENDF-6 file to read from - - Returns - ------- - str - Text within the TEXT record - - """ - return file_obj.readline()[:66] - - -def get_cont_record(file_obj, skip_c=False): - """Return data from a CONT record in an ENDF-6 file. - - Parameters - ---------- - file_obj : file-like object - ENDF-6 file to read from - skip_c : bool - Determine whether to skip the first two quantities (C1, C2) of the CONT - record. - - Returns - ------- - tuple - The six items within the CONT record - - """ - line = file_obj.readline() - if skip_c: - C1 = None - C2 = None - else: - C1 = float_endf(line[:11]) - C2 = float_endf(line[11:22]) - L1 = int_endf(line[22:33]) - L2 = int_endf(line[33:44]) - N1 = int_endf(line[44:55]) - N2 = int_endf(line[55:66]) - return (C1, C2, L1, L2, N1, N2) - - -def get_head_record(file_obj): - """Return data from a HEAD record in an ENDF-6 file. - - Parameters - ---------- - file_obj : file-like object - ENDF-6 file to read from - - Returns - ------- - tuple - The six items within the HEAD record - - """ - line = file_obj.readline() - ZA = int(float_endf(line[:11])) - AWR = float_endf(line[11:22]) - L1 = int_endf(line[22:33]) - L2 = int_endf(line[33:44]) - N1 = int_endf(line[44:55]) - N2 = int_endf(line[55:66]) - return (ZA, AWR, L1, L2, N1, N2) - - -def get_list_record(file_obj): - """Return data from a LIST record in an ENDF-6 file. - - Parameters - ---------- - file_obj : file-like object - ENDF-6 file to read from - - Returns - ------- - list - The six items within the header - list - The values within the list - - """ - # determine how many items are in list - items = get_cont_record(file_obj) - NPL = items[4] - - # read items - b = [] - for i in range((NPL - 1)//6 + 1): - line = file_obj.readline() - n = min(6, NPL - 6*i) - for j in range(n): - b.append(float_endf(line[11*j:11*(j + 1)])) - - return (items, b) - - -def get_tab1_record(file_obj): - """Return data from a TAB1 record in an ENDF-6 file. - - Parameters - ---------- - file_obj : file-like object - ENDF-6 file to read from - - Returns - ------- - list - The six items within the header - openmc.data.Tabulated1D - The tabulated function - - """ - # Determine how many interpolation regions and total points there are - line = file_obj.readline() - C1 = float_endf(line[:11]) - C2 = float_endf(line[11:22]) - L1 = int_endf(line[22:33]) - L2 = int_endf(line[33:44]) - n_regions = int_endf(line[44:55]) - n_pairs = int_endf(line[55:66]) - params = [C1, C2, L1, L2] - - # Read the interpolation region data, namely NBT and INT - breakpoints = np.zeros(n_regions, dtype=int) - interpolation = np.zeros(n_regions, dtype=int) - m = 0 - for i in range((n_regions - 1)//3 + 1): - line = file_obj.readline() - to_read = min(3, n_regions - m) - for j in range(to_read): - breakpoints[m] = int_endf(line[0:11]) - interpolation[m] = int_endf(line[11:22]) - line = line[22:] - m += 1 - - # Read tabulated pairs x(n) and y(n) - x = np.zeros(n_pairs) - y = np.zeros(n_pairs) - m = 0 - for i in range((n_pairs - 1)//3 + 1): - line = file_obj.readline() - to_read = min(3, n_pairs - m) - for j in range(to_read): - x[m] = float_endf(line[:11]) - y[m] = float_endf(line[11:22]) - line = line[22:] - m += 1 - - return params, Tabulated1D(x, y, breakpoints, interpolation) - - -def get_tab2_record(file_obj): - # Determine how many interpolation regions and total points there are - params = get_cont_record(file_obj) - n_regions = params[4] - - # Read the interpolation region data, namely NBT and INT - breakpoints = np.zeros(n_regions, dtype=int) - interpolation = np.zeros(n_regions, dtype=int) - m = 0 - for i in range((n_regions - 1)//3 + 1): - line = file_obj.readline() - to_read = min(3, n_regions - m) - for j in range(to_read): - breakpoints[m] = int(line[0:11]) - interpolation[m] = int(line[11:22]) - line = line[22:] - m += 1 - - return params, Tabulated2D(breakpoints, interpolation) - - -def get_intg_record(file_obj): - """ - Return data from an INTG record in an ENDF-6 file. Used to store the - covariance matrix in a compact format. - - Parameters - ---------- - file_obj : file-like object - ENDF-6 file to read from - - Returns - ------- - numpy.ndarray - The correlation matrix described in the INTG record - """ - # determine how many items are in list and NDIGIT - items = get_cont_record(file_obj) - ndigit = items[2] - npar = items[3] # Number of parameters - nlines = items[4] # Lines to read - NROW_RULES = {2: 18, 3: 12, 4: 11, 5: 9, 6: 8} - nrow = NROW_RULES[ndigit] - - # read lines and build correlation matrix - corr = np.identity(npar) - for i in range(nlines): - line = file_obj.readline() - ii = int_endf(line[:5]) - 1 # -1 to account for 0 indexing - jj = int_endf(line[5:10]) - 1 - factor = 10**ndigit - for j in range(nrow): - if jj+j >= ii: - break - element = int_endf(line[11+(ndigit+1)*j:11+(ndigit+1)*(j+1)]) - if element > 0: - corr[ii, jj] = (element+0.5)/factor - elif element < 0: - corr[ii, jj] = (element-0.5)/factor - - # Symmetrize the correlation matrix - corr = corr + corr.T - np.diag(corr.diagonal()) - return corr - def get_evaluations(filename): """Return a list of all evaluations within an ENDF file.