Skip to content

Commit a17fe6c

Browse files
authored
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
1 parent 7a6d8cc commit a17fe6c

5 files changed

Lines changed: 138 additions & 2 deletions

File tree

‎docs/source/pythonapi/data.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ Core Functions
6363
atomic_weight
6464
combine_distributions
6565
decay_constant
66+
decay_energy
6667
decay_photon_energy
6768
dose_coefficients
6869
gnd_name

‎openmc/data/decay.py‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,3 +621,49 @@ def decay_photon_energy(nuclide: str) -> Optional[Univariate]:
621621
"sources listed.")
622622

623623
return _DECAY_PHOTON_ENERGY.get(nuclide)
624+
625+
626+
_DECAY_ENERGY = {}
627+
628+
629+
def decay_energy(nuclide: str):
630+
"""Get decay energy value resulting from the decay of a nuclide
631+
632+
This function relies on data stored in a depletion chain. Before calling it
633+
for the first time, you need to ensure that a depletion chain has been
634+
specified in openmc.config['chain_file'].
635+
636+
.. versionadded:: 0.13.3
637+
638+
Parameters
639+
----------
640+
nuclide : str
641+
Name of nuclide, e.g., 'H3'
642+
643+
Returns
644+
-------
645+
float
646+
Decay energy of nuclide in [eV]. If the nuclide is stable, a value of
647+
0.0 is returned.
648+
"""
649+
if not _DECAY_ENERGY:
650+
chain_file = openmc.config.get('chain_file')
651+
if chain_file is None:
652+
raise DataError(
653+
"A depletion chain file must be specified with "
654+
"openmc.config['chain_file'] in order to load decay data."
655+
)
656+
657+
from openmc.deplete import Chain
658+
chain = Chain.from_xml(chain_file)
659+
for nuc in chain.nuclides:
660+
if nuc.decay_energy:
661+
_DECAY_ENERGY[nuc.name] = nuc.decay_energy
662+
663+
# If the chain file contained no decay energy, warn the user
664+
if not _DECAY_ENERGY:
665+
warn(f"Chain file '{chain_file}' does not have any decay energy.")
666+
667+
return _DECAY_ENERGY.get(nuclide, 0.0)
668+
669+

‎openmc/material.py‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,50 @@ def get_activity(self, units: str = 'Bq/cm3', by_nuclide: bool = False):
990990
activity[nuclide] = inv_seconds * 1e24 * atoms_per_bcm * multiplier
991991

