# 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.
"""Testing for reactors.py."""
import copy
import logging
import os
import unittest
from math import sqrt
from unittest.mock import patch
from numpy.testing import assert_allclose, assert_equal
from six.moves import cPickle
from armi import operators
from armi import runLog
from armi import settings
from armi import tests
from armi.materials import uZr
from armi.physics.neutronics.settings import CONF_XS_KERNEL
from armi.reactor import assemblies
from armi.reactor import blocks
from armi.reactor import geometry
from armi.reactor import grids
from armi.reactor import reactors
from armi.reactor.assemblyLists import SpentFuelPool
from armi.reactor.components import Hexagon, Rectangle
from armi.reactor.composites import Composite
from armi.reactor.converters import geometryConverters
from armi.reactor.converters.axialExpansionChanger import AxialExpansionChanger
from armi.reactor.flags import Flags
from armi.settings.fwSettings.globalSettings import CONF_ASSEM_FLAGS_SKIP_AXIAL_EXP
from armi.settings.fwSettings.globalSettings import CONF_SORT_REACTOR
from armi.tests import ARMI_RUN_PATH, mockRunLogs, TEST_ROOT
from armi.utils import directoryChangers
THIS_DIR = os.path.dirname(__file__)
TEST_REACTOR = None # pickled string of test reactor (for fast caching)
[docs]def buildOperatorOfEmptyHexBlocks(customSettings=None):
"""
Builds a operator w/ a reactor object with some hex assemblies and blocks, but all are empty.
Doesn't depend on inputs and loads quickly.
Parameters
----------
customSettings : dict
Dictionary of off-default settings to update
"""
cs = settings.Settings() # fetch new
if customSettings is None:
customSettings = {}
customSettings["db"] = False # stop use of database
cs = cs.modified(newSettings=customSettings)
r = tests.getEmptyHexReactor()
r.core.setOptionsFromCs(cs)
o = operators.Operator(cs)
o.initializeInterfaces(r)
a = assemblies.HexAssembly("fuel")
a.spatialGrid = grids.axialUnitGrid(1)
b = blocks.HexBlock("TestBlock")
b.setType("fuel")
dims = {"Tinput": 600, "Thot": 600, "op": 16.0, "ip": 1, "mult": 1}
c = Hexagon("fuel", uZr.UZr(), **dims)
b.add(c)
a.add(b)
a.spatialLocator = r.core.spatialGrid[1, 0, 0]
o.r.core.add(a)
o.r.sort()
return o
[docs]def buildOperatorOfEmptyCartesianBlocks(customSettings=None):
"""
Builds a operator w/ a reactor object with some Cartesian assemblies and blocks, but all are empty.
Doesn't depend on inputs and loads quickly.
Parameters
----------
customSettings : dict
Off-default settings to update
"""
cs = settings.Settings() # fetch new
if customSettings is None:
customSettings = {}
customSettings["db"] = False # stop use of database
cs = cs.modified(newSettings=customSettings)
r = tests.getEmptyCartesianReactor()
r.core.setOptionsFromCs(cs)
o = operators.Operator(cs)
o.initializeInterfaces(r)
a = assemblies.CartesianAssembly("fuel")
a.spatialGrid = grids.axialUnitGrid(1)
b = blocks.CartesianBlock("TestBlock")
b.setType("fuel")
dims = {
"Tinput": 600,
"Thot": 600,
"widthOuter": 16.0,
"lengthOuter": 10.0,
"widthInner": 1,
"lengthInner": 1,
"mult": 1,
}
c = Rectangle("fuel", uZr.UZr(), **dims)
b.add(c)
a.add(b)
a.spatialLocator = r.core.spatialGrid[1, 0, 0]
o.r.core.add(a)
o.r.sort()
return o
"""
NOTE: If this reactor had 3 rings instead of 9, most unit tests that use it
go 4 times faster (based on testing). The problem is it would breat a LOT
of downstream tests that import this method. Probably still worth it though.
"""
[docs]def loadTestReactor(
inputFilePath=TEST_ROOT,
customSettings=None,
inputFileName="armiRun.yaml",
):
"""
Loads a test reactor. Can be used in other test modules.
Parameters
----------
inputFilePath : str, default=TEST_ROOT
Path to the directory of the input file.
customSettings : dict with str keys and values of any type, default=None
For each key in customSettings, the cs which is loaded from the
armiRun.yaml will be overwritten to the value given in customSettings
for that key.
inputFileName : str, default="armiRun.yaml"
Name of the input file to run.
Returns
-------
o : Operator
r : Reactor
"""
global TEST_REACTOR
fName = os.path.join(inputFilePath, inputFileName)
customSettings = customSettings or {}
isPickeledReactor = fName == ARMI_RUN_PATH and customSettings == {}
if isPickeledReactor and TEST_REACTOR:
# return test reactor only if no custom settings are needed.
o, r, assemNum = cPickle.loads(TEST_REACTOR)
o.reattach(r, o.cs)
return o, r
cs = settings.Settings(fName=fName)
# Overwrite settings if desired
if customSettings:
cs = cs.modified(newSettings=customSettings)
if "verbosity" not in customSettings:
runLog.setVerbosity("error")
newSettings = {}
cs = cs.modified(newSettings=newSettings)
o = operators.factory(cs)
r = reactors.loadFromCs(cs)
o.initializeInterfaces(r)
o.r.core.regenAssemblyLists()
if isPickeledReactor:
# cache it for fast load for other future tests
# protocol=2 allows for classes with __slots__ but not __getstate__ to be pickled
TEST_REACTOR = cPickle.dumps((o, o.r, o.r.p.maxAssemNum), protocol=2)
return o, o.r
[docs]def reduceTestReactorRings(r, cs, maxNumRings):
"""Helper method for the test reactor above.
The goal is to reduce the size of the reactor for tests that don't neeed
such a large reactor, and would run much faster with a smaller one.
"""
maxRings = r.core.getNumRings()
if maxNumRings > maxRings:
runLog.info("The test reactor has a maximum of {} rings.".format(maxRings))
return
elif maxNumRings <= 1:
raise ValueError("The test reactor must have multiple rings.")
# reducing the size of the test reactor, by removing the outer rings
for ring in range(maxRings, maxNumRings, -1):
r.core.removeAssembliesInRing(ring, cs)
[docs]class ReactorTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
# prepare the input files. This is important so the unit tests run from wherever
# they need to run from.
cls.directoryChanger = directoryChangers.DirectoryChanger(TEST_ROOT)
cls.directoryChanger.open()
@classmethod
def tearDownClass(cls):
cls.directoryChanger.close()
[docs]class HexReactorTests(ReactorTests):
def setUp(self):
self.o, self.r = loadTestReactor(
self.directoryChanger.destination, customSettings={"trackAssems": True}
)
[docs] def test_coreSfp(self):
"""The reactor object includes a core and an SFP.
.. test:: The reactor object is a composite.
:id: T_ARMI_R
:tests: R_ARMI_R
"""
self.assertTrue(isinstance(self.r.core, reactors.Core))
self.assertTrue(isinstance(self.r.sfp, SpentFuelPool))
self.assertTrue(isinstance(self.r, Composite))
self.assertTrue(isinstance(self.r.core, Composite))
self.assertTrue(isinstance(self.r.sfp, Composite))
[docs] def test_factorySortSetting(self):
"""
Create a core object from an input yaml.
.. test:: Create core object from input yaml.
:id: T_ARMI_R_CORE
:tests: R_ARMI_R_CORE
"""
# get a sorted Reactor (the default)
cs = settings.Settings(fName="armiRun.yaml")
r0 = reactors.loadFromCs(cs)
# get an unsorted Reactor (for whatever reason)
customSettings = {CONF_SORT_REACTOR: False}
cs = cs.modified(newSettings=customSettings)
r1 = reactors.loadFromCs(cs)
# the reactor / core should be the same size
self.assertEqual(len(r0), len(r1))
self.assertEqual(len(r0.core), len(r1.core))
# the reactor / core should be in a different order
a0 = [a.name for a in r0.core]
a1 = [a.name for a in r1.core]
self.assertNotEqual(a0, a1)
# The reactor object is a Composite
self.assertTrue(isinstance(r0.core, Composite))
[docs] def test_getSetParameters(self):
"""
This test works through multiple levels of the hierarchy to test ability to
modify parameters at different levels.
.. test:: Parameters are accessible throughout the armi tree.
:id: T_ARMI_PARAM_PART
:tests: R_ARMI_PARAM_PART
.. test:: Ensure there is a setting for total core power.
:id: T_ARMI_SETTINGS_POWER0
:tests: R_ARMI_SETTINGS_POWER
"""
# Test at reactor level
self.assertEqual(self.r.p.cycle, 0)
self.assertEqual(self.r.p.availabilityFactor, 1.0)
# Test at core level
core = self.r.core
self.assertGreater(core.p.power, -1)
core.p.power = 123
self.assertEqual(core.p.power, 123)
# Test at assembly level
assembly = core.getFirstAssembly()
self.assertGreater(assembly.p.crRodLength, -1)
assembly.p.crRodLength = 234
self.assertEqual(assembly.p.crRodLength, 234)
# Test at block level
block = core.getFirstBlock()
self.assertGreater(block.p.THTfuelCL, -1)
block.p.THTfuelCL = 57
self.assertEqual(block.p.THTfuelCL, 57)
# Test at component level
component = block[0]
self.assertEqual(component.p.temperatureInC, 450.0)
[docs] def test_sortChildren(self):
self.assertEqual(next(self.r.core.__iter__()), self.r.core[0])
self.assertEqual(self.r.core._children, sorted(self.r.core._children))
[docs] def test_sortAssemByRing(self):
"""Demonstrate ring/pos sorting."""
self.r.core.sortAssemsByRing()
self.assertEqual((1, 1), self.r.core[0].spatialLocator.getRingPos())
currentRing = -1
currentPos = -1
for a in self.r.core:
ring, pos = a.spatialLocator.getRingPos()
self.assertGreaterEqual(ring, currentRing)
if ring > currentRing:
ring = currentRing
currentPos = -1
self.assertGreater(pos, currentPos)
currentPos = pos
[docs] def test_getTotalParam(self):
# verify that the block params are being read.
val = self.r.core.getTotalBlockParam("power")
val2 = self.r.core.getTotalBlockParam("power", addSymmetricPositions=True)
self.assertEqual(val2 / self.r.core.powerMultiplier, val)
with self.assertRaises(ValueError):
self.r.core.getTotalBlockParam(generationNum=1)
[docs] def test_geomType(self):
self.assertEqual(self.r.core.geomType, geometry.GeomType.HEX)
[docs] def test_growToFullCore(self):
nAssemThird = len(self.r.core)
self.assertEqual(self.r.core.powerMultiplier, 3.0)
self.assertFalse(self.r.core.isFullCore)
self.r.core.growToFullCore(self.o.cs)
aNums = []
for a in self.r.core.getChildren():
self.assertNotIn(a.getNum(), aNums)
aNums.append(a.getNum())
bNames = [b.getName() for b in self.r.core.getBlocks()]
for bName in bNames:
self.assertEqual(bNames.count(bName), 1)
self.assertEqual(self.r.core.powerMultiplier, 1.0)
self.assertTrue(self.r.core.isFullCore)
nAssemFull = len(self.r.core)
self.assertEqual(nAssemFull, (nAssemThird - 1) * 3 + 1)
[docs] def test_getBlocksByIndices(self):
indices = [(1, 1, 1), (3, 2, 2)]
actualBlocks = self.r.core.getBlocksByIndices(indices)
actualNames = [b.getName() for b in actualBlocks]
expectedNames = ["B0014-001", "B0035-002"]
self.assertListEqual(expectedNames, actualNames)
[docs] def test_getAllXsSuffixes(self):
actualSuffixes = self.r.core.getAllXsSuffixes()
expectedSuffixes = ["AA"]
self.assertListEqual(expectedSuffixes, actualSuffixes)
[docs] def test_countBlocksOfType(self):
numControlBlocks = self.r.core.countBlocksWithFlags([Flags.DUCT, Flags.CONTROL])
self.assertEqual(numControlBlocks, 3)
numControlBlocks = self.r.core.countBlocksWithFlags(
[Flags.DUCT, Flags.CONTROL, Flags.FUEL], Flags.CONTROL
)
self.assertEqual(numControlBlocks, 3)
[docs] def test_normalizeNames(self):
# these are the correct, normalized names
numAssems = 73
a = self.r.core.getFirstAssembly()
correctNames = [a.makeNameFromAssemNum(n) for n in range(numAssems)]
# validate the reactor is what we think now
self.assertEqual(len(self.r.core), numAssems)
currentNames = [a.getName() for a in self.r.core]
self.assertNotEqual(correctNames, currentNames)
# validate that we can normalize the names correctly once
self.r.normalizeNames()
currentNames = [a.getName() for a in self.r.core]
self.assertEqual(correctNames, currentNames)
# validate that repeated applications of this method are stable
for _ in range(3):
self.r.normalizeNames()
currentNames = [a.getName() for a in self.r.core]
self.assertEqual(correctNames, currentNames)
[docs] def test_setB10VolOnCreation(self):
"""Test the setting of b.p.initialB10ComponentVol."""
for controlBlock in self.r.core.getBlocks(Flags.CONTROL):
controlComps = [c for c in controlBlock if c.getNumberDensity("B10") > 0]
self.assertEqual(len(controlComps), 1)
controlComp = controlComps[0]
startingVol = controlBlock.p.initialB10ComponentVol
self.assertGreater(startingVol, 0)
self.assertAlmostEqual(
controlComp.getArea(cold=True) * controlBlock.getHeight(), startingVol
)
# input temp is same as hot temp, so change input temp to test that behavior
controlComp.inputTemperatureInC = 30
# somewhat non-sensical since its hot, not cold but we just want to check the ratio
controlBlock.setB10VolParam(True)
self.assertGreater(startingVol, controlBlock.p.initialB10ComponentVol)
self.assertAlmostEqual(
startingVol / controlComp.getThermalExpansionFactor(),
controlBlock.p.initialB10ComponentVol,
)
[docs] def test_countFuelAxialBlocks(self):
"""Tests that the users definition of fuel blocks is preserved."""
numFuelBlocks = self.r.core.countFuelAxialBlocks()
self.assertEqual(numFuelBlocks, 3)
[docs] def test_getFirstFuelBlockAxialNode(self):
firstFuelBlock = self.r.core.getFirstFuelBlockAxialNode()
self.assertEqual(firstFuelBlock, 1)
[docs] def test_getMaxAssembliesInHexRing(self):
maxAssems = self.r.core.getMaxAssembliesInHexRing(3)
self.assertEqual(maxAssems, 4)
[docs] def test_getMaxNumPins(self):
numPins = self.r.core.getMaxNumPins()
self.assertEqual(169, numPins)
[docs] def test_addMultipleCores(self):
"""Test the catch that a reactor can only have one core."""
with self.assertRaises(RuntimeError):
self.r.add(self.r.core)
[docs] def test_getReactor(self):
"""The Core object can return its Reactor parent; test that getter."""
self.assertTrue(isinstance(self.r.core.r, reactors.Reactor))
self.r.core.parent = None
self.assertIsNone(self.r.core.r)
[docs] def test_addMoreNodes(self):
originalMesh = self.r.core.p.axialMesh
bigMesh = list(originalMesh)
bigMesh[2] = 30.0
smallMesh = originalMesh[0:2] + [40.0, 47.0] + originalMesh[2:]
newMesh1, originalMeshGood = self.r.core.addMoreNodes(originalMesh)
newMesh2, bigMeshGood = self.r.core.addMoreNodes(bigMesh)
newMesh3, smallMeshGood = self.r.core.addMoreNodes(smallMesh)
expectedMesh = [0.0, 25.0, 50.0, 75.0, 100.0, 118.75, 137.5, 156.25, 175.0]
expectedBigMesh = [
0.0,
25.0,
30.0,
36.75,
75.0,
100.0,
118.75,
137.5,
156.25,
175.0,
]
expectedSmallMesh = [
0.0,
25.0,
40.0,
47.0,
50.0,
53.75,
75.0,
100.0,
118.75,
137.5,
156.25,
175.0,
]
self.assertListEqual(expectedMesh, newMesh1)
self.assertListEqual(expectedBigMesh, newMesh2)
self.assertListEqual(expectedSmallMesh, newMesh3)
self.assertTrue(originalMeshGood)
self.assertFalse(bigMeshGood)
self.assertFalse(smallMeshGood)
[docs] def test_findAxialMeshIndexOf(self):
numMeshPoints = (
len(self.r.core.p.axialMesh) - 2
) # -1 for typical reason, -1 more because mesh includes 0
self.assertEqual(self.r.core.findAxialMeshIndexOf(0.0), 0)
self.assertEqual(self.r.core.findAxialMeshIndexOf(0.1), 0)
self.assertEqual(
self.r.core.findAxialMeshIndexOf(self.r.core[0].getHeight()), numMeshPoints
)
self.assertEqual(
self.r.core.findAxialMeshIndexOf(self.r.core[0].getHeight() - 0.1),
numMeshPoints,
)
self.assertEqual(
self.r.core.findAxialMeshIndexOf(self.r.core[0][0].getHeight() + 0.1), 1
)
[docs] def test_findAllAxialMeshPoints(self):
mesh = self.r.core.findAllAxialMeshPoints(applySubMesh=False)
self.assertEqual(mesh[0], 0)
self.assertAlmostEqual(mesh[-1], self.r.core[0].getHeight())
blockMesh = self.r.core.getFirstAssembly(Flags.FUEL).spatialGrid._bounds[2]
assert_allclose(blockMesh, mesh)
[docs] def test_findAllAxialMeshPoints_wSubmesh(self):
referenceMesh = [0.0, 25.0, 50.0, 75.0, 100.0, 118.75, 137.5, 156.25, 175.0]
mesh = self.r.core.findAllAxialMeshPoints(
assems=[self.r.core.getFirstAssembly(Flags.FUEL)], applySubMesh=True
)
self.assertListEqual(referenceMesh, mesh)
[docs] def test_findAllAziMeshPoints(self):
aziPoints = self.r.core.findAllAziMeshPoints()
expectedPoints = [
-50.7707392969,
-36.2648137835,
-21.7588882701,
-7.2529627567,
7.2529627567,
21.7588882701,
36.2648137835,
50.7707392969,
65.2766648103,
79.7825903236,
94.288515837,
108.7944413504,
123.3003668638,
]
assert_allclose(expectedPoints, aziPoints)
[docs] def test_findAllRadMeshPoints(self):
radPoints = self.r.core.findAllRadMeshPoints()
expectedPoints = [
-12.5625,
-4.1875,
4.1875,
12.5625,
20.9375,
29.3125,
37.6875,
46.0625,
54.4375,
62.8125,
71.1875,
79.5625,
87.9375,
96.3125,
104.6875,
113.0625,
121.4375,
129.8125,
138.1875,
146.5625,
]
assert_allclose(expectedPoints, radPoints)
[docs] def test_findNeighbors(self):
"""
Find neighbors of a given assembly.
.. test:: Retrieve neighboring assemblies of a given assembly.
:id: T_ARMI_R_FIND_NEIGHBORS
:tests: R_ARMI_R_FIND_NEIGHBORS
"""
loc = self.r.core.spatialGrid.getLocatorFromRingAndPos(1, 1)
a = self.r.core.childrenByLocator[loc]
neighbs = self.r.core.findNeighbors(
a, duplicateAssembliesOnReflectiveBoundary=True
)
locs = [a.spatialLocator.getRingPos() for a in neighbs]
self.assertEqual(len(neighbs), 6)
self.assertIn((2, 1), locs)
self.assertIn((2, 2), locs)
self.assertEqual(locs.count((2, 1)), 3)
loc = self.r.core.spatialGrid.getLocatorFromRingAndPos(1, 1)
a = self.r.core.childrenByLocator[loc]
neighbs = self.r.core.findNeighbors(
a, duplicateAssembliesOnReflectiveBoundary=True
)
locs = [a.spatialLocator.getRingPos() for a in neighbs]
self.assertEqual(locs, [(2, 1), (2, 2)] * 3, 6)
loc = self.r.core.spatialGrid.getLocatorFromRingAndPos(2, 2)
a = self.r.core.childrenByLocator[loc]
neighbs = self.r.core.findNeighbors(
a, duplicateAssembliesOnReflectiveBoundary=True
)
locs = [a.spatialLocator.getRingPos() for a in neighbs]
self.assertEqual(len(neighbs), 6)
self.assertEqual(locs, [(3, 2), (3, 3), (3, 12), (2, 1), (1, 1), (2, 1)])
# try with edge assemblies
# With edges, the neighbor is the one that's actually next to it.
converter = geometryConverters.EdgeAssemblyChanger()
converter.addEdgeAssemblies(self.r.core)
loc = self.r.core.spatialGrid.getLocatorFromRingAndPos(2, 2)
a = self.r.core.childrenByLocator[loc]
neighbs = self.r.core.findNeighbors(
a, duplicateAssembliesOnReflectiveBoundary=True
)
locs = [a.spatialLocator.getRingPos() for a in neighbs]
self.assertEqual(len(neighbs), 6)
# in this case no locations that aren't actually in the core should be returned
self.assertEqual(locs, [(3, 2), (3, 3), (3, 4), (2, 1), (1, 1), (2, 1)])
converter.removeEdgeAssemblies(self.r.core)
# try with full core
self.r.core.growToFullCore(self.o.cs)
loc = self.r.core.spatialGrid.getLocatorFromRingAndPos(3, 4)
a = self.r.core.childrenByLocator[loc]
neighbs = self.r.core.findNeighbors(a)
self.assertEqual(len(neighbs), 6)
locs = [a.spatialLocator.getRingPos() for a in neighbs]
for loc in [(2, 2), (2, 3), (3, 3), (3, 5), (4, 5), (4, 6)]:
self.assertIn(loc, locs)
loc = self.r.core.spatialGrid.getLocatorFromRingAndPos(2, 2)
a = self.r.core.childrenByLocator[loc]
neighbs = self.r.core.findNeighbors(a)
locs = [a.spatialLocator.getRingPos() for a in neighbs]
for loc in [(1, 1), (2, 1), (2, 3), (3, 2), (3, 3), (3, 4)]:
self.assertIn(loc, locs)
# Try the duplicate option in full core as well
loc = self.r.core.spatialGrid.getLocatorFromRingAndPos(2, 2)
a = self.r.core.childrenByLocator[loc]
neighbs = self.r.core.findNeighbors(
a, duplicateAssembliesOnReflectiveBoundary=True
)
locs = [a.spatialLocator.getRingPos() for a in neighbs]
self.assertEqual(len(neighbs), 6)
self.assertEqual(locs, [(3, 2), (3, 3), (3, 4), (2, 3), (1, 1), (2, 1)])
[docs] def test_getAssembliesInCircularRing(self):
expectedAssemsInRing = [5, 6, 8, 10, 12, 16, 14, 2]
actualAssemsInRing = []
for ring in range(1, self.r.core.getNumRings()):
actualAssemsInRing.append(
len(self.r.core.getAssembliesInCircularRing(ring))
)
self.assertSequenceEqual(actualAssemsInRing, expectedAssemsInRing)
[docs] def test_getAssembliesInHexRing(self):
expectedAssemsInRing = [1, 2, 4, 6, 8, 10, 12, 14, 16]
actualAssemsInRing = []
for ring in range(1, self.r.core.getNumRings() + 1):
actualAssemsInRing.append(
len(self.r.core.getAssembliesInSquareOrHexRing(ring))
)
self.assertSequenceEqual(actualAssemsInRing, expectedAssemsInRing)
[docs] def test_genAssembliesAddedThisCycle(self):
allAssems = self.r.core.getAssemblies()
self.assertTrue(
all(
a1 is a2
for a1, a2 in zip(allAssems, self.r.core.genAssembliesAddedThisCycle())
)
)
a = self.r.core.getAssemblies()[0]
newA = copy.deepcopy(a)
newA.name = None
self.r.p.cycle = 1
self.assertEqual(len(list(self.r.core.genAssembliesAddedThisCycle())), 0)
self.r.core.removeAssembly(a)
self.r.core.add(newA)
self.assertEqual(next(self.r.core.genAssembliesAddedThisCycle()), newA)
[docs] def test_getAssemblyPitch(self):
self.assertEqual(self.r.core.getAssemblyPitch(), 16.75)
[docs] def test_getNumAssembliesWithAllRingsFilledOut(self):
nRings = self.r.core.getNumRings(indexBased=True)
nAssmWithBlanks = self.r.core.getNumAssembliesWithAllRingsFilledOut(nRings)
self.assertEqual(77, nAssmWithBlanks)
[docs] @patch("armi.reactor.reactors.Core.powerMultiplier", 1)
def test_getNumAssembliesWithAllRingsFilledOutBipass(self):
nAssems = self.r.core.getNumAssembliesWithAllRingsFilledOut(3)
self.assertEqual(19, nAssems)
[docs] def test_getNumEnergyGroups(self):
# this Core doesn't have a loaded ISOTXS library, so this test is minimally useful
with self.assertRaises(AttributeError):
self.r.core.getNumEnergyGroups()
[docs] def test_getMinimumPercentFluxInFuel(self):
# there is no flux in the test reactor YET, so this test is minimally useful
with self.assertRaises(ZeroDivisionError):
_targetRing, _fluxFraction = self.r.core.getMinimumPercentFluxInFuel()
[docs] def test_getAssemblyWithLoc(self):
"""
Get assembly by location, in a couple different ways to ensure they all work.
.. test:: Get assembly by location.
:id: T_ARMI_R_GET_ASSEM_LOC
:tests: R_ARMI_R_GET_ASSEM_LOC
"""
a0 = self.r.core.getAssemblyWithStringLocation("003-001")
a1 = self.r.core.getAssemblyWithAssemNum(assemNum=10)
a2 = self.r.core.getAssembly(locationString="003-001")
self.assertEqual(a0, a2)
self.assertEqual(a1, a2)
self.assertEqual(a1.getLocation(), "003-001")
[docs] def test_getAssemblyWithName(self):
"""
Get assembly by name.
.. test:: Get assembly by name.
:id: T_ARMI_R_GET_ASSEM_NAME
:tests: R_ARMI_R_GET_ASSEM_NAME
"""
a1 = self.r.core.getAssemblyWithAssemNum(assemNum=10)
a2 = self.r.core.getAssembly(assemblyName="A0010")
self.assertEqual(a1, a2)
self.assertEqual(a1.name, "A0010")
[docs] def test_restoreReactor(self):
"""Restore a reactor after growing it from third to full core.
.. test:: Convert a third-core to a full-core geometry and then restore it.
:id: T_ARMI_THIRD_TO_FULL_CORE1
:tests: R_ARMI_THIRD_TO_FULL_CORE
"""
numOfAssembliesOneThird = len(self.r.core.getAssemblies())
self.assertFalse(self.r.core.isFullCore)
self.assertEqual(
self.r.core.symmetry,
geometry.SymmetryType(
geometry.DomainType.THIRD_CORE, geometry.BoundaryType.PERIODIC
),
)
# grow to full core
converter = self.r.core.growToFullCore(self.o.cs)
self.assertTrue(self.r.core.isFullCore)
self.assertGreater(len(self.r.core.getAssemblies()), numOfAssembliesOneThird)
self.assertEqual(self.r.core.symmetry.domain, geometry.DomainType.FULL_CORE)
# restore back to 1/3 core
converter.restorePreviousGeometry(self.r)
self.assertEqual(numOfAssembliesOneThird, len(self.r.core.getAssemblies()))
self.assertEqual(
self.r.core.symmetry,
geometry.SymmetryType(
geometry.DomainType.THIRD_CORE, geometry.BoundaryType.PERIODIC
),
)
self.assertFalse(self.r.core.isFullCore)
self.assertEqual(numOfAssembliesOneThird, len(self.r.core.getAssemblies()))
self.assertEqual(
self.r.core.symmetry,
geometry.SymmetryType(
geometry.DomainType.THIRD_CORE, geometry.BoundaryType.PERIODIC
),
)
[docs] def test_differentNuclideModels(self):
self.assertEqual(self.o.cs[CONF_XS_KERNEL], "MC2v3")
_o2, r2 = loadTestReactor(customSettings={CONF_XS_KERNEL: "MC2v2"})
self.assertNotEqual(
set(self.r.blueprints.elementsToExpand), set(r2.blueprints.elementsToExpand)
)
for b2, b3 in zip(r2.core.getBlocks(), self.r.core.getBlocks()):
for element in self.r.blueprints.elementsToExpand:
# nucspec allows elemental mass to be computed
mass2 = b2.getMass(element.symbol)
mass3 = b3.getMass(element.symbol)
assert_allclose(mass2, mass3)
constituentNucs = [nn.name for nn in element.nuclides if nn.a > 0]
nuclideLevelMass3 = b3.getMass(constituentNucs)
assert_allclose(mass3, nuclideLevelMass3)
[docs] def test_getDominantMaterial(self):
dominantDuct = self.r.core.getDominantMaterial(Flags.DUCT)
dominantFuel = self.r.core.getDominantMaterial(Flags.FUEL)
dominantCool = self.r.core.getDominantMaterial(Flags.COOLANT)
self.assertEqual(dominantDuct.getName(), "HT9")
self.assertEqual(dominantFuel.getName(), "UZr")
self.assertEqual(dominantCool.getName(), "Sodium")
self.assertEqual(list(dominantCool.getNuclides()), ["NA23"])
[docs] def test_getSymmetryFactor(self):
"""
Test getSymmetryFactor().
.. test:: Get the core symmetry.
:id: T_ARMI_R_SYMM
:tests: R_ARMI_R_SYMM
"""
for b in self.r.core.getBlocks():
sym = b.getSymmetryFactor()
i, j, _ = b.spatialLocator.getCompleteIndices()
if i == 0 and j == 0:
self.assertEqual(sym, 3.0)
else:
self.assertEqual(sym, 1.0)
[docs] def test_getAssembliesOnSymmetryLine(self):
center = self.r.core.getAssembliesOnSymmetryLine(grids.BOUNDARY_CENTER)
self.assertEqual(len(center), 1)
upper = self.r.core.getAssembliesOnSymmetryLine(grids.BOUNDARY_120_DEGREES)
self.assertEqual(len(upper), 0)
lower = self.r.core.getAssembliesOnSymmetryLine(grids.BOUNDARY_0_DEGREES)
self.assertGreater(len(lower), 1)
[docs] def test_saveAllFlux(self):
# need a lightweight library to indicate number of groups.
class MockLib:
numGroups = 5
self.r.core.lib = MockLib()
for b in self.r.core.getBlocks():
b.p.mgFlux = range(5)
b.p.adjMgFlux = range(5)
with directoryChangers.TemporaryDirectoryChanger(root=THIS_DIR):
self.r.core.saveAllFlux()
[docs] def test_getFluxVector(self):
class MockLib:
numGroups = 5
self.r.core.lib = MockLib()
for b in self.r.core.getBlocks():
b.p.mgFlux = range(5)
b.p.adjMgFlux = [i + 0.1 for i in range(5)]
b.p.extSrc = [i + 0.2 for i in range(5)]
mgFlux = self.r.core.getFluxVector(energyOrder=1)
adjFlux = self.r.core.getFluxVector(adjoint=True)
srcVec = self.r.core.getFluxVector(extSrc=True)
fluxVol = self.r.core.getFluxVector(volumeIntegrated=True)
expFlux = [i for i in range(5) for b in self.r.core.getBlocks()]
expAdjFlux = [i + 0.1 for b in self.r.core.getBlocks() for i in range(5)]
expSrcVec = [i + 0.2 for b in self.r.core.getBlocks() for i in range(5)]
expFluxVol = list(range(5)) * len(self.r.core.getBlocks())
assert_allclose(expFlux, mgFlux)
assert_allclose(expAdjFlux, adjFlux)
assert_allclose(expSrcVec, srcVec)
assert_allclose(expFluxVol, fluxVol)
[docs] def test_getFuelBottomHeight(self):
for a in self.r.core.getAssemblies(Flags.FUEL):
if a[0].hasFlags(Flags.FUEL):
a[0].setType("mud")
a[1].setType("fuel")
fuelBottomHeightRef = self.r.core.getFirstAssembly(Flags.FUEL)[0].getHeight()
fuelBottomHeightInCm = self.r.core.getFuelBottomHeight()
self.assertEqual(fuelBottomHeightInCm, fuelBottomHeightRef)
[docs] def test_getGridBounds(self):
"""Test getGridBounds() works on different scales.
.. test:: Test that assembly grids nest inside core grids.
:id: T_ARMI_GRID_NEST
:tests: R_ARMI_GRID_NEST
"""
(minI, maxI), (minJ, maxJ), (_minK, _maxK) = self.r.core.getBoundingIndices()
self.assertEqual((minI, maxI), (-3, 8))
self.assertEqual((minJ, maxJ), (-4, 8))
randomBlock = self.r.core.getFirstAssembly()
(minI, maxI), (minJ, maxJ), (_minK, _maxK) = randomBlock.getBoundingIndices()
self.assertEqual((minI, maxI), (8, 8))
self.assertEqual((minJ, maxJ), (-4, -4))
[docs] def test_locations(self):
loc = self.r.core.spatialGrid.getLocatorFromRingAndPos(3, 2)
a = self.r.core.childrenByLocator[loc]
assert_allclose(a.spatialLocator.indices, [1, 1, 0])
for bi, b in enumerate(a):
assert_allclose(b.spatialLocator.getCompleteIndices(), [1, 1, bi])
self.assertEqual(a.getLocation(), "003-002")
self.assertEqual(a[0].getLocation(), "003-002-000")
[docs] def test_getMass(self):
# If these are not in agreement check on block symmetry factor being applied to volumes
mass1 = self.r.core.getMass()
mass2 = sum([b.getMass() for b in self.r.core.getBlocks()])
assert_allclose(mass1, mass2)
[docs] def test_isPickleable(self):
loaded = cPickle.loads(cPickle.dumps(self.r))
# ensure we didn't break the current reactor
self.assertIs(self.r.core.spatialGrid.armiObject, self.r.core)
# make sure that the loaded reactor and grid are aligned
self.assertIs(loaded.core.spatialGrid.armiObject, loaded.core)
self.assertTrue(
all(
isinstance(key, grids.LocationBase)
for key in loaded.core.childrenByLocator.keys()
)
)
loc = loaded.core.spatialGrid[0, 0, 0]
loaded.core.sortAssemsByRing()
self.r.core.sortAssemsByRing()
self.assertIs(loc.grid, loaded.core.spatialGrid)
self.assertEqual(loaded.core.childrenByLocator[loc], loaded.core[0])
allIDs = set()
def checkAdd(comp):
self.assertNotIn(id(comp), allIDs)
self.assertNotIn(id(comp.p), allIDs)
allIDs.add(id(comp))
allIDs.add(id(comp.p))
# check a few locations to be equivalent
for a0, a1 in zip(self.r.core, loaded.core):
self.assertEqual(str(a0.getLocation()), str(a1.getLocation()))
self.assertIs(a0.spatialLocator.grid, self.r.core.spatialGrid)
self.assertIs(a1.spatialLocator.grid, loaded.core.spatialGrid)
checkAdd(a0)
checkAdd(a1)
for b0, b1 in zip(a0, a1):
self.assertIs(b0.spatialLocator.grid, a0.spatialGrid)
self.assertIs(b1.spatialLocator.grid, a1.spatialGrid)
self.assertEqual(str(b0.getLocation()), str(b1.getLocation()))
self.assertEqual(b0.getSymmetryFactor(), b1.getSymmetryFactor())
self.assertEqual(b0.getHMMoles(), b1.getHMMoles())
checkAdd(b0)
checkAdd(b1)
[docs] def test_removeAssembly(self):
a = self.r.core[-1] # last assembly
b = a[-1] # use the last block in case we ever figure out stationary blocks
aLoc = a.spatialLocator
self.assertIsNotNone(aLoc.grid)
bLoc = b.spatialLocator
self.r.core.removeAssembly(a)
self.assertNotEqual(aLoc, a.spatialLocator)
self.assertEqual(a.spatialLocator.grid, self.r.sfp.spatialGrid)
# confirm only attached to removed assem
self.assertIs(bLoc, b.spatialLocator) # block location does not change
self.assertIs(a, b.parent)
self.assertIs(a, b.spatialLocator.grid.armiObject)
[docs] def test_removeAssemblyNoSfp(self):
with mockRunLogs.BufferLog() as mock:
# we should start with a clean slate
self.assertEqual("", mock.getStdout())
runLog.LOG.startLog("test_removeAssemblyNoSfp")
runLog.LOG.setVerbosity(logging.INFO)
a = self.r.core[-1] # last assembly
aLoc = a.spatialLocator
self.assertIsNotNone(aLoc.grid)
self.r.sfp = None
self.r.core.removeAssembly(a)
self.assertIn("No Spent Fuel Pool", mock.getStdout())
[docs] def test_removeAssembliesInRing(self):
aLoc = [
self.r.core.spatialGrid.getLocatorFromRingAndPos(3, i + 1)
for i in range(12)
]
assems = {
i: self.r.core.childrenByLocator[loc]
for i, loc in enumerate(aLoc)
if loc in self.r.core.childrenByLocator
}
self.r.core.removeAssembliesInRing(3, self.o.cs)
for i, a in assems.items():
self.assertNotEqual(aLoc[i], a.spatialLocator)
self.assertEqual(a.spatialLocator.grid, self.r.sfp.spatialGrid)
[docs] def test_removeAssembliesInRingByCount(self):
"""Tests retrieving ring numbers and removing a ring.
.. test:: Retrieve number of rings in core.
:id: T_ARMI_R_NUM_RINGS
:tests: R_ARMI_R_NUM_RINGS
"""
self.assertEqual(self.r.core.getNumRings(), 9)
self.r.core.removeAssembliesInRing(9, self.o.cs)
self.assertEqual(self.r.core.getNumRings(), 8)
[docs] def test_getNumRings(self):
self.assertEqual(len(self.r.core.circularRingList), 0)
self.assertEqual(self.r.core.getNumRings(indexBased=True), 9)
self.assertEqual(self.r.core.getNumRings(indexBased=False), 9)
self.r.core.circularRingList = {1, 2, 3}
self.assertEqual(len(self.r.core.circularRingList), 3)
self.assertEqual(self.r.core.getNumRings(indexBased=True), 9)
self.assertEqual(self.r.core.getNumRings(indexBased=False), 3)
[docs] @patch("armi.reactor.reactors.Core.getAssemblies")
def test_whenNoAssemblies(self, mockGetAssemblies):
"""Test various edge cases when there are no assemblies."""
mockGetAssemblies.return_value = []
self.assertEqual(self.r.core.countBlocksWithFlags(Flags.FUEL), 0)
self.assertEqual(self.r.core.countFuelAxialBlocks(), 0)
self.assertGreater(self.r.core.getFirstFuelBlockAxialNode(), 9e9)
[docs] def test_removeAssembliesInRingHex(self):
"""
Since the test reactor is hex, we need to use the overrideCircularRingMode option
to remove assemblies from it.
"""
self.assertEqual(self.r.core.getNumRings(), 9)
for ringNum in range(6, 10):
self.r.core.removeAssembliesInRing(
ringNum, self.o.cs, overrideCircularRingMode=True
)
self.assertEqual(self.r.core.getNumRings(), 5)
[docs] def test_getNozzleTypes(self):
nozzleTypes = self.r.core.getNozzleTypes()
expectedTypes = ["Inner", "Outer", "lta", "Default"]
for nozzle in expectedTypes:
self.assertIn(nozzle, nozzleTypes)
[docs] def test_createAssemblyOfType(self):
"""Test creation of new assemblies."""
# basic creation
aOld = self.r.core.getFirstAssembly(Flags.FUEL)
aNew = self.r.core.createAssemblyOfType(aOld.getType(), cs=self.o.cs)
self.assertAlmostEqual(aOld.getMass(), aNew.getMass())
# test axial mesh alignment
aNewMesh = aNew.getAxialMesh()
for i, meshValue in enumerate(aNewMesh):
self.assertAlmostEqual(
meshValue, self.r.core.p.referenceBlockAxialMesh[i + 1]
) # use i+1 to skip 0.0
# creation with modified enrichment
aNew2 = self.r.core.createAssemblyOfType(aOld.getType(), 0.195, self.o.cs)
fuelBlock = aNew2.getFirstBlock(Flags.FUEL)
self.assertAlmostEqual(fuelBlock.getUraniumMassEnrich(), 0.195)
# creation with modified enrichment on an expanded BOL assem.
fuelComp = fuelBlock.getComponent(Flags.FUEL)
bol = self.r.blueprints.assemblies[aOld.getType()]
changer = AxialExpansionChanger()
changer.performPrescribedAxialExpansion(bol, [fuelComp], [0.05])
aNew3 = self.r.core.createAssemblyOfType(aOld.getType(), 0.195, self.o.cs)
self.assertAlmostEqual(
aNew3.getFirstBlock(Flags.FUEL).getUraniumMassEnrich(), 0.195
)
self.assertAlmostEqual(aNew3.getMass(), bol.getMass())
[docs] def test_createFreshFeed(self):
# basic creation
aOld = self.r.core.getFirstAssembly(Flags.FEED)
aNew = self.r.core.createFreshFeed(cs=self.o.cs)
self.assertAlmostEqual(aOld.getMass(), aNew.getMass())
[docs] def test_createAssemblyOfTypeExpandedCore(self):
"""Test creation of new assemblies in an expanded core."""
# change the mesh of inner blocks
mesh = self.r.core.p.referenceBlockAxialMesh[1:]
lastIndex = len(mesh) - 1
mesh = [val + 5 for val in mesh]
mesh[0] -= 5
mesh[lastIndex] -= 5
# expand the core
self.r.core.p.referenceBlockAxialMesh = [0] + mesh
for a in self.r.core:
a.setBlockMesh(mesh)
aType = self.r.core.getFirstAssembly(Flags.FUEL).getType()
# demonstrate we can still create assemblies
self.assertTrue(self.r.core.createAssemblyOfType(aType, cs=self.o.cs))
[docs] def test_getAvgTemp(self):
t0 = self.r.core.getAvgTemp([Flags.CLAD, Flags.WIRE, Flags.DUCT])
self.assertAlmostEqual(t0, 459.267, delta=0.01)
t1 = self.r.core.getAvgTemp([Flags.CLAD, Flags.FUEL])
self.assertAlmostEqual(t1, 545.043, delta=0.01)
t2 = self.r.core.getAvgTemp([Flags.CLAD, Flags.WIRE, Flags.DUCT, Flags.FUEL])
self.assertAlmostEqual(t2, 521.95269, delta=0.01)
[docs] def test_getScalarEvolution(self):
self.r.core.scalarVals["fake"] = 123
x = self.r.core.getScalarEvolution("fake")
self.assertEqual(x, 123)
[docs] def test_ifMissingSpatialGrid(self):
self.r.core.spatialGrid = None
with self.assertRaises(ValueError):
self.r.core.symmetry
with self.assertRaises(ValueError):
self.r.core.geomType
[docs] def test_removeAllAssemblies(self):
self.assertGreater(len(self.r.core.blocksByName), 100)
self.assertGreater(len(self.r.core.assembliesByName), 12)
self.r.core.removeAllAssemblies()
self.assertEqual(0, len(self.r.core.blocksByName))
self.assertEqual(0, len(self.r.core.assembliesByName))
[docs] def test_pinCoordsAllBlocks(self):
"""Make sure all blocks can get pin coords."""
for b in self.r.core.getBlocks():
coords = b.getPinCoordinates()
self.assertGreater(len(coords), -1)
[docs] def test_applyThermalExpansion_CoreConstruct(self):
r"""Test that assemblies in core are correctly expanded.
Notes
-----
- all assertions skip the first block as it has no $\Delta T$ and does not expand
"""
originalAssems = self.r.core.getAssemblies()
# stash original axial mesh info
oldRefBlockAxialMesh = self.r.core.p.referenceBlockAxialMesh
oldAxialMesh = self.r.core.p.axialMesh
nonEqualParameters = ["heightBOL", "molesHmBOL", "massHmBOL"]
equalParameters = ["smearDensity", "nHMAtBOL", "enrichmentBOL"]
o, coldHeightR = loadTestReactor(
self.directoryChanger.destination,
customSettings={
"inputHeightsConsideredHot": False,
"assemFlagsToSkipAxialExpansion": ["feed fuel"],
},
)
aToSkip = list(
Flags.fromStringIgnoreErrors(t)
for t in o.cs[CONF_ASSEM_FLAGS_SKIP_AXIAL_EXP]
)
for i, val in enumerate(oldRefBlockAxialMesh[1:]):
self.assertNotEqual(val, coldHeightR.core.p.referenceBlockAxialMesh[i])
for i, val in enumerate(oldAxialMesh[1:]):
self.assertNotEqual(val, coldHeightR.core.p.axialMesh[i])
coldHeightAssems = coldHeightR.core.getAssemblies()
for a, coldHeightA in zip(originalAssems, coldHeightAssems):
if a.hasFlags(Flags.CONTROL) or any(
a.hasFlags(aFlags) for aFlags in aToSkip
):
continue
for b, coldHeightB in zip(a[1:], coldHeightA[1:]):
for param in nonEqualParameters:
p, coldHeightP = b.p[param], coldHeightB.p[param]
if p and coldHeightP:
self.assertNotEqual(
p, coldHeightP, f"{param} {p} {coldHeightP}"
)
else:
self.assertAlmostEqual(p, coldHeightP)
for param in equalParameters:
p, coldHeightP = b.p[param], coldHeightB.p[param]
self.assertAlmostEqual(p, coldHeightP)
[docs] def test_updateBlockBOLHeights_DBLoad(self):
r"""Test that blueprints assemblies are expanded in DB load.
Notes
-----
All assertions skip the first block as it has no $\Delta T$ and does not expand.
"""
originalAssems = sorted(a for a in self.r.blueprints.assemblies.values())
nonEqualParameters = ["heightBOL", "molesHmBOL", "massHmBOL"]
equalParameters = ["smearDensity", "nHMAtBOL", "enrichmentBOL"]
_o, coldHeightR = loadTestReactor(
self.directoryChanger.destination,
customSettings={"inputHeightsConsideredHot": False},
)
coldHeightAssems = sorted(a for a in coldHeightR.blueprints.assemblies.values())
for a, coldHeightA in zip(originalAssems, coldHeightAssems):
if not a.hasFlags(Flags.CONTROL):
for b, coldHeightB in zip(a[1:], coldHeightA[1:]):
for param in nonEqualParameters:
p, coldHeightP = b.p[param], coldHeightB.p[param]
if p and coldHeightP:
self.assertNotEqual(p, coldHeightP)
else:
self.assertAlmostEqual(p, coldHeightP)
for param in equalParameters:
p, coldHeightP = b.p[param], coldHeightB.p[param]
self.assertAlmostEqual(p, coldHeightP)
[docs] def test_buildManualZones(self):
# define some manual zones in the settings
newSettings = {}
newSettings["zoneDefinitions"] = [
"ring-1: 001-001",
"ring-2: 002-001, 002-002",
"ring-3: 003-001, 003-002, 003-003",
]
cs = self.o.cs.modified(newSettings=newSettings)
self.r.core.buildManualZones(cs)
zonez = self.r.core.zones
self.assertEqual(len(list(zonez)), 3)
self.assertIn("002-001", zonez["ring-2"])
self.assertIn("003-002", zonez["ring-3"])
[docs] def test_buildManualZonesEmpty(self):
# ensure there are no zone definitions in the settings
newSettings = {}
newSettings["zoneDefinitions"] = []
cs = self.o.cs.modified(newSettings=newSettings)
# verify that buildZones behaves well when no zones are defined
self.r.core.buildManualZones(cs)
self.assertEqual(len(list(self.r.core.zones)), 0)
[docs] def test_getNuclideCategories(self):
"""Test that nuclides are categorized correctly."""
self.r.core.getNuclideCategories()
self.assertIn("coolant", self.r.core._nuclideCategories)
self.assertIn("structure", self.r.core._nuclideCategories)
self.assertIn("fuel", self.r.core._nuclideCategories)
self.assertEqual(self.r.core._nuclideCategories["coolant"], set(["NA23"]))
self.assertIn("FE56", self.r.core._nuclideCategories["structure"])
self.assertIn("U235", self.r.core._nuclideCategories["fuel"])
[docs] def test_setPowerIfNecessary(self):
self.assertAlmostEqual(self.r.core.p.power, 0)
self.assertAlmostEqual(self.r.core.p.powerDensity, 0)
# to start, this method shouldn't do anything
self.r.core.setPowerIfNecessary()
self.assertAlmostEqual(self.r.core.p.power, 0)
# take the powerDensity when needed
self.r.core.p.power = 0
self.r.core.p.powerDensity = 1e9
mass = self.r.core.getHMMass()
self.r.core.setPowerIfNecessary()
self.assertAlmostEqual(self.r.core.p.power, 1e9 * mass)
# don't take the powerDensity when not needed
self.r.core.p.power = 3e9
self.r.core.p.powerDensity = 2e9
self.r.core.setPowerIfNecessary()
self.assertAlmostEqual(self.r.core.p.power, 3e9)
[docs] def test_findAllMeshPoints(self):
"""Test findAllMeshPoints().
.. test:: Test that the reactor can calculate its core block mesh.
:id: T_ARMI_R_MESH
:tests: R_ARMI_R_MESH
"""
# lets do some basic sanity checking of the meshpoints
x, y, z = self.r.core.findAllMeshPoints()
# no two meshpoints should be the same, and they should all be monotonically increasing
for xx in range(1, len(x)):
self.assertGreater(x[xx], x[xx - 1], msg=f"x={xx}")
for yy in range(1, len(y)):
self.assertGreater(y[yy], y[yy - 1], msg=f"y={yy}")
for zz in range(1, len(z)):
self.assertGreater(z[zz], z[zz - 1], msg=f"z={zz}")
# the z-index should start at zero (the bottom)
self.assertEqual(z[0], 0)
# ensure the X and Y mesh spacing is correct (for a hex core)
pitch = self.r.core.spatialGrid.pitch
xPitch = sqrt(3) * pitch / 2
for xx in range(1, len(x)):
self.assertAlmostEqual(x[xx] - x[xx - 1], xPitch, delta=0.0001)
yPitch = pitch / 2
for yy in range(1, len(y)):
self.assertAlmostEqual(y[yy] - y[yy - 1], yPitch, delta=0.001)
[docs]class CartesianReactorTests(ReactorTests):
def setUp(self):
self.o = buildOperatorOfEmptyCartesianBlocks()
self.r = self.o.r
[docs] def test_getAssemblyPitch(self):
# Cartesian pitch should have 2 dims since it could be a rectangle that is not square.
assert_equal(self.r.core.getAssemblyPitch(), [10.0, 16.0])
[docs] def test_getAssembliesInSquareRing(self, exclusions=[2]):
expectedAssemsInRing = [1, 0]
actualAssemsInRing = []
for ring in range(1, self.r.core.getNumRings() + 1):
actualAssemsInRing.append(
len(self.r.core.getAssembliesInSquareOrHexRing(ring))
)
self.assertSequenceEqual(actualAssemsInRing, expectedAssemsInRing)
[docs] def test_getNuclideCategoriesLogging(self):
"""Simplest possible test of the getNuclideCategories method and its logging."""
log = mockRunLogs.BufferLog()
# this strange namespace-stomping is used to the test to set the logger in reactors.Core
from armi.reactor import reactors
reactors.runLog = runLog
runLog.LOG = log
# run the actual method in question
self.r.core.getNuclideCategories()
messages = log.getStdout()
self.assertIn("Nuclide categorization", messages)
self.assertIn("Structure", messages)
[docs]class CartesianReactorNeighborTests(ReactorTests):
def setUp(self):
self.r = loadTestReactor(TEST_ROOT, inputFileName="zpprTest.yaml")[1]
[docs] def test_findNeighborsCartesian(self):
"""Find neighbors of a given assembly in a Cartesian grid."""
loc = self.r.core.spatialGrid[1, 1, 0]
a = self.r.core.childrenByLocator[loc]
neighbs = self.r.core.findNeighbors(a)
locs = [tuple(a.spatialLocator.indices[:2]) for a in neighbs]
self.assertEqual(len(neighbs), 4)
self.assertIn((2, 1), locs)
self.assertIn((1, 2), locs)
self.assertIn((0, 1), locs)
self.assertIn((1, 0), locs)
# try with edge assembly
loc = self.r.core.spatialGrid[0, 0, 0]
a = self.r.core.childrenByLocator[loc]
neighbs = self.r.core.findNeighbors(a, showBlanks=False)
locs = [tuple(a.spatialLocator.indices[:2]) for a in neighbs]
self.assertEqual(len(neighbs), 2)
# in this case no locations that aren't actually in the core should be returned
self.assertIn((1, 0), locs)
self.assertIn((0, 1), locs)