<commit_before>from collections import Callable
from copy import deepcopy
from io import StringIO
import sys

import h5py
import numpy as np

from .data import ATOMIC_SYMBOL
from .endf import get_cont_record, get_list_record, Evaluation
from .function import Function1D, Tabulated1D, Polynomial, Sum
import openmc.checkvalue as cv
from openmc.mixin import EqualityMixin

if sys.version_info[0] >= 3:
    basestring = str


def _extract_458_data(ev):
    """Read an ENDF file and extract the MF=1, MT=458 values.

    Parameters
    ----------
    ev : openmc.data.Evaluation
        ENDF evaluation

    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.

    """
    if not ev.target['fissionable']:
        # This nuclide isn't fissionable.
        return None

    if (1, 458) not in ev.section:
        # No 458 data here.
        return None

    file_obj = StringIO(ev.section[1, 458])

    # Read the number of coefficients in this LIST record.
    items = get_cont_record(file_obj)
    NPL = items[3]

    # Parse the ENDF LIST into an array.
    items, data = get_list_record(file_obj)

    # 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 = {}
    uncertainty = {}
    for i, label in enumerate(labels):
        value[label] = data[2*i::18]
        uncertainty[label] = 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

    # 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='fission_Q_data.h5',
                              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.
    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:  '
    #           '<NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, A. <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           'and <NAME>. 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)

        ev = Evaluation(fname)

        # Skip non-fissionable nuclides.
        if not ev.target['fissionable']:
            continue

        # Get the important bits.
        data = _extract_458_data(ev)
        if data is None: continue
        value, uncertainty = data

        # Make a group for this isomer.
        name = ATOMIC_SYMBOL[ev.target['atomic_number']] + \
               str(ev.target['mass_number'])
        if ev.target['isomeric_state'] != 0:
            name += '_m' + str(ev.target['isomeric_state'])
        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()
<commit_msg>Fix units for fission energy release in ENDF files<commit_after>from collections import Callable
from copy import deepcopy
from io import StringIO
import sys

import h5py
import numpy as np

from .data import ATOMIC_SYMBOL
from .endf import get_cont_record, get_list_record, Evaluation
from .function import Function1D, Tabulated1D, Polynomial, Sum
import openmc.checkvalue as cv
from openmc.mixin import EqualityMixin

if sys.version_info[0] >= 3:
    basestring = str


def _extract_458_data(ev, units='eV'):
    """Read an ENDF file and extract the MF=1, MT=458 values.

    Parameters
    ----------
    ev : openmc.data.Evaluation
        ENDF evaluation
    units : {'eV', 'MeV'}
        The units are used in values returned.

    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.

    """
    if not ev.target['fissionable']:
        # This nuclide isn't fissionable.
        return None

    if (1, 458) not in ev.section:
        # No 458 data here.
        return None

    file_obj = StringIO(ev.section[1, 458])

    # Read the number of coefficients in this LIST record.
    items = get_cont_record(file_obj)
    NPL = items[3]

    # Parse the ENDF LIST into an array.
    items, data = get_list_record(file_obj)

    # 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 = {}
    uncertainty = {}
    for i, label in enumerate(labels):
        value[label] = data[2*i::18]
        uncertainty[label] = 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

    # Convert eV to MeV.
    if units == '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='fission_Q_data.h5',
                              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.
    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:  '
    #           '<NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           '<NAME>, <NAME>, <NAME>, <NAME>, '
    #           'and <NAME>. 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)

        ev = Evaluation(fname)

        # Skip non-fissionable nuclides.
        if not ev.target['fissionable']:
            continue

        # Get the important bits.
        data = _extract_458_data(ev, 'MeV')
        if data is None: continue
        value, uncertainty = data

        # Make a group for this isomer.
        name = ATOMIC_SYMBOL[ev.target['atomic_number']] + \
               str(ev.target['mass_number'])
        if ev.target['isomeric_state'] != 0:
            name += '_m' + str(ev.target['isomeric_state'])
        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()

{'fraction_non_alphanumeric': 0.1151161954097417, 'fraction_numerical': 0.01654795309015037, 'mean_word_length': 2.2273043882052472, 'pattern_counts': {'":': 0, '<': 107, '<?xml version=': 0, '>': 111, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3195497', 'n_tokens_mistral': 4385, 'n_tokens_neox': 4168, 'n_words': 1488}