Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jakehader committed Feb 15, 2022
1 parent 12af1d8 commit 67df5a0
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 170 deletions.
Expand Up @@ -204,11 +204,9 @@ def _getAllNuclidesByCategory(self, component=None):
"""
dfpDensities = self._getDetailedFPDensities()
(
coolantNuclides,
fuelNuclides,
structureNuclides,
) = self.r.core.getNuclideCategories()
coolantNuclides = self.r.core.nuclideCategories["coolant"]
fuelNuclides = self.r.core.nuclideCategories["fuel"]
structureNuclides = self.r.core.nuclideCategories["structure"]
nucDensities = {}
subjectObject = component or self.block
depletableNuclides = nuclideBases.getDepletableNuclides(
Expand Down
286 changes: 176 additions & 110 deletions armi/reactor/converters/geometryConverters.py

Large diffs are not rendered by default.

33 changes: 13 additions & 20 deletions armi/reactor/converters/tests/test_geometryConverters.py
Expand Up @@ -146,7 +146,7 @@ def tearDown(self):
del self.cs
del self.r

def testConvert(self):
def test_convert(self):
converterSettings = {
"radialConversionType": "Ring Compositions",
"axialConversionType": "Axial Coordinates",
Expand All @@ -168,7 +168,7 @@ def testConvert(self):
self._checkNuclideMasses(expectedMassDict, newR)
self._checkBlockAtMeshPoint(geomConv)
self._checkReactorMeshCoordinates(geomConv)
figs = geomConv.plotConvertedReactor()
_figs = geomConv.plotConvertedReactor()
with directoryChangers.TemporaryDirectoryChanger():
geomConv.plotConvertedReactor("fname")

Expand Down Expand Up @@ -209,13 +209,11 @@ def _getExpectedData(self):
return expectedMassDict, expectedNuclideList

def _checkBlockComponents(self, newR):
expectedNumComponents = len(newR.core.nuclideCategories.keys())
for b in newR.core.getBlocks():
if len(b) != 1:
raise ValueError(
"Block {} has {} components and should only have 1".format(
b, len(b)
)
)
if len(b) > expectedNumComponents:
raise ValueError(f"Block {b} has {len(b)} components and "
f"should have {expectedNumComponents}.")

def _checkNuclidesMatch(self, expectedNuclideList, newR):
"""Check that the nuclide lists match before and after conversion"""
Expand Down Expand Up @@ -253,26 +251,21 @@ def _checkNuclideMasses(self, expectedMassDict, newR):
def _checkNuclideCategoriesAreSame(self, newR):
"""Check that the nuclide categories between the original core and the converted core are identical."""
self.assertDictEqual(
self.r.core._nuclideCategories, newR.core._nuclideCategories
self.r.core.nuclideCategories, newR.core.nuclideCategories
)

def test_createHomogenizedRZTBlock(self):
newBlock = blocks.ThRZBlock("testBlock", self.cs)
"""Test the creation of a new homogenized RZT block."""
a = self.r.core[0]
converterSettings = {}
geomConv = geometryConverters.HexToRZConverter(
self.cs, converterSettings, expandReactor=self._expandReactor
)
volumeExpected = a.getVolume()
(
_atoms,
_newBlockType,
_newBlockTemp,
newBlockVol,
) = geomConv.createHomogenizedRZTBlock(newBlock, 0, a.getHeight(), [a])

# The volume of the radialZone and the radialThetaZone should be equal for RZ geometry
self.assertAlmostEqual(volumeExpected, newBlockVol)
newBlock, homBlockVolume = geomConv.createHomogenizedRZTBlock("mixture fuel", 0, a.getHeight(), [a])

self.assertEqual(a.getVolume(), homBlockVolume)
self.assertEqual(newBlock.p.type, "FUEL")
self.assertEqual(newBlock.p.flags, Flags.FUEL)


class TestEdgeAssemblyChanger(unittest.TestCase):
Expand Down
14 changes: 12 additions & 2 deletions armi/reactor/flags.py
Expand Up @@ -279,7 +279,18 @@ class Flags(Flag):

# Allows movement of lower plenum with control rod
MOVEABLE = auto()


def __lt__(self, other):
"""
Allows the ``Flags`` to be sorted.
Notes
-----
Sorting can be required to have deterministic behavior
when looping over a set/list of ``Flags``.
"""
return self._value < other._value

@classmethod
def fromStringIgnoreErrors(cls, typeSpec):
return _fromStringIgnoreErrors(cls, typeSpec)
Expand All @@ -292,7 +303,6 @@ def fromString(cls, typeSpec):
def toString(cls, typeSpec):
return _toString(cls, typeSpec)


class InvalidFlagsError(KeyError):
"""Raised when code attempts to look for an undefined flag."""

Expand Down
70 changes: 40 additions & 30 deletions armi/reactor/reactors.py
Expand Up @@ -340,6 +340,23 @@ def refAssem(self):

return assems[0]

@property
def nuclideCategories(self):
if not self._nuclideCategories:
self._getNuclideCategories()

expectedNuclideCategories = ["coolant", "fuel", "structure"]
assert sorted(self._nuclideCategories.keys()) == expectedNuclideCategories, (
f"Nuclide categories in {self} do not match the expected "
f"values of {expectedNuclideCategories}. "
f"Current values: {self._nuclideCategories.keys()}"
)
return self._nuclideCategories

@nuclideCategories.setter
def nuclideCategories(self, value):
self._nuclideCategories = value

def summarizeReactorStats(self):
"""Writes a summary of the reactor to check the mass and volume of all of the blocks."""
totalMass = 0.0
Expand Down Expand Up @@ -1238,7 +1255,7 @@ def getAllXsSuffixes(self):
"""Return all XS suffices (e.g. AA, AB, etc.) in the core."""
return sorted(set(b.getMicroSuffix() for b in self.getBlocks()))

def getNuclideCategories(self):
def _getNuclideCategories(self):
"""
Categorize nuclides as coolant, fuel and structure.
Expand Down Expand Up @@ -1267,35 +1284,28 @@ def getNuclideCategories(self):
set of nuclide names
"""
if not self._nuclideCategories:
coolantNuclides = set()
fuelNuclides = set()
structureNuclides = set()
for c in self.iterComponents():
if c.getName() == "coolant":
coolantNuclides.update(c.getNuclides())
elif "fuel" in c.getName():
fuelNuclides.update(c.getNuclides())
else:
structureNuclides.update(c.getNuclides())
structureNuclides -= coolantNuclides
structureNuclides -= fuelNuclides
remainingNuclides = (
set(self.parent.blueprints.allNuclidesInProblem)
- structureNuclides
- coolantNuclides
)
fuelNuclides.update(remainingNuclides)
self._nuclideCategories["coolant"] = coolantNuclides
self._nuclideCategories["fuel"] = fuelNuclides
self._nuclideCategories["structure"] = structureNuclides
self.summarizeNuclideCategories()

return (
self._nuclideCategories["coolant"],
self._nuclideCategories["fuel"],
self._nuclideCategories["structure"],
coolantNuclides = set()
fuelNuclides = set()
structureNuclides = set()
for c in self.iterComponents():
if c.getName() == "coolant":
coolantNuclides.update(c.getNuclides())
elif "fuel" in c.getName():
fuelNuclides.update(c.getNuclides())
else:
structureNuclides.update(c.getNuclides())
structureNuclides -= coolantNuclides
structureNuclides -= fuelNuclides
remainingNuclides = (
set(self.parent.blueprints.allNuclidesInProblem)
- structureNuclides
- coolantNuclides
)
fuelNuclides.update(remainingNuclides)
self._nuclideCategories["coolant"] = sorted(coolantNuclides)
self._nuclideCategories["fuel"] = sorted(fuelNuclides)
self._nuclideCategories["structure"] = sorted(structureNuclides)
self.summarizeNuclideCategories()

def summarizeNuclideCategories(self):
"""Write summary table of the various nuclide categories within the reactor."""
Expand Down Expand Up @@ -2284,7 +2294,7 @@ def processLoading(self, cs):

self.numRings = self.getNumRings() # TODO: why needed?

self.getNuclideCategories()
self.nuclideCategories

# some blocks will not move in the core like grid plates... Find them and fix them in place
stationaryBlocks = []
Expand Down
29 changes: 29 additions & 0 deletions armi/reactor/tests/test_flags.py
Expand Up @@ -104,6 +104,35 @@ def test_isPickleable(self):
stream = pickle.dumps(flags.Flags.BOND | flags.Flags.A)
flag = pickle.loads(stream)
self.assertEqual(flag, flags.Flags.BOND | flags.Flags.A)

def test_areHashable(self):
"""
Test that the implemented ``Flags`` can be hashed.
This is important for inserting flags into the keys of a dictionary.
"""
self.assertTrue(hash(flags.Flags.FUEL))

def test_areSortable(self):
"""Test that flags can be ordered."""
self.assertEqual(flags.Flags.FUEL, flags.Flags.FUEL)
#self.assertGreater(flags.Flags.CONTROL, flags.Flags.FUEL)
#self.assertGreaterEqual(flags.Flags.CONTROL, flags.Flags.FUEL)
#self.assertLessEqual(flags.Flags.FUEL, flags.Flags.CONTROL)
#self.assertLess(flags.Flags.FUEL, flags.Flags.CONTROL)
self.assertNotEqual(flags.Flags.FUEL, flags.Flags.CONTROL)

strings = ["control", "axial shield", "radial shield", "fuel", "structure",
"plenum", "duct"]
sortedStrings = ["axial shield", "control", "duct", "fuel", "plenum", "radial shield",
"structure"]
self.assertEqual(sorted(strings), sortedStrings)

print(sorted([flags.Flags.fromString(x) for x in strings]))
print([flags.Flags.fromString(x) for x in sortedStrings])
self.assertEqual(sorted([flags.Flags.fromString(x) for x in strings]),
[flags.Flags.fromString(x) for x in sortedStrings])



if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions armi/reactor/tests/test_reactors.py
Expand Up @@ -759,7 +759,7 @@ def test_getAssembliesInSquareRing(self, exclusions=[2]):
self.assertSequenceEqual(actualAssemsInRing, expectedAssemsInRing)

def test_getNuclideCategoriesLogging(self):
"""Simplest possible test of the getNuclideCategories method and its logging"""
"""Simplest possible test of the setting the ``nuclideCategories`` on the core and its logging"""
log = mockRunLogs.BufferLog()

# this strange namespace-stomping is used to the test to set the logger in reactors.Core
Expand All @@ -769,7 +769,7 @@ def test_getNuclideCategoriesLogging(self):
runLog.LOG = log

# run the actual method in question
self.r.core.getNuclideCategories()
self.r.core.nuclideCategories
messages = log.getStdoutValue()

self.assertIn("Nuclide categorization", messages)
Expand Down
2 changes: 1 addition & 1 deletion armi/utils/tests/test_flags.py
Expand Up @@ -130,4 +130,4 @@ def test_hashable(self):
self.assertNotEqual(hash(f1), hash(f2))

def test_getitem(self):
self.assertEqual(ExampleFlag["FOO"], ExampleFlag.FOO)
self.assertEqual(ExampleFlag["FOO"], ExampleFlag.FOO)

0 comments on commit 67df5a0

Please sign in to comment.