Data Navigation

The core functionality of endf-parserpy is to map the nuclear data stored in an ENDF-6 file into a nested Python dictionary. These dictionaries can be somewhat deeply nested and hence the navigation in them to access or manipulate data cumbersome. endf-parserpy introduces the concept of an EndfPath for referencing a piece of data in a dictionary associated with ENDF-6 data.

EndfPath

Most people are familiar with the hierarchical organization of data in file systems by nesting directories, and how a file can be referenced by the provision of a path. Similarly, it is convenient to use a path for referring to a variable or section in an ENDF-6 dictionary. The path is constructed by combining the keys of the nested dictionaries to a single string using a / character as separator. For instance, there may be a variable AJ in a Python dictionary endf_dict accessible via:

endf_dict[2][151]['isotope'][1]['range'][1]['spingroup'][1]['AJ']

The corresponding EndfPath would be given by

2/151/isotope/1/range/1/spingroup/1/AJ

Some objects in a nested dictionary can be considered as an array of objects and we may want to use numpy-like syntax. Therefore, a path should also support notation of the form isotope[1], which is equivalent to isotope/1. Making use of this alternative notation, the path above could be equally written as:

2/151/isotope[1]/range[1]/spingroup[1]/AJ

The class EndfPath provides a container for the storage of a path. However, paths become only useful when they can actually be used to navigate dictionaries with ENDF-6 data and retrieve the desired data from them. The EndfDict class helps with this requirement.

EndfDict

An instance of EndfDict behaves like a standard dict but enables the use of paths (as described above) to access data. When the class is instantiated with a dictionary as argument, it can be regarded as a view object of the original dictionary. Any modification of data through the EndfDict object will lead to the same modification of the original dictionary (technically speaking: EndfDict objects manage a reference to the original dict or other MutableMapping object). Suppose that endf_dict is an instance of EndfDict. Using the example given above, we could access the variable AJ via:

aj = endf_dict['2/151/isotope[1]/range[1]/spingroup[1]/AJ']

An EndfDict instance also allows keys to be separated by commas so the same variable could also be accessed via the notation:

aj = endf_dict[2, 151, 'isotope', 1, 'range', 1, 'spingroup', 1, 'AJ']

It’s also possible to mix these two notation forms, e.g.

aj = endf_dict[2, 151, 'isotope/1', 'range[1]', 'spingroup', '1/AJ']

The flexible notation allows to write down specific instructions in a very intuitive form. Assume that you want to modify a covariance matrix in the MF=33/MT=1 section. You could use the following code:

F = endf_dict['33/1/subsection[1]/ni_subsection[1]/F']
F[2, 3] = 0.5

This code works because any dictionary-like object retrieved from an EndfDict object will be automatically wrapped into an EndfDict object itself before being returned. Consequently, the extended indexing capabilities are available for these retrieved objects, such as demonstrated here by the assignment involving F.

Another useful design feature regarding the construction of dictionaries is the implicit creation of missing dictionaries. For example, the assignment

endf_dict['2/151/isotope[1]/range[2]/spingroup[3]/AJ'] = 12.

will create all intermediate dictionaries, hence this instruction even works for an empty dictionary endf_dict = EndfDict({}).

See the documentation of the endf_parserpy.EndfDict class for further details.

Finally, we may want to use abbreviations to read and modify data in a dictionary with ENDF-6 data. Perhaps we would like to assign a new value to the aj variable and expect that the same value is also assigned to the corresponding location in endf_dict. However, this will not be the case. The EndfVariable class provides a mechanism to achieve this behavior.

EndfVariable

An instance of EndfVariable possesses a .value attribute that is always kept in sync with a specific location in a nested dictionary with ENDF-6 data. It can be instantiated by providing an EndfPath object and a dictionary:

ajvar = EndfVariable('2/151/isotope[1]/range[1]/spingroup[1]/AJ', endf_dict)

Any change of the value of AJ in endf_dict will be reflected in ajvar.value and vice-versa. This class may be a good basis for implementing higher-level functionality, such as linear interpolation of cross sections with the link to the original data being preserved. More technical details are provided in the documentation of the endf_parserpy.EndfVariable class.