diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index 2e598fb74a..1be3e27ff9 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -49,6 +49,9 @@ class TransportOperator(metaclass=ABCMeta): Path to the depletion chain XML file. Defaults to the file listed under ``depletion_chain`` in :envvar:`OPENMC_CROSS_SECTIONS` environment variable. + fission_q : dict, optional + Dictionary of nuclides and their fission Q values [eV]. If not given, + values will be pulled from the ``chain_file``. Attributes ---------- @@ -58,7 +61,7 @@ class TransportOperator(metaclass=ABCMeta): nuclides with reaction rates. Defaults to 1.0e3. """ - def __init__(self, chain_file=None): + def __init__(self, chain_file=None, fission_q=None): self.dilute_initial = 1.0e3 self.output_dir = '.' @@ -81,7 +84,7 @@ class TransportOperator(metaclass=ABCMeta): 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) + self.chain = Chain.from_xml(chain_file, fission_q) @abstractmethod def __call__(self, vec, print_out=True): diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index b8b6a3644a..643235d9f5 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -4,16 +4,18 @@ This module contains information about a depletion chain. A depletion chain is loaded from an .xml file and all the nuclides are linked together. """ -from collections import OrderedDict, defaultdict from io import StringIO from itertools import chain import math import re -import os +from collections import OrderedDict, defaultdict +from collections.abc import Mapping + +from openmc.checkvalue import check_type # Try to use lxml if it is available. It preserves the order of attributes and -# provides a pretty-printer by default. If not available, use OpenMC function to -# pretty print. +# provides a pretty-printer by default. If not available, +# use OpenMC function to pretty print. try: import lxml.etree as ET _have_lxml = True @@ -312,22 +314,32 @@ class Chain(object): return chain @classmethod - def from_xml(cls, filename): + def from_xml(cls, filename, fission_q=None): """Reads a depletion chain XML file. Parameters ---------- filename : str The path to the depletion chain XML file. + fission_q : dict, optional + Dictionary of nuclides and their fission Q values [eV]. + If not given, values will be pulled from ``filename`` """ chain = cls() + if fission_q is not None: + check_type("fission_q", fission_q, Mapping) + else: + fission_q = {} + # Load XML tree root = ET.parse(str(filename)) for i, nuclide_elem in enumerate(root.findall('nuclide')): - nuc = Nuclide.from_xml(nuclide_elem) + this_q = fission_q.get(nuclide_elem.get("name")) + + nuc = Nuclide.from_xml(nuclide_elem, this_q) chain.nuclide_dict[nuc.name] = i # Check for reaction paths diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index 8a30214c2c..88d5ac6eae 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -112,13 +112,16 @@ class Nuclide(object): return len(self.reactions) @classmethod - def from_xml(cls, element): + def from_xml(cls, element, fission_q=None): """Read nuclide from an XML element. Parameters ---------- element : xml.etree.ElementTree.Element XML element to write nuclide data to + fission_q : None or float + User-supplied fission Q value [eV]. + Will be read from the element if not given Returns ------- @@ -153,6 +156,8 @@ class Nuclide(object): target = reaction_elem.get('target') else: target = None + if fission_q is not None: + Q = fission_q # Append reaction nuc.reactions.append(ReactionTuple( diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index d284d98567..ffbffbc734 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -73,6 +73,9 @@ class Operator(TransportOperator): in the previous results. diff_burnable_mats : bool, optional Whether to differentiate burnable materials with multiple instances + fission_q : dict, optional + Dictionary of nuclides and their fission Q values [eV]. If not given, + values will be pulled from the ``chain_file``. Attributes ---------- @@ -110,14 +113,14 @@ class Operator(TransportOperator): """ def __init__(self, geometry, settings, chain_file=None, prev_results=None, - diff_burnable_mats=False): - super().__init__(chain_file) + diff_burnable_mats=False, fission_q=None): + super().__init__(chain_file, fission_q) self.round_number = False self.settings = settings self.geometry = geometry self.diff_burnable_mats = diff_burnable_mats - if prev_results != None: + if prev_results is not None: # Reload volumes into geometry prev_results[-1].transfer_volumes(geometry) @@ -149,7 +152,6 @@ class Operator(TransportOperator): self.reaction_rates = ReactionRates( self.local_mats, self._burnable_nucs, self.chain.reactions) - def __call__(self, vec, power, print_out=True): """Runs a simulation. diff --git a/openmc/model/model.py b/openmc/model/model.py index f66bd8f16d..2f515ca78f 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -124,7 +124,7 @@ class Model(object): self._plots.append(plot) def deplete(self, timesteps, chain_file=None, method='cecm', - **kwargs): + fission_q=None, **kwargs): """Deplete model using specified timesteps/power Parameters @@ -138,6 +138,9 @@ class Model(object): :envvar:`OPENMC_CROSS_SECTIONS` environment variable if it exists. method : str Integration method used for depletion (e.g., 'cecm', 'predictor') + fission_q : dict, optional + Dictionary of nuclides and their fission Q values [eV]. + If not given, values will be pulled from the ``chain_file``. **kwargs Keyword arguments passed to integration function (e.g., :func:`openmc.deplete.integrator.cecm`) @@ -149,7 +152,10 @@ class Model(object): import openmc.deplete as dep # Create OpenMC transport operator - op = dep.Operator(self.geometry, self.settings, chain_file) + op = dep.Operator( + self.geometry, self.settings, chain_file, + fission_q=fission_q, + ) # Perform depletion check_value('method', method, ('cecm', 'predictor', 'cf4', 'epc_rk4', diff --git a/tests/unit_tests/test_deplete_chain.py b/tests/unit_tests/test_deplete_chain.py index 1fe83ad98a..dd6817a8e4 100644 --- a/tests/unit_tests/test_deplete_chain.py +++ b/tests/unit_tests/test_deplete_chain.py @@ -231,3 +231,15 @@ def test_getitem(): assert "NucA" == chain["NucA"] assert "NucB" == chain["NucB"] assert "NucC" == chain["NucC"] + + +def test_set_fiss_q(): + """Make sure new fission q values can be set on the chain""" + new_q = {"U235": 2.0E8, "U238": 2.0E8, "U234": 5.0E7} + chain_file = Path(__file__).parents[1] / "chain_simple.xml" + mod_chain = Chain.from_xml(chain_file, new_q) + for name, q in new_q.items(): + chain_nuc = mod_chain[name] + for rx in chain_nuc.reactions: + if rx.type == 'fission': + assert rx.Q == q diff --git a/tests/unit_tests/test_deplete_operator.py b/tests/unit_tests/test_deplete_operator.py index 757b51d923..45c2aada4e 100644 --- a/tests/unit_tests/test_deplete_operator.py +++ b/tests/unit_tests/test_deplete_operator.py @@ -68,3 +68,16 @@ def test_operator_init(bare_xs): 'decay_modes', 'yield_data', 'yield_energies', ]: assert getattr(act_nuc, prop) == getattr(ref_nuc, prop), prop + + +def test_operator_fiss_q(): + """Make sure fission q values can be set""" + new_q = {"U235": 2.0E8, "U238": 2.0E8, "U234": 5.0E7} + chain_file = Path(__file__).parents[1] / "chain_simple.xml" + operator = BareDepleteOperator(chain_file=chain_file, fission_q=new_q) + mod_chain = operator.chain + for name, q in new_q.items(): + chain_nuc = mod_chain[name] + for rx in chain_nuc.reactions: + if rx.type == 'fission': + assert rx.Q == q