Skip to content

Commit

Permalink
Update the uniform mesh converter to handle the reaction rate calcula…
Browse files Browse the repository at this point in the history
…tions and to remove the overly restrictive power conversion check
  • Loading branch information
jakehader committed Oct 3, 2022
1 parent 6557412 commit 8cdd901
Showing 1 changed file with 49 additions and 47 deletions.
96 changes: 49 additions & 47 deletions armi/reactor/converters/uniformMesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# 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.
from framework.armi.physics.neutronics.globalFlux.globalFluxInterface import calcReactionRates

"""
Converts reactor with arbitrary axial meshing (e.g. multiple assemblies with different
Expand Down Expand Up @@ -63,11 +64,11 @@

import armi
from armi import runLog
from armi import settings
from armi.utils.mathematics import average1DWithinTolerance
from armi.utils import iterables
from armi.utils import plotting
from armi.reactor import grids
from armi.reactor.reactors import Core
from armi.reactor.flags import Flags
from armi.reactor.converters.geometryConverters import GeometryConverter
from armi.reactor import parameters
Expand Down Expand Up @@ -99,6 +100,7 @@ def __init__(self, cs=None):
self._uniformMesh = None
self.reactorParamNames = []
self.blockParamNames = []
self.calcReactionRates = False

# These dictionaries represent back-up data from the source reactor
# that can be recovered if the data is not being brought back from
Expand Down Expand Up @@ -231,6 +233,7 @@ def applyStateToOriginal(self):
storedAssem,
self.blockParamNames,
mapNumberDensities=False,
calcReactionRates=self.calcReactionRates,
)

# Remove the stored assembly from the temporary storage list
Expand Down Expand Up @@ -397,6 +400,7 @@ def setAssemblyStateFromOverlaps(
destinationAssembly,
blockParamNames=None,
mapNumberDensities=True,
calcReactionRates=False,
):
"""
Set state data (i.e., number densities and block-level parameters) on a assembly based on a source
Expand Down Expand Up @@ -426,6 +430,10 @@ def setAssemblyStateFromOverlaps(
This can show up in specific instances with moving meshes (i.e., control rods) in some applications.
In those cases, the mapping of number densities can be treated independent of this more general
implementation.
calcReactionRates : bool, optional
If True, the neutron reaction rates will be calculated on each block within the destination
assembly. Note that this will skip the reaction rate calculations for a block if it does
not contain a valid multi-group flux.
See Also
--------
Expand Down Expand Up @@ -536,6 +544,18 @@ def setAssemblyStateFromOverlaps(
UniformMeshGeometryConverter._applyCachedParamValues(
destBlock, blockParamNames, cachedParams
)

# If requested, the reaction rates will be calculated based on the
# mapped neutron flux and the XS library.
if calcReactionRates:
core = destinationAssembly.getAncestor(lambda c: isinstance(c, Core))
if core is not None:
UniformMeshGeometryConverter._calculateReactionRates(lib=core.lib,
keff=core.p.keff,
assem=destinationAssembly)
else:
runLog.warning(f"Reaction rates requested for {destinationAssembly}, but no core object exists. This calculation "
"will be skipped.")

@staticmethod
def _applyCachedParamValues(destBlock, paramNames, cachedParams):
Expand Down Expand Up @@ -731,7 +751,10 @@ def _mapStateFromReactorToOther(
This can be implemented in sub-classes to map specific reactor and assembly data.
"""
pass


@staticmethod
def _calculateReactionRates(lib, keff, assem):
pass

class NeutronicsUniformMeshConverter(UniformMeshGeometryConverter):
"""
Expand Down Expand Up @@ -780,34 +803,6 @@ def _setParamsToUpdate(self):
for category in self.BLOCK_PARAM_MAPPING_CATEGORIES:
self.blockParamNames.extend(b.p.paramDefs.inCategory(category).names)

def _checkConversion(self):
"""
Make sure both reactors have the same power and that it's equal to user-input.
On the initial neutronics run, of course source power will be zero.
"""
UniformMeshGeometryConverter._checkConversion(self)
sourcePow = self._sourceReactor.core.getTotalBlockParam("power")
convPow = self.convReactor.core.getTotalBlockParam("power")
if sourcePow > 0.0 and convPow > 0.0:
if abs(sourcePow - convPow) / sourcePow > 1e-5:
runLog.info(
f"Source reactor power ({sourcePow}) is too different from "
f"converted power ({convPow})."
)

if self._sourceReactor.p.timeNode != 0:
# only check on nodes other than BOC
expectedPow = (
self._sourceReactor.core.p.power
/ self._sourceReactor.core.powerMultiplier
)
if sourcePow and abs(sourcePow - expectedPow) / sourcePow > 1e-5:
raise ValueError(
f"Source reactor power ({sourcePow}) is too different from "
f"user-input power ({expectedPow})."
)

def _mapStateFromReactorToOther(
self, sourceReactor, destReactor, mapNumberDensities=True
):
Expand Down Expand Up @@ -840,28 +835,35 @@ def _mapStateFromReactorToOther(
aDest,
self.blockParamNames,
mapNumberDensities,
calcReactionRates=self.calcReactionRates,
)

# If requested, the reaction rates will be calculated based on the
# mapped neutron flux and the XS library.
if self.calcReactionRates:
for b in aDest:
# Checks if the block has a multi-group flux defined and if it
# does not then this will skip the reaction rate calculation. This
# is captured by the TypeError, due to a `NoneType` divide by float
# error.
try:
b.getMgFlux()
except TypeError:
continue
globalFluxInterface.calcReactionRates(
b, destReactor.core.p.keff, destReactor.core.lib
)

# Clear the cached data after it has been mapped to prevent issues with
# holding on to block data long-term.
self._cachedReactorCoreParamData = {}


@staticmethod
def _calculateReactionRates(self, lib, keff, assem):
"""
Calculates the neutron reaction rates on the given assembly.
Notes
-----
If a block in the assembly does not contain any multi-group flux
than the reaction rate calculation for this block will be skipped.
"""
for b in assem:
# Checks if the block has a multi-group flux defined and if it
# does not then this will skip the reaction rate calculation. This
# is captured by the TypeError, due to a `NoneType` divide by float
# error.
try:
b.getMgFlux()
except TypeError:
continue
globalFluxInterface.calcReactionRates(
b, keff, lib
)

class BlockParamMapper:
"""
Expand Down

0 comments on commit 8cdd901

Please sign in to comment.