Skip to content

Commit

Permalink
Updating stationary block methods (#665)
Browse files Browse the repository at this point in the history
Updated the way that ARMI handles stationary blocks. ARMI now has a global setting called stationaryBlockFlags which can be set by the user to identify flags for stationary blocks. By default, GRID_PLATE has a stationary flag, unless set by user in the input file. When a reactor object is initialized, a reactor attribute called stationaryBlockFlagsList is generated which contains a list of the flags in the reactor that are considered stationary. ARMI now operates on stationaryBlockFlagsList in order to control stationary blocks.
  • Loading branch information
HunterPSmith authored Aug 9, 2022
1 parent 081a74a commit 9a510ca
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 276 deletions.
6 changes: 5 additions & 1 deletion armi/bookkeeping/historyTracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,11 @@ def writeAssemHistory(self, a, fName=""):

params = self.getTrackedParams()
blocks = [
b for bi, b in enumerate(a) if bi not in self.cs["stationaryBlocks"]
b
for b in a
if not any(
b.hasFlags(sbf) for sbf in self.r.core.stationaryBlockFlagsList
)
]
blockHistories = dbi.getHistories(blocks, params)

Expand Down
5 changes: 1 addition & 4 deletions armi/physics/fuelCycle/fuelHandlerInterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,12 @@
# limitations under the License.

"""A place for the FuelHandler's Interface"""
import logging

from armi import runLog
from armi import interfaces
from armi.utils import plotting
from armi.physics.fuelCycle import fuelHandlers
from armi.physics.fuelCycle import fuelHandlerFactory

runLog = logging.getLogger(__name__)


class FuelHandlerInterface(interfaces.Interface):
"""
Expand Down
138 changes: 37 additions & 101 deletions armi/physics/fuelCycle/fuelHandlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,17 @@
import os
import re
import warnings
import logging

import numpy


from armi import runLog
from armi.utils.customExceptions import InputError
from armi.reactor.flags import Flags
from armi.utils.mathematics import findClosest, resampleStepwise
from armi.physics.fuelCycle.fuelHandlerFactory import fuelHandlerFactory
from armi.physics.fuelCycle.fuelHandlerInterface import FuelHandlerInterface

runLog = logging.getLogger(__name__)


class FuelHandler:
"""
Expand Down Expand Up @@ -1160,25 +1159,49 @@ def swapAssemblies(self, a1, a2):
a1.moveTo(a2.spatialLocator)
a2.moveTo(oldA1Location)

self._swapFluxParam(a1, a2)

def _transferStationaryBlocks(self, assembly1, assembly2):
"""
Exchange the stationary blocks (e.g. grid plate) between the moving assemblies
These blocks in effect are not moved at all.
"""
for index in self.cs["stationaryBlocks"]:
# this block swap is designed to ensure that all blocks have the
# correct parents and children structure at the end of the swaps.
tempBlock1 = assembly1[index]
assembly1.remove(tempBlock1)
# grab stationary block flags
sBFList = self.r.core.stationaryBlockFlagsList

# identify stationary blocks for assembly 1
a1StationaryBlocks = [
[block, block.spatialLocator.k]
for block in assembly1
if any(block.hasFlags(sbf) for sbf in sBFList)
]
# identify stationary blocks for assembly 2
a2StationaryBlocks = [
[block, block.spatialLocator.k]
for block in assembly2
if any(block.hasFlags(sbf) for sbf in sBFList)
]

tempBlock2 = assembly2[index]
assembly2.remove(tempBlock2)
# check for any inconsistencies in stationary blocks and ensure alignment
if [block[1] for block in a1StationaryBlocks] != [
block[1] for block in a2StationaryBlocks
]:
raise ValueError(
"""Different number and/or locations of stationary blocks
between {} (Stationary Blocks: {}) and {} (Stationary Blocks: {}).""".format(
assembly1, a1StationaryBlocks, assembly2, a2StationaryBlocks
)
)

assembly1.insert(index, tempBlock2)
assembly2.insert(index, tempBlock1)
# swap stationary blocks
for (assem1Block, assem1BlockIndex), (assem2Block, assem2BlockIndex) in zip(
a1StationaryBlocks, a2StationaryBlocks
):
# remove stationary blocks
assembly1.remove(assem1Block)
assembly2.remove(assem2Block)
# insert stationary blocks
assembly1.insert(assem1BlockIndex, assem2Block)
assembly2.insert(assem2BlockIndex, assem1Block)

def dischargeSwap(self, incoming, outgoing):
r"""
Expand Down Expand Up @@ -1225,93 +1248,6 @@ def dischargeSwap(self, incoming, outgoing):
incoming.p.multiplicity = 1
self.r.core.add(incoming, loc)

self._swapFluxParam(incoming, outgoing)

def _swapFluxParam(self, incoming, outgoing):
"""
Set the flux and power params of the new blocks to that of the old and vice versa.
This is essential for getting loosely-coupled flux-averaged cross sections from things like
:py:class:`armi.physics.neutronics.crossSectionGroupManager.BlockCollectionAverageFluxWeighted`
Parameters
----------
incoming, outgoing : Assembly
Assembly objects to be swapped
"""
# Find the block-based mesh points for each assembly
meshIn = self.r.core.findAllAxialMeshPoints([incoming], False)
meshOut = self.r.core.findAllAxialMeshPoints([outgoing], False)

# If the assembly mesh points don't match, the swap won't be easy
if meshIn != meshOut:
runLog.debug(
"{0} and {1} have different meshes, resampling.".format(
incoming, outgoing
)
)

# grab the current values for incoming and outgoing
fluxIn = [b.p.flux for b in incoming]
mgFluxIn = [b.p.mgFlux for b in incoming]
powerIn = [b.p.power for b in incoming]
fluxOut = [b.p.flux for b in outgoing]
mgFluxOut = [b.p.mgFlux for b in outgoing]
powerOut = [b.p.power for b in outgoing]

# resample incoming to outgoing, and vice versa
fluxOutNew = resampleStepwise(meshIn, fluxIn, meshOut)
mgFluxOutNew = resampleStepwise(meshIn, mgFluxIn, meshOut)
powerOutNew = resampleStepwise(meshIn, powerIn, meshOut, avg=False)
fluxInNew = resampleStepwise(meshOut, fluxOut, meshIn)
mgFluxInNew = resampleStepwise(meshOut, mgFluxOut, meshIn)
powerInNew = resampleStepwise(meshOut, powerOut, meshIn, avg=False)

# load the new outgoing values into place
for b, flux, mgFlux, power in zip(
outgoing, fluxOutNew, mgFluxOutNew, powerOutNew
):
b.p.flux = flux
b.p.mgFlux = mgFlux
b.p.power = power
b.p.pdens = power / b.getVolume()

# load the new incoming values into place
for b, flux, mgFlux, power in zip(
incoming, fluxInNew, mgFluxInNew, powerInNew
):
b.p.flux = flux
b.p.mgFlux = mgFlux
b.p.power = power
b.p.pdens = power / b.getVolume()

return

# Since the axial mesh points match, do the simple swap
for bi, (bIncoming, bOutgoing) in enumerate(zip(incoming, outgoing)):
if bi in self.cs["stationaryBlocks"]:
# stationary blocks are already swapped
continue

incomingFlux = bIncoming.p.flux
incomingMgFlux = bIncoming.p.mgFlux
incomingPower = bIncoming.p.power
outgoingFlux = bOutgoing.p.flux
outgoingMgFlux = bOutgoing.p.mgFlux
outgoingPower = bOutgoing.p.power

if outgoingFlux > 0.0:
bIncoming.p.flux = outgoingFlux
bIncoming.p.mgFlux = outgoingMgFlux
bIncoming.p.power = outgoingPower
bIncoming.p.pdens = outgoingPower / bIncoming.getVolume()

if incomingFlux > 0.0:
bOutgoing.p.flux = incomingFlux
bOutgoing.p.mgFlux = incomingMgFlux
bOutgoing.p.power = incomingPower
bOutgoing.p.pdens = incomingPower / bOutgoing.getVolume()

def swapCascade(self, assemList):
"""
Perform swaps on a list of assemblies.
Expand Down
Loading

0 comments on commit 9a510ca

Please sign in to comment.