Skip to content

Commit

Permalink
more WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
onufer committed Oct 21, 2024
1 parent b1eef01 commit 03cf800
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 124 deletions.
2 changes: 1 addition & 1 deletion armi/bookkeeping/historyTracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ def writeAssemHistory(self, a, fName=""):
out.write("\n\n\nAssembly info\n")
out.write("{0} {1}\n".format(a.getName(), a.getType()))
for b in blocks:
out.write('"{}" {} {}\n'.format(b.getType(), b.p.xsType, b.p.buGroup))
out.write('"{}" {} {}\n'.format(b.getType(), b.p.xsType, b.p.envGroup))

def preloadBlockHistoryVals(self, names, keys, timesteps):
"""
Expand Down
134 changes: 80 additions & 54 deletions armi/physics/neutronics/crossSectionGroupManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,10 @@ def __init__(self, r, cs):
self._upperBuGroupBounds = None
self.representativeBlocks = collections.OrderedDict()
self.avgNucTemperatures = {}
self._buGroupUpdatesEnabled = True

# this turns off updates for when core changes are made, but dont want to re-evaluate XS
# for example if lattice physics was only once per cycle we might not want to re-evaluate groups
self._envGroupUpdatesEnabled = True
self._setBuGroupBounds(self.cs["buGroups"])
self._unrepresentedXSIDs = []

Expand Down Expand Up @@ -991,9 +994,9 @@ def _setBuGroupBounds(self, upperBuGroupBounds):
ValueError
If the provided burnup groups are invalid
"""
self._upperBuGroupBounds = upperBuGroupBounds
lastBu = 0.0
for upperBu in self._upperBuGroupBounds:
# validate structure
for upperBu in upperBuGroupBounds:
if upperBu <= 0 or upperBu > 100:
raise ValueError(
"Burnup group upper bound {0} is invalid".format(upperBu)
Expand All @@ -1002,7 +1005,13 @@ def _setBuGroupBounds(self, upperBuGroupBounds):
raise ValueError("Burnup groups must be ascending")
lastBu = upperBu

def _updateBurnupGroups(self, blockList):
self._upperBuGroupBounds = upperBuGroupBounds

def _setTempGroupBounds(self, upperTempGroupBounds):
# implement here
pass

def _updateEnviromentGroups(self, blockList):
"""
Update the burnup group of each block based on its burnup.
Expand All @@ -1013,32 +1022,40 @@ def _updateBurnupGroups(self, blockList):
--------
armi.reactor.blocks.Block.getMicroSuffix
"""
if self._buGroupUpdatesEnabled and len(self._upperBuGroupBounds) > 1:
if self._envGroupUpdatesEnabled:
runLog.debug(
"Skipping burnup group update of {0} blocks because it is disabled"
"".format(len(blockList))
)

if self._envGroupUpdatesEnabled and len(self._upperBuGroupBounds) > 0:
runLog.debug("Updating burnup groups of {0} blocks".format(len(blockList)))
for block in blockList:
bu = block.p.percentBu
for buGroupIndex, upperBu in enumerate(self._upperBuGroupBounds):

if bu <= upperBu:
block.p.buGroupNum = buGroupIndex
break
if self.cs["tempGroupBoundaries"]:
# Avg temp will still just be average temp in each group, but not exactly the midpoint of the boundaries
# ["tempGroupBoundaries"] = [300, 350, 400, 450, 500, 550, 600, 650]
for tempGroupIndex, upperTemp in enumerate(
self._upperTempGroupBounds
):


getIndex(buGroupIndex, tempGroupIndex)
block.p.envGroupNum = buGroupIndex
else:
raise ValueError("no bu group found for bu={0}".format(bu))
else:
runLog.debug(
"Skipping burnup group update of {0} blocks because it is disabled"
"".format(len(blockList))
)


def _addXsGroupsFromBlocks(self, blockCollectionsByXsGroup, blockList):
"""
Build all the cross section groups based on their XS type and BU group.
Also ensures that their BU group is up to date with their burnup.
"""
self._updateBurnupGroups(blockList)
if self.cs["tempGroupBoundaries"]:
# Avg temp will still just be average temp in each group, but not exactly the midpoint of the boundaries
# ["tempGroupBoundaries"] = [300, 350, 400, 450, 500, 550, 600, 650] # similar to mcnp
self._updateEnviromentGroups(blockList)
self._updateTempGroups(blockList)
for b in blockList:
xsID = b.getMicroSuffix()
Expand Down Expand Up @@ -1164,9 +1181,8 @@ def createRepresentativeBlocks(self):
self.avgNucTemperatures[xsID] = collection.avgNucTemperatures
else:
runLog.debug(
"No candidate blocks for {} will apply different burnup group".format(
xsID
)
"No candidate blocks in group for {} (with a valid representative block flag). "
"Will apply different burnup group".format(xsID)
)
self._unrepresentedXSIDs.append(xsID)

Expand Down Expand Up @@ -1346,22 +1362,22 @@ def getNextAvailableXsTypes(self, howMany=1, excludedXSTypes=None):
)
return availableXsTypes[:howMany]

