Add decay heat function in material (#2287)

* add decay heat in material.py

* add decay_energy.json into eggs

* fix a bug when returned decay_ery is None

* add unit test for decayheat

* fix typo in doc string

* address reviewer's comments

* add user defined decay energy

* remove json for decay energy

* address a few comments

* add to docs
This commit is contained in:
jiankai-yu 2022-11-17 23:57:01 -05:00 committed by GitHub
parent 7a6d8cc58f
commit a17fe6cc82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 138 additions and 2 deletions

View file

@ -63,6 +63,7 @@ Core Functions
atomic_weight
combine_distributions
decay_constant
decay_energy
decay_photon_energy
dose_coefficients
gnd_name

View file

@ -621,3 +621,49 @@ def decay_photon_energy(nuclide: str) -> Optional[Univariate]:
"sources listed.")
return _DECAY_PHOTON_ENERGY.get(nuclide)
_DECAY_ENERGY = {}
def decay_energy(nuclide: str):
"""Get decay energy value resulting from the decay of a nuclide
This function relies on data stored in a depletion chain. Before calling it
for the first time, you need to ensure that a depletion chain has been
specified in openmc.config['chain_file'].
.. versionadded:: 0.13.3
Parameters
----------
nuclide : str
Name of nuclide, e.g., 'H3'
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:
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 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)

View file

@ -990,7 +990,50 @@ class Material(IDManagerMixin):
activity[nuclide] = inv_seconds * 1e24 * atoms_per_bcm * multiplier
return activity if by_nuclide else sum(activity.values())
def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False):
"""Returns the decay heat of the material or for each nuclide in the
material in units of [W], [W/g] or [W/cm3].
.. versionadded:: 0.13.3
Parameters
----------
units : {'W', 'W/g', 'W/cm3'}
Specifies the units of decay heat to return. Options include total
heat [W], specific [W/g] or volumetric heat [W/cm3].
Default is total heat [W].
by_nuclide : bool
Specifies if the decay heat should be returned for the material as a
whole or per nuclide. Default is False.
Returns
-------
Union[dict, float]
If `by_nuclide` is True then a dictionary whose keys are nuclide
names and values are decay heat is returned. Otherwise the decay heat
of the material is returned as a float.
"""
cv.check_value('units', units, {'W', 'W/g', 'W/cm3'})
cv.check_type('by_nuclide', by_nuclide, bool)
if units == 'W':
multiplier = self.volume
elif units == 'W/cm3':
multiplier = 1
elif units == 'W/g':
multiplier = 1.0 / self.get_mass_density()
decayheat = {}
for nuclide, atoms_per_bcm in self.get_nuclide_atom_densities().items():
decay_erg = openmc.data.decay_energy(nuclide)
inv_seconds = openmc.data.decay_constant(nuclide)
decay_erg *= openmc.data.JOULE_PER_EV
decayheat[nuclide] = inv_seconds * decay_erg * 1e24 * atoms_per_bcm * multiplier
return decayheat if by_nuclide else sum(decayheat.values())
def get_nuclide_atoms(self):
"""Return number of atoms of each nuclide in the material

View file

@ -1,13 +1,13 @@
<?xml version="1.0"?>
<depletion_chain>
<nuclide name="I135" decay_modes="1" reactions="1" half_life="2.36520E+04">
<nuclide name="I135" decay_modes="1" reactions="1" half_life="2.36520E+04" decay_energy="1916827.5">
<decay type="beta" target="Xe135" branching_ratio="1.0" />
<reaction type="(n,gamma)" Q="0.0" target="Xe136" /> <!-- Not precisely true, but whatever -->
<source type="discrete" particle="photon">
<parameters>3696.125 4095.822 4477.27 5097.122 29452.1 29781.3 33566.5 33629.4 33865.1 33878.5 34395.3 34408.0 34486.2 34488.2 112780.0 113150.0 162650.0 165740.0 184490.0 197190.0 220502.0 229720.0 247500.0 254740.0 264260.0 288451.0 290270.0 304910.0 305830.0 326000.0 333600.0 342520.0 361850.0 403030.0 414830.0 417633.0 429930.0 433741.0 451630.0 530800.0 546557.0 575970.0 588280.0 616900.0 649850.0 656090.0 679220.0 684600.0 690130.0 707920.0 785480.0 795500.0 797710.0 807200.0 836804.0 960290.0 961430.0 971960.0 972620.0 995090.0 1038760.0 1096860.0 1101580.0 1124000.0 1131511.0 1151510.0 1159900.0 1169040.0 1225600.0 1240470.0 1254800.0 1260409.0 1315770.0 1334800.0 1343660.0 1367890.0 1441800.0 1448350.0 1457560.0 1502790.0 1521990.0 1543700.0 1566410.0 1678027.0 1706459.0 1791196.0 1830690.0 1845300.0 1927300.0 1948490.0 2045880.0 2112400.0 2151500.0 2189400.0 2255457.0 2408650.0 2466070.0 2477100.0 9.714352819815078e-10 7.460941651551526e-09 6.047882056201745e-09 8.510389107205747e-10 3.7979729633684727e-08 7.033747061198817e-08 6.602815946851458e-09 1.2800909198245071e-08 6.513060244589454e-11 8.894667887850525e-11 1.3843783302275766e-09 2.700005498135763e-09 1.2141115256573815e-11 1.654893875618862e-11 3.7007705885806645e-09 2.0186021392258173e-09 2.859686363903241e-09 9.16781804898392e-09 6.896890642354875e-09 9.588360161322631e-09 5.130613770532286e-07 7.06510748729036e-08 8.410842246774237e-09 6.7286737974193905e-09 5.3829390379355124e-08 9.083709626516178e-07 8.915492781580693e-08 9.251926471451662e-09 2.783988783682273e-08 6.728673797419391e-10 1.093409492080651e-08 2.5232526740322717e-10 5.467047460403255e-08 6.812782219887132e-08 8.83138435911295e-08 1.0345335963532313e-06 8.915492781580693e-08 1.623292553627428e-07 9.251926471451663e-08 9.251926471451662e-09 2.0942997194467853e-06 3.784879011048407e-08 1.513951604419363e-08 1.093409492080651e-08 1.337323917237104e-07 2.186818984161302e-08 1.5980600268871052e-08 6.7286737974193905e-09 3.784879011048407e-08 1.9344937167580748e-07 4.457746390790346e-08 6.7286737974193905e-09 5.0465053480645434e-08 1.3457347594838781e-08 1.9597262434983976e-06 1.0093010696129087e-08 4.289529545854862e-08 2.607361096500014e-07 3.53255374364518e-07 4.541854813258089e-08 2.329803302356464e-06 2.607361096500014e-08 4.7100716581935733e-07 1.059766123093554e-06 6.6193328482113254e-06 4.205421123387119e-10 3.027903208838726e-08 2.565306885266143e-07 1.2616263370161358e-08 2.649415307733885e-07 3.3643368987096953e-09 8.410842246774237e-06 1.934493716758075e-08 9.251926471451662e-09 2.2709274066290446e-08 1.7830985563161385e-07 5.046505348064544e-09 9.251926471451663e-08 2.5400743585258203e-06 3.154065842540339e-07 1.093409492080651e-08 7.569758022096815e-09 3.784879011048407e-07 2.8008104681758214e-06 1.2027504412887161e-06 2.26251656438227e-06 1.6989901338483962e-07 1.6821684493548476e-09 8.663167514177466e-08 1.8503852942903323e-08 2.5568960430193683e-07 2.0186021392258175e-08 6.560456952483906e-09 3.784879011048407e-09 1.7999202408096872e-07 2.800810468175822e-07 2.1027105616935597e-08 4.205421123387119e-10</parameters>
</source>
</nuclide>
<nuclide name="Xe135" decay_modes="1" reactions="1" half_life="3.29040E+04">
<nuclide name="Xe135" decay_modes="1" reactions="1" half_life="3.29040E+04" decay_energy="567890.1">
<decay type=" beta" target="Cs135" branching_ratio="1.0" />
<reaction type="(n,gamma)" Q="0.0" target="Xe136" />
<source type="tabular" interpolation="histogram" particle="photon">

View file

@ -545,6 +545,52 @@ def test_get_activity():
assert pytest.approx(m4.get_activity(units='Bq')) == 355978108155965.94*3/2*10 # [Bq]
def test_get_decay_heat():
# Set chain file for testing
openmc.config['chain_file'] = Path(__file__).parents[1] / 'chain_simple.xml'
"""Tests the decay heat of stable, metastable and active materials"""
m1 = openmc.Material()
m1.add_nuclide("U235", 0.2)
m1.add_nuclide("U238", 0.8)
m1.set_density('g/cm3', 10.5)
# decay heat in W/cc and W/g should not require volume setting
assert m1.get_decay_heat(units='W/cm3') == 0
assert m1.get_decay_heat(units='W/g') == 0
m1.volume = 1
assert m1.get_decay_heat(units='W') == 0
# Checks that 1g of tritium has the correct decay heat scaling
m2 = openmc.Material()
m2.add_nuclide("I135", 1)
m2.set_density('g/cm3', 1)
m2.volume = 1
assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193
m2.set_density('g/cm3', 2)
assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193*2
m2.volume = 3
assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193*2*3
# Checks that 1 mol of a metastable nuclides has the correct decay heat
m3 = openmc.Material()
m3.add_nuclide("Xe135", 1)
m3.set_density('g/cm3', 1)
m3.volume = 98.9
assert pytest.approx(m3.get_decay_heat(units='W'), rel=0.001) == 846181.2921143445
# Checks that specific and volumetric decay heat of tritium are correct
m4 = openmc.Material()
m4.add_nuclide("I135", 1)
m4.set_density('g/cm3', 1.5)
assert pytest.approx(m4.get_decay_heat(units='W/g')) == 40175.15720273193 # [W/g]
assert pytest.approx(m4.get_decay_heat(units='W/g', by_nuclide=True)["I135"]) == 40175.15720273193 # [W/g]
assert pytest.approx(m4.get_decay_heat(units='W/cm3')) == 40175.15720273193*3/2 # [W/cc]
assert pytest.approx(m4.get_decay_heat(units='W/cm3', by_nuclide=True)["I135"]) == 40175.15720273193*3/2 #[W/cc]
# volume is required to calculate total decay heat
m4.volume = 10.
assert pytest.approx(m4.get_decay_heat(units='W')) == 40175.15720273193*3/2*10 # [W]
def test_decay_photon_energy():
# Set chain file for testing
openmc.config['chain_file'] = Path(__file__).parents[1] / 'chain_simple.xml'