Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions openmc/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from typing import Any, Dict, Iterator

from openmc.data import DataLibrary
from openmc.data.decay import _DECAY_ENERGY, _DECAY_PHOTON_ENERGY

__all__ = ["config"]

Expand Down Expand Up @@ -76,9 +75,6 @@ def __delitem__(self, key: str):
env_var = self._PATH_KEYS[key]
if env_var in os.environ:
del os.environ[env_var]
if key == 'chain_file':
_DECAY_PHOTON_ENERGY.clear()
_DECAY_ENERGY.clear()

def __setitem__(self, key: str, value: Any):
"""Set a configuration key and its corresponding value.
Expand All @@ -102,9 +98,6 @@ def __setitem__(self, key: str, value: Any):
self._mapping[key] = stored_path
os.environ[self._PATH_KEYS[key]] = str(stored_path)

if key == 'chain_file':
_DECAY_PHOTON_ENERGY.clear()
_DECAY_ENERGY.clear()

if not stored_path.exists():
warnings.warn(f"Path '{stored_path}' does not exist.", UserWarning)
Expand Down Expand Up @@ -168,7 +161,10 @@ def patch(self, key: str, value: Any):

"""
previous_value = self.get(key)
self[key] = value
if value is not None:
self[key] = value
else:
del self[key]
try:
yield
finally:
Expand Down
17 changes: 14 additions & 3 deletions openmc/data/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from math import sqrt, log
from warnings import warn

from ..exceptions import DataError

# Isotopic abundances from Meija J, Coplen T B, et al, "Isotopic compositions
# of the elements 2013 (IUPAC Technical Report)", Pure. Appl. Chem. 88 (3),
# pp. 293-306 (2013). The "representative isotopic abundance" values from
Expand Down Expand Up @@ -363,7 +365,7 @@ def atomic_weight(element):
raise ValueError(f"No naturally-occurring isotopes for element '{element}'.")


def half_life(isotope):
def half_life(isotope, chain_file = None):
"""Return half-life of isotope in seconds or None if isotope is stable

Half-life values are from the `ENDF/B-VIII.0 decay sublibrary
Expand All @@ -382,6 +384,15 @@ def half_life(isotope):
Half-life of isotope in [s]

"""
from openmc.deplete.chain import _get_chain

try:
chain = _get_chain(chain_file)
if isotope in chain and chain[isotope].half_life is not None:
return chain[isotope].half_life
except DataError:
pass

global _HALF_LIFE
if not _HALF_LIFE:
# Load ENDF/B-VIII.0 data from JSON file
Expand All @@ -391,7 +402,7 @@ def half_life(isotope):
return _HALF_LIFE.get(isotope.lower())


def decay_constant(isotope):
def decay_constant(isotope, chain_file = None):
"""Return decay constant of isotope in [s^-1]

Decay constants are based on half-life values from the
Expand All @@ -415,7 +426,7 @@ def decay_constant(isotope):
openmc.data.half_life

"""
t = half_life(isotope)
t = half_life(isotope, chain_file = chain_file)
return _LOG_TWO / t if t else 0.0


Expand Down
59 changes: 21 additions & 38 deletions openmc/data/decay.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,10 +569,7 @@ def sources(self):
return merged_sources


_DECAY_PHOTON_ENERGY = {}


def decay_photon_energy(nuclide: str) -> Univariate | None:
def decay_photon_energy(nuclide: str, chain_file = None) -> Univariate | None:
"""Get photon energy distribution resulting from the decay of a nuclide

This function relies on data stored in a depletion chain. Before calling it
Expand All @@ -585,6 +582,8 @@ def decay_photon_energy(nuclide: str) -> Univariate | None:
----------
nuclide : str
Name of nuclide, e.g., 'Co58'
chain_file : str or path-like
Chain file to get decay photon energy from.

Returns
-------
Expand All @@ -593,32 +592,20 @@ def decay_photon_energy(nuclide: str) -> Univariate | None:
if no photon source exists. Note that the probabilities represent
intensities, given as [Bq].
"""
if not _DECAY_PHOTON_ENERGY:
chain_file = openmc.config.get('chain_file')
if chain_file is None:
from openmc.deplete.chain import _get_chain

chain = _get_chain(chain_file)
if chain is None:
raise DataError(
"A depletion chain file must be specified with "
"openmc.config['chain_file'] in order to load decay data."
)

from openmc.deplete import Chain
chain = Chain.from_xml(chain_file)
for nuc in chain.nuclides:
if 'photon' in nuc.sources:
_DECAY_PHOTON_ENERGY[nuc.name] = nuc.sources['photon']

# If the chain file contained no sources at all, warn the user
if not _DECAY_PHOTON_ENERGY:
warn(f"Chain file '{chain_file}' does not have any decay photon "
"sources listed.")

