OpenMC/openmc/data/library.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

193 lines
6.1 KiB
Python
Raw Permalink Normal View History

import os
import pathlib
import h5py
import lxml.etree as ET
import openmc
from openmc._xml import clean_indentation, reorder_attributes
2016-08-17 18:55:08 -04:00
class DataLibrary(list):
"""Collection of cross section data libraries.
This class behaves like a list where each item is a dictionary summarizing
cross section data from a single file. The dictionary has keys 'path',
'type', and 'materials'.
2023-10-31 16:55:19 -05:00
.. versionchanged:: 0.14.0
This class now behaves like a list rather than requiring you to access
the list of libraries through a special attribute.
"""
def __init__(self):
super().__init__()
@property
def libraries(self):
# For backwards compatibility
return self
def get_by_material(self, name, data_type='neutron'):
2016-11-09 18:19:13 -05:00
"""Return the library dictionary containing a given material.
2016-11-08 18:45:17 -05:00
Parameters
----------
name : str
Name of material, e.g. 'Am241'
data_type : str
Name of data type, e.g. 'neutron', 'photon', 'wmp', or 'thermal'
.. versionadded:: 0.12
2016-11-08 18:45:17 -05:00
Returns
-------
2016-11-09 18:19:13 -05:00
library : dict or None
2016-11-08 18:45:17 -05:00
Dictionary summarizing cross section data from a single file;
the dictionary has keys 'path', 'type', and 'materials'.
"""
for library in self:
if name in library['materials'] and data_type in library['type']:
2016-11-07 19:01:35 -05:00
return library
return None
def remove_by_material(self, name: str, data_type='neutron'):
"""Remove the library dictionary containing a specific material
Parameters
----------
name : str
Name of material, e.g. 'Am241'
data_type : str
Name of data type, e.g. 'neutron', 'photon', 'wmp', or 'thermal'
"""
library = self.get_by_material(name, data_type)
if library is not None:
self.remove(library)
def register_file(self, filename):
"""Register a file with the data library.
Parameters
----------
filename : str or Path
Path to the file to be registered.
If an ``xml`` file, treat as the depletion chain file without
materials.
"""
if not isinstance(filename, pathlib.Path):
path = pathlib.Path(filename)
else:
path = filename
2018-11-02 07:34:34 -05:00
if path.suffix == '.xml':
filetype = 'depletion_chain'
materials = []
elif path.suffix == '.h5':
with h5py.File(path, 'r') as h5file:
filetype = h5file.attrs['filetype'].decode()[5:]
materials = list(h5file)
else:
raise ValueError(
"File type {} not supported by {}"
.format(path.name, self.__class__.__name__))
library = {'path': str(path), 'type': filetype, 'materials': materials}
self.append(library)
def export_to_xml(self, path='cross_sections.xml'):
"""Export cross section data library to an XML file.
Parameters
----------
path : str
Path to file to write. Defaults to 'cross_sections.xml'.
"""
root = ET.Element('cross_sections')
# Determine common directory for library paths
common_dir = os.path.dirname(os.path.commonprefix(
[lib['path'] for lib in self]))
if common_dir == '':
common_dir = '.'
2018-11-02 07:34:34 -05:00
if os.path.relpath(common_dir, os.path.dirname(str(path))) != '.':
dir_element = ET.SubElement(root, "directory")
2018-07-18 07:55:00 -05:00
dir_element.text = os.path.realpath(common_dir)
for library in self:
if library['type'] == "depletion_chain":
lib_element = ET.SubElement(root, "depletion_chain")
else:
lib_element = ET.SubElement(root, "library")
lib_element.set('materials', ' '.join(library['materials']))
lib_element.set('path', os.path.relpath(library['path'], common_dir))
lib_element.set('type', library['type'])
# Clean the indentation to be user-readable
clean_indentation(root)
# Write XML file
reorder_attributes(root) # TODO: Remove when support is Python 3.8+
tree = ET.ElementTree(root)
2018-11-02 07:34:34 -05:00
tree.write(str(path), xml_declaration=True, encoding='utf-8',
method='xml')
2016-11-07 19:01:35 -05:00
@classmethod
def from_xml(cls, path=None):
2016-11-07 19:01:35 -05:00
"""Read cross section data library from an XML file.
Parameters
----------
path : str, optional
Path to XML file to read. If not provided,
openmc.config['cross_sections'] will be used.
2016-11-07 19:01:35 -05:00
2016-11-08 18:45:17 -05:00
Returns
-------
data : openmc.data.DataLibrary
2016-11-09 18:19:13 -05:00
Data library object initialized from the provided XML
2016-11-08 18:45:17 -05:00
2016-11-07 19:01:35 -05:00
"""
data = cls()
# If path is None, get the cross sections from the global configuration
if path is None:
path = openmc.config.get('cross_sections')
# Check to make sure we picked up cross sections
if path is None:
raise ValueError("Either path or openmc.config['cross_sections'] "
"must be set")
2016-11-07 19:01:35 -05:00
tree = ET.parse(path)
root = tree.getroot()
if root.find('directory') is not None:
directory = root.find('directory').text
else:
directory = os.path.dirname(path)
for lib_element in root.findall('library'):
filename = os.path.join(directory, lib_element.attrib['path'])
filetype = lib_element.attrib['type']
materials = lib_element.attrib['materials'].split()
library = {'path': filename, 'type': filetype,
'materials': materials}
data.libraries.append(library)
# get depletion chain data
dep_node = root.find("depletion_chain")
if dep_node is not None:
filename = os.path.join(directory, dep_node.attrib['path'])
library = {'path': filename, 'type': 'depletion_chain',
'materials': []}
data.libraries.append(library)
2016-11-07 19:01:35 -05:00
return data