Source code for openmc.data.endf

"""Module for parsing and manipulating data from ENDF evaluations.

All the classes and functions in this module are based on document ENDF-102
titled "Data Formats and Procedures for the Evaluated Nuclear Data File ENDF-6".
The version from September 2023 can be found at
https://www.nndc.bnl.gov/endfdocs/ENDF-102-2023.pdf

"""
import io
from pathlib import PurePath
import re

from .data import gnds_name
from .function import Tabulated1D
from endf.material import (
    Material,
    _LIBRARY,
    _SUBLIBRARY,
    get_materials as get_evaluations,
)
from endf.incident_neutron import SUM_RULES
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,
)


[docs] 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)
[docs] class Evaluation: """ENDF material evaluation with multiple files/sections Parameters ---------- filename_or_obj : str, file-like, or endf.Material Path to ENDF file to read or an open file positioned at the start of an ENDF material Attributes ---------- info : dict Miscellaneous information about the evaluation. target : dict Information about the target material, such as its mass, isomeric state, whether it's stable, and whether it's fissionable. projectile : dict Information about the projectile such as its mass. reaction_list : list of 4-tuples List of sections in the evaluation. The entries of the tuples are the file (MF), section (MT), number of records (NC), and modification indicator (MOD). """ def __init__(self, filename_or_obj): self.section = {} self.info = {} self.target = {} self.projectile = {} self.reaction_list = [] if isinstance(filename_or_obj, Material): self.section = dict(filename_or_obj.section_text) self.section_data = filename_or_obj.section_data self.material = filename_or_obj.MAT self._read_header() return if isinstance(filename_or_obj, (str, PurePath)): fh = open(str(filename_or_obj), 'r') need_to_close = True else: fh = filename_or_obj need_to_close = False # Skip TPID record. Evaluators sometimes put in TPID records that are # ill-formated because they lack MF/MT values or put them in the wrong # columns. if fh.tell() == 0: fh.readline() MF = 0 # Determine MAT number for this evaluation while MF == 0: position = fh.tell() line = fh.readline() MF = int(line[70:72]) self.material = int(line[66:70]) fh.seek(position) while True: # Find next section while True: position = fh.tell() line = fh.readline() MAT = int(line[66:70]) MF = int(line[70:72]) MT = int(line[72:75]) if MT > 0 or MAT == 0: fh.seek(position) break # If end of material reached, exit loop if MAT == 0: fh.readline() break section_data = '' while True: line = fh.readline() if line[72:75] == ' 0': break else: section_data += line self.section[MF, MT] = section_data if need_to_close: fh.close() self._read_header() def __repr__(self): name = self.target['zsymam'].replace(' ', '') return f"<{self.info['sublibrary']} for {name} {self.info['library']}>" def _read_header(self): file_obj = io.StringIO(self.section[1, 451]) # Information about target/projectile items = get_head_record(file_obj) Z, A = divmod(items[0], 1000) self.target['atomic_number'] = Z self.target['mass_number'] = A self.target['mass'] = items[1] self._LRP = items[2] self.target['fissionable'] = (items[3] == 1) try: library = _LIBRARY[items[4]] except KeyError: library = 'Unknown' self.info['modification'] = items[5] # Control record 1 items = get_cont_record(file_obj) self.target['excitation_energy'] = items[0] self.target['stable'] = (int(items[1]) == 0) self.target['state'] = items[2] self.target['isomeric_state'] = m = items[3] self.info['format'] = items[5] assert self.info['format'] == 6 # Set correct excited state for Am242_m1, which is wrong in ENDF/B-VII.1 if Z == 95 and A == 242 and m == 1: self.target['state'] = 2 # Control record 2 items = get_cont_record(file_obj) self.projectile['mass'] = items[0] self.info['energy_max'] = items[1] library_release = items[2] self.info['sublibrary'] = _SUBLIBRARY[items[4]] library_version = items[5] self.info['library'] = (library, library_version, library_release) # Control record 3 items = get_cont_record(file_obj) self.target['temperature'] = items[0] self.info['derived'] = (items[2] > 0) NWD = items[4] NXC = items[5] # Text records text = [get_text_record(file_obj) for i in range(NWD)] if len(text) >= 5: self.target['zsymam'] = text[0][0:11] self.info['laboratory'] = text[0][11:22] self.info['date'] = text[0][22:32] self.info['author'] = text[0][32:66] self.info['reference'] = text[1][1:22] self.info['date_distribution'] = text[1][22:32] self.info['date_release'] = text[1][33:43] self.info['date_entry'] = text[1][55:63] self.info['identifier'] = text[2:5] self.info['description'] = text[5:] else: self.target['zsymam'] = 'Unknown' # File numbers, reaction designations, and number of records for i in range(NXC): _, _, mf, mt, nc, mod = get_cont_record(file_obj, skip_c=True) self.reaction_list.append((mf, mt, nc, mod)) @property def gnds_name(self): return gnds_name(self.target['atomic_number'], self.target['mass_number'], self.target['isomeric_state'])
def as_evaluation(ev_or_filename): """Return an object supporting OpenMC's legacy Evaluation interface.""" if isinstance(ev_or_filename, Evaluation): return ev_or_filename else: return Evaluation(ev_or_filename)