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

Adding axial expansion support for CRAs #1226

Merged
merged 10 commits into from
Apr 25, 2023
2 changes: 1 addition & 1 deletion armi/reactor/blueprints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ def _prepConstruction(self, cs):
)
if not cs["inputHeightsConsideredHot"]:
runLog.header(
"=========== Axially expanding all assemblies (except control) from Tinput to Thot ==========="
"=========== Axially expanding all assemblies from Tinput to Thot ==========="
)
# expand axial heights from cold to hot so dims and masses are consistent
# with specified component hot temperatures.
Expand Down
24 changes: 8 additions & 16 deletions armi/reactor/converters/axialExpansionChanger.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,26 +74,18 @@ def expandColdDimsToHot(assems, isDetailedAxialExpansion, referenceAssembly=None
referenceAssembly = getDefaultReferenceAssem(assems)
axialExpChanger = AxialExpansionChanger(isDetailedAxialExpansion)
for a in assems:
if not a.hasFlags(Flags.CONTROL):
axialExpChanger.setAssembly(a)
# this doesn't get applied to control assems, so CR will be interpreted
# as hot. This should be conservative because the control rods will
# be modeled as slightly shorter with the correct hot density. Density
# is more important than height, so we are forcing density to be correct
# since we can't do axial expansion (yet)
axialExpChanger.applyColdHeightMassIncrease()
axialExpChanger.expansionData.computeThermalExpansionFactors()
axialExpChanger.axiallyExpandAssembly()
axialExpChanger.setAssembly(a)
albeanth marked this conversation as resolved.
Show resolved Hide resolved
axialExpChanger.applyColdHeightMassIncrease()
axialExpChanger.expansionData.computeThermalExpansionFactors()
axialExpChanger.axiallyExpandAssembly()
if not isDetailedAxialExpansion:
for a in assems:
if not a.hasFlags(Flags.CONTROL):
a.setBlockMesh(referenceAssembly.getAxialMesh())
a.setBlockMesh(referenceAssembly.getAxialMesh())
# update block BOL heights to reflect hot heights
for a in assems:
if not a.hasFlags(Flags.CONTROL):
for b in a:
b.p.heightBOL = b.getHeight()
b.completeInitialLoading()
for b in a:
b.p.heightBOL = b.getHeight()
b.completeInitialLoading()


class AxialExpansionChanger:
Expand Down
72 changes: 40 additions & 32 deletions armi/reactor/converters/tests/test_axialExpansionChanger.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,15 +831,15 @@ def setUp(self):
os.path.join(TEST_ROOT, "detailedAxialExpansion"),
customSettings={"inputHeightsConsideredHot": True},
)
reduceTestReactorRings(r, o.cs, 3)
reduceTestReactorRings(r, o.cs, 5)

self.stdAssems = [a for a in r.core.getAssemblies()]

oCold, rCold = loadTestReactor(
os.path.join(TEST_ROOT, "detailedAxialExpansion"),
customSettings={"inputHeightsConsideredHot": False},
)
reduceTestReactorRings(rCold, oCold.cs, 3)
reduceTestReactorRings(rCold, oCold.cs, 5)

self.testAssems = [a for a in rCold.core.getAssemblies()]

Expand All @@ -853,9 +853,6 @@ def test_coldAssemblyExpansion(self):
2. in armi.tests.detailedAxialExpansion.refSmallReactorBase.yaml,
Thot > Tinput resulting in a non-zero DeltaT. Each block in the
expanded case should therefore be a different height than that of the standard case.
- The one exception is for control assemblies. These designs can be unique from regular
pin type assemblies by allowing downward expansion. Because of this, they are skipped
for axial expansion.
"""
for aStd, aExp in zip(self.stdAssems, self.testAssems):
self.assertAlmostEqual(
Expand All @@ -869,37 +866,48 @@ def test_coldAssemblyExpansion(self):
hasCustomMaterial = any(
isinstance(c.material, custom.Custom) for c in bStd
)
if (aStd.hasFlags(Flags.CONTROL)) or (hasCustomMaterial):
if hasCustomMaterial:
checkColdBlockHeight(bStd, bExp, self.assertAlmostEqual, "the same")
else:
checkColdBlockHeight(bStd, bExp, self.assertNotEqual, "different")
if bStd.hasFlags(Flags.FUEL):
# fuel mass should grow because heights are considered cold heights
# and a cold 1 cm column has more mass than a hot 1 cm column
if not isinstance(
bStd.getComponent(Flags.FUEL).material, custom.Custom
):
# custom materials don't expand
self.assertGreater(bExp.getMass("U235"), bStd.getMass("U235"))

if not aStd.hasFlags(Flags.CONTROL) and not aStd.hasFlags(Flags.TEST):
if not hasCustomMaterial:
# skip blocks of custom material where liner is merged with clad
for cExp in bExp:
if not isinstance(cExp.material, custom.Custom):
matDens = cExp.material.density(Tc=cExp.temperatureInC)
compDens = cExp.getMassDensity()
msg = (
f"{cExp} {cExp.material} in {bExp} was not at correct density. \n"
+ f"expansion = {bExp.p.height / bStd.p.height} \n"
+ f"density = {matDens}, component density = {compDens} \n"
)
self.assertAlmostEqual(
matDens,
compDens,
places=7,
msg=msg,
)
self.checkColdHeightBlockMass(bStd, bExp, Flags.FUEL, "U235")
elif bStd.hasFlags(Flags.CONTROL):
self.checkColdHeightBlockMass(bStd, bExp, Flags.CONTROL, "B10")

if not aStd.hasFlags(Flags.TEST) and not hasCustomMaterial:
# skip blocks of custom material where liner is merged with clad
for cExp in bExp:
if not isinstance(cExp.material, custom.Custom):
matDens = cExp.material.density(Tc=cExp.temperatureInC)
compDens = cExp.getMassDensity()
msg = (
f"{cExp} {cExp.material} in {bExp} was not at correct density. \n"
+ f"expansion = {bExp.p.height / bStd.p.height} \n"
+ f"density = {matDens}, component density = {compDens} \n"
)
self.assertAlmostEqual(
matDens,
compDens,
places=7,
msg=msg,
)

def checkColdHeightBlockMass(
self, bStd: HexBlock, bExp: HexBlock, flagType: Flags, nuclide: str
):
"""checks that nuclide masses for blocks with input cold heights and "inputHeightsConsideredHot": True are underpredicted

Notes
-----
If blueprints have cold blocks heights with "inputHeightsConsideredHot": True in the inputs, then
the nuclide densities are thermally expanded but the block height is not. This ultimately results in
nuclide masses being underpredicted relative to the case where both nuclide densities and block heights
are thermally expanded.
"""
# custom materials don't expand
if not isinstance(bStd.getComponent(flagType).material, custom.Custom):
self.assertGreater(bExp.getMass(nuclide), bStd.getMass(nuclide))


def checkColdBlockHeight(bStd, bExp, assertType, strForAssertion):
Expand Down
1 change: 1 addition & 0 deletions doc/release/0.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ What's new in ARMI
#. Use TemporaryDirectoryChanger for executer.run() so dirs are cleaned up during run. (`PR#1219 <https://github.com/terrapower/armi/pull/1219>`_)
#. New option ``copyOutput`` for globalFluxInterface to not copy output back to working directory. (`PR#1218 <https://github.com/terrapower/armi/pull/1218>`_, `PR#1227 <https://github.com/terrapower/armi/pull/1227>`_)
#. `Executer` class has a ``dcType`` attribute to define the type of ``DirectoryChanger`` it will use. (`PR#1228 <https://github.com/terrapower/armi/pull/1228>`_)
#. Enabling one-way (upwards) axial expansion of control assemblies. (`PR#1226 <https://github.com/terrapower/armi/pull/1226>`_)

Bug fixes
---------
Expand Down