Perfect Precision Control

Various parser options exist to define globally the output format of float numbers, which were covered in the section on Writing ENDF files. They are typically used to avoid precision loss in a read/write operation in cases where the original ENDF file adopts an unconventional number format. However, as these options apply globally, even numbers formatted according to common convention (e.g. 1.234567-8) are potentially written out in a different style. For these cases, it would be a more satisfying approach to keep the numbers as they were encountered in the source ENDF file. With this approach, comparisons between source and target ENDF files to ensure no precision was lost become unnecessary.

The endf-parserpy package can keep track of the string representation of any float number read from an ENDF file and it also permits the user to use a custom string representation for any float number written to an ENDF file. In the following, we explain in more detail how these capabilities can be used.

Keeping track of float strings while reading

To keep track of string representations of float numbers while reading an ENDF file, we need to initialize the EndfParserPy class with the preserve_value_strings=True option:

from endf_parserpy import EndfParserPy
parser = EndfParserPy(preserve_value_strings=True)

Using this option, all float numbers will be stored as EndfFloat objects in the dictionary obtained by the parsefile() or parse() method, e.g.

endf_dict = parser.parsefile('input.endf')

The string representation of an EndfFloat object can be retrieved by using the get_original_string() method. For example, for the AWR variable stored in MF1/MT451:

endf_float_obj = endf_dict[1][451]['AWR']
endf_float_obj.get_original_string()

Importantly, even though EndfFloat objects represent float numbers, they can’t be used in arithmetic operations. Trying to do so will yield an error message. This limitation is a design feature to prevent the user or any processing script to inadvertently break the association with the original string representation. If the float values represented by EndfFloat objects should enter arithmetic expressions, they must be explicilty converted to float types:

float_number = float(endf_float_obj)

Comparisons between EndfFloat objects and Python numeric datatypes float and int will work without problems, though. Here are a few examples:

from endf_parserpy import EndfFloat
endf_float_obj = EndfFloat(7, '7')
assert endf_float_obj == 7
assert 3 <= endf_float_obj
assert endf_float_obj < 10

Perfect precision control for writing

Writing an ENDF file is not influenced by the preserve_value_strings parameter of the EndfParserPy constructor but only depends on whether a float is represented as an EndfFloat object or not. For instance, when writing ENDF formatted data via the writefile() method,

parser.writefile('output.endf', endf_dict)

the parser will always use the original string representation if a float value is represented by an EndfFloat object. On the other hand, values represented by the float Python data type will be formatted according to the global formatting options, such as prefer_noexp, explained in the section on Writing ENDF files.

Importantly, this means that no precision is lost in a read/write sequence such as the following, as the original string representations are copied verbatim into the target ENDF File:

from endf_parserpy import EndfParserPy
parser = EndfParserPy(preserve_value_strings=True)
endf_dict = parser.parsefile('input.endf')
parser.writefile('output.endf', endf_dict)

Finally, if one wants to modify a number in an ENDF file and control the string formatting oneself, one can create an EndfFloat object and assign it to the appropriate place in a nested ENDF dictionary. For example, the following code snippet demonstrates this for the assignment of a number to the ZA variable:

from endf_parserpy import EndfFloat
A = 56  # mass and charge number
Z = 26  # for iron-56
ZA = 1000.0*Z + A
endf_dict[1][451]['ZA'] = EndfFloat(ZA, "26056".rjust(11))
parser.writefile('output.endf', endf_dict)

Using default options during the EndfParserPy object initialization, the ZA variable would be written as 2.605600+4. However, the storage as an EndfFloat object with the string representation explicitly stated ensures that it is written as 26056 right-aligned in the appropriate 11-character slot field.

Note

During the creation of an EndfFloat object, no checking is performed whether the provided string qualifies as a syntactic valid number.