From b2f487f98d7f8a0f225d80733b281325d6ef61cc Mon Sep 17 00:00:00 2001 From: Michael Jarrett Date: Fri, 26 Jul 2024 19:23:55 -0700 Subject: [PATCH] Improve efficiency of UMC and calc RR --- .../globalFlux/globalFluxInterface.py | 70 +++++++++++++++---- armi/reactor/converters/uniformMesh.py | 43 ++++++++---- 2 files changed, 89 insertions(+), 24 deletions(-) diff --git a/armi/physics/neutronics/globalFlux/globalFluxInterface.py b/armi/physics/neutronics/globalFlux/globalFluxInterface.py index 3f43fe19e..a2e7108b1 100644 --- a/armi/physics/neutronics/globalFlux/globalFluxInterface.py +++ b/armi/physics/neutronics/globalFlux/globalFluxInterface.py @@ -16,24 +16,20 @@ and/or photon flux. """ import math +from collections import defaultdict from typing import Dict, Optional import numpy import scipy.integrate -from armi import interfaces -from armi import runLog -from armi.physics import constants -from armi.physics import executers -from armi.physics import neutronics -from armi.reactor import geometry -from armi.reactor import reactors +from armi import interfaces, runLog +from armi.physics import constants, executers, neutronics +from armi.reactor import geometry, reactors from armi.reactor.blocks import Block -from armi.reactor.converters import geometryConverters -from armi.reactor.converters import uniformMesh +from armi.reactor.converters import geometryConverters, uniformMesh from armi.reactor.flags import Flags from armi.settings.caseSettings import Settings -from armi.utils import units, codeTiming, getMaxBurnSteps, getBurnSteps +from armi.utils import codeTiming, getBurnSteps, getMaxBurnSteps, units ORDER = interfaces.STACK_ORDER.FLUX @@ -499,9 +495,9 @@ def fromUserSettings(self, cs: Settings): CONF_XS_KERNEL, ) from armi.settings.fwSettings.globalSettings import ( - CONF_PHYSICS_FILES, - CONF_NON_UNIFORM_ASSEM_FLAGS, CONF_DETAILED_AXIAL_EXPANSION, + CONF_NON_UNIFORM_ASSEM_FLAGS, + CONF_PHYSICS_FILES, ) self.kernelName = cs[CONF_NEUTRONICS_KERNEL] @@ -1315,6 +1311,56 @@ def computeDpaRate(mgFlux, dpaXs): return dpaPerSecond +def calcReactionRatesBlockList(objList, keff, xsNucDict): + r""" + Compute 1-group reaction rates for the objcects in objList (usually a block) + """ + + rate = {} + for simple in RX_PARAM_NAMES: + rate[simple] = 0.0 + + for obj in objList: + numberDensities = obj.getNumberDensities() + mgFlux = numpy.array(obj.getMgFlux()) + + for nucName, numberDensity in numberDensities.items(): + if numberDensity == 0.0: + continue + nucrate = {} + for simple in RX_PARAM_NAMES: + nucrate[simple] = 0.0 + + micros = xsNucDict[nucName].micros + + # absorption is fission + capture (no n2n here) + for name in RX_ABS_MICRO_LABELS: + volumetricRR = numberDensity * mgFlux.dot(micros[name]) + nucrate["rateAbs"] = volumetricRR + if name != "fission": + nucrate["rateCap"] += volumetricRR + else: + nucrate["rateFis"] += volumetricRR + # scale nu by keff. + nusigmaF = micros["fission"] * micros.neutronsPerFission + nucrate["rateProdFis"] += ( + numberDensity * mgFlux.dot(nusigmaF) / keff + ) + + nucrate["rateProdN2n"] += 2.0 * numberDensity * mgFlux.dot(micros.n2n) + + for simple in RX_PARAM_NAMES: + if nucrate[simple]: + rate[simple] += nucrate[simple] + + for paramName, val in rate.items(): + obj.p[paramName] = val # put in #/cm^3/s + + vFuel = obj.getComponentAreaFrac(Flags.FUEL) if rate["rateFis"] > 0.0 else 1.0 + obj.p.fisDens = rate["rateFis"] / vFuel + obj.p.fisDensHom = rate["rateFis"] + + def calcReactionRates(obj, keff, lib): r""" Compute 1-group reaction rates for this object (usually a block). diff --git a/armi/reactor/converters/uniformMesh.py b/armi/reactor/converters/uniformMesh.py index 88022be0f..9e258b75b 100644 --- a/armi/reactor/converters/uniformMesh.py +++ b/armi/reactor/converters/uniformMesh.py @@ -52,26 +52,23 @@ .. figure:: /.static/axial_homogenization.png """ -import re -import glob -import copy import collections +import copy +import glob +import re from timeit import default_timer as timer import numpy import armi from armi import runLog -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 import grids, parameters from armi.reactor.converters.geometryConverters import GeometryConverter -from armi.reactor import parameters -from armi.reactor.reactors import Reactor +from armi.reactor.flags import Flags +from armi.reactor.reactors import Core, Reactor from armi.settings.fwSettings.globalSettings import CONF_UNIFORM_MESH_MINIMUM_SIZE +from armi.utils import iterables, plotting +from armi.utils.mathematics import average1DWithinTolerance HEAVY_METAL_PARAMS = ["molesHmBOL", "massHmBOL"] @@ -1100,13 +1097,35 @@ def _mapStateFromReactorToOther( aDest, self.paramMapper, mapNumberDensities, - calcReactionRates=self.calcReactionRates, + calcReactionRates=False ) + if self.calcReactionRates: + self._calculateReactionRatesEfficient(destReactor.core.lib, sourceReactor.core.keff) + # Clear the cached data after it has been mapped to prevent issues with # holding on to block data long-term. self._cachedReactorCoreParamData = {} + @staticmethod + def _calculateReactionRatesEfficient(lib, keff): + """ + First, sort blocks into groups by XS type. Then, we just need to grab micros for each XS type once. + + Iterate over list of blocks with the given XS type; calculate reaction rates for these blocks + """ + xsTypeGroups = collections.defaultdict(list) + for b in self.r.core.getBlocks(): + xsTypeGroups[b.getMicroSuffix()].append(b) + + + for xsID, blockList in xsTypeGroups.items(): + xsNucDict = { + nuclide.name: self.r.core.lib.getNuclide(nuclide.name, xsID) + for nuclide in self.r.core.lib.getNuclides(xsID) + } + globalFluxInterface.calcReactionRatesBlockList(blockList, keff, xsNucDict) + @staticmethod def _calculateReactionRates(lib, keff, assem): """