From 42a637b7380301777ea3ef94be06dcec61a1b00f Mon Sep 17 00:00:00 2001 From: Mark Onufer Date: Wed, 10 Aug 2022 11:21:20 -0700 Subject: [PATCH 1/5] init compoenent with correct density --- armi/reactor/blueprints/blockBlueprint.py | 10 ----- .../blueprints/tests/test_blockBlueprints.py | 13 +------ armi/reactor/components/component.py | 4 +- .../tests/test_axialExpansionChanger.py | 5 +++ armi/reactor/tests/test_components.py | 38 ++++++++++--------- 5 files changed, 29 insertions(+), 41 deletions(-) diff --git a/armi/reactor/blueprints/blockBlueprint.py b/armi/reactor/blueprints/blockBlueprint.py index 6ee555cf38..0a41a9e00c 100644 --- a/armi/reactor/blueprints/blockBlueprint.py +++ b/armi/reactor/blueprints/blockBlueprint.py @@ -119,16 +119,6 @@ def construct( materialInput, componentDesign ) c = componentDesign.construct(blueprint, filteredMaterialInput) - if cs["inputHeightsConsideredHot"]: - if "group" in c.name: - for component in c: - component.applyHotHeightDensityReduction() - componentBlueprint.insertDepletableNuclideKeys( - component, blueprint - ) - else: - c.applyHotHeightDensityReduction() - componentBlueprint.insertDepletableNuclideKeys(c, blueprint) components[c.name] = c if spatialGrid: componentLocators = gridDesign.getMultiLocator( diff --git a/armi/reactor/blueprints/tests/test_blockBlueprints.py b/armi/reactor/blueprints/tests/test_blockBlueprints.py index ab3f7eabfa..98f0387b95 100644 --- a/armi/reactor/blueprints/tests/test_blockBlueprints.py +++ b/armi/reactor/blueprints/tests/test_blockBlueprints.py @@ -339,20 +339,9 @@ def test_densityConsistentWithComponentConstructor(self): delta=biggerDelta, ) - # This should be equal, but block construction calls applyHotHeightDensityReduction - # while programmatic construction allows components to exist in a state where - # their density is not consistent with material density. - self.assertNotAlmostEqual( + self.assertAlmostEqual( clad.getMassDensity(), programaticClad.getMassDensity(), - delta=biggerDelta, - ) - # its off by a factor of thermal expansion - self.assertNotAlmostEqual( - clad.getMassDensity(), - programaticClad.getMassDensity() - * programaticClad.getThermalExpansionFactor(), - delta=biggerDelta, ) diff --git a/armi/reactor/components/component.py b/armi/reactor/components/component.py index 9e5ee72d89..df20479f59 100644 --- a/armi/reactor/components/component.py +++ b/armi/reactor/components/component.py @@ -348,10 +348,12 @@ def applyMaterialMassFracsToNumberDensities(self): self.p.numberDensities = densityTools.getNDensFromMasses( density, self.material.p.massFrac ) + self.applyHotHeightDensityReduction() def applyHotHeightDensityReduction(self): """ - Adjust number densities to account for prescribed hot block heights (axial expansion). + Adjust number densities to account for hot block heights (axial expansion) + (crucial for preserving 3D density). Notes ----- diff --git a/armi/reactor/converters/tests/test_axialExpansionChanger.py b/armi/reactor/converters/tests/test_axialExpansionChanger.py index 4db12e6119..e36943dcb7 100644 --- a/armi/reactor/converters/tests/test_axialExpansionChanger.py +++ b/armi/reactor/converters/tests/test_axialExpansionChanger.py @@ -759,6 +759,11 @@ def test_coldAssemblyHeight(self): checkColdBlockHeight(bStd, bExp, self.assertEqual, "the same") else: checkColdBlockHeight(bStd, bExp, self.assertNotEqual, "different") + if bStd.hasFlags(Flags.FUEL): + self.assertTrue(bExp.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 + self.assertGreater(bExp.getMass("U235"), bStd.getMass("U235")) def checkColdBlockHeight(bStd, bExp, assertType, strForAssertion): diff --git a/armi/reactor/tests/test_components.py b/armi/reactor/tests/test_components.py index edf7e3dae8..5207a2fb6b 100644 --- a/armi/reactor/tests/test_components.py +++ b/armi/reactor/tests/test_components.py @@ -218,7 +218,6 @@ def test_getComponentArea(self): ) # show that area expansion is consistent with the density change in the material - self.component.applyHotHeightDensityReduction() hotDensity = self.component.density() hotArea = self.component.getArea() thermalExpansionFactor = self.component.getThermalExpansionFactor( @@ -234,7 +233,6 @@ def test_getComponentArea(self): area=math.pi, ) ) - coldComponent.applyHotHeightDensityReduction() coldDensity = coldComponent.density() coldArea = coldComponent.getArea() @@ -502,7 +500,6 @@ def test_demonstrateWaysToExpand(self): ########### # # 1 2D Expansion ########### - # expansion only happens in 2D so only area is necessary # when comparing to 3D density, the comparison is not quite correct. # We need a bigger delta, this will be investigated/fixed in another PR @@ -512,47 +509,52 @@ def test_demonstrateWaysToExpand(self): tHotC = 500 circle2 = Circle("circle", "HT9", 20, tHotC, 1.0) self.assertAlmostEqual( - circle1.p.numberDensities["FE"] * circle1.getArea(), - circle2.p.numberDensities["FE"] * circle2.getArea(), + circle1.p.numberDensities["FE"] + * circle1.getArea() + * circle1.getThermalExpansionFactor(), + circle2.p.numberDensities["FE"] + * circle2.getArea() + * circle2.getThermalExpansionFactor(), ) # material.density is the 2D density of a material # material.density3 is true density and not equal in this case # density must be density by calling applyHotHeightDensityReduction # or other methods (see rest of test). for circle in [circle1, circle2]: + # 2D density is not equal because of applyHotHeightDensityReduction self.assertAlmostEqual( - circle.getMassDensity(), + circle.getMassDensity() * circle.getThermalExpansionFactor(), circle.material.density(Tc=circle.temperatureInC), ) - # True density not equal because we expand in 2D - self.assertNotAlmostEqual( - circle.getMassDensity(), - circle.material.density3(Tc=circle.temperatureInC), - delta=biggerDelta, - ) - # True density off by factor of thermal expansion - expFac = circle.getThermalExpansionFactor() self.assertAlmostEqual( - circle.getMassDensity() / expFac, + circle.getMassDensity(), circle.material.density3(Tc=circle.temperatureInC), delta=biggerDelta, ) # Change temp forward and backward and show equal oldArea = circle1.getArea() circle1.setTemperature(tHotC) + circle1.applyHotHeightDensityReduction() self.assertAlmostEqual( circle1.p.numberDensities["FE"], circle2.p.numberDensities["FE"], + delta=biggerDelta, ) self.assertAlmostEqual( circle1.getMassDensity(), - circle1.material.density(Tc=circle2.temperatureInC), + circle1.material.density3(Tc=circle2.temperatureInC), + delta=biggerDelta, ) circle1.setTemperature(tCold) + circle1.applyHotHeightDensityReduction() self.assertAlmostEqual(oldArea, circle1.getArea()) self.assertAlmostEqual( - circle1.p.numberDensities["FE"] * circle1.getArea(), - circle2.p.numberDensities["FE"] * circle2.getArea(), + circle1.p.numberDensities["FE"] + * circle1.getArea() + * circle1.getThermalExpansionFactor(), + circle2.p.numberDensities["FE"] + * circle2.getArea() + * circle2.getThermalExpansionFactor(), ) ########### From 73c1be60f254ab6d2c7639c7ae790a4798ea18fc Mon Sep 17 00:00:00 2001 From: Mark Onufer Date: Fri, 12 Aug 2022 13:01:52 -0700 Subject: [PATCH 2/5] axial cold workds --- armi/reactor/components/component.py | 3 ++ .../converters/axialExpansionChanger.py | 25 +++++++-- .../tests/test_axialExpansionChanger.py | 16 +++--- armi/reactor/reactors.py | 8 ++- armi/reactor/tests/test_components.py | 52 +++++++++++++------ 5 files changed, 77 insertions(+), 27 deletions(-) diff --git a/armi/reactor/components/component.py b/armi/reactor/components/component.py index df20479f59..8763712636 100644 --- a/armi/reactor/components/component.py +++ b/armi/reactor/components/component.py @@ -365,6 +365,9 @@ def applyHotHeightDensityReduction(self): -------- self.applyMaterialMassFracsToNumberDensities """ + # this is the same as getThermalExpansionFactor but doesn't fail + # on non-fluid materials that have 0 or undefined thermal expansion + # (we don't want materials to fail on __init__ which calls this) axialExpansionFactor = 1.0 + self.material.linearExpansionFactor( self.temperatureInC, self.inputTemperatureInC ) diff --git a/armi/reactor/converters/axialExpansionChanger.py b/armi/reactor/converters/axialExpansionChanger.py index 94b67200af..d67b4dde97 100644 --- a/armi/reactor/converters/axialExpansionChanger.py +++ b/armi/reactor/converters/axialExpansionChanger.py @@ -124,6 +124,23 @@ def setAssembly(self, a, setFuel=True): self.expansionData = ExpansionData(a, setFuel) self._isTopDummyBlockPresent() + def applyColdHeightMassIncrease(self): + """ + Increase component mass because they are declared at cold dims + + Notes + ----- + A cold 1 cm tall component will have more mass that a component with the + same mass/length as a component with a hot height of 1 cm. This should be + called when the setting `inputHeightsConsideredHot` is used. This basically + undoes component.applyHotHeightDensityReduction + """ + for c in self.linked.a.getComponents(): + axialExpansionFactor = 1.0 + c.material.linearExpansionFactor( + c.temperatureInC, c.inputTemperatureInC + ) + c.changeNDensByFactor(axialExpansionFactor) + def _isTopDummyBlockPresent(self): """determines if top most block of assembly is a dummy block @@ -174,9 +191,10 @@ def axiallyExpandAssembly(self, thermal: bool = False): # if ib == 0, leave block bottom = 0.0 if ib > 0: b.p.zbottom = self.linked.linkedBlocks[b][0].p.ztop - # if not in the dummy block, get expansion factor, do alignment, and modify block - if ib < (numOfBlocks - 1): + isDummyBlock = ib == (numOfBlocks - 1) + if not isDummyBlock: for c in b: + growFrac = self.expansionData.getExpansionFactor(c) runLog.debug( msg=" Component {0}, growFrac = {1:.4e}".format( @@ -221,8 +239,9 @@ def axiallyExpandAssembly(self, thermal: bool = False): # see also b.setHeight() # - the above not chosen due to call to calculateZCoords oldComponentVolumes = [c.getVolume() for c in b] - oldHeight = b.getHeight() + oldHeight = b.p.height b.p.height = b.p.ztop - b.p.zbottom + _checkBlockHeight(b) _conserveComponentMass(b, oldHeight, oldComponentVolumes) # set block mid point and redo mesh diff --git a/armi/reactor/converters/tests/test_axialExpansionChanger.py b/armi/reactor/converters/tests/test_axialExpansionChanger.py index e36943dcb7..fb2e091b09 100644 --- a/armi/reactor/converters/tests/test_axialExpansionChanger.py +++ b/armi/reactor/converters/tests/test_axialExpansionChanger.py @@ -716,20 +716,21 @@ class TestInputHeightsConsideredHot(unittest.TestCase): """verify thermal expansion for process loading of core""" def setUp(self): - """provide the base case""" + """This test uses a different armiRun.yaml than the default""" + _o, r = loadTestReactor( os.path.join(TEST_ROOT, "detailedAxialExpansion"), - {"inputHeightsConsideredHot": True}, + customSettings={"inputHeightsConsideredHot": True}, ) self.stdAssems = [a for a in r.core.getAssemblies()] _oCold, rCold = loadTestReactor( os.path.join(TEST_ROOT, "detailedAxialExpansion"), - {"inputHeightsConsideredHot": False}, + customSettings={"inputHeightsConsideredHot": False}, ) self.testAssems = [a for a in rCold.core.getAssemblies()] - def test_coldAssemblyHeight(self): + def test_coldAssemblyExpansion(self): """block heights are cold and should be expanded Notes @@ -760,10 +761,13 @@ def test_coldAssemblyHeight(self): else: checkColdBlockHeight(bStd, bExp, self.assertNotEqual, "different") if bStd.hasFlags(Flags.FUEL): - self.assertTrue(bExp.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 - self.assertGreater(bExp.getMass("U235"), bStd.getMass("U235")) + if not isinstance( + bStd.getComponent(Flags.FUEL).material, custom.Custom + ): + # custom materials don't expand + self.assertGreater(bExp.getMass("U235"), bStd.getMass("U235")) def checkColdBlockHeight(bStd, bExp, assertType, strForAssertion): diff --git a/armi/reactor/reactors.py b/armi/reactor/reactors.py index e006334cc8..e2ccb56440 100644 --- a/armi/reactor/reactors.py +++ b/armi/reactor/reactors.py @@ -215,7 +215,6 @@ def __init__(self, name): self._circularRingPitch = 1.0 self._automaticVariableMesh = False self._minMeshSizeRatio = 0.15 - self._inputHeightsConsideredHot = True self._detailedAxialExpansion = False def setOptionsFromCs(self, cs): @@ -227,7 +226,6 @@ def setOptionsFromCs(self, cs): self._circularRingPitch = cs["circularRingPitch"] self._automaticVariableMesh = cs["automaticVariableMesh"] self._minMeshSizeRatio = cs["minMeshSizeRatio"] - self._inputHeightsConsideredHot = cs["inputHeightsConsideredHot"] self._detailedAxialExpansion = cs["detailedAxialExpansion"] def __getstate__(self): @@ -2293,6 +2291,12 @@ def processLoading(self, cs, dbLoad: bool = False): for a in self.getAssemblies(): if not a.hasFlags(Flags.CONTROL): axialExpChngr.setAssembly(a) + # this doesn't get applied to control rods, 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) + axialExpChngr.applyColdHeightMassIncrease() axialExpChngr.expansionData.computeThermalExpansionFactors() axialExpChngr.axiallyExpandAssembly(thermal=True) axialExpChngr.manageCoreMesh(self.parent) diff --git a/armi/reactor/tests/test_components.py b/armi/reactor/tests/test_components.py index 5207a2fb6b..9a0c860aa3 100644 --- a/armi/reactor/tests/test_components.py +++ b/armi/reactor/tests/test_components.py @@ -500,14 +500,14 @@ def test_demonstrateWaysToExpand(self): ########### # # 1 2D Expansion ########### - # when comparing to 3D density, the comparison is not quite correct. # We need a bigger delta, this will be investigated/fixed in another PR - biggerDelta = 0.001 # g/cc + biggerDelta = 0.01 # g/cc tCold = 50 circle1 = Circle("circle", "HT9", 20, tCold, 1.0) - tHotC = 500 - circle2 = Circle("circle", "HT9", 20, tHotC, 1.0) + tHot = 500 + circle2 = Circle("circle", "HT9", 20, tHot, 1.0) + self.assertAlmostEqual( circle1.p.numberDensities["FE"] * circle1.getArea() @@ -533,20 +533,35 @@ def test_demonstrateWaysToExpand(self): ) # Change temp forward and backward and show equal oldArea = circle1.getArea() - circle1.setTemperature(tHotC) + initialDens = circle1.getMassDensity() + + factorToUndoHotHeight = circle1.getThermalExpansionFactor() + self.assertAlmostEqual( + 1.0 + + circle1.material.linearExpansionFactor( + circle1.temperatureInC, circle1.inputTemperatureInC + ), + factorToUndoHotHeight, + ) + circle1.changeNDensByFactor(factorToUndoHotHeight) + + circle1.setTemperature(tHot) + # reapply hot height and new temp circle1.applyHotHeightDensityReduction() + # now its is same as hot component self.assertAlmostEqual( circle1.p.numberDensities["FE"], circle2.p.numberDensities["FE"], - delta=biggerDelta, ) self.assertAlmostEqual( circle1.getMassDensity(), circle1.material.density3(Tc=circle2.temperatureInC), delta=biggerDelta, ) + circle1.changeNDensByFactor(circle1.getThermalExpansionFactor()) circle1.setTemperature(tCold) circle1.applyHotHeightDensityReduction() + self.assertAlmostEqual(initialDens, circle1.getMassDensity()) self.assertAlmostEqual(oldArea, circle1.getArea()) self.assertAlmostEqual( circle1.p.numberDensities["FE"] @@ -555,14 +570,13 @@ def test_demonstrateWaysToExpand(self): circle2.p.numberDensities["FE"] * circle2.getArea() * circle2.getThermalExpansionFactor(), + delta=biggerDelta, ) ########### # # 2 3D with applyHotHeightDensityReduction and equal hot height ########### hotHeight = 1.0 - circle1.applyHotHeightDensityReduction() - circle2.applyHotHeightDensityReduction() # circle 1 has bigger mass because it has taller cold height and same # cold radius coldHeight1 = hotHeight / circle1.getThermalExpansionFactor() @@ -605,7 +619,7 @@ def test_demonstrateWaysToExpand(self): self.assertAlmostEqual(coldMass, hotMass) # now change the temperature of Circle 2 - newHot = tHotC / 2.0 + newHot = tHot / 2.0 # undo original applyHotHeightDensityReduction axialExpansionFactor = 1.0 + circle2.material.linearExpansionFactor( circle2.temperatureInC, circle2.inputTemperatureInC @@ -613,7 +627,7 @@ def test_demonstrateWaysToExpand(self): circle2.changeNDensByFactor(axialExpansionFactor) # change temp circle2.setTemperature(newHot) - densityAdjustment = circle2.getThermalExpansionFactor(Tc=newHot, T0=tHotC) + densityAdjustment = circle2.getThermalExpansionFactor(Tc=newHot, T0=tHot) circle2.applyHotHeightDensityReduction() self.assertAlmostEqual( circle2.getMassDensity(), @@ -627,15 +641,21 @@ def test_demonstrateWaysToExpand(self): ########### coldHeight = 1.0 circle1 = Circle("circle", "HT9", 20, tCold, 1.0) - circle2 = Circle("circle", "HT9", 20, tHotC, 1.0) - circle1.setTemperature(500) # the should be the same now + circle2 = Circle("circle", "HT9", 20, tHot, 1.0) + + circle1.changeNDensByFactor(circle1.getThermalExpansionFactor()) + circle1.setTemperature(tHot) + # when block.setHeight is called (which effectively changes component height) + # component.setNumberDensity is called (for solid isotopes) to adjust the number + # density so that now the 2D expansion will be approximated/expanded around the hot temp + circle1.applyHotHeightDensityReduction() + self.assertAlmostEqual( + circle1.p.numberDensities["FE"], + circle2.p.numberDensities["FE"], + ) for circle in [circle1, circle2]: - # when block.setHeight is called (which effectively changes component height) - # component.setNumberDensity is called (for solid isotopes) to adjust the number - # density so that now the 2D expansion will be approximated around the hot temp - circle.changeNDensByFactor(1 / circle.getThermalExpansionFactor()) self.assertAlmostEqual( circle.getMassDensity(), circle.material.density3(Tc=circle.temperatureInC), From bc07f399ff82a0cd7128866c8c0de662fbe97f16 Mon Sep 17 00:00:00 2001 From: Mark Onufer Date: Tue, 16 Aug 2022 07:50:10 -0700 Subject: [PATCH 3/5] update tests --- armi/reactor/tests/test_components.py | 194 ++++++++++++-------------- 1 file changed, 87 insertions(+), 107 deletions(-) diff --git a/armi/reactor/tests/test_components.py b/armi/reactor/tests/test_components.py index 9a0c860aa3..d5194357db 100644 --- a/armi/reactor/tests/test_components.py +++ b/armi/reactor/tests/test_components.py @@ -497,31 +497,51 @@ def test_changeNumberDensities(self): def test_demonstrateWaysToExpand(self): """Demonstrate that material is conserved at during expansion""" - ########### - # # 1 2D Expansion - ########### # when comparing to 3D density, the comparison is not quite correct. # We need a bigger delta, this will be investigated/fixed in another PR biggerDelta = 0.01 # g/cc - tCold = 50 - circle1 = Circle("circle", "HT9", 20, tCold, 1.0) + + ########### + # # 1 height taken as hot height (how components defined by default) + ########### + hotHeight = 1.0 + tWarm = 50 + coldOuterDiameter = 1.0 + circle1 = Circle("circle", "HT9", 20, tWarm, coldOuterDiameter) tHot = 500 - circle2 = Circle("circle", "HT9", 20, tHot, 1.0) + circle2 = Circle("circle", "HT9", 20, tHot, coldOuterDiameter) + # mass density is proportional to Fe number density and derived from + # all the number densities and atomic masses self.assertAlmostEqual( - circle1.p.numberDensities["FE"] - * circle1.getArea() - * circle1.getThermalExpansionFactor(), - circle2.p.numberDensities["FE"] - * circle2.getArea() - * circle2.getThermalExpansionFactor(), + circle1.p.numberDensities["FE"] / circle2.p.numberDensities["FE"], + circle1.getMassDensity() / circle2.getMassDensity(), ) + + # the colder one has more because it is the same cold outer diameter + # but it would be taller at the same temperature + mass1 = circle1.getMassDensity() * circle1.getArea() * hotHeight + mass2 = circle2.getMassDensity() * circle2.getArea() * hotHeight + self.assertGreater(mass1, mass2) + + # they are off by factor of thermal exp + self.assertAlmostEqual( + mass1 * circle1.getThermalExpansionFactor(), + mass2 * circle2.getThermalExpansionFactor(), + ) + # material.density is the 2D density of a material # material.density3 is true density and not equal in this case # density must be density by calling applyHotHeightDensityReduction # or other methods (see rest of test). for circle in [circle1, circle2]: - # 2D density is not equal because of applyHotHeightDensityReduction + # 2D density is not equal after application of applyHotHeightDensityReduction + # which happens during construction + self.assertNotAlmostEqual( + circle.getMassDensity(), + circle.material.density(Tc=circle.temperatureInC), + ) + # 2D density is off by the thermal exp factor self.assertAlmostEqual( circle.getMassDensity() * circle.getThermalExpansionFactor(), circle.material.density(Tc=circle.temperatureInC), @@ -534,135 +554,95 @@ def test_demonstrateWaysToExpand(self): # Change temp forward and backward and show equal oldArea = circle1.getArea() initialDens = circle1.getMassDensity() - - factorToUndoHotHeight = circle1.getThermalExpansionFactor() - self.assertAlmostEqual( + # this math is done in applyHotHeightDensityReduction + applyHotHeightDensityReductionFactor = ( 1.0 + circle1.material.linearExpansionFactor( circle1.temperatureInC, circle1.inputTemperatureInC - ), + ) + ) + factorToUndoHotHeight = circle1.getThermalExpansionFactor() + self.assertAlmostEqual( + applyHotHeightDensityReductionFactor, factorToUndoHotHeight, ) - circle1.changeNDensByFactor(factorToUndoHotHeight) + # when block.setHeight is called (which effectively changes component height) + # component.setNumberDensity is called (for solid isotopes) to adjust the number + # density so that now the 2D expansion will be approximated/expanded around + # the hot temp which is akin to these adjustments + # undo the old applyHotHeightDensityReduction + circle1.changeNDensByFactor(factorToUndoHotHeight) circle1.setTemperature(tHot) - # reapply hot height and new temp - circle1.applyHotHeightDensityReduction() - # now its is same as hot component - self.assertAlmostEqual( - circle1.p.numberDensities["FE"], - circle2.p.numberDensities["FE"], - ) + circle1.applyHotHeightDensityReduction() # apply at new temp + + # now its density is same as hot component self.assertAlmostEqual( circle1.getMassDensity(), - circle1.material.density3(Tc=circle2.temperatureInC), - delta=biggerDelta, - ) - circle1.changeNDensByFactor(circle1.getThermalExpansionFactor()) - circle1.setTemperature(tCold) - circle1.applyHotHeightDensityReduction() - self.assertAlmostEqual(initialDens, circle1.getMassDensity()) - self.assertAlmostEqual(oldArea, circle1.getArea()) - self.assertAlmostEqual( - circle1.p.numberDensities["FE"] - * circle1.getArea() - * circle1.getThermalExpansionFactor(), - circle2.p.numberDensities["FE"] - * circle2.getArea() - * circle2.getThermalExpansionFactor(), - delta=biggerDelta, + circle2.getMassDensity(), ) - ########### - # # 2 3D with applyHotHeightDensityReduction and equal hot height - ########### - hotHeight = 1.0 - # circle 1 has bigger mass because it has taller cold height and same - # cold radius - coldHeight1 = hotHeight / circle1.getThermalExpansionFactor() - coldHeight2 = hotHeight / circle2.getThermalExpansionFactor() - self.assertGreater(coldHeight1, coldHeight2) - self.assertGreater( - circle1.p.numberDensities["FE"] * circle1.getArea() * hotHeight, - circle2.p.numberDensities["FE"] * circle2.getArea() * hotHeight, + # show that mass is conserved after expansion + circle1NewHotHeight = ( + hotHeight * circle1.getThermalExpansionFactor() / factorToUndoHotHeight ) - # they are off in mass by a factor of thermal expansion self.assertAlmostEqual( - circle1.p.numberDensities["FE"] - * circle1.getArea() - * hotHeight - * circle1.getThermalExpansionFactor(), - circle2.p.numberDensities["FE"] - * circle2.getArea() - * hotHeight - * circle2.getThermalExpansionFactor(), + mass1, circle1.getMassDensity() * circle1.getArea() * circle1NewHotHeight ) - # Because of applyHotHeightDensityReduction the mass density is now - # Consistent with density3 but different from density (2D) by a factor of - # thermal expansion + # you can calculate the height exp factor directly this way self.assertAlmostEqual( - circle2.getMassDensity(), - circle2.material.density(Tc=circle2.temperatureInC) - / circle2.getThermalExpansionFactor(), + circle1.getThermalExpansionFactor() / factorToUndoHotHeight, + circle1.getThermalExpansionFactor(Tc=circle1.temperatureInC, T0=tWarm), ) + self.assertAlmostEqual( - circle2.getMassDensity(), - circle2.material.density3(Tc=circle2.temperatureInC), + circle1.getMassDensity(), + circle1.material.density3(Tc=circle2.temperatureInC), delta=biggerDelta, ) - coldMass = ( - circle2.material.density(Tc=circle2.inputTemperatureInC) - * circle2.getArea(cold=True) - * coldHeight2 - ) - hotMass = circle2.getArea() * hotHeight * circle2.getMassDensity() - self.assertAlmostEqual(coldMass, hotMass) - - # now change the temperature of Circle 2 - newHot = tHot / 2.0 - # undo original applyHotHeightDensityReduction - axialExpansionFactor = 1.0 + circle2.material.linearExpansionFactor( - circle2.temperatureInC, circle2.inputTemperatureInC - ) - circle2.changeNDensByFactor(axialExpansionFactor) - # change temp - circle2.setTemperature(newHot) - densityAdjustment = circle2.getThermalExpansionFactor(Tc=newHot, T0=tHot) - circle2.applyHotHeightDensityReduction() + # change back to old temp + circle1.changeNDensByFactor(circle1.getThermalExpansionFactor()) + circle1.setTemperature(tWarm) + circle1.applyHotHeightDensityReduction() + + # check for consistency + self.assertAlmostEqual(initialDens, circle1.getMassDensity()) + self.assertAlmostEqual(oldArea, circle1.getArea()) self.assertAlmostEqual( - circle2.getMassDensity(), - circle2.material.density3(Tc=circle2.temperatureInC), - delta=biggerDelta, + mass1, circle1.getMassDensity() * circle1.getArea() * hotHeight ) ########### - # # 3 "True" 3D start with cold or hot and show how quantity is - # conserved with inputHeightsConsideredHot + # # 2 height taken as cold height and show how quantity is + # conserved with inputHeightsConsideredHot = False ########### coldHeight = 1.0 - circle1 = Circle("circle", "HT9", 20, tCold, 1.0) + circle1 = Circle("circle", "HT9", 20, tWarm, 1.0) circle2 = Circle("circle", "HT9", 20, tHot, 1.0) + # same as 1 but we will make like 2 + circle1AdjustTo2 = Circle("circle", "HT9", 20, tWarm, 1.0) - circle1.changeNDensByFactor(circle1.getThermalExpansionFactor()) - circle1.setTemperature(tHot) - # when block.setHeight is called (which effectively changes component height) - # component.setNumberDensity is called (for solid isotopes) to adjust the number - # density so that now the 2D expansion will be approximated/expanded around the hot temp - circle1.applyHotHeightDensityReduction() - + # make it hot like 2 + circle1AdjustTo2.changeNDensByFactor( + circle1AdjustTo2.getThermalExpansionFactor() + ) + circle1AdjustTo2.setTemperature(tHot) + circle1AdjustTo2.applyHotHeightDensityReduction() + # check that its like 2 self.assertAlmostEqual( - circle1.p.numberDensities["FE"], - circle2.p.numberDensities["FE"], + circle2.getMassDensity(), circle1AdjustTo2.getMassDensity() ) - for circle in [circle1, circle2]: + self.assertAlmostEqual(circle2.getArea(), circle1AdjustTo2.getArea()) + + for circle in [circle1, circle2, circle1AdjustTo2]: self.assertAlmostEqual( circle.getMassDensity(), circle.material.density3(Tc=circle.temperatureInC), delta=biggerDelta, ) - # total mass consistent - # New height will be taller + # total mass consistent between hot and cold + # Hot height will be taller hotHeight = coldHeight * circle.getThermalExpansionFactor() self.assertAlmostEqual( coldHeight From b52369079f4dd9f3e722e85b7d9a484cf520d2ef Mon Sep 17 00:00:00 2001 From: Mark Onufer Date: Tue, 16 Aug 2022 08:07:03 -0700 Subject: [PATCH 4/5] remove calls to hot height dens reduction from test code --- armi/reactor/converters/tests/test_blockConverter.py | 1 - armi/reactor/tests/test_blocks.py | 1 - 2 files changed, 2 deletions(-) diff --git a/armi/reactor/converters/tests/test_blockConverter.py b/armi/reactor/converters/tests/test_blockConverter.py index 5aced75744..a640ff52e4 100644 --- a/armi/reactor/converters/tests/test_blockConverter.py +++ b/armi/reactor/converters/tests/test_blockConverter.py @@ -55,7 +55,6 @@ def _perturbTemps(self, block, cName, tCold, tHot): "Give the component different ref and hot temperatures than in test_Blocks." c = block.getComponent(Flags.fromString(cName)) c.refTemp, c.refHot = tCold, tHot - c.applyHotHeightDensityReduction() c.setTemperature(tHot) return block diff --git a/armi/reactor/tests/test_blocks.py b/armi/reactor/tests/test_blocks.py index cacad79e3e..b655802987 100644 --- a/armi/reactor/tests/test_blocks.py +++ b/armi/reactor/tests/test_blocks.py @@ -2282,7 +2282,6 @@ def test_coldMass(self): and hot height. """ fuel = self.b.getComponent(Flags.FUEL) - fuel.applyHotHeightDensityReduction() # set ref (input/cold) temperature. Thot = fuel.temperatureInC Tcold = fuel.inputTemperatureInC From 5c3d0b183fa1abf20f055de30088e6af8d64efb0 Mon Sep 17 00:00:00 2001 From: Mark Onufer Date: Wed, 17 Aug 2022 11:00:13 -0700 Subject: [PATCH 5/5] reviewer comments --- armi/reactor/components/component.py | 7 +-- armi/reactor/tests/test_components.py | 67 ++++++++++++++++----------- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/armi/reactor/components/component.py b/armi/reactor/components/component.py index 8763712636..c7cad5a014 100644 --- a/armi/reactor/components/component.py +++ b/armi/reactor/components/component.py @@ -329,13 +329,14 @@ def setProperties(self, properties): def applyMaterialMassFracsToNumberDensities(self): """ - Set number densities for the component based on material mass fractions using hot temperatures. + Set the hot number densities for the component based on material mass fractions/density. Notes ----- - - the density returned accounts for the radial expansion of the component + - the density returned accounts for the expansion of the component due to the difference in self.inputTemperatureInC and self.temperatureInC - - axial expansion effects are not included here. + - After the expansion, the density of the component should reflect the 3d + density of the material See Also -------- diff --git a/armi/reactor/tests/test_components.py b/armi/reactor/tests/test_components.py index d5194357db..b311d532a3 100644 --- a/armi/reactor/tests/test_components.py +++ b/armi/reactor/tests/test_components.py @@ -495,21 +495,27 @@ def test_changeNumberDensities(self): self.component.changeNDensByFactor(3.0) self.assertEqual(self.component.getNumberDensity("NA23"), 3.0) - def test_demonstrateWaysToExpand(self): - """Demonstrate that material is conserved at during expansion""" - # when comparing to 3D density, the comparison is not quite correct. - # We need a bigger delta, this will be investigated/fixed in another PR - biggerDelta = 0.01 # g/cc - - ########### - # # 1 height taken as hot height (how components defined by default) - ########### + +class TestComponentExpansion(unittest.TestCase): + # when comparing to 3D density, the comparison is not quite correct. + # We need a bigger delta, this will be investigated/fixed in another PR + biggerDelta = 0.01 # g/cc + tWarm = 50 + tHot = 500 + + def test_ExpansionConservationHotHeightDefined(self): + """ + Demonstrate tutorial for how to expand and relation ships conserved at during expansion. + + Notes + ----- + - height taken as hot height and show how quantity is conserved with + inputHeightsConsideredHot = True (the default) + """ hotHeight = 1.0 - tWarm = 50 coldOuterDiameter = 1.0 - circle1 = Circle("circle", "HT9", 20, tWarm, coldOuterDiameter) - tHot = 500 - circle2 = Circle("circle", "HT9", 20, tHot, coldOuterDiameter) + circle1 = Circle("circle", "HT9", 20, self.tWarm, coldOuterDiameter) + circle2 = Circle("circle", "HT9", 20, self.tHot, coldOuterDiameter) # mass density is proportional to Fe number density and derived from # all the number densities and atomic masses @@ -549,7 +555,7 @@ def test_demonstrateWaysToExpand(self): self.assertAlmostEqual( circle.getMassDensity(), circle.material.density3(Tc=circle.temperatureInC), - delta=biggerDelta, + delta=self.biggerDelta, ) # Change temp forward and backward and show equal oldArea = circle1.getArea() @@ -573,7 +579,7 @@ def test_demonstrateWaysToExpand(self): # undo the old applyHotHeightDensityReduction circle1.changeNDensByFactor(factorToUndoHotHeight) - circle1.setTemperature(tHot) + circle1.setTemperature(self.tHot) circle1.applyHotHeightDensityReduction() # apply at new temp # now its density is same as hot component @@ -592,17 +598,17 @@ def test_demonstrateWaysToExpand(self): # you can calculate the height exp factor directly this way self.assertAlmostEqual( circle1.getThermalExpansionFactor() / factorToUndoHotHeight, - circle1.getThermalExpansionFactor(Tc=circle1.temperatureInC, T0=tWarm), + circle1.getThermalExpansionFactor(Tc=circle1.temperatureInC, T0=self.tWarm), ) self.assertAlmostEqual( circle1.getMassDensity(), circle1.material.density3(Tc=circle2.temperatureInC), - delta=biggerDelta, + delta=self.biggerDelta, ) # change back to old temp circle1.changeNDensByFactor(circle1.getThermalExpansionFactor()) - circle1.setTemperature(tWarm) + circle1.setTemperature(self.tWarm) circle1.applyHotHeightDensityReduction() # check for consistency @@ -612,21 +618,26 @@ def test_demonstrateWaysToExpand(self): mass1, circle1.getMassDensity() * circle1.getArea() * hotHeight ) - ########### - # # 2 height taken as cold height and show how quantity is - # conserved with inputHeightsConsideredHot = False - ########### + def test_ExpansionConservationColdHeightDefined(self): + """ + Demonstrate that material is conserved at during expansion + + Notes + ----- + - height taken as cold height and show how quantity is conserved with + inputHeightsConsideredHot = False + """ coldHeight = 1.0 - circle1 = Circle("circle", "HT9", 20, tWarm, 1.0) - circle2 = Circle("circle", "HT9", 20, tHot, 1.0) + circle1 = Circle("circle", "HT9", 20, self.tWarm, 1.0) + circle2 = Circle("circle", "HT9", 20, self.tHot, 1.0) # same as 1 but we will make like 2 - circle1AdjustTo2 = Circle("circle", "HT9", 20, tWarm, 1.0) + circle1AdjustTo2 = Circle("circle", "HT9", 20, self.tWarm, 1.0) # make it hot like 2 circle1AdjustTo2.changeNDensByFactor( circle1AdjustTo2.getThermalExpansionFactor() ) - circle1AdjustTo2.setTemperature(tHot) + circle1AdjustTo2.setTemperature(self.tHot) circle1AdjustTo2.applyHotHeightDensityReduction() # check that its like 2 self.assertAlmostEqual( @@ -639,7 +650,7 @@ def test_demonstrateWaysToExpand(self): self.assertAlmostEqual( circle.getMassDensity(), circle.material.density3(Tc=circle.temperatureInC), - delta=biggerDelta, + delta=self.biggerDelta, ) # total mass consistent between hot and cold # Hot height will be taller @@ -649,7 +660,7 @@ def test_demonstrateWaysToExpand(self): * circle.getArea(cold=True) * circle.material.density3(Tc=circle.inputTemperatureInC), hotHeight * circle.getArea() * circle.getMassDensity(), - delta=biggerDelta, + delta=self.biggerDelta, )