# Copyright 2019 TerraPower, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for lumpedFissionProduce module."""
import unittest
import io
import math
import os
from armi.physics.neutronics.fissionProductModel import (
lumpedFissionProduct,
REFERENCE_LUMPED_FISSION_PRODUCT_FILE,
)
from armi.context import RES
from armi.settings import Settings
from armi.reactor.tests.test_reactors import buildOperatorOfEmptyHexBlocks
from armi.reactor.flags import Flags
from armi.nucDirectory import nuclideBases
from armi.physics.neutronics.fissionProductModel.fissionProductModelSettings import (
CONF_FP_MODEL,
CONF_LFP_COMPOSITION_FILE_PATH,
)
LFP_TEXT = """LFP35 GE73 5.9000E-06
LFP35 GE74 1.4000E-05
LFP35 GE76 1.6000E-04
LFP35 AS75 8.9000E-05
LFP35 KR85 8.9000E-05
LFP35 MO99 8.9000E-05
LFP35 SM150 8.9000E-05
LFP35 XE135 8.9000E-05
LFP39 XE135 8.9000E-05
LFP38 XE135 8.9000E-05
"""
[docs]def getDummyLFPFile():
return lumpedFissionProduct.FissionProductDefinitionFile(io.StringIO(LFP_TEXT))
[docs]class TestFissionProductDefinitionFile(unittest.TestCase):
"""Test of the fission product model."""
def setUp(self):
self.fpd = getDummyLFPFile()
[docs] def test_createLFPs(self):
"""Test of the fission product model creation."""
lfps = self.fpd.createLFPsFromFile()
xe135 = nuclideBases.fromName("XE135")
self.assertEqual(len(lfps), 3)
self.assertIn("LFP35", lfps)
for lfp in lfps.values():
self.assertIn(xe135, lfp)
[docs] def test_createReferenceLFPs(self):
"""Test of the reference fission product model creation."""
with open(REFERENCE_LUMPED_FISSION_PRODUCT_FILE, "r") as LFP_FILE:
LFP_TEXT = LFP_FILE.read()
fpd = lumpedFissionProduct.FissionProductDefinitionFile(io.StringIO(LFP_TEXT))
fpd.fName = REFERENCE_LUMPED_FISSION_PRODUCT_FILE
lfps = fpd.createLFPsFromFile()
self.assertEqual(len(lfps), 5)
LFP_IDS = [
"LFP35",
"LFP38",
"LFP39",
"LFP40",
"LFP41",
]
for lfp_id in LFP_IDS:
self.assertIn(lfp_id, lfps)
mo99 = nuclideBases.fromName("MO99")
ref_mo99_yields = [0.00091, 0.00112, 0.00099, 0.00108, 0.00101]
for ref_fp_yield, lfp_id in zip(ref_mo99_yields, LFP_IDS):
lfp = lfps[lfp_id]
self.assertIn(mo99, lfp)
error = math.fabs(ref_fp_yield - lfp[mo99]) / ref_fp_yield
self.assertLess(error, 1e-6)
[docs]class TestLumpedFissionProduct(unittest.TestCase):
"""Test of the lumped fission product yields."""
def setUp(self):
self.fpd = lumpedFissionProduct.FissionProductDefinitionFile(
io.StringIO(LFP_TEXT)
)
[docs] def test_getYield(self):
"""Test of the yield of a fission product."""
xe135 = nuclideBases.fromName("XE135")
lfp = self.fpd.createSingleLFPFromFile("LFP39")
lfp[xe135] = 1.2
val3 = lfp[xe135]
self.assertEqual(val3, 1.2)
self.assertEqual(lfp[5], 0.0)
[docs] def test_gaseousYieldFraction(self):
lfp = self.fpd.createSingleLFPFromFile("LFP39")
# This is equal to the Xe yield set in the dummy ``LFP_TEXT``
# data for these tests.
self.assertEqual(lfp.getGaseousYieldFraction(), 8.9000e-05)
[docs] def test_isGas(self):
"""Tests that a nuclide is a gas or not at STP based on its chemical phase."""
nb = nuclideBases.byName["H1"]
self.assertTrue(lumpedFissionProduct.isGas(nb))
nb = nuclideBases.byName["H2"]
self.assertTrue(lumpedFissionProduct.isGas(nb))
nb = nuclideBases.byName["H3"]
self.assertTrue(lumpedFissionProduct.isGas(nb))
nb = nuclideBases.byName["U235"]
self.assertFalse(lumpedFissionProduct.isGas(nb))
nb = nuclideBases.byName["O16"]
self.assertTrue(lumpedFissionProduct.isGas(nb))
nb = nuclideBases.byName["XE135"]
self.assertTrue(lumpedFissionProduct.isGas(nb))
[docs]class TestLumpedFissionProductCollection(unittest.TestCase):
"""Test of the fission product collection."""
def setUp(self):
fpd = lumpedFissionProduct.FissionProductDefinitionFile(io.StringIO(LFP_TEXT))
self.lfps = fpd.createLFPsFromFile()
[docs] def test_getAllFissionProductNames(self):
"""Test to ensure the fission product names are present."""
names = self.lfps.getAllFissionProductNames()
self.assertIn("XE135", names)
self.assertIn("KR85", names)
[docs] def test_getAllFissionProductNuclideBases(self):
"""Test to ensure the fission product nuclide bases are present."""
clideBases = self.lfps.getAllFissionProductNuclideBases()
xe135 = nuclideBases.fromName("XE135")
kr85 = nuclideBases.fromName("KR85")
self.assertIn(xe135, clideBases)
self.assertIn(kr85, clideBases)
[docs] def test_duplicate(self):
"""Test to ensure that when we duplicate, we don't adjust the original file."""
newLfps = self.lfps.duplicate()
ba = nuclideBases.fromName("XE135")
lfp1 = self.lfps["LFP39"]
lfp2 = newLfps["LFP39"]
v1 = lfp1[ba]
lfp1[ba] += 1.3 # make sure copy doesn't change w/ first.
v2 = lfp2[ba]
self.assertEqual(v1, v2)
[docs] def test_getNumberDensities(self):
o = buildOperatorOfEmptyHexBlocks()
assems = o.r.core.getAssemblies(Flags.FUEL)
blocks = assems[0].getBlocks(Flags.FUEL)
b = blocks[0]
fpDensities = self.lfps.getNumberDensities(objectWithParentDensities=b)
for fp in ["GE73", "GE74", "GE76", "AS75", "KR85", "MO99", "SM150", "XE135"]:
self.assertEqual(fpDensities[fp], 0.0)
# basic test reactor has no fission products in it
[docs] def test_getMassFrac(self):
with self.assertRaises(ValueError):
self.lfps.getMassFrac(oldMassFrac=None)
oldMassFrac = {
"LFP35": 0.5,
"LFP38": 0.2,
"LFP39": 0.3,
}
newMassFracs = self.lfps.getMassFrac(oldMassFrac)
refMassFrac = {
"GE73": 0.0034703064077030933,
"GE74": 0.00834728937688672,
"GE76": 0.09797894499881823,
"AS75": 0.053783069618403435,
"KR85": 0.0609551394006646,
"MO99": 0.07100169460812283,
"SM150": 0.1076193196365748,
"XE135": 0.5968442359528263,
}
for fp, newMassFrac in newMassFracs.items():
self.assertAlmostEqual(newMassFrac, refMassFrac[fp.name])
[docs]class TestLumpedFissionProductsFromReferenceFile(unittest.TestCase):
"""Tests loading from the `referenceFissionProducts.dat` file."""
[docs] def test_fissionProductYields(self):
"""Test that the fission product yields for the lumped fission products sums to 2.0."""
cs = Settings()
cs[CONF_FP_MODEL] = "infinitelyDilute"
cs[CONF_LFP_COMPOSITION_FILE_PATH] = os.path.join(
RES, "referenceFissionProducts.dat"
)
self.lfps = lumpedFissionProduct.lumpedFissionProductFactory(cs)
for lfp in self.lfps.values():
self.assertAlmostEqual(lfp.getTotalYield(), 2.0, places=3)
[docs]class TestLumpedFissionProductsExplicit(unittest.TestCase):
"""Tests loading fission products with explicit modeling."""
[docs] def test_explicitFissionProducts(self):
"""Tests that there are no lumped fission products added when the `explicitFissionProducts` model is enabled."""
cs = Settings()
cs[CONF_FP_MODEL] = "explicitFissionProducts"
self.lfps = lumpedFissionProduct.lumpedFissionProductFactory(cs)
self.assertIsNone(self.lfps)
[docs]class TestMo99LFP(unittest.TestCase):
"""Test of the fission product model from Mo99."""
def setUp(self):
self.lfps = lumpedFissionProduct._buildMo99LumpedFissionProduct()
[docs] def test_getAllFissionProductNames(self):
"""Test to ensure that Mo99 is present, but other FP are not."""
names = self.lfps.getAllFissionProductNames()
self.assertIn("MO99", names)
self.assertNotIn("KR85", names)
self.assertAlmostEqual(self.lfps["LFP35"].getTotalYield(), 2.0)