def _getUnrepresentedBlocks(self, blockCollectionsByXsGroup):
def _getMissingBlueprintBlocks(self, blockCollectionsByXsGroup):
"""
Gets all blocks with suffixes not yet represented (for blocks in assemblies in the blueprints but not the core).
Gets all blocks with suffixes not yet represented (for blocks in assemblies in the blueprints but not in the core).
Notes
-----
Certain cases (ZPPR validation cases) need to run cross sections for assemblies not in
the core to get by region cross sections and flux factors.
"""
unrepresentedBlocks = []
missingBlueprintBlocks = []
for a in self.r.blueprints.assemblies.values():
for b in a:
if b.getMicroSuffix() not in blockCollectionsByXsGroup:
b2 = copy.deepcopy(b)
unrepresentedBlocks.append(b2)
return unrepresentedBlocks
missingBlueprintBlocks.append(b2)
return missingBlueprintBlocks

def makeCrossSectionGroups(self):
"""Make cross section groups for all blocks in reactor and unrepresented blocks from blueprints."""
Expand All @@ -1370,40 +1386,50 @@ def makeCrossSectionGroups(self):
bCollectXSGroup, self.r.core.getBlocks()
)
bCollectXSGroup = self._addXsGroupsFromBlocks(
bCollectXSGroup, self._getUnrepresentedBlocks(bCollectXSGroup)
bCollectXSGroup, self._getMissingBlueprintBlocks(bCollectXSGroup)
)
blockCollectionsByXsGroup = collections.OrderedDict(
sorted(bCollectXSGroup.items())
)
return blockCollectionsByXsGroup

def _getTempGroupOfDifferentBurnup(self, missingXsType):
"""Get a substitute block to use since there are no blacks with flags for xs gen."""
# Update here
for otherXsID in self.representativeBlocks: # order gets closest BU
repType, repBuGroup = otherXsID
if repType == missingXsType:
pass

