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.