Working with Legacy Formats

The ENDF format evolved over decades with the current version being ENDF-6. While the ENDF recipes shipped with endf-parserpy only implement the current ENDF-6 format standard, users can provide their own recipe definitions to enable the parsing and writing of legacy nuclear data files that are either based on earlier ENDF format versions or rely on non-standard customizations of the official ENDF format.

In this tutorial, we take the European Activation File 2007 (EAF-2007) as an example of nuclear data stored in a legacy format and demonstrate how it can be parsed with endf-parserpy. Noteworthy, the EAF-2007 file also contains several materials, and we explain one way of dealing with that.

The format underlying EAF-2007 is described in this document. To try out the following code blocks, download the abridged version of the EAF-2007 file stored in the examples/data directory of the endf-parserpy GitHub repository.

The EAF-2007 file contains cross section data in MF=3 sections for various materials. The structure of an MT section in MF=3 follows closely the ENDF-6 format (manual page 122) but is additionally prefixed by two TEXT records (manual page 52) with human-readable extra information. Therefore, as a first step, we can create an ENDF recipe for this specific MF=3/MT section structure:

recipes = dict()
recipes[3] = """
for i=1 to 2:
    [MAT, 3,MT / DESCRIPTION[i]]TEXT
endfor
[MAT, 3, MT/ ZA, AWR, 0, LFS, 0, 0] HEAD
[MAT, 3, MT/ QM, QI, 0, LR, NR, NP / E / xs]TAB1 (xstable)
SEND
"""

The formal language underlying recipe definitions is described in detail in this ArXiV preprint.

We can now instantiate an EndfParserPy class making use of the recipes dictionary:

from endf_parserpy import EndfParserPy
parser = EndfParserPy(
    recipes=recipes,
    ignore_missing_tpid=True,
    ignore_send_records=True
)

Please note that this parser instance will only be able to parse the custom MF=3 sections according to the structure defined above and nothing else. For supporting other MF/MT sections, it would be necessary to augment the recipes dictionary with additional recipe definitions. For example, see here how it is done for the entire ENDF-6 formats standard.

In the instantiation of the EndfParserPy class, we also specified ignore_missing_tpid=True to tolerate the absence of a TPID record, which is not present in the EAF-2007 file. The TPID record is a TEXT record in the first line of an ENDF file that can be used for a free-form text comment.

The specification of ignore_send_records=True is an essential ingredient to deal with several materials in a single ENDF file, as is the case for the EAF-2007 file. With this option active, we can split the file into several chunks, one for each material, and disregard the SEND/FEND/MEND/TEND records. For splitting up the file into chunks, we can use the following code snippet:

with open("data/eaf2007_abridged.endf", "r") as f:
    lines = f.readlines()

# split up the various materials
mat_dict = {}
for l in lines:
    curmat = int(l[66:70])
    if curmat > 0:
        curlines = mat_dict.setdefault(curmat, [])
        curlines.append(l)

After loading the file content into a list of strings, the dictionary mat_dict is populated with lists of strings associated with the various materials. Because of the argument ignore_send_records=True passed to the EndfParserPy class, we can strip away any kind of section end record. Technically, these records are filtered out by only dealing with records that contain a material number (MAT) greater than zero.

Note

For files that follow the standard ENDF-6 structure, the parse_tape_file() function performs this splitting and parsing automatically; see the guide on multi-material files. The manual approach shown here remains useful when, as for EAF-2007, a custom recipe and relaxed record handling are required.

Finally, we can loop over the materials stored in mat_dict under keys given by the material number and individually parse the list of strings associated with each material.

parsed_mat_dict = {}
for matnr in mat_dict.keys():
    parsed_mat = parser.parse(mat_dict[matnr])
    parsed_mat_dict[matnr] = parsed_mat

As a result, parsed_mat_dict contains for each material a dictionary with values stored under the symbol names as defined in the ENDF recipe above. For instance, to see what variable names are present in the MF=3/MT=16 section of material 102, run the following code:

parsed_mat_dict[102][3][16].keys()

In summary, this tutorial showed how to parse a nuclear data file (EAF-2007) given in a legacy format by creating a custom ENDF recipe definition. It was also demonstrated how to deal with a file that contains several materials. Finally, below we provide the full code example including all code snippets explained step-by-step in this tutorial:

from endf_parserpy import EndfParserPy

recipes = dict()
recipes[3] = """
for i=1 to 2:
    [MAT, 3,MT / DESCRIPTION[i]]TEXT
endfor
[MAT, 3, MT/ ZA, AWR, 0, LFS, 0, 0] HEAD
[MAT, 3, MT/ QM, QI, 0, LR, NR, NP / E / xs]TAB1 (xstable)
SEND
"""

parser = EndfParserPy(
    recipes=recipes,
    ignore_missing_tpid=True,
    ignore_send_records=True
)

with open("data/eaf2007_abridged.endf", "r") as f:
    lines = f.readlines()

# split up the various materials
mat_dict = {}
for l in lines:
    curmat = int(l[66:70])
    if curmat > 0:
        curlines = mat_dict.setdefault(curmat, [])
        curlines.append(l)

# parse each material
parsed_mat_dict = {}
for matnr in mat_dict.keys():
    parsed_mat = parser.parse(mat_dict[matnr])
    parsed_mat_dict[matnr] = parsed_mat


# inspect parsed sections, e.g. MF=3/MT=16 of material 102
parsed_mat_dict[102][3][16].keys()