def _modifyUnrepresentedXSIDs(self, blockCollectionsByXsGroup):
"""
Adjust the xsID of blocks in the groups that are not represented.
Try to just adjust the burnup group up to something that is represented
(can happen to structure in AA when only AB, AC, AD still remain).
(can happen to structure in AA when only AB, AC, AD still remain,
but if some fresh AA happened to be added it might be needed).
"""
for xsID in self._unrepresentedXSIDs:
missingXsType, _missingBuGroup = xsID
for otherXsID in self.representativeBlocks: # order gets closest BU
repType, repBuGroup = otherXsID
if repType == missingXsType:
nonRepBlocks = blockCollectionsByXsGroup.get(xsID)
if nonRepBlocks:
runLog.extra(
"Changing XSID of {0} blocks from {1} to {2}"
"".format(len(nonRepBlocks), xsID, otherXsID)
)
for b in nonRepBlocks:
b.p.buGroup = repBuGroup
break
else:
runLog.warning(
"No representative blocks with XS type {0} exist in the core. "
"These XS cannot be generated and must exist in the working "
"directory or the run will fail.".format(xsID)
)
missingXsType, _missingEnvGroup = xsID
nonRepBlocks = blockCollectionsByXsGroup.get(xsID)
if nonRepBlocks:
newEnvType = self._getTempGroupOfDifferentBurnup(missingXsType)
if newEnvType:
# there were no blocks flagged to xs gen even though there were some not suitable for
# generation in the group so can't make XS and use different.
runLog.warning(
"Changing XSID of {0} blocks from {1} to {2}"
"".format(len(nonRepBlocks), xsID, missingXsType[0] +newEnvType)
)
for b in nonRepBlocks:
b.p.envGroup = newEnvType
else:
runLog.warning(
"No representative blocks with XS type {0} exist in the core. "
"There were also no similar blocks to use"
"These XS cannot be generated and must exist in the working "
"directory or the run will fail.".format(xsID)
)

def _summarizeGroups(self, blockCollectionsByXsGroup):
"""Summarize current contents of the XS groups."""
Expand Down Expand Up @@ -1448,31 +1474,31 @@ def _getXsIDGroup(self, xsID):
return self._NON_REPR_GROUP
return None

def disableBuGroupUpdates(self):
def disableEnvGroupUpdates(self):
"""
Turn off updating bu groups based on burnup.
Useful during reactivity coefficient calculations to be consistent with ref. run.
See Also
--------
enableBuGroupUpdates
enableGroupUpdates
"""
runLog.extra("Burnup group updating disabled")
wasEnabled = self._buGroupUpdatesEnabled
self._buGroupUpdatesEnabled = False
runLog.extra("Enviroment xs group updating disabled")
wasEnabled = self._envGroupUpdatesEnabled
self._envGroupUpdatesEnabled = False
return wasEnabled

def enableBuGroupUpdates(self):
def enableGroupUpdates(self):
"""
Turn on updating bu groups based on burnup.
See Also
--------
disableBuGroupUpdates
disableEnvGroupUpdates
"""
runLog.extra("Burnup group updating enabled")
self._buGroupUpdatesEnabled = True
runLog.extra("Enviroment xs group updating enabled")
self._envGroupUpdatesEnabled = True

def getNucTemperature(self, xsID, nucName):
"""
Expand Down
9 changes: 5 additions & 4 deletions armi/physics/neutronics/crossSectionSettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,19 +250,20 @@ def __getitem__(self, xsID):
if xsID in self:
return dict.__getitem__(self, xsID)

# exact key not present so give lowest env group key
xsType = xsID[0]
buGroup = xsID[1]
envGroup = xsID[1]
existingXsOpts = [
xsOpt
for xsOpt in self.values()
if xsOpt.xsType == xsType and xsOpt.buGroup < buGroup
if xsOpt.xsType == xsType and xsOpt.envGroup < envGroup
]

if not any(existingXsOpts):
return self._getDefault(xsID)

else:
return sorted(existingXsOpts, key=lambda xsOpt: xsOpt.buGroup)[0]
return sorted(existingXsOpts, key=lambda xsOpt: xsOpt.envGroup)[0]

def setDefaults(self, blockRepresentation, validBlockTypes):
"""
Expand Down Expand Up @@ -509,7 +510,7 @@ def xsType(self):
return self.xsID[0]

@property
def buGroup(self):
def envGroup(self):
"""Return the single-char burnup group indicator."""
return self.xsID[1]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class LatticePhysicsWriter(interfaces.InputWriter):
DEPLETABLE = "Depletable" + 4 * _SPACE
UNDEPLETABLE = "Non-Depletable"
REPRESENTED = "Represented" + 2 * _SPACE
UNREPRESENTED = "Unrepresented"
INF_DILUTE = "Inf Dilute"

This comment has been minimized.

Copy link
@jakehader

jakehader Oct 23, 2024

Member

Maybe just Infinitely Dilute to be more expressive.


def __init__(
self,
Expand Down Expand Up @@ -300,7 +300,7 @@ def _getAllNuclidesByCategory(self, component=None):
if nucName in objNuclides:
nucCategory += self.REPRESENTED + self._SEPARATOR
else:
nucCategory += self.UNREPRESENTED + self._SEPARATOR
nucCategory += self.INF_DILUTE + self._SEPARATOR

if nucName in depletableNuclides:
nucCategory += self.DEPLETABLE
Expand Down
16 changes: 8 additions & 8 deletions armi/physics/neutronics/tests/test_crossSectionManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ def test_ComponentAverageRepBlock(self):

assert "AC" in xsgm.representativeBlocks, (
"Assemblies not in the core should still have XS groups"
"see getUnrepresentedBlocks()"
"see _getMissingBlueprintBlocks()"
)


Expand Down Expand Up @@ -701,16 +701,16 @@ def setUp(self):
self.csm._setBuGroupBounds([3, 10, 30, 100])
self.csm.interactBOL()

def test_updateBurnupGroups(self):
def test_updateEnviromentGroups(self):
self.blockList[1].p.percentBu = 3.1
self.blockList[2].p.percentBu = 10.0

self.csm._updateBurnupGroups(self.blockList)
self.csm._updateEnviromentGroups(self.blockList)

self.assertEqual(self.blockList[0].p.buGroup, "A")
self.assertEqual(self.blockList[1].p.buGroup, "B")
self.assertEqual(self.blockList[2].p.buGroup, "B")
self.assertEqual(self.blockList[-1].p.buGroup, "D")
self.assertEqual(self.blockList[0].p.envGroup, "A")
self.assertEqual(self.blockList[1].p.envGroup, "B")
self.assertEqual(self.blockList[2].p.envGroup, "B")
self.assertEqual(self.blockList[-1].p.envGroup, "D")

def test_setBuGroupBounds(self):
self.assertAlmostEqual(self.csm._upperBuGroupBounds[2], 30.0)
Expand Down Expand Up @@ -738,7 +738,7 @@ def test_calcWeightedBurnup(self):
self.blockList[3].p.percentBu = 1.5
for b in self.blockList[4:]:
b.p.percentBu = 0.0
self.csm._updateBurnupGroups(self.blockList)
self.csm._updateEnviromentGroups(self.blockList)
blockCollectionsByXsGroup = {}
blockCollectionsByXsGroup = self.csm._addXsGroupsFromBlocks(
blockCollectionsByXsGroup, self.blockList
Expand Down
4 changes: 2 additions & 2 deletions armi/physics/neutronics/tests/test_crossSectionSettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def test_homogeneousXsDefaultSettingAssignment(self):
self.assertEqual(xsModel["YA"].geometry, "0D")
self.assertEqual(xsModel["YA"].criticalBuckling, True)

def test_setDefaultSettingsByLowestBuGroupHomogeneous(self):
def test_setDefaultSettingsByLowestEnvGroupHomogeneous(self):
# Initialize some micro suffix in the cross sections
cs = settings.Settings()
xs = XSSettings()
Expand All @@ -145,7 +145,7 @@ def test_setDefaultSettingsByLowestBuGroupHomogeneous(self):
self.assertNotIn("JB", xs)
self.assertNotEqual(xs["JD"], xs["JB"])

def test_setDefaultSettingsByLowestBuGroupOneDimensional(self):
def test_setDefaultSettingsByLowestEnvGroupOneDimensional(self):
# Initialize some micro suffix in the cross sections
cs = settings.Settings()
xsModel = XSSettings()
Expand Down
Loading

0 comments on commit 03cf800

Please sign in to comment.