• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmc-dev / openmc / 26783873914

01 Jun 2026 09:43PM UTC coverage: 81.362% (+0.03%) from 81.333%
26783873914

Pull #3948

github

web-flow
Merge 6314ea576 into 111eb7706
Pull Request #3948: Fix get_index_in_direction for regular meshes

18027 of 26121 branches covered (69.01%)

Branch coverage included in aggregate %.

43 of 45 new or added lines in 9 files covered. (95.56%)

443 existing lines in 12 files now uncovered.

59175 of 68766 relevant lines covered (86.05%)

48551677.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.72
/openmc/data/endf.py
1
"""Module for parsing and manipulating data from ENDF evaluations.
2

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

8
"""
9
import io
11✔
10
from pathlib import PurePath
11✔
11
import re
11✔
12

13
from .data import gnds_name
11✔
14
from .function import Tabulated1D
11✔
15
from endf.material import (
11✔
16
    Material,
17
    _LIBRARY,
18
    _SUBLIBRARY,
19
    get_materials as get_evaluations,
20
)
21
from endf.incident_neutron import SUM_RULES
11✔
22
from endf.records import (
11✔
23
    float_endf,
24
    py_float_endf,
25
    int_endf,
26
    get_text_record,
27
    get_cont_record,
28
    get_head_record,
29
    get_list_record,
30
    get_tab1_record as _get_tab1_record,
31
    get_tab2_record,
32
    get_intg_record,
33
)
34

35

36
def get_tab1_record(file_obj):
11✔
37
    """Return data from a TAB1 record in an ENDF-6 file.
38

39
    This wraps the endf package's get_tab1_record to return an
40
    openmc.data.Tabulated1D (which has HDF5 support and is a Function1D)
41
    instead of endf.Tabulated1D.
42
    """
43
    params, tab = _get_tab1_record(file_obj)
11✔
44
    return params, Tabulated1D(tab.x, tab.y, tab.breakpoints, tab.interpolation)
11✔
45

46

47
class Evaluation:
11✔
48
    """ENDF material evaluation with multiple files/sections
49

50
    Parameters
51
    ----------
52
    filename_or_obj : str, file-like, or endf.Material
53
        Path to ENDF file to read or an open file positioned at the start of an
54
        ENDF material
55

56
    Attributes
57
    ----------
58
    info : dict
59
        Miscellaneous information about the evaluation.
60
    target : dict
61
        Information about the target material, such as its mass, isomeric state,
62
        whether it's stable, and whether it's fissionable.
63
    projectile : dict
64
        Information about the projectile such as its mass.
65
    reaction_list : list of 4-tuples
66
        List of sections in the evaluation. The entries of the tuples are the
67
        file (MF), section (MT), number of records (NC), and modification
68
        indicator (MOD).
69

70
    """
71
    def __init__(self, filename_or_obj):
11✔
72
        self.section = {}
11✔
73
        self.info = {}
11✔
74
        self.target = {}
11✔
75
        self.projectile = {}
11✔
76
        self.reaction_list = []
11✔
77

78
        if isinstance(filename_or_obj, Material):
11✔
79
            self.section = dict(filename_or_obj.section_text)
11✔
80
            self.section_data = filename_or_obj.section_data
11✔
81
            self.material = filename_or_obj.MAT
11✔
82
            self._read_header()
11✔
83
            return
11✔
84

85
        if isinstance(filename_or_obj, (str, PurePath)):
11✔
86
            fh = open(str(filename_or_obj), 'r')
11✔
87
            need_to_close = True
11✔
88
        else:
UNCOV
89
            fh = filename_or_obj
×
UNCOV
90
            need_to_close = False
×
91

92
        # Skip TPID record. Evaluators sometimes put in TPID records that are
93
        # ill-formated because they lack MF/MT values or put them in the wrong
94
        # columns.
95
        if fh.tell() == 0:
11✔
96
            fh.readline()
11✔
97
        MF = 0
11✔
98

99
        # Determine MAT number for this evaluation
100
        while MF == 0:
11✔
101
            position = fh.tell()
11✔
102
            line = fh.readline()
11✔
103
            MF = int(line[70:72])
11✔
104
        self.material = int(line[66:70])
11✔
105
        fh.seek(position)
11✔
106

107
        while True:
11✔
108
            # Find next section
109
            while True:
11✔
110
                position = fh.tell()
11✔
111
                line = fh.readline()
11✔
112
                MAT = int(line[66:70])
11✔
113
                MF = int(line[70:72])
11✔
114
                MT = int(line[72:75])
11✔
115
                if MT > 0 or MAT == 0:
11✔
116
                    fh.seek(position)
11✔
117
                    break
11✔
118

119
            # If end of material reached, exit loop
120
            if MAT == 0:
11✔
121
                fh.readline()
11✔
122
                break
11✔
123

124
            section_data = ''
11✔
125
            while True:
11✔
126
                line = fh.readline()
11✔
127
                if line[72:75] == '  0':
11✔
128
                    break
11✔
129
                else:
130
                    section_data += line
11✔
131
            self.section[MF, MT] = section_data
11✔
132

133
        if need_to_close:
11✔
134
            fh.close()
11✔
135

136
        self._read_header()
11✔
137

138
    def __repr__(self):
11✔
UNCOV
139
        name = self.target['zsymam'].replace(' ', '')
×
UNCOV
140
        return f"<{self.info['sublibrary']} for {name} {self.info['library']}>"
×
141

142
    def _read_header(self):
11✔
143
        file_obj = io.StringIO(self.section[1, 451])
11✔
144

145
        # Information about target/projectile
146
        items = get_head_record(file_obj)
11✔
147
        Z, A = divmod(items[0], 1000)
11✔
148
        self.target['atomic_number'] = Z
11✔
149
        self.target['mass_number'] = A
11✔
150
        self.target['mass'] = items[1]
11✔
151
        self._LRP = items[2]
11✔
152
        self.target['fissionable'] = (items[3] == 1)
11✔
153
        try:
11✔
154
            library = _LIBRARY[items[4]]
11✔
155
        except KeyError:
11✔
156
            library = 'Unknown'
11✔
157
        self.info['modification'] = items[5]
11✔
158

159
        # Control record 1
160
        items = get_cont_record(file_obj)
11✔
161
        self.target['excitation_energy'] = items[0]
11✔
162
        self.target['stable'] = (int(items[1]) == 0)
11✔
163
        self.target['state'] = items[2]
11✔
164
        self.target['isomeric_state'] = m = items[3]
11✔
165
        self.info['format'] = items[5]
11✔
166
        assert self.info['format'] == 6
11✔
167

168
        # Set correct excited state for Am242_m1, which is wrong in ENDF/B-VII.1
169
        if Z == 95 and A == 242 and m == 1:
11✔
170
            self.target['state'] = 2
11✔
171

172
        # Control record 2
173
        items = get_cont_record(file_obj)
11✔
174
        self.projectile['mass'] = items[0]
11✔
175
        self.info['energy_max'] = items[1]
11✔
176
        library_release = items[2]
11✔
177
        self.info['sublibrary'] = _SUBLIBRARY[items[4]]
11✔
178
        library_version = items[5]
11✔
179
        self.info['library'] = (library, library_version, library_release)
11✔
180

181
        # Control record 3
182
        items = get_cont_record(file_obj)
11✔
183
        self.target['temperature'] = items[0]
11✔
184
        self.info['derived'] = (items[2] > 0)
11✔
185
        NWD = items[4]
11✔
186
        NXC = items[5]
11✔
187

188
        # Text records
189
        text = [get_text_record(file_obj) for i in range(NWD)]
11✔
190
        if len(text) >= 5:
11✔
191
            self.target['zsymam'] = text[0][0:11]
11✔
192
            self.info['laboratory'] = text[0][11:22]
11✔
193
            self.info['date'] = text[0][22:32]
11✔
194
            self.info['author'] = text[0][32:66]
11✔
195
            self.info['reference'] = text[1][1:22]
11✔
196
            self.info['date_distribution'] = text[1][22:32]
11✔
197
            self.info['date_release'] = text[1][33:43]
11✔
198
            self.info['date_entry'] = text[1][55:63]
11✔
199
            self.info['identifier'] = text[2:5]
11✔
200
            self.info['description'] = text[5:]
11✔
201
        else:
202
            self.target['zsymam'] = 'Unknown'
11✔
203

204
        # File numbers, reaction designations, and number of records
205
        for i in range(NXC):
11✔
206
            _, _, mf, mt, nc, mod = get_cont_record(file_obj, skip_c=True)
11✔
207
            self.reaction_list.append((mf, mt, nc, mod))
11✔
208

209
    @property
11✔
210
    def gnds_name(self):
11✔
211
        return gnds_name(self.target['atomic_number'],
11✔
212
                         self.target['mass_number'],
213
                         self.target['isomeric_state'])
214

215

216
def as_evaluation(ev_or_filename):
11✔
217
    """Return an object supporting OpenMC's legacy Evaluation interface."""
218
    if isinstance(ev_or_filename, Evaluation):
11✔
219
        return ev_or_filename
11✔
220
    else:
221
        return Evaluation(ev_or_filename)
11✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc