Skip to content

Commit

Permalink
Merge branch 'main' into component_flux_up
Browse files Browse the repository at this point in the history
  • Loading branch information
albeanth committed Nov 6, 2024
2 parents 1f869aa + 1bb50ee commit fb66716
Show file tree
Hide file tree
Showing 17 changed files with 149 additions and 40 deletions.
13 changes: 7 additions & 6 deletions .github/workflows/unittests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ jobs:
runs-on: ubuntu-24.04
strategy:
matrix:
python: [3.9, '3.10', '3.11', '3.12']
python: [3.9, '3.10', '3.11', '3.12', '3.13']

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
allow-prereleases: true
- name: Update package index
run: sudo apt-get update
- name: Install mpi libs
Expand All @@ -37,6 +38,6 @@ jobs:
run: |
pip install -e .[memprof,mpi,test]
pytest -n 4 armi
mpiexec -n 2 --use-hwthread-cpus coverage run --rcfile=pyproject.toml -m pytest --cov=armi --cov-config=pyproject.toml --ignore=venv armi/tests/test_mpiFeatures.py || true
mpiexec -n 2 --use-hwthread-cpus coverage run --rcfile=pyproject.toml -m pytest --cov=armi --cov-config=pyproject.toml --ignore=venv armi/tests/test_mpiParameters.py || true
mpiexec -n 2 --use-hwthread-cpus coverage run --rcfile=pyproject.toml -m pytest --cov=armi --cov-config=pyproject.toml --ignore=venv armi/utils/tests/test_directoryChangersMpi.py || true
mpiexec -n 2 --use-hwthread-cpus pytest armi/tests/test_mpiFeatures.py
mpiexec -n 2 --use-hwthread-cpus pytest armi/tests/test_mpiParameters.py
mpiexec -n 2 --use-hwthread-cpus pytest armi/utils/tests/test_directoryChangersMpi.py
8 changes: 5 additions & 3 deletions armi/bookkeeping/db/tests/test_comparedb3.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,15 +228,17 @@ def test_diffSpecialData(self):
refData4 = f4.create_dataset("numberDensities", data=a2)
refData4.attrs["shapes"] = "2"
refData4.attrs["numDens"] = a2
refData4.attrs["specialFormatting"] = True
f5 = h5py.File("test_diffSpecialData5.hdf5", "w")
srcData5 = f5.create_dataset("numberDensities", data=a2)
srcData5.attrs["shapes"] = "2"
srcData5.attrs["numDens"] = a2
srcData5.attrs["specialFormatting"] = True

# there should an exception
with self.assertRaises(Exception) as e:
# there should a log message
with mockRunLogs.BufferLog() as mock:
_diffSpecialData(refData4, srcData5, out, dr)
self.assertIn("Unable to unpack special data for paramName", e)
self.assertIn("Unable to unpack special data for", mock.getStdout())

# make an H5 datasets that will add a np.inf diff because keys don't match
f6 = h5py.File("test_diffSpecialData6.hdf5", "w")
Expand Down
2 changes: 0 additions & 2 deletions armi/physics/neutronics/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,8 +655,6 @@ def _getNeutronicsBlockParams():
categories=[parameters.Category.neutronics],
)

pb.defParam("powerDecay", units=units.WATTS, description="Total decay power")

pb.defParam(
"powerGamma",
units=units.WATTS,
Expand Down
21 changes: 18 additions & 3 deletions armi/reactor/components/componentParameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""Component parameter definitions."""
from armi.reactor import parameters
from armi.reactor.parameters import ParamLocation
from armi.reactor.parameters.parameterDefinitions import isNumpyArray
from armi.utils import units
from armi.reactor.parameters.parameterDefinitions import isNumpyArray

Expand All @@ -36,7 +37,7 @@ def getComponentParameterDefinitions():
pb.defParam(
"mult",
units=units.UNITLESS,
description="The multiplicity of this component, i.e. how many of them there are. ",
description="The multiplicity of this component, i.e. how many of them there are.",
default=1,
)

Expand Down Expand Up @@ -64,6 +65,20 @@ def getComponentParameterDefinitions():
description="Number densities of each nuclide.",
)

pb.defParam(
"detailedNDens",
setter=isNumpyArray("detailedNDens"),
units=f"atoms/(bn*{units.CM})",
description=(
"High-fidelity number density vector with up to thousands of nuclides. "
"Used in high-fi depletion runs where low-fi depletion may also be occurring. "
"This param keeps the hi-fi and low-fi depletion values from interfering. "
"See core.p.detailedNucKeys for keys."
),
saveToDB=True,
default=None,
)

pb.defParam(
"percentBu",
units=f"{units.PERCENT_FIMA}",
Expand Down Expand Up @@ -99,7 +114,7 @@ def getComponentParameterDefinitions():
pb.defParam(
"customIsotopicsName",
units=units.UNITLESS,
description="Label of isotopics applied to this component. ",
description="Label of isotopics applied to this component.",
)

pb.defParam(
Expand All @@ -112,7 +127,7 @@ def getComponentParameterDefinitions():
pb.defParam(
"zrFrac",
units=units.UNITLESS,
description="Original Zr frac of this, used for material properties. ",
description="Original Zr frac of this, used for material properties.",
)

pb.defParam(
Expand Down
3 changes: 3 additions & 0 deletions armi/reactor/composites.py
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,9 @@ def changeNDensByFactor(self, factor):
nuc: val * factor for nuc, val in self.getNumberDensities().items()
}
self.setNumberDensities(densitiesScaled)
# Update detailedNDens
if self.p.detailedNDens is not None:
self.p.detailedNDens *= factor

def clearNumberDensities(self):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,11 +348,7 @@ def axiallyExpandAssembly(self):
c.zbottom = self.linked.linkedBlocks[b].lower.p.ztop
c.ztop = c.zbottom + c.height
# update component number densities
newNumberDensities = {
nuc: c.getNumberDensity(nuc) / growFrac
for nuc in c.getNuclides()
}
c.setNumberDensities(newNumberDensities)
c.changeNDensByFactor(1.0 / growFrac)
# redistribute block boundaries if on the target component
if self.expansionData.isTargetComponent(c):
b.p.ztop = c.ztop
Expand Down Expand Up @@ -393,7 +389,7 @@ def manageCoreMesh(self, r):
if not self._detailedAxialExpansion:
# loop through again now that the reference is adjusted and adjust the non-fuel assemblies.
for a in r.core.getAssemblies():
a.setBlockMesh(r.core.refAssem.getAxialMesh())
a.setBlockMesh(r.core.refAssem.getAxialMesh(), conserveMassFlag="auto")

oldMesh = r.core.p.axialMesh
r.core.updateAxialMesh()
Expand Down
100 changes: 93 additions & 7 deletions armi/reactor/converters/tests/test_axialExpansionChanger.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

"""Test axialExpansionChanger."""
import collections
import copy
import os
import unittest
from statistics import mean
Expand All @@ -28,15 +29,15 @@
from armi.reactor.components.basicShapes import Circle, Hexagon, Rectangle
from armi.reactor.components.complexShapes import Helix
from armi.reactor.converters.axialExpansionChanger import (
AxialExpansionChanger,
AssemblyAxialLinkage,
AxialExpansionChanger,
ExpansionData,
getSolidComponents,
iterSolidComponents,
)
from armi.reactor.converters.axialExpansionChanger.assemblyAxialLinkage import (
areAxiallyLinked,
AxialLink,
areAxiallyLinked,
)
from armi.reactor.flags import Flags
from armi.reactor.tests.test_reactors import loadTestReactor, reduceTestReactorRings
Expand Down Expand Up @@ -270,6 +271,7 @@ def test_thermalExpansionContractionConservation_simple(self):
a = buildTestAssemblyWithFakeMaterial(name="HT9")
origMesh = a.getAxialMesh()[:-1]
origMasses, origNDens = self._getComponentMassAndNDens(a)
origDetailedNDens = self._setComponentDetailedNDens(a, origNDens)
axialExpChngr = AxialExpansionChanger(detailedAxialExpansion=True)

tempGrid = linspace(0.0, a.getHeight())
Expand All @@ -284,16 +286,20 @@ def test_thermalExpansionContractionConservation_simple(self):
# Set new isothermal temp and expand
tempField = array([temp] * len(tempGrid))
oldMasses, oldNDens = self._getComponentMassAndNDens(a)
oldDetailedNDens = self._getComponentDetailedNDens(a)
axialExpChngr.performThermalAxialExpansion(a, tempGrid, tempField)
newMasses, newNDens = self._getComponentMassAndNDens(a)
newDetailedNDens = self._getComponentDetailedNDens(a)
self._checkMass(oldMasses, newMasses)
self._checkNDens(oldNDens, newNDens, totGrowthFrac)
self._checkDetailedNDens(oldDetailedNDens, newDetailedNDens, totGrowthFrac)

# make sure that the assembly returned to the original state
for orig, new in zip(origMesh, a.getAxialMesh()):
self.assertAlmostEqual(orig, new, places=12)
self._checkMass(origMasses, newMasses)
self._checkNDens(origNDens, newNDens, 1.0)
self._checkDetailedNDens(origDetailedNDens, newDetailedNDens, 1.0)

def test_thermalExpansionContractionConservation_complex(self):
"""Thermally expand and then contract to ensure original state is recovered.
Expand Down Expand Up @@ -416,6 +422,17 @@ def _checkNDens(self, prevNDen, newNDens, ratio):
if prev:
self.assertAlmostEqual(prev / new, ratio, msg=f"{prev} / {new}")

def _checkDetailedNDens(self, prevDetailedNDen, newDetailedNDens, ratio):
"""Check whether the detailedNDens of two input dictionaries containing the
detailedNDens arrays for all components of an assembly are conserved.
"""
for prevComp, newComp in zip(
prevDetailedNDen.values(), newDetailedNDens.values()
):
for prev, new in zip(prevComp, newComp):
if prev:
self.assertAlmostEqual(prev / new, ratio, msg=f"{prev} / {new}")

@staticmethod
def _getComponentMassAndNDens(a):
masses = {}
Expand All @@ -426,6 +443,30 @@ def _getComponentMassAndNDens(a):
nDens[c] = c.getNumberDensities()
return masses, nDens

@staticmethod
def _setComponentDetailedNDens(a, nDens):
"""Returns a dictionary that contains detailedNDens for all components in an
assembly object input which are set to the corresponding component number densities
from a number density dictionary input.
"""
detailedNDens = {}
for b in a:
for c in getSolidComponents(b):
c.p.detailedNDens = copy.deepcopy([val for val in nDens[c].values()])
detailedNDens[c] = c.p.detailedNDens
return detailedNDens

@staticmethod
def _getComponentDetailedNDens(a):
"""Returns a dictionary containing all solid components and their corresponding
detailedNDens from an assembly object input.
"""
detailedNDens = {}
for b in a:
for c in getSolidComponents(b):
detailedNDens[c] = copy.deepcopy(c.p.detailedNDens)
return detailedNDens

def test_targetComponentMassConservation(self):
"""Tests mass conservation for target components."""
self.expandAssemForMassConservationTest()
Expand Down Expand Up @@ -571,20 +612,65 @@ def setUp(self):
reduceTestReactorRings(self.r, o.cs, 3)

self.oldAxialMesh = self.r.core.p.axialMesh
self.componentLst = []
for b in self.r.core.refAssem:
if b.hasFlags([Flags.FUEL, Flags.PLENUM]):
self.componentLst.extend(getSolidComponents(b))
# expand refAssem by 1.01 L1/L0
componentLst = [c for b in self.r.core.refAssem for c in b]
expansionGrowthFracs = 1.01 + zeros(len(componentLst))
expansionGrowthFracs = 1.01 + zeros(len(self.componentLst))
(
self.origDetailedNDens,
self.origVolumes,
) = self._getComponentDetailedNDensAndVol(self.componentLst)
self.axialExpChngr.performPrescribedAxialExpansion(
self.r.core.refAssem, componentLst, expansionGrowthFracs, setFuel=True
self.r.core.refAssem, self.componentLst, expansionGrowthFracs, setFuel=True
)

def test_manageCoreMesh(self):
self.axialExpChngr.manageCoreMesh(self.r)
newAxialMesh = self.r.core.p.axialMesh
# skip first and last entries as they do not change
for old, new in zip(self.oldAxialMesh[1:-1], newAxialMesh[1:-1]):
# the top and bottom and top of the grid plate block are not expected to change
for old, new in zip(self.oldAxialMesh[2:-1], newAxialMesh[2:-1]):
self.assertLess(old, new)

def test_componentConservation(self):
self.axialExpChngr.manageCoreMesh(self.r)
newDetailedNDens, newVolumes = self._getComponentDetailedNDensAndVol(
self.componentLst
)
for c in newVolumes.keys():
self._checkMass(
self.origDetailedNDens[c],
self.origVolumes[c],
newDetailedNDens[c],
newVolumes[c],
c,
)

def _getComponentDetailedNDensAndVol(self, componentLst):
"""Returns a tuple containing dictionaries of detailedNDens and volumes of
all components from a component list input.
"""
detailedNDens = {}
volumes = {}
for c in componentLst:
c.p.detailedNDens = [val for val in c.getNumberDensities().values()]
detailedNDens[c] = copy.deepcopy(c.p.detailedNDens)
volumes[c] = c.getVolume()
return (detailedNDens, volumes)

def _checkMass(self, origDetailedNDens, origVolume, newDetailedNDens, newVolume, c):
for prevMass, newMass in zip(
origDetailedNDens * origVolume, newDetailedNDens * newVolume
):
if c.parent.hasFlags(Flags.FUEL):
self.assertAlmostEqual(
prevMass, newMass, delta=1e-12, msg=f"{c}, {c.parent}"
)
else:
# should not conserve mass here as it is structural material above active fuel
self.assertAlmostEqual(newMass / prevMass, 0.99, msg=f"{c}, {c.parent}")


class TestExceptions(AxialExpansionTestBase, unittest.TestCase):
"""Verify exceptions are caught."""
Expand Down
2 changes: 2 additions & 0 deletions armi/reactor/tests/test_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,9 +700,11 @@ def test_getNumberDensities(self):
def test_changeNumberDensities(self):
"""Test that demonstates that the number densities on a component can be modified."""
self.component.p.numberDensities = {"NA23": 1.0}
self.component.p.detailedNDens = [1.0]
self.assertEqual(self.component.getNumberDensity("NA23"), 1.0)
self.component.changeNDensByFactor(3.0)
self.assertEqual(self.component.getNumberDensity("NA23"), 3.0)
self.assertEqual(self.component.p.detailedNDens[0], 3.0)

def test_fuelMass(self):
nominalMass = self.component.getMass()
Expand Down
2 changes: 1 addition & 1 deletion armi/tests/ThRZSettings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ settings:
comment: "Revised benchmark "
geomFile: ThRZGeom.xml
loadingFile: ThRZloading.yaml
numProcessors: 12
nTasks: 12
outputFileExtension: png
power: 1000000.0

Expand Down
2 changes: 1 addition & 1 deletion armi/tests/anl-afci-177/anl-afci-177.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ settings:
- 100
comment: ANL-AFCI-177 CR 1.0 metal core but with HALEU instead of TRU
genXS: Neutron
numProcessors: 1
nTasks: 1
versions:
armi: uncontrolled
2 changes: 1 addition & 1 deletion armi/tests/c5g7/c5g7-settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ settings:
cycleLength: 411.11
loadingFile: c5g7-blueprints.yaml
nCycles: 10
numProcessors: 1
nTasks: 1
power: 1000000000.0
versions:
armi: uncontrolled
Expand Down
2 changes: 1 addition & 1 deletion armi/tests/godiva/godiva.armi.unittest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ settings:
neutronicsKernel: DIF3D-FD
neutronicsOutputsToSave: All
neutronicsType: both
numProcessors: 36
nTasks: 36
outers: 200
power: 0.001
verbosity: debug
Expand Down
2 changes: 1 addition & 1 deletion armi/tests/zpprTest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ settings:
geomFile: zpprTestGeom.xml
loadingFile: 1DslabXSByCompTest.yaml
mpiTasksPerNode: 6
numProcessors: 12
nTasks: 12
outputFileExtension: pdf
power: 75000000.0
sortReactor: false # zpprs dont sor the right way. need better component sorting for slab...
Expand Down
5 changes: 5 additions & 0 deletions armi/utils/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ def __new__(cls, name, bases, attrs):

# Auto fields have been resolved, so now collect all ints
allFields = {name: val for name, val in attrs.items() if isinstance(val, int)}
allFields = {n: v for n, v in allFields.items() if not _FlagMeta.isdunder(n)}
flagClass._nameToValue = allFields
flagClass._valuesTaken = set(val for _, val in allFields.items())
flagClass._autoAt = autoAt
Expand All @@ -104,6 +105,10 @@ def __new__(cls, name, bases, attrs):

return flagClass

@staticmethod
def isdunder(s):
return s.startswith("__") and s.endswith("__")

def __getitem__(cls, key):
"""
Implement indexing at the class level.
Expand Down
Loading

0 comments on commit fb66716

Please sign in to comment.