Comparing ENDF-6 Files

Producing a new nuclear data evaluation typically involves many iterations with various adjustments to an ENDF-6 file before eventually converging to one version of the file whose performance in relevant benchmark experiments compares favorably to other candidate files.

Version control software, such as git, enables the systematic tracking of file modifications. However, it is not enough to know that a file changed but it is also very important to be able to understand the exact differences between different versions of a file. Line-based comparison tools are unfortunately of limited use for comparing ENDF-6 files, as they can’t take into consideration the inherent logical structure of these files.

In contrast, the dictionaries with ENDF-6 data returned by the parsefile() method of the EndfParserPy class are a perfect starting point for insightful comparisons between two ENDF-6 files. It it is straightforward to implement a recursive function that walks through the nested structure of two dictionaries in sync and reports any differences found. The function compare_objects() does exactly that. Following, we want to showcase the use of this function and discuss the form of its output.

Assume that we have two ENDF-6 files named input1.endf and input2.endf. We can compare their content with the following code snippet:

from endf_parserpy import EndfParserPy
from endf_parserpy import compare_objects
parser = EndfParserPy()
endf_dict1 = parser.parsefile('input1.endf')
endf_dict2 = parser.parsefile('input2.endf')
compare_objects(endf_dict1, endf_dict2, atol=1e-6, rtol=1e-6, fail_on_diff=False)

After the content of the two ENDF-6 files has been read into two dictionaries named endf_dict1 and endf_dict2, the compare_objects() function is invoked for the comparison. The argument atol defines the absolute difference and the argument rtol the relative difference below which two floating point numbers are considered equal. The provision of the fail_on_diff=False argument is important because this function would otherwise raise an exception after the first encounter of a difference between the two dictionaries.

Apart from returning True if no differences were encountered and False otherwise, the compare_objects() function prints the discrepancies found on standard output. The output may look like this:

at path /2/151/isotope/1/range: only obj2 contains {2}
Value mismatch at /2/151/isotope/1/range/1/EH (36000.0 vs 34490.0)
Value mismatch at /3/1/AWR (12.0 vs 134.737)

The example output indicates the path where the difference occured and the type of difference. The first line informs us that only the second object passed to compare_objects() possesses a key 2 at the path 2/151/isotope/1/range. The second line points out a different value of the EH variable in the first range section of the first isotope. Similarly, the third line signals a difference of a value in a MF=3/MT=1 section. While the aesthetical appearance of the output can certainly be improved, the indication of differences on the basis of the logical structure is much more meaningful than the result of a line-based diff tool.