992992
return activity if by_nuclide else sum(activity.values())
993+
994+
def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False):
995+
"""Returns the decay heat of the material or for each nuclide in the
996+
material in units of [W], [W/g] or [W/cm3].
993997
998+
.. versionadded:: 0.13.3
999+
1000+
Parameters
1001+
----------
1002+
units : {'W', 'W/g', 'W/cm3'}
1003+
Specifies the units of decay heat to return. Options include total
1004+
heat [W], specific [W/g] or volumetric heat [W/cm3].
1005+
Default is total heat [W].
1006+
by_nuclide : bool
1007+
Specifies if the decay heat should be returned for the material as a
1008+
whole or per nuclide. Default is False.
1009+
1010+
Returns
1011+
-------
1012+
Union[dict, float]
1013+
If `by_nuclide` is True then a dictionary whose keys are nuclide
1014+
names and values are decay heat is returned. Otherwise the decay heat
1015+
of the material is returned as a float.
1016+
"""
1017+
1018+
cv.check_value('units', units, {'W', 'W/g', 'W/cm3'})
1019+
cv.check_type('by_nuclide', by_nuclide, bool)
1020+
1021+
if units == 'W':
1022+
multiplier = self.volume
1023+
elif units == 'W/cm3':
1024+
multiplier = 1
1025+
elif units == 'W/g':
1026+
multiplier = 1.0 / self.get_mass_density()
1027+
1028+
decayheat = {}
1029+
for nuclide, atoms_per_bcm in self.get_nuclide_atom_densities().items():
1030+
decay_erg = openmc.data.decay_energy(nuclide)
1031+
inv_seconds = openmc.data.decay_constant(nuclide)
1032+
decay_erg *= openmc.data.JOULE_PER_EV
1033+
decayheat[nuclide] = inv_seconds * decay_erg * 1e24 * atoms_per_bcm * multiplier
1034+
1035+
return decayheat if by_nuclide else sum(decayheat.values())
1036+
9941037
def get_nuclide_atoms(self):
9951038
"""Return number of atoms of each nuclide in the material
9961039

‎tests/chain_simple.xml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<?xml version="1.0"?>
22
<depletion_chain>
3-
<nuclide name="I135" decay_modes="1" reactions="1" half_life="2.36520E+04">
3+
<nuclide name="I135" decay_modes="1" reactions="1" half_life="2.36520E+04" decay_energy="1916827.5">
44
<decay type="beta" target="Xe135" branching_ratio="1.0" />
55
<reaction type="(n,gamma)" Q="0.0" target="Xe136" /> <!-- Not precisely true, but whatever -->
66
<source type="discrete" particle="photon">
77
<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>
88
</source>
99
</nuclide>
10-
<nuclide name="Xe135" decay_modes="1" reactions="1" half_life="3.29040E+04">
10+
<nuclide name="Xe135" decay_modes="1" reactions="1" half_life="3.29040E+04" decay_energy="567890.1">
1111
<decay type=" beta" target="Cs135" branching_ratio="1.0" />
1212
<reaction type="(n,gamma)" Q="0.0" target="Xe136" />
1313
<source type="tabular" interpolation="histogram" particle="photon">

‎tests/unit_tests/test_material.py‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,52 @@ def test_get_activity():
545545
assert pytest.approx(m4.get_activity(units='Bq')) == 355978108155965.94*3/2*10 # [Bq]
546546

547547

548+
def test_get_decay_heat():
549+
# Set chain file for testing
550+
openmc.config['chain_file'] = Path(__file__).parents[1] / 'chain_simple.xml'
551+
552+
"""Tests the decay heat of stable, metastable and active materials"""
553+
m1 = openmc.Material()
554+
m1.add_nuclide("U235", 0.2)
555+
m1.add_nuclide("U238", 0.8)
556+
m1.set_density('g/cm3', 10.5)
557+
# decay heat in W/cc and W/g should not require volume setting
558+
assert m1.get_decay_heat(units='W/cm3') == 0
559+
assert m1.get_decay_heat(units='W/g') == 0
560+
m1.volume = 1
561+
assert m1.get_decay_heat(units='W') == 0
562+
563+
# Checks that 1g of tritium has the correct decay heat scaling
564+
m2 = openmc.Material()
565+
m2.add_nuclide("I135", 1)
566+
m2.set_density('g/cm3', 1)
567+
m2.volume = 1
568+
assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193
569+
m2.set_density('g/cm3', 2)
570+
assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193*2
571+
m2.volume = 3
572+
assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193*2*3
573+
574+
# Checks that 1 mol of a metastable nuclides has the correct decay heat
575+
m3 = openmc.Material()
576+
m3.add_nuclide("Xe135", 1)
577+
m3.set_density('g/cm3', 1)
578+
m3.volume = 98.9
579+
assert pytest.approx(m3.get_decay_heat(units='W'), rel=0.001) == 846181.2921143445
580+
581+
# Checks that specific and volumetric decay heat of tritium are correct
582+
m4 = openmc.Material()
583+
m4.add_nuclide("I135", 1)
584+
m4.set_density('g/cm3', 1.5)
585+
assert pytest.approx(m4.get_decay_heat(units='W/g')) == 40175.15720273193 # [W/g]
586+
assert pytest.approx(m4.get_decay_heat(units='W/g', by_nuclide=True)["I135"]) == 40175.15720273193 # [W/g]
587+
assert pytest.approx(m4.get_decay_heat(units='W/cm3')) == 40175.15720273193*3/2 # [W/cc]
588+
assert pytest.approx(m4.get_decay_heat(units='W/cm3', by_nuclide=True)["I135"]) == 40175.15720273193*3/2 #[W/cc]
589+
# volume is required to calculate total decay heat
590+
m4.volume = 10.
591+
assert pytest.approx(m4.get_decay_heat(units='W')) == 40175.15720273193*3/2*10 # [W]
592+
593+
548594
def test_decay_photon_energy():
549595
# Set chain file for testing
550596
openmc.config['chain_file'] = Path(__file__).parents[1] / 'chain_simple.xml'

0 commit comments

Comments
 (0)