Skip to content

Commit

Permalink
Removing usages of getMasterCs in blocks.py (#937)
Browse files Browse the repository at this point in the history
  • Loading branch information
john-science committed Oct 12, 2022
1 parent 889777c commit 5db1134
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 79 deletions.
2 changes: 1 addition & 1 deletion armi/cli/reportsEntryPoint.py
Expand Up @@ -158,7 +158,7 @@ def createReportFromSettings(cs):
This will construct a reactor from the given settings and create BOL reports for
that reactor/settings.
"""
# not sure if this is necessary, but need to investigate more to understand possible
# TODO: not sure if this is necessary, but need to investigate more to understand possible
# side-effects before removing. Probably better to get rid of all uses of
# getMasterCs(), then we can remove all setMasterCs() calls without worrying.
settings.setMasterCs(cs)
Expand Down
94 changes: 75 additions & 19 deletions armi/physics/neutronics/globalFlux/globalFluxInterface.py
Expand Up @@ -21,23 +21,25 @@
import numpy
import scipy.integrate

from armi import runLog
from armi import interfaces
from armi.utils import units, codeTiming, getMaxBurnSteps
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.reactor.converters import uniformMesh
from armi.reactor.blocks import Block
from armi.reactor.converters import geometryConverters
from armi.reactor import assemblies
from armi.reactor.converters import uniformMesh
from armi.reactor.flags import Flags
from armi.physics import neutronics
from armi.physics import executers
from armi.utils import units, codeTiming, getMaxBurnSteps

ORDER = interfaces.STACK_ORDER.FLUX

RX_ABS_MICRO_LABELS = ["nGamma", "fission", "nalph", "np", "nd", "nt"]
RX_PARAM_NAMES = ["rateCap", "rateFis", "rateProdN2n", "rateProdFis", "rateAbs"]


# pylint: disable=too-many-public-methods
class GlobalFluxInterface(interfaces.Interface):
"""
Expand All @@ -63,7 +65,8 @@ def __init__(self, r, cs):
self.nodeFmt = "1d" # produce ig001_1.inp.
self._bocKeff = None # for tracking rxSwing

def getHistoryParams(self):
@staticmethod
def getHistoryParams():
"""Return parameters that will be added to assembly versus time history printouts."""
return ["detailedDpa", "detailedDpaPeak", "detailedDpaPeakRate"]

Expand Down Expand Up @@ -304,6 +307,7 @@ def __init__(self, label: Optional[str] = None):
self.aclpDoseLimit = None
self.loadPadElevation = None
self.loadPadLength = None
self.cs = None

self._geomType: geometry.GeomType
self.symmetry: str
Expand Down Expand Up @@ -573,37 +577,88 @@ def _updateDerivedParams(self):
self.r.core.p.maxPD = self.r.core.getMaxParam("arealPd")
self._updateAssemblyLevelParams()

def getDpaXs(self, b: Block):
"""Determine which cross sections should be used to compute dpa for a block.
Parameters
----------
b: Block
The block we want the cross sections for
Returns
-------
list : cross section values
"""
if self.cs["gridPlateDpaXsSet"] and b.hasFlags(Flags.GRID_PLATE):
dpaXsSetName = self.cs["gridPlateDpaXsSet"]
else:
dpaXsSetName = self.cs["dpaXsSet"]

try:
return constants.DPA_CROSS_SECTIONS[dpaXsSetName]
except KeyError:
raise KeyError(
"DPA cross section set {} does not exist".format(dpaXsSetName)
)

def getBurnupPeakingFactor(self, b: Block):
"""
Get the radial peaking factor to be applied to burnup and DPA for a Block
This may be informed by previous runs which used
detailed pin reconstruction and rotation. In that case,
it should be set on the cs setting ``burnupPeakingFactor``.
Otherwise, it just takes the current flux peaking, which
is typically conservatively high.
Parameters
----------
b: Block
The block we want the peaking factor for
Returns
-------
burnupPeakingFactor : float
The peak/avg factor for burnup and DPA.
"""
burnupPeakingFactor = self.cs["burnupPeakingFactor"]
if not burnupPeakingFactor and b.p.fluxPeak:
burnupPeakingFactor = b.p.fluxPeak / b.p.flux
elif not burnupPeakingFactor:
# no peak available. Finite difference model?
burnupPeakingFactor = 1.0

return burnupPeakingFactor

def updateDpaRate(self, blockList=None):
"""
Update state parameters that can be known right after the flux is computed
See Also
--------
updateFluenceAndDpa : uses values computed here to update cumulative dpa
"""
if blockList is None:
blockList = self.r.core.getBlocks()

hasDPA = False
for b in blockList:
xs = b.getDpaXs()
if not xs:
continue
xs = self.getDpaXs(b)
hasDPA = True
flux = b.getMgFlux() # n/cm^2/s
dpaPerSecond = computeDpaRate(flux, xs)
b.p.detailedDpaPeakRate = dpaPerSecond * b.getBurnupPeakingFactor()
b.p.detailedDpaPeakRate = dpaPerSecond * self.getBurnupPeakingFactor(b)
b.p.detailedDpaRate = dpaPerSecond

if not hasDPA:
return
self.r.core.p.peakGridDpaAt60Years = (
self.r.core.getMaxBlockParam(
"detailedDpaPeakRate", typeSpec=Flags.GRID_PLATE, absolute=False
)
* 60.0
* units.SECONDS_PER_YEAR

peakRate = self.r.core.getMaxBlockParam(
"detailedDpaPeakRate", typeSpec=Flags.GRID_PLATE, absolute=False
)
self.r.core.p.peakGridDpaAt60Years = peakRate * 60.0 * units.SECONDS_PER_YEAR

# also update maxes at this point (since this runs at every timenode, not just those w/ depletion steps)
self.updateMaxDpaParams()

Expand Down Expand Up @@ -665,6 +720,7 @@ class DoseResultsMapper(GlobalFluxResultMapper):
def __init__(self, depletionSeconds, options):
self.success = False
self.options = options
self.cs = self.options.cs
self.r = None
self.depletionSeconds = depletionSeconds

Expand Down Expand Up @@ -725,7 +781,7 @@ def updateFluenceAndDpa(self, stepTimeInSeconds, blockList=None):
)

for b in blockList:
burnupPeakingFactor = b.getBurnupPeakingFactor()
burnupPeakingFactor = self.getBurnupPeakingFactor(b)
b.p.residence += stepTimeInSeconds / units.SECONDS_PER_DAY
b.p.fluence += b.p.flux * stepTimeInSeconds
b.p.fastFluence += b.p.flux * stepTimeInSeconds * b.p.fastFluxFr
Expand Down
Expand Up @@ -11,25 +11,24 @@
# 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.
"""
Tests for generic global flux interface.
"""
"""Tests for generic global flux interface"""
# pylint: disable=missing-function-docstring,missing-class-docstring,protected-access,invalid-name,no-self-use,no-method-argument,import-outside-toplevel
import unittest

import numpy

from armi import settings

from armi.nuclearDataIO.cccc import isotxs
from armi.physics.neutronics.globalFlux import globalFluxInterface
from armi.reactor.tests import test_reactors
from armi.reactor.tests import test_blocks
from armi.reactor import geometry
from armi.reactor.blocks import HexBlock
from armi.reactor.flags import Flags
from armi.reactor.tests import test_blocks
from armi.reactor.tests import test_reactors
from armi.tests import ISOAA_PATH
from armi.nuclearDataIO.cccc import isotxs

# pylint: disable=missing-class-docstring

# pylint: disable=abstract-method
# pylint: disable=protected-access
class MockParams:
pass

Expand Down Expand Up @@ -120,6 +119,17 @@ def test_getIOFileNames(self):
inf, _outf, _stdname = gfi.getIOFileNames(1, 2, 1)
self.assertEqual(inf, "armi001_2_001.GlobalFlux.inp")

def test_getHistoryParams(self):
params = globalFluxInterface.GlobalFluxInterface.getHistoryParams()
self.assertEqual(len(params), 3)
self.assertIn("detailedDpa", params)

def test_checkEnergyBalance(self):
cs = settings.Settings()
_o, r = test_reactors.loadTestReactor()
gfi = MockGlobalFluxInterface(r, cs)
gfi._checkEnergyBalance()


class TestGlobalFluxInterfaceWithExecuters(unittest.TestCase):
"""Tests for the default global flux execution."""
Expand All @@ -143,6 +153,10 @@ def test_executerInteraction(self):
def test_calculateKeff(self):
self.assertEqual(self.gfi.calculateKeff(), 1.05) # set in mock

def test_getExecuterCls(self):
class0 = globalFluxInterface.GlobalFluxInterfaceUsingExecuters.getExecuterCls()
self.assertEqual(class0, globalFluxInterface.GlobalFluxExecuter)


class TestGlobalFluxResultMapper(unittest.TestCase):
"""
Expand All @@ -157,7 +171,7 @@ class TestGlobalFluxResultMapper(unittest.TestCase):
def test_mapper(self):
# Switch to MC2v2 setting to make sure the isotopic/elemental expansions are compatible
# with actually doing some math using the ISOAA test microscopic library
_o, r = test_reactors.loadTestReactor(customSettings={"xsKernel": "MC2v2"})
o, r = test_reactors.loadTestReactor(customSettings={"xsKernel": "MC2v2"})
applyDummyFlux(r)
r.core.lib = isotxs.readBinary(ISOAA_PATH)
mapper = globalFluxInterface.GlobalFluxResultMapper()
Expand All @@ -178,13 +192,50 @@ def test_mapper(self):
# to exercise blockList option (does not change behavior, since this is what
# apply() does anyway)
opts = globalFluxInterface.GlobalFluxOptions("test")
opts.fromUserSettings(o.cs)
dosemapper = globalFluxInterface.DoseResultsMapper(1000, opts)
dosemapper.apply(r, blockList=r.core.getBlocks())
self.assertGreater(block.p.detailedDpa, 0)

mapper.clearFlux()
self.assertEqual(len(block.p.mgFlux), 0)

def test_getDpaXs(self):
mapper = globalFluxInterface.GlobalFluxResultMapper()

# test fuel block
b = HexBlock("fuel", height=10.0)
vals = mapper.getDpaXs(b)
self.assertEqual(len(vals), 33)
self.assertAlmostEqual(vals[0], 2345.69, 1)

# build a grid plate block
b = HexBlock("grid_plate", height=10.0)
b.p.flags = Flags.GRID_PLATE
self.assertTrue(b.hasFlags(Flags.GRID_PLATE))

# test grid plate block
mapper.cs["gridPlateDpaXsSet"] = "dpa_EBRII_PE16"
vals = mapper.getDpaXs(b)
self.assertEqual(len(vals), 33)
self.assertAlmostEqual(vals[0], 2478.95, 1)

# test null case
mapper.cs["gridPlateDpaXsSet"] = "fake"
with self.assertRaises(KeyError):
mapper.getDpaXs(b)

def test_getBurnupPeakingFactor(self):
mapper = globalFluxInterface.GlobalFluxResultMapper()

# test fuel block
mapper.cs["burnupPeakingFactor"] = 0.0
b = HexBlock("fuel", height=10.0)
b.p.flux = 100.0
b.p.fluxPeak = 250.0
factor = mapper.getBurnupPeakingFactor(b)
self.assertEqual(factor, 2.5)


class TestGlobalFluxUtils(unittest.TestCase):
def test_calcReactionRates(self):
Expand All @@ -208,5 +259,4 @@ def applyDummyFlux(r, ng=33):


if __name__ == "__main__":
# import sys;sys.argv = ['', 'Test.testName']
unittest.main()
48 changes: 0 additions & 48 deletions armi/reactor/blocks.py
Expand Up @@ -40,7 +40,6 @@
from armi.utils import units
from armi.utils.plotting import plotBlockFlux
from armi.bookkeeping import report
from armi.physics import constants
from armi.utils.units import TRACE_NUMBER_DENSITY
from armi.utils import hexagon
from armi.utils import densityTools
Expand Down Expand Up @@ -1312,35 +1311,6 @@ def setAreaFractionsReport(self):
# return the group the information went to
return report.ALL[report.BLOCK_AREA_FRACS]

def getBurnupPeakingFactor(self):
"""
Get the radial peaking factor to be applied to burnup and DPA
This may be informed by previous runs which used
detailed pin reconstruction and rotation. In that case,
it should be set on the cs setting ``burnupPeakingFactor``.
Otherwise, it just takes the current flux peaking, which
is typically conservatively high.
Returns
-------
burnupPeakingFactor : float
The peak/avg factor for burnup and DPA.
See Also
--------
armi.physics.neutronics.globalFlux.globalFluxInterface.GlobalFluxInterface.updateFluenceAndDPA : uses this
"""
burnupPeakingFactor = settings.getMasterCs()["burnupPeakingFactor"]
if not burnupPeakingFactor and self.p.fluxPeak:
burnupPeakingFactor = self.p.fluxPeak / self.p.flux
elif not burnupPeakingFactor:
# no peak available. Finite difference model?
burnupPeakingFactor = 1.0

return burnupPeakingFactor

def getBlocks(self):
"""
This method returns all the block(s) included in this block
Expand Down Expand Up @@ -1374,24 +1344,6 @@ def updateComponentDims(self):
except NotImplementedError:
runLog.warning("{0} has no updatedDims method -- skipping".format(c))

def getDpaXs(self):
"""Determine which cross sections should be used to compute dpa for this block."""
if settings.getMasterCs()["gridPlateDpaXsSet"] and self.hasFlags(
Flags.GRID_PLATE
):
dpaXsSetName = settings.getMasterCs()["gridPlateDpaXsSet"]
else:
dpaXsSetName = settings.getMasterCs()["dpaXsSet"]

if not dpaXsSetName:
return None
try:
return constants.DPA_CROSS_SECTIONS[dpaXsSetName]
except KeyError:
raise KeyError(
"DPA cross section set {} does not exist".format(dpaXsSetName)
)

def breakFuelComponentsIntoIndividuals(self):
"""
Split block-level components (in fuel blocks) into pin-level components.
Expand Down

0 comments on commit 5db1134

Please sign in to comment.