mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 14:15:42 -04:00
Merge pull request #1247 from drewejohnson/dep-chain-file
Read depletion_chain information from main cross section file
This commit is contained in:
commit
ca608653e3
7 changed files with 159 additions and 20 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
import xml.etree.ElementTree as ET
|
||||
import pathlib
|
||||
|
||||
import h5py
|
||||
|
||||
|
|
@ -48,19 +49,30 @@ class DataLibrary(EqualityMixin):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
filename : str or Path
|
||||
Path to the file to be registered.
|
||||
If an ``xml`` file, treat as the depletion chain file without
|
||||
materials.
|
||||
|
||||
"""
|
||||
# Support pathlib
|
||||
# TODO: Remove when support is Python 3.6+ only
|
||||
filename = str(filename)
|
||||
if not isinstance(filename, pathlib.Path):
|
||||
path = pathlib.Path(filename)
|
||||
else:
|
||||
path = filename
|
||||
|
||||
with h5py.File(filename, 'r') as h5file:
|
||||
filetype = h5file.attrs['filetype'].decode()[5:]
|
||||
materials = list(h5file)
|
||||
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': filename, 'type': filetype, 'materials': materials}
|
||||
library = {'path': str(path), 'type': filetype, 'materials': materials}
|
||||
self.libraries.append(library)
|
||||
|
||||
def export_to_xml(self, path='cross_sections.xml'):
|
||||
|
|
@ -85,8 +97,11 @@ class DataLibrary(EqualityMixin):
|
|||
dir_element.text = os.path.realpath(common_dir)
|
||||
|
||||
for library in self.libraries:
|
||||
lib_element = ET.SubElement(root, "library")
|
||||
lib_element.set('materials', ' '.join(library['materials']))
|
||||
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'])
|
||||
|
||||
|
|
@ -106,7 +121,7 @@ class DataLibrary(EqualityMixin):
|
|||
----------
|
||||
path : str, optional
|
||||
Path to XML file to read. If not provided, the
|
||||
`OPENMC_CROSS_SECTIONS` environment variable will be used.
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable will be used.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -146,4 +161,13 @@ class DataLibrary(EqualityMixin):
|
|||
'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)
|
||||
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ from collections import namedtuple
|
|||
import os
|
||||
from pathlib import Path
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from xml.etree import ElementTree as ET
|
||||
from warnings import warn
|
||||
|
||||
from openmc.data import DataLibrary
|
||||
from .chain import Chain
|
||||
|
||||
OperatorResult = namedtuple('OperatorResult', ['k', 'rates'])
|
||||
|
|
@ -43,8 +46,9 @@ class TransportOperator(metaclass=ABCMeta):
|
|||
Parameters
|
||||
----------
|
||||
chain_file : str, optional
|
||||
Path to the depletion chain XML file. Defaults to the
|
||||
:envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists.
|
||||
Path to the depletion chain XML file. Defaults to the file
|
||||
listed under ``depletion_chain`` in
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
@ -62,8 +66,21 @@ class TransportOperator(metaclass=ABCMeta):
|
|||
if chain_file is None:
|
||||
chain_file = os.environ.get("OPENMC_DEPLETE_CHAIN", None)
|
||||
if chain_file is None:
|
||||
raise IOError("No chain specified, either manually or in "
|
||||
"environment variable OPENMC_DEPLETE_CHAIN.")
|
||||
data = DataLibrary.from_xml()
|
||||
# search for depletion_chain path from end of list
|
||||
for lib in reversed(data.libraries):
|
||||
if lib['type'] == 'depletion_chain':
|
||||
break
|
||||
else:
|
||||
raise IOError(
|
||||
"No chain specified, either manually or "
|
||||
"under depletion_chain in environment variable "
|
||||
"OPENMC_CROSS_SECTIONS.")
|
||||
chain_file = lib['path']
|
||||
else:
|
||||
warn("Use of OPENMC_DEPLETE_CHAIN is deprecated in favor "
|
||||
"of adding depletion_chain to OPENMC_CROSS_SECTIONS",
|
||||
FutureWarning)
|
||||
self.chain = Chain.from_xml(chain_file)
|
||||
|
||||
@abstractmethod
|
||||
|
|
|
|||
|
|
@ -107,7 +107,8 @@ class Chain(object):
|
|||
yield sublibrary files. The depletion chain used during a depletion
|
||||
simulation is indicated by either an argument to
|
||||
:class:`openmc.deplete.Operator` or through the
|
||||
:envvar:`OPENMC_DEPLETE_CHAIN` environment variable.
|
||||
``depletion_chain`` item in the :envvar:`OPENMC_CROSS_SECTIONS`
|
||||
environment variable.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
|
|||
|
|
@ -64,8 +64,9 @@ class Operator(TransportOperator):
|
|||
settings : openmc.Settings
|
||||
OpenMC Settings object
|
||||
chain_file : str, optional
|
||||
Path to the depletion chain XML file. Defaults to the
|
||||
:envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists.
|
||||
Path to the depletion chain XML file. Defaults to the file
|
||||
listed under ``depletion_chain`` in
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable.
|
||||
prev_results : ResultsList, optional
|
||||
Results from a previous depletion calculation. If this argument is
|
||||
specified, the depletion calculation will start from the latest state
|
||||
|
|
|
|||
|
|
@ -133,8 +133,9 @@ class Model(object):
|
|||
Array of timesteps in units of [s]. Note that values are not
|
||||
cumulative.
|
||||
chain_file : str, optional
|
||||
Path to the depletion chain XML file. Defaults to the
|
||||
:envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists.
|
||||
Path to the depletion chain XML file. Defaults to the chain
|
||||
found under the ``depletion_chain`` in the
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable if it exists.
|
||||
method : str
|
||||
Integration method used for depletion (e.g., 'cecm', 'predictor')
|
||||
**kwargs
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from collections.abc import Mapping
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
|
@ -33,6 +34,30 @@ def test_data_library(tmpdir):
|
|||
assert new_lib.libraries[-1]['type'] == 'thermal'
|
||||
|
||||
|
||||
def test_depletion_chain_data_library(run_in_tmpdir):
|
||||
dep_lib = openmc.data.DataLibrary.from_xml()
|
||||
prev_len = len(dep_lib.libraries)
|
||||
chain_path = Path(__file__).parents[1] / "chain_simple.xml"
|
||||
dep_lib.register_file(chain_path)
|
||||
assert len(dep_lib.libraries) == prev_len + 1
|
||||
# Inspect
|
||||
dep_dict = dep_lib.libraries[-1]
|
||||
assert dep_dict['materials'] == []
|
||||
assert dep_dict['type'] == 'depletion_chain'
|
||||
assert dep_dict['path'] == str(chain_path)
|
||||
|
||||
out_path = "cross_section_chain.xml"
|
||||
dep_lib.export_to_xml(out_path)
|
||||
|
||||
dep_import = openmc.data.DataLibrary.from_xml(out_path)
|
||||
for lib in reversed(dep_import.libraries):
|
||||
if lib['type'] == 'depletion_chain':
|
||||
break
|
||||
else:
|
||||
raise IndexError("depletion_chain not found in exported DataLibrary")
|
||||
assert os.path.exists(lib['path'])
|
||||
|
||||
|
||||
def test_linearize():
|
||||
"""Test linearization of a continuous function."""
|
||||
x, y = openmc.data.linearize([-1., 1.], lambda x: 1 - x*x)
|
||||
|
|
|
|||
70
tests/unit_tests/test_deplete_operator.py
Normal file
70
tests/unit_tests/test_deplete_operator.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
"""Basic unit tests for openmc.deplete.Operator instantiation
|
||||
|
||||
Modifies and resets environment variable OPENMC_CROSS_SECTIONS
|
||||
to a custom file with new depletion_chain node
|
||||
"""
|
||||
|
||||
from os import environ
|
||||
from unittest import mock
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from openmc.deplete.abc import TransportOperator
|
||||
from openmc.deplete.chain import Chain
|
||||
|
||||
BARE_XS_FILE = "bare_cross_sections.xml"
|
||||
CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml"
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def bare_xs(run_in_tmpdir):
|
||||
"""Create a very basic cross_sections file, return simple Chain.
|
||||
|
||||
"""
|
||||
|
||||
bare_xs_contents = """<?xml version="1.0"?>
|
||||
<cross_sections>
|
||||
<depletion_chain path="{}" />
|
||||
</cross_sections>
|
||||
""".format(CHAIN_PATH)
|
||||
|
||||
with open(BARE_XS_FILE, "w") as out:
|
||||
out.write(bare_xs_contents)
|
||||
|
||||
yield
|
||||
|
||||
|
||||
class BareDepleteOperator(TransportOperator):
|
||||
"""Very basic class for testing the initialization."""
|
||||
|
||||
# declare abstract methods so object can be created
|
||||
def __call__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def initial_condition(self):
|
||||
pass
|
||||
|
||||
def get_results_info(self):
|
||||
pass
|
||||
|
||||
|
||||
@mock.patch.dict(environ, {"OPENMC_CROSS_SECTIONS": BARE_XS_FILE})
|
||||
def test_operator_init(bare_xs):
|
||||
"""The test will set and unset environment variable OPENMC_CROSS_SECTIONS
|
||||
to point towards a temporary dummy file. This file will be removed
|
||||
at the end of the test, and only contains a
|
||||
depletion_chain node."""
|
||||
# force operator to read from OPENMC_CROSS_SECTIONS
|
||||
bare_op = BareDepleteOperator(chain_file=None)
|
||||
act_chain = bare_op.chain
|
||||
ref_chain = Chain.from_xml(CHAIN_PATH)
|
||||
assert len(act_chain) == len(ref_chain)
|
||||
for name in ref_chain.nuclide_dict:
|
||||
# compare openmc.deplete.Nuclide objects
|
||||
ref_nuc = ref_chain[name]
|
||||
act_nuc = act_chain[name]
|
||||
for prop in [
|
||||
'name', 'half_life', 'decay_energy', 'reactions',
|
||||
'decay_modes', 'yield_data', 'yield_energies',
|
||||
]:
|
||||
assert getattr(act_nuc, prop) == getattr(ref_nuc, prop), prop
|
||||
Loading…
Add table
Add a link
Reference in a new issue