Skip to content

Commit

Permalink
Updating 1D block converter to prevent negative area components (#775)
Browse files Browse the repository at this point in the history
  • Loading branch information
jakehader committed Jul 19, 2022
1 parent 6b94942 commit 2be48d3
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 3 deletions.
23 changes: 21 additions & 2 deletions armi/reactor/converters/blockConverters.py
Expand Up @@ -518,7 +518,6 @@ def __init__(
driverFuelBlock
)
)

self.pinPitch = sourceBlock.getPinPitch()
self.mergeIntoClad = mergeIntoClad or []
self.interRingComponent = sourceBlock.getComponent(Flags.COOLANT, exact=True)
Expand Down Expand Up @@ -547,6 +546,13 @@ def convert(self):
self._buildNthRing(pinComponents, ring)
self._buildNonPinRings(nonPins)
self._addDriverFuelRings()

for comp in self.convertedBlock.getComponents():
assert comp.getArea() >= 0.0, (
f"{comp} in {self.convertedBlock} has a negative area of {comp.getArea()}. "
"Negative areas are not supported."
)

return self.convertedBlock

def _dissolveComponents(self):
Expand All @@ -566,12 +572,25 @@ def _classifyComponents(self):
"""
Figure out which components are in each pin ring and which are not.
Notes
-----
Assumption is that anything with multiplicity equal to numPins is a pin (clad, wire, bond, etc.)
Non-pins will include things like coolant, duct, interduct, whatever else.
Non-pins will include things like coolant, duct, interduct, etc.
This skips components that have a negative area, which can exist if a user implements a linked
component containing void or non-solid materials (e.g., gaps)
"""
pinComponents, nonPins = [], []

for c in self._sourceBlock:

# If the area of the component is negative than this component should be skipped
# altogether. If not skipped, the conversion process still works, but this would
# result in one or more rings having an outer diameter than is smaller than the
# inner diameter.
if c.getArea() < 0.0:
continue

if (
self._sourceBlock.getNumComponents(c.p.flags)
== self._sourceBlock.getNumPins()
Expand Down
90 changes: 89 additions & 1 deletion armi/reactor/converters/tests/test_blockConverter.py
Expand Up @@ -19,6 +19,7 @@
import numpy

from armi.reactor.converters import blockConverters
from armi.reactor import blocks
from armi.reactor import components
from armi.reactor.flags import Flags
from armi.reactor.tests.test_blocks import loadTestBlock
Expand Down Expand Up @@ -163,6 +164,34 @@ def test_convertHexWithFuelDriver(self):
hexagon.numPositionsInRing,
)

def test_convertHexWithFuelDriverOnNegativeComponentAreaBlock(self):
"""
Tests the conversion of a control block with linked components, where
a component contains a negative area due to thermal expansion.
"""
driverBlock = (
loadTestReactor(TEST_ROOT)[1]
.core.getAssemblies(Flags.FUEL)[2]
.getFirstBlock(Flags.FUEL)
)

block = buildControlBlockWithLinkedNegativeAreaComponent()
areas = [c.getArea() for c in block]

# Check that a negative area component exists.
self.assertLess(min(areas), 0.0)

driverBlock.spatialGrid = None
block.spatialGrid = grids.HexGrid.fromPitch(1.0)

converter = blockConverters.HexComponentsToCylConverter(
block, driverFuelBlock=driverBlock, numExternalRings=2
)
convertedBlock = converter.convert()
# The area is increased because the negative area components are
# removed.
self.assertGreater(convertedBlock.getArea(), block.getArea())

def test_convertCartesianLatticeWithFuelDriver(self):
"""Test conversion with fuel driver."""
r = loadTestReactor(TEST_ROOT, inputFileName="zpprTest.yaml")[1]
Expand Down Expand Up @@ -277,7 +306,7 @@ def _buildJoyoFuel():
mult=91,
)
clad = components.Circle(
name="fuel",
name="clad",
material="HT9",
Tinput=20.0,
Thot=20.0,
Expand All @@ -288,6 +317,65 @@ def _buildJoyoFuel():
return fuel, clad


def buildControlBlockWithLinkedNegativeAreaComponent():
"""
Return a block that contains a bond component that resolves to a negative area
once the fuel and clad thermal expansion have occurred.
"""
b = blocks.HexBlock("control", height=10.0)

controlDims = {"Tinput": 25.0, "Thot": 600, "od": 0.77, "id": 0.00, "mult": 127.0}
bondDims = {
"Tinput": 600,
"Thot": 600,
"od": "clad.id",
"id": "control.od",
"mult": 127.0,
}
cladDims = {"Tinput": 25.0, "Thot": 450, "od": 0.80, "id": 0.77, "mult": 127.0}
wireDims = {
"Tinput": 25.0,
"Thot": 450,
"od": 0.1,
"id": 0.0,
"mult": 127.0,
"axialPitch": 30.0,
"helixDiameter": 0.9,
}
ductDims = {"Tinput": 25.0, "Thot": 400, "op": 16, "ip": 15.3, "mult": 1.0}
intercoolantDims = {
"Tinput": 400,
"Thot": 400,
"op": 17.0,
"ip": ductDims["op"],
"mult": 1.0,
}
coolDims = {"Tinput": 25.0, "Thot": 400}

control = components.Circle("control", "UZr", **controlDims)
clad = components.Circle("clad", "HT9", **cladDims)
# This sets up the linking of the bond to the fuel and the clad components.
bond = components.Circle(
"bond", "Sodium", components={"control": control, "clad": clad}, **bondDims
)
wire = components.Helix("wire", "HT9", **wireDims)
duct = components.Hexagon("duct", "HT9", **ductDims)
coolant = components.DerivedShape("coolant", "Sodium", **coolDims)
intercoolant = components.Hexagon("intercoolant", "Sodium", **intercoolantDims)

b.add(control)
b.add(bond)
b.add(clad)
b.add(wire)
b.add(duct)
b.add(coolant)
b.add(intercoolant)

b.getVolumeFractions() # TODO: remove, should be no-op when removed self.cached

return b


if __name__ == "__main__":
# import sys;sys.argv = ['', 'TestBlockConverter.test_convertHexWithFuelDriver']
unittest.main()

0 comments on commit 2be48d3

Please sign in to comment.