return _DECAY_PHOTON_ENERGY.get(nuclide)
if nuclide in chain:
nuclide = chain[nuclide]
return nuclide.sources.get('photon')


_DECAY_ENERGY = {}


def decay_energy(nuclide: str):
def decay_energy(nuclide: str, chain_file = None):
"""Get decay energy value resulting from the decay of a nuclide

This function relies on data stored in a depletion chain. Before calling it
Expand All @@ -631,31 +618,27 @@ def decay_energy(nuclide: str):
----------
nuclide : str
Name of nuclide, e.g., 'H3'
chain_file : str or path-like
Chain file to get decay energy from.

Returns
-------
float
Decay energy of nuclide in [eV]. If the nuclide is stable, a value of
0.0 is returned.
"""
if not _DECAY_ENERGY:
chain_file = openmc.config.get('chain_file')
if chain_file is None:
from openmc.deplete.chain import _get_chain

chain = _get_chain(chain_file)
if chain is None:
raise DataError(
"A depletion chain file must be specified with "
"openmc.config['chain_file'] in order to load decay data."
)
if nuclide in chain:
nuclide = chain[nuclide]
return nuclide.decay_energy

from openmc.deplete import Chain
chain = Chain.from_xml(chain_file)
for nuc in chain.nuclides:
if nuc.decay_energy:
_DECAY_ENERGY[nuc.name] = nuc.decay_energy

# If the chain file contained no decay energy, warn the user
if not _DECAY_ENERGY:
warn(f"Chain file '{chain_file}' does not have any decay energy.")

return _DECAY_ENERGY.get(nuclide, 0.0)
return 0.0


3 changes: 2 additions & 1 deletion openmc/deplete/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import numpy as np
from uncertainties import ufloat

from openmc.checkvalue import check_type, check_greater_than, PathLike
from openmc.checkvalue import check_type, check_greater_than, check_value, PathLike
from openmc.mpi import comm
from openmc.utility_funcs import change_directory
from openmc import Material
Expand Down Expand Up @@ -155,6 +155,7 @@ def __init__(self, chain_file=None, fission_q=None, prev_results=None):
self.prev_res = None
else:
check_type("previous results", prev_results, Results)
check_value("previous results chain file", prev_results.chain, [self.chain])
self.prev_res = prev_results

@abstractmethod
Expand Down
47 changes: 46 additions & 1 deletion openmc/deplete/chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from openmc.data import gnds_name, zam
from openmc.exceptions import DataError
from .nuclide import FissionYieldDistribution, Nuclide
from ..mixin import EqualityMixin
from .._xml import get_text
import openmc.data

Expand Down Expand Up @@ -229,7 +230,7 @@ def replace_missing_fpy(actinide, fpy_data, decay_data):
return 'U235'


class Chain:
class Chain(EqualityMixin):
"""Full representation of a depletion chain.

A depletion chain can be created by using the :meth:`from_endf` method which
Expand Down Expand Up @@ -580,6 +581,50 @@ def export_to_xml(self, filename):

tree = ET.ElementTree(root_elem)
tree.write(str(filename), encoding='utf-8', pretty_print=True)

@classmethod
def from_hdf5(cls, group, fission_q=None):
"""Reads a depletion chain XML file.

Parameters
----------
group : str
The hdf5 group to read depletion chain from.
fission_q : dict, optional
Dictionary of nuclides and their fission Q values [eV].
If not given, values will be pulled from ``filename``

"""
cgroup = group["depletion_chain"]
if fission_q is not None:
check_type("fission_q", fission_q, Mapping)
else:
fission_q = {}
if "path" in cgroup.attrs:
path = Path(__file__).resolve().parent.joinpath(cgroup.attrs["path"])
return Chain.from_xml(path, fission_q)
else:
chain = cls()
nuc_group = cgroup["nuclides"]
for name, ngroup in nuc_group.items():
this_q = fission_q.get(name)
nuc = Nuclide.from_hdf5(nuc_group, name, this_q)
chain.add_nuclide(nuc)

def to_hdf5(self, group):
cgroup = group.create_group("depletion_chain")
if hasattr(self, "_xml_path"):
path = Path(self._xml_path)
if openmc.config["resolve_paths"]:
path = path.resolve()
else:
path = path.relative_to(Path(__file__).resolve().parent, walk_up=True)
cgroup.attrs["path"] = str(path)
else:
nuc_group = cgroup.create_group("nuclides")
for nuclide in self.nuclides:
nuclide.to_hdf5(nuc_group)


def get_default_fission_yields(self):
"""Return fission yields at lowest incident neutron energy
Expand Down
Loading
Loading