Reading & Writing ENDF-6
A typical workflow with an ENDF-6 file involves reading the data into a Python dictionary, performing some actions on the data and finally writing the modified data to a new ENDF-6 file. On this page, we explain how to read and write ENDF-6 data along with some considerations to be heeded.
Reading an ENDF-6 file
An ENDF-6 file can be read with the parsefile()
method of the EndfParserPy class:
from endf_parserpy import EndfParserPy
parser = EndfParserPy()
endf_dict = parser.parsefile('input.endf')
The variable endf_dict is a dictionary (dict) in which
all quantities are associated with the symbol names
described in the ENDF-6 formats manual.
The data organization within this dictionary is determined by
ENDF-6 recipes.
An EndfDict object,
instantiated by
view_dict = EndfDict(endf_dict), can facilitate
data navigation.
The parsing process can be influenced by using additional
arguments in the instantiation of the
EndfParserPy class.
The default choices of these arguments are forgiving so that
even ENDF-6 files that do not strictly comply with the ENDF-6
format can be read without failure. For the purpose of
format validation, one would
adopt parameters to enforce strict compliance with the ENDF-6 format.
The only argument that is relevant for reading is accept_spaces
whose default is True. With this default, numbers with spaces inserted
will be accepted, such as 1.234 +3. However, even ENDF-6 files
with some numbers leaking into neighboring fields may be successfully
read then, but with nonsensical values. Because this issue can be easily
missed by the user, it may be a good idea to enforce failure in such a
situation by adopting the following initialization:
parser = EndfParserPy(accept_spaces=False)
When only one part of the ENDF-6 file is relevant to the user,
the parsefile() method
can be instructed to only parse
this part for greater speed. For instance, if you are only interested
in MT sections within MF=3,4,5 sections, you can use:
endf_dict = parser.parsefile('input.endf', include=[3, 4, 5])
In the resulting dictionary, only the subdictionaries associated
with the included MF numbers contain keys named according to
symbol names as described in the ENDF-6 manual.
The objects associated with the other MF numbers are lists of
strings with the raw content of the ENDF-6 file, e.g.
type(endf_dict[6][2]) == list. Instead of include, you can
also use the exclude argument, e.g.
endf_dict = parser.parsefile('input.endf', exclude=[3, 4, 5])
to parse everything except MF=3,4,5. Both inclusion and exclusion
can be performed more fine-grained at the level of MT sections.
For example, to include everything of MF=6 and only MT=1 from MF=3,
specify include=[6, [3, 1]].
If one wants to check which MF/MT sections have been parsed,
one can utilize the
list_parsed_sections() and
list_unparsed_sections() function,
respectively:
from endf_parserpy import list_parsed_sections
from endf_parserpy import list_unparsed_sections
list_parsed_sections(endf_dict)
list_unparsed_sections(endf_dict)
Writing an ENDF-6 file
Writing an ENDF-6 file is as simple as reading one.
Assume that the dictionary endf_dict is of appropriate
structure, e.g., as returned by the
parsefile() method.
The following code snippet demonstrates writing:
from endf_parserpy import EndfParserPy
parser = EndfParserPy()
parser.writefile('output.endf', endf_dict)
Also the writefile()
method supports the
include and exclude argument. If the
include argument is provided, only included
MF/MT sections will be written to the file.
Similarly, if exclude is provided, only all
non-excluded sections will be written.
If data in the endf_dict dictionary
have been added or deleted, it is important to
update the directory listing in MF1/MT451 first
before writing to an ENDF-6 file (see manual page 57).
This can be achieved with the
update_directory() function.
from endf_parserpy import update_directory
update_directory(endf_dict, parser)
Note
Don’t use the include and exclude argument
of the writefile() method
if it is important that the ENDF directory in MF1/MT451 is in sync with
the file. Rather remove the sections manually before the
invocation of update_directory().
Finally, we want to discuss how to control the output precision
of numerical data. The ENDF-6 format only provides eleven
character slots for the representation of numbers,
limiting the achievable precision. By default, numbers are written
in a specific floating point notation that appears to be commonly adopted
by nuclear data library projects. This notation is of the form
␣1.234567+8, with the first character slot being reserved
for a potential minus sign and skipping the e, which would
usually be included to indicate the start of the exponent.
If more output precision is required, several options are available
to tweak the output format, which can be passed as arguments
to the constructor of the EndfParserPy class.
With abuse_signpos=True, positive numbers
are allowed to consume the first character slot usually
reserved for the sign.
The argument prefer_noexp=True will switch to a decimal
notation (i.e. without exponent) if the number can be
represented with more significant digits. The choice
skip_intzero=True will skip the zero of a number
in decimal notation if the integer part is zero, e.g.,
0.1234 will become .12345, giving in some situations
one extra digit of precision.
Therefore, for maximal output precision (and ugly display)
initialize the EndfParserPy instance like this:
parser = EndfParserPy(abuse_signpose=True, prefer_noexp=True, skip_intzero=True)
If you want to increase compatibility with programming
languages different from Fortran that don’t accept the omission
of the e character in the scientific notation of a number,
include the argument keep_E=True.
Finally, if you want to ensure that you haven’t
lost (too much) precision, you can make use of the
compare_objects() function.
Just read the output file again, and compare it with
the original endf_dict:
from endf_parserpy import compare_objects
test_dict = parser.parsefile('output.endf')
compare_objects(endf_dict, test_dict, atol=1e-6, rtol=1e-6, fail_on_diff=False)