Add function to create compact MT=458 data library

This commit is contained in:
Sterling Harper 2016-08-01 22:34:04 -05:00
parent b64dcd24c8
commit 725ef2d923
2 changed files with 196 additions and 61 deletions

View file

@ -24,6 +24,7 @@ def read_CONT_line(line):
int(line[66:70]), int(line[70:72]), int(line[72:75]),
int(line[75:80]))
def identify_nuclide(fname):
"""Read the header of an ENDF file and extract identifying information."""
with open(fname, 'r') as fh:
@ -39,5 +40,6 @@ def identify_nuclide(fname):
# Return dictionary of the most important identifying information.
return {'Z': int(ZA) // 1000,
'A': int(ZA) % 1000,
'LFI': bool(LFI),
'LIS': LIS,
'LISO': LISO}

View file

@ -3,6 +3,7 @@ from copy import deepcopy
import sys
#from warnings import warn
import h5py
import numpy as np
from numpy.polynomial.polynomial import Polynomial
@ -14,6 +15,189 @@ if sys.version_info[0] >= 3:
basestring = str
def _extract_458_data(filename):
"""Read an ENDF file and extract the MF=1, MT=458 values.
Parameters
----------
filename : str
Path to and ENDF file
Returns
-------
value : dict of str to list of float
Dictionary that gives lists of coefficients for each energy component.
The keys are the 2-3 letter strings used in ENDF-102, e.g. 'EFR' and
'ET'. The list will have a length of 1 for Sher-Beck data, more for
polynomial data.
uncertainty : dict of str to list of float
A dictionary with the same format as above. This is probably a
one-standard deviation value, but that is not specified explicitly in
ENDF-102. Also, some evaluations will give zero uncertainty. Use with
caution.
"""
ident = identify_nuclide(filename)
if not ident['LFI']:
# This nuclide isn't fissionable.
return None
# Extract the MF=1, MT=458 section.
lines = []
with open(filename, 'r') as fh:
line = fh.readline()
while line != '':
if line[70:75] == ' 1458':
lines.append(line)
line = fh.readline()
if len(lines) == 0:
# No 458 data here.
return None
# Read the number of coefficients in this LIST record.
NPL = read_CONT_line(lines[1])[4]
# Parse the ENDF LIST into an array.
data = []
for i in range(NPL):
row, column = divmod(i, 6)
data.append(read_float(lines[2 + row][11*column:11*(column+1)]))
# Declare the coefficient names and the order they are given in. The LIST
# contains a value followed immediately by an uncertainty for each of these
# components, times the polynomial order + 1.
labels = ('EFR', 'ENP', 'END', 'EGP', 'EGD', 'EB', 'ENU', 'ER', 'ET')
# Associate each set of values and uncertainties with its label.
value = dict()
uncertainty = dict()
for i in range(len(labels)):
value[labels[i]] = data[2*i::18]
uncertainty[labels[i]] = data[2*i + 1::18]
# In ENDF/B-7.1, data for 2nd-order coefficients were mistakenly not
# converted from MeV to eV. Check for this error and fix it if present.
n_coeffs = len(value['EFR'])
if n_coeffs == 3: # Only check 2nd-order data.
# Check each energy component for the error. If a 1 MeV neutron
# causes a change of more than 100 MeV, we know something is wrong.
error_present = False
for coeffs in value.values():
second_order = coeffs[2]
if abs(second_order) * 1e12 > 1e8:
error_present = True
break
# If we found the error, reduce all 2nd-order coeffs by 10**6.
if error_present:
for coeffs in value.values(): coeffs[2] *= 1e-6
for coeffs in uncertainty.values(): coeffs[2] *= 1e-6
# Perform the sanity check again... just in case.
for coeffs in value.values():
second_order = coeffs[2]
if abs(second_order) * 1e12 > 1e8:
raise ValueError("Encountered a ludicrously large second-"
"order polynomial coefficient.")
# Convert eV to MeV.
for coeffs in value.values():
for i in range(len(coeffs)):
coeffs[i] *= 10**(-6 + 6*i)
for coeffs in uncertainty.values():
for i in range(len(coeffs)):
coeffs[i] *= 10**(-6 + 6*i)
return value, uncertainty
def write_compact_458_library(endf_files, output_name=None, comment=None,
verbose=False):
"""Read ENDF files, strip the MF=1 MT=458 data and write to small HDF5.
Parameters
----------
endf_files : Collection of str
Strings giving the paths to the ENDF files that will be parsed for data.
output_name : str
Name of the output HDF5 file. Default is 'fission_Q_data.h5'.
comment : str
Comment to write in the output HDF5 file. Defaults to no comment.
verbose : bool
If True, print the name of each isomer as it is read. Defaults to
False.
"""
# Open the output file.
if output_name is None: output_name = 'fission_Q_data.h5'
out = h5py.File(output_name, 'w', libver='latest')
# Write comments, if given. This commented out comment is the one used for
# the library distributed with OpenMC.
#comment = ('This data is extracted from ENDF/B-VII.1 library. Thanks '
# 'evaluators, for all your hard work :) Citation: '
# 'M. B. Chadwick, M. Herman, P. Oblozinsky, '
# 'M. E. Dunn, Y. Danon, A. C. Kahler, D. L. Smith, '
# 'B. Pritychenko, G. Arbanas, R. Arcilla, R. Brewer, '
# 'D. A. Brown, R. Capote, A. D. Carlson, Y. S. Cho, H. Derrien, '
# 'K. Guber, G. M. Hale, S. Hoblit, S. Holloway, T. D. Johnson, '
# 'T. Kawano, B. C. Kiedrowski, H. Kim, S. Kunieda, '
# 'N. M. Larson, L. Leal, J. P. Lestone, R. C. Little, '
# 'E. A. McCutchan, R. E. MacFarlane, M. MacInnes, '
# 'C. M. Mattoon, R. D. McKnight, S. F. Mughabghab, '
# 'G. P. A. Nobre, G. Palmiotti, A. Palumbo, M. T. Pigni, '
# 'V. G. Pronyaev, R. O. Sayer, A. A. Sonzogni, N. C. Summers, '
# 'P. Talou, I. J. Thompson, A. Trkov, R. L. Vogt, '
# 'S. C. van der Marck, A. Wallner, M. C. White, D. Wiarda, '
# 'and P. G. Young. ENDF/B-VII.1 nuclear data for science and '
# 'technology: Cross sections, covariances, fission product '
# 'yields and decay data", Nuclear Data Sheets, '
# '112(12):2887-2996 (2011).')
if comment is not None:
out.attrs['comment'] = np.string_(comment)
# Declare the order of the components. Use fixed-length numpy strings
# because they work well with h5py.
labels = np.array(('EFR', 'ENP', 'END', 'EGP', 'EGD', 'EB', 'ENU', 'ER',
'ET'), dtype='S3')
out.attrs['component order'] = labels
# Iterate over the given files.
if verbose: print('Reading ENDF files:')
for fname in endf_files:
if verbose: print(fname)
ident = identify_nuclide(fname)
# Skip non-fissionable nuclides.
if not ident['LFI']: continue
# Get the important bits.
data = _extract_458_data(fname)
if data is None: continue
value, uncertainty = data
# Make a group for this isomer.
name = str(ident['Z']) + str(ident['A'])
if ident['LISO'] != 0:
name += '_m' + str(ident['LISO'])
nuclide_group = out.create_group(name)
# Write all the coefficients into one array. The first dimension gives
# the component (e.g. fragments or prompt neutrons); the second switches
# between value and uncertainty; the third gives the polynomial order.
n_coeffs = len(value['EFR'])
data_out = np.zeros((len(labels), 2, n_coeffs))
for i, label in enumerate(labels):
data_out[i, 0, :] = value[label.decode()]
data_out[i, 1, :] = uncertainty[label.decode()]
nuclide_group.create_dataset('data', data=data_out)
out.close()
class FissionEnergyRelease(object):
def __init__(self):
self._fragments = None
@ -148,72 +332,21 @@ class FissionEnergyRelease(object):
pass
if ident['LISO'] != incident_neutron.metastable:
pass
if not ident['LIF']:
pass
# Extract the MF=1, MT=458 section.
lines = []
with open(filename, 'r') as fh:
line = fh.readline()
while line != '':
if line[70:75] == ' 1458':
lines.append(line)
line = fh.readline()
# Read the 458 data from the ENDF file.
value, uncertainty = _extract_458_data(filename)
# Read the number of coefficients in this LIST record.
NPL = read_CONT_line(lines[1])[4]
# Parse the ENDF LIST into an array.
data = []
for i in range(NPL):
row, column = divmod(i, 6)
data.append(read_float(lines[2 + row][11*column:11*(column+1)]))
# Declare the coefficient names and the order they are given in. The
# LIST contains a value followed immediately by an uncertainty for each
# of these components, times the polynomial order + 1. If we only find
# one value for each of these components, then we need to use the
# Sher-Beck formula for energy dependence. Otherwise, it is a
# polynomial.
# Declare the coefficient names. If we only find one value for each of
# these components, then we need to use the Sher-Beck formula for energy
# dependence. Otherwise, it is a polynomial.
labels = ('EFR', 'ENP', 'END', 'EGP', 'EGD', 'EB', 'ENU', 'ER', 'ET')
# Associate each set of values and uncertainties with its label.
value = dict()
uncertainty = dict()
for i in range(len(labels)):
value[labels[i]] = data[2*i::18]
uncertainty[labels[i]] = data[2*i + 1::18]
# In ENDF/B-7.1, data for 2nd-order coefficients were mistakenly not
# converted from MeV to eV. Check for this error and fix it if present.
# How many coefficients are given for each coefficient? If we only find
# one value for each, then we need to use the Sher-Beck formula for
# energy dependence. Otherwise, it is a polynomial.
n_coeffs = len(value['EFR'])
if n_coeffs == 3: # Only check 2nd-order data.
# Check each energy component for the error. If a 1 MeV neutron
# causes a change of more than 100 MeV, we know something is wrong.
error_present = False
for coeffs in value.values():
second_order = coeffs[2]
if abs(second_order) * 1e12 > 1e8:
error_present = True
break
# If we found the error, reduce all 2nd-order coeffs by 10**6.
if error_present:
for coeffs in value.values(): coeffs[2] *= 1e-6
for coeffs in uncertainty.values(): coeffs[2] *= 1e-6
# Perform the sanity check again... just in case.
for coeffs in value.values():
second_order = coeffs[2]
if abs(second_order) * 1e12 > 1e8:
raise ValueError("Encountered a ludicrously large second-"
"order polynomial coefficient.")
# Convert eV to MeV.
for coeffs in value.values():
for i in range(len(coeffs)):
coeffs[i] *= 10**(-6 + 6*i)
for coeffs in uncertainty.values():
for i in range(len(coeffs)):
coeffs[i] *= 10**(-6 + 6*i)
out = cls()
if n_coeffs > 1: