Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force plotAssemblyTypes to always use instantiated assemblies, and al… #2030

Merged
merged 11 commits into from
Jan 2, 2025
Merged
18 changes: 13 additions & 5 deletions armi/bookkeeping/report/newReportUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,9 +628,17 @@ def insertCoreAndAssemblyMaps(
generateFullCoreMap : bool, default False
showBlockAxMesh : bool, default True
"""
assemPrototypes = set()
assemPrototypes = []
for aKey in blueprint.assemDesigns.keys():
assemPrototypes.add(blueprint.constructAssem(cs, name=aKey))
a = blueprint.constructAssem(cs, name=aKey)
# since we will be plotting cold input heights, we need to make sure that
# that these new assemblies have access to a blueprints somewhere up the
# composite chain. normally this would happen through an assembly's parent
# reactor, but because these newly created assemblies are in the load queue,
# they will not have a parent reactor. to get around this, we just attach
# the blueprints to the assembly directly.
a.blueprints = blueprint
assemPrototypes.append(a)

counts = {
assemDesign.name: len(r.core.getChildrenOfType(assemDesign.name))
Expand All @@ -648,19 +656,19 @@ def insertCoreAndAssemblyMaps(
report[DESIGN]["Assembly Designs"] = newReports.Section("Assembly Designs")
currentSection = report[DESIGN]["Assembly Designs"]
for plotNum, assemBatch in enumerate(
iterables.chunk(list(assemPrototypes), MAX_ASSEMS_PER_ASSEM_PLOT), start=1
iterables.chunk(assemPrototypes, MAX_ASSEMS_PER_ASSEM_PLOT), start=1
):
assemPlotImage = newReports.Image(
imageCaption,
os.path.abspath(f"{core.name}AssemblyTypes{plotNum}.png"),
)
assemPlotName = os.path.abspath(f"{core.name}AssemblyTypes{plotNum}.png")
plotting.plotAssemblyTypes(
blueprint,
assemPlotName,
assemBatch,
assemPlotName,
maxAssems=MAX_ASSEMS_PER_ASSEM_PLOT,
showBlockAxMesh=showBlockAxMesh,
hot=False,
)
currentSection.addChildElement(assemPlotImage, assemPlotName)

Expand Down
20 changes: 16 additions & 4 deletions armi/bookkeeping/report/reportingUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1062,22 +1062,34 @@ def makeCoreAndAssemblyMaps(r, cs, generateFullCoreMap=False, showBlockAxMesh=Tr
generateFullCoreMap : bool, default False
showBlockAxMesh : bool, default True
"""
assemsInCore = list(r.blueprints.assemblies.values())
assems = []
blueprints = r.blueprints
for aKey in blueprints.assemDesigns.keys():
a = blueprints.constructAssem(cs, name=aKey)
# since we will be plotting cold input heights, we need to make sure that
# that these new assemblies have access to a blueprints somewhere up the
# composite chain. normally this would happen through an assembly's parent
# reactor, but because these newly created assemblies are in the load queue,
# they will not have a parent reactor. to get around this, we just attach
# the blueprints to the assembly directly.
a.blueprints = blueprints
assems.append(a)

core = r.core
for plotNum, assemBatch in enumerate(
iterables.chunk(assemsInCore, MAX_ASSEMS_PER_ASSEM_PLOT), start=1
iterables.chunk(assems, MAX_ASSEMS_PER_ASSEM_PLOT), start=1
):
assemPlotImage = copy(report.ASSEM_TYPES)
assemPlotImage.title = assemPlotImage.title + " ({})".format(plotNum)
report.data.Report.groupsOrderFirst.insert(-1, assemPlotImage)
report.data.Report.componentWellGroups.insert(-1, assemPlotImage)
assemPlotName = os.path.abspath(f"{core.name}AssemblyTypes{plotNum}.png")
plotting.plotAssemblyTypes(
core.parent.blueprints,
assemPlotName,
assemBatch,
assemPlotName,
maxAssems=MAX_ASSEMS_PER_ASSEM_PLOT,
showBlockAxMesh=showBlockAxMesh,
hot=False,
)

# Create radial core map
Expand Down
4 changes: 2 additions & 2 deletions armi/bookkeeping/tests/test_historyTracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ def test_historyParameters(self):
params[param] = []
for ts, years in enumerate(timesInYears):
cycle, node = utils.getCycleNodeFromCumulativeNode(ts, self.o.cs)

params[param].append(
hti.getBlockHistoryVal(bName, param, (cycle, node))
)
Expand All @@ -196,7 +195,8 @@ def test_historyParameters(self):
# verify the power parameter is retrievable from the history
self.assertEqual(o.cs["power"], 1000000000.0)
self.assertAlmostEqual(params["power"][0], 360, delta=0.1)
self.assertEqual(params["power"][0], params["power"][1])
# assembly was moved to the central location with 1/3rd symmetry
self.assertEqual(params["power"][0] / 3, params["power"][1])

# verify the power density parameter is retrievable from the history
self.assertAlmostEqual(params["pdens"][0], 0.0785, delta=0.001)
Expand Down
4 changes: 2 additions & 2 deletions armi/cases/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import ast
import cProfile
import glob
import io
import os
import pathlib
import pstats
Expand All @@ -38,7 +39,6 @@
import trace

import coverage
import six

from armi import context
from armi import getPluginManager
Expand Down Expand Up @@ -492,7 +492,7 @@ def _endProfiling(profiler=None):

profiler.disable()
profiler.dump_stats("profiler.{:0>3}.stats".format(context.MPI_RANK))
statsStream = six.StringIO()
statsStream = io.StringIO()
summary = pstats.Stats(profiler, stream=statsStream).sort_stats("cumulative")
summary.print_stats()
if context.MPI_SIZE > 0 and context.MPI_COMM is not None:
Expand Down
5 changes: 1 addition & 4 deletions armi/cli/entryPoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
import argparse
from typing import Optional, Union

import six

from armi import context, runLog, settings


Expand All @@ -46,8 +44,7 @@ def __new__(mcs, name, bases, attrs):
return type.__new__(mcs, name, bases, attrs)


@six.add_metaclass(_EntryPointEnforcer)
class EntryPoint:
class EntryPoint(metaclass=_EntryPointEnforcer):
"""
Generic command line entry point.

Expand Down
7 changes: 3 additions & 4 deletions armi/mpiActions.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,9 @@
import collections
import gc
import math
import pickle
import timeit

from six.moves import cPickle

from armi import context
from armi import interfaces
from armi import runLog
Expand Down Expand Up @@ -138,7 +137,7 @@ def _mpiOperationHelper(self, obj, mpiFunction):
self.o = self.r = self.cs = None
try:
return mpiFunction(obj, root=0)
except cPickle.PicklingError as error:
except pickle.PicklingError as error:
runLog.error("Failed to {} {}.".format(mpiFunction.__name__, obj))
runLog.error(error)
raise
Expand Down Expand Up @@ -539,7 +538,7 @@ def invokeHook(self):
# or how the interfaces are distributed.
self.r._markSynchronized()

except (cPickle.PicklingError, TypeError) as error:
except (pickle.PicklingError, TypeError) as error:
runLog.error("Failed to transmit on distribute state root MPI bcast")
runLog.error(error)
# workers are still waiting for a reactor object
Expand Down
4 changes: 1 addition & 3 deletions armi/nuclearDataIO/cccc/tests/test_cccc.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
import io
import unittest

import six

from armi.nuclearDataIO import cccc


Expand Down Expand Up @@ -104,4 +102,4 @@ def setUpClass(cls):
cls.readerClass = cccc.AsciiRecordReader

def setUp(self):
self.streamCls = six.StringIO
self.streamCls = io.StringIO
8 changes: 4 additions & 4 deletions armi/nuclearDataIO/tests/test_xsLibraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
import copy
import filecmp
import os
import pickle
import traceback
import unittest

import numpy as np
from six.moves import cPickle

from armi.nucDirectory import nuclideBases
from armi.nuclearDataIO import xsLibraries
Expand Down Expand Up @@ -87,15 +87,15 @@ def setUpClass(cls):
cls.xsLibGenerationErrorStack = traceback.format_exc()

def test_canPickleAndUnpickleISOTXS(self):
pikAA = cPickle.loads(cPickle.dumps(self.isotxsAA))
pikAA = pickle.loads(pickle.dumps(self.isotxsAA))
self.assertTrue(xsLibraries.compare(pikAA, self.isotxsAA))

def test_canPickleAndUnpickleGAMISO(self):
pikAA = cPickle.loads(cPickle.dumps(self.gamisoAA))
pikAA = pickle.loads(pickle.dumps(self.gamisoAA))
self.assertTrue(xsLibraries.compare(pikAA, self.gamisoAA))

def test_canPickleAndUnpicklePMATRX(self):
pikAA = cPickle.loads(cPickle.dumps(self.pmatrxAA))
pikAA = pickle.loads(pickle.dumps(self.pmatrxAA))
self.assertTrue(xsLibraries.compare(pikAA, self.pmatrxAA))

def test_compareWorks(self):
Expand Down
22 changes: 18 additions & 4 deletions armi/physics/fuelCycle/fuelHandlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from armi.physics.fuelCycle.settings import CONF_ASSEMBLY_ROTATION_ALG
from armi.reactor.flags import Flags
from armi.utils.customExceptions import InputError
from armi.reactor.parameters import ParamLocation


class FuelHandler:
Expand Down Expand Up @@ -211,11 +212,24 @@ def _compareAssem(candidate, current):

@staticmethod
def _getParamMax(a, paramName, blockLevelMax=True):
"""Get parameter with Block-level maximum."""
if blockLevelMax:
return a.getChildParamValues(paramName).max()
"""Get assembly/block-level maximum parameter value in assembly."""
multiplier = a.getSymmetryFactor()
if multiplier != 1:
# handle special case: volume-integrated parameters where symmetry factor is not 1
if blockLevelMax:
paramCollection = a.getBlocks()[0].p
else:
paramCollection = a.p
isVolumeIntegrated = (
paramCollection.paramDefs[paramName].location
== ParamLocation.VOLUME_INTEGRATED
)
multiplier = a.getSymmetryFactor() if isVolumeIntegrated else 1.0

return a.p[paramName]
if blockLevelMax:
return a.getChildParamValues(paramName).max() * multiplier
else:
return a.p[paramName] * multiplier

def findAssembly(
self,
Expand Down
28 changes: 24 additions & 4 deletions armi/physics/fuelCycle/tests/test_fuelHandlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from armi.settings import caseSettings
from armi.tests import TEST_ROOT, ArmiTestHelper, mockRunLogs
from armi.utils import directoryChangers
from armi.reactor.parameters import ParamLocation


class FuelHandlerTestHelper(ArmiTestHelper):
Expand Down Expand Up @@ -153,14 +154,32 @@ def interactBOC(self, cycle=None):


class TestFuelHandler(FuelHandlerTestHelper):
def test_getParamMax(self):
@patch("armi.reactor.assemblies.Assembly.getSymmetryFactor")
def test_getParamMax(self, mockGetSymmetry):

a = self.assembly
mockGetSymmetry.return_value = 1
expectedValue = 0.5
a.p["kInf"] = expectedValue
for b in a:
b.p["kInf"] = expectedValue

# symmetry factor == 1
res = fuelHandlers.FuelHandler._getParamMax(a, "kInf", True)
self.assertEqual(res, expectedValue)

res = fuelHandlers.FuelHandler._getParamMax(a, "kInf", False)
self.assertEqual(res, expectedValue)

# symmetry factor == 3
mockGetSymmetry.return_value = 3
a.p.paramDefs["kInf"].location = ParamLocation.VOLUME_INTEGRATED
a.getBlocks()[0].p.paramDefs["kInf"].location = ParamLocation.VOLUME_INTEGRATED
res = fuelHandlers.FuelHandler._getParamMax(a, "kInf", True)
self.assertEqual(res, 0.0)
self.assertAlmostEqual(res, expectedValue * 3)

res = fuelHandlers.FuelHandler._getParamMax(a, "kInf", False)
self.assertEqual(res, 0.0)
self.assertAlmostEqual(res, expectedValue * 3)

def test_interactBOC(self):
# set up mock interface
Expand Down Expand Up @@ -261,8 +280,9 @@ def test_width(self):
for ring, power in zip(range(1, 8), range(10, 80, 10)):
aList = assemsByRing[ring]
for a in aList:
sf = a.getSymmetryFactor() # center assembly is only 1/3rd in the core
for b in a:
b.p.power = power
b.p.power = power / sf

paramName = "power"
# 1 ring outer and inner from ring 3
Expand Down
8 changes: 4 additions & 4 deletions armi/physics/neutronics/tests/test_crossSectionManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@
"""
import copy
import os
import pickle
import unittest
from io import BytesIO
from unittest.mock import MagicMock

from six.moves import cPickle

from armi import settings
from armi.physics.neutronics import crossSectionGroupManager
from armi.physics.neutronics.const import CONF_CROSS_SECTION
Expand Down Expand Up @@ -73,9 +72,9 @@ def test_getBlocksInGroup(self):
def test_is_pickleable(self):
self.bc.weightingParam = "test"
buf = BytesIO()
cPickle.dump(self.bc, buf)
pickle.dump(self.bc, buf)
buf.seek(0)
newBc = cPickle.load(buf)
newBc = pickle.load(buf)
self.assertEqual(self.bc.weightingParam, newBc.weightingParam)


Expand All @@ -85,6 +84,7 @@ def setUp(self):
for bi, b in enumerate(self.blockList):
b.setType("fuel")
b.p.percentBu = bi / 4.0 * 100

self.blockList[0], self.blockList[2] = self.blockList[2], self.blockList[0]
self.bc = MedianBlockCollection(
self.blockList[0].core.r.blueprints.allNuclidesInProblem
Expand Down
22 changes: 22 additions & 0 deletions armi/reactor/assemblies.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import pickle
from random import randint
from typing import ClassVar, Optional, Type
from collections.abc import Iterable

import numpy as np
from scipy import interpolate
Expand Down Expand Up @@ -206,13 +207,34 @@ def insert(self, index, obj):

def moveTo(self, locator):
"""Move an assembly somewhere else."""
oldSymmetryFactor = self.getSymmetryFactor()
composites.Composite.moveTo(self, locator)
if self.lastLocationLabel != self.DATABASE:
self.p.numMoves += 1
self.p.daysSinceLastMove = 0.0
self.parent.childrenByLocator[locator] = self
# symmetry may have changed (either moving on or off of symmetry line)
self.clearCache()
self.scaleParamsToNewSymmetryFactor(oldSymmetryFactor)

def scaleParamsToNewSymmetryFactor(self, oldSymmetryFactor):
scalingFactor = oldSymmetryFactor / self.getSymmetryFactor()
if scalingFactor == 1:
return

volIntegratedParamsToScale = self.getBlocks()[0].p.paramDefs.atLocation(
ParamLocation.VOLUME_INTEGRATED
)
for b in self.getBlocks():
for param in volIntegratedParamsToScale:
name = param.name
if b.p[name] is None or isinstance(b.p[name], str):
continue
elif isinstance(b.p[name], Iterable):
b.p[name] = [value * scalingFactor for value in b.p[name]]
else:
# numpy array or other
b.p[name] = b.p[name] * scalingFactor

def getNum(self):
"""Return unique integer for this assembly."""
Expand Down
Loading