From a20e7097ddb5f79ccebc5783698b1a6c0b6349ff Mon Sep 17 00:00:00 2001 From: Mark Onufer Date: Wed, 21 Dec 2022 17:16:19 -0800 Subject: [PATCH 01/11] initial cut, no tests --- armi/bookkeeping/db/__init__.py | 12 ++++--- armi/reactor/blueprints/__init__.py | 51 ++++++++++++++++++++++++++ armi/reactor/reactors.py | 56 +---------------------------- 3 files changed, 59 insertions(+), 60 deletions(-) diff --git a/armi/bookkeeping/db/__init__.py b/armi/bookkeeping/db/__init__.py index 96e627598..dba20d92e 100644 --- a/armi/bookkeeping/db/__init__.py +++ b/armi/bookkeeping/db/__init__.py @@ -154,11 +154,13 @@ def loadOperator(pathToDb, loadCycle, loadNode, allowMissing=False): updateGlobalAssemblyNum(r) o = thisCase.initializeOperator(r=r) - runLog.warning( - "The operator provided is not in the same state as the operator was.\n" - "When the reactor was at the prescribed cycle and node, it should have\n" - "access to the same interface stack, but the interfaces will also not be in the " - "same state.\n" + runLog.important( + "The operator will not be in the same state that it was at that cycle and " + "node, only the reactor.\n" + "The operator should have access to the same interface stack, but the " + "interfaces will not be in the same state (they will be fresh instances " + "of each interface as if __init__ was just called rather than the state " + "during the run at this time node.)\n" "ARMI does not support loading operator states, as they are not stored." ) return o diff --git a/armi/reactor/blueprints/__init__.py b/armi/reactor/blueprints/__init__.py index f3a3e1bd0..9c93d1fb2 100644 --- a/armi/reactor/blueprints/__init__.py +++ b/armi/reactor/blueprints/__init__.py @@ -89,7 +89,9 @@ from armi.reactor import assemblies from armi.reactor import geometry from armi.reactor import systemLayoutInput +from armi.reactor.flags import Flags from armi.scripts import migration + from armi.utils import textProcessors # NOTE: using non-ARMI-standard imports because these are all a part of this package, @@ -102,6 +104,7 @@ from armi.reactor.blueprints.componentBlueprint import ComponentGroups from armi.reactor.blueprints import isotopicOptions from armi.reactor.blueprints.gridBlueprint import Grids, Triplet +from armi.reactor.converters.axialExpansionChanger import AxialExpansionChanger context.BLUEPRINTS_IMPORTED = True context.BLUEPRINTS_IMPORT_CONTEXT = "".join(traceback.format_stack()) @@ -325,6 +328,8 @@ def _prepConstruction(self, cs): self._checkAssemblyAreaConsistency(cs) runLog.header("=========== Verifying Assembly Configurations ===========") + if not cs["inputHeightsConsideredHot"]: + self._applyThermalExpansion(cs["detailedAxialExpansion"]) # pylint: disable=no-member getPluginManagerOrFail().hook.afterConstructionOfAssemblies( @@ -333,6 +338,52 @@ def _prepConstruction(self, cs): self._prepped = True + def _applyThermalExpansion( + self, + isDetailedAxialExpansion, + ): + """ + Expand BOL assemblies, resolve disjoint axial mesh (if needed), and update block BOL heights + + Parameters + ---------- + assems: list + list of :py:class:`Assembly ` objects to be thermally expanded + """ + runLog.header( + "=========== Axially expanding all assemblies (except control) from Tinput to Thot ===========" + ) + assems = (self.assemblies.values(),) + + # assume finest mesh is reference + referenceAssembly = sorted( + self.parent.blueprints.assemblies.values(), + key=lambda a: len(a), + reverse=True, + )[0] + axialExpChngr = AxialExpansionChanger(isDetailedAxialExpansion) + for a in assems: + if not a.hasFlags(Flags.CONTROL): + axialExpChngr.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) + axialExpChngr.applyColdHeightMassIncrease() + axialExpChngr.expansionData.computeThermalExpansionFactors() + axialExpChngr.axiallyExpandAssembly(thermal=True) + if not isDetailedAxialExpansion: + for a in assems: + if not a.hasFlags(Flags.CONTROL): + 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() + def _assignTypeNums(self): if self.blockDesigns is None: # this happens when directly defining assemblies. diff --git a/armi/reactor/reactors.py b/armi/reactor/reactors.py index c07aa7dc5..0469a6dd6 100644 --- a/armi/reactor/reactors.py +++ b/armi/reactor/reactors.py @@ -2262,15 +2262,7 @@ def processLoading(self, cs, dbLoad: bool = False): if any(a.hasFlags(f) for f in nonUniformAssems): continue a.makeAxialSnapList(refAssem=finestMeshAssembly) - if not cs["inputHeightsConsideredHot"]: - runLog.header( - "=========== Axially expanding blueprints assemblies (except control) from Tinput to Thot ===========" - ) - self._applyThermalExpansion( - self.parent.blueprints.assemblies.values(), - dbLoad, - finestMeshAssembly, - ) + self.updateAxialMesh() else: if not cs["detailedAxialExpansion"]: @@ -2279,11 +2271,6 @@ def processLoading(self, cs, dbLoad: bool = False): if any(a.hasFlags(f) for f in nonUniformAssems): continue a.makeAxialSnapList(self.refAssem) - if not cs["inputHeightsConsideredHot"]: - runLog.header( - "=========== Axially expanding all assemblies (except control) from Tinput to Thot ===========" - ) - self._applyThermalExpansion(self.getAssemblies(includeAll=True), dbLoad) # some assemblies, like control assemblies, have a non-conforming mesh # and should not be included in self.p.referenceBlockAxialMesh and self.p.axialMesh @@ -2360,44 +2347,3 @@ def buildManualZones(self, cs): if not len(self.zones): runLog.debug("No manual zones defined in `zoneDefinitions` setting") - - def _applyThermalExpansion( - self, assems: list, dbLoad: bool, referenceAssembly=None - ): - """expand assemblies, resolve disjoint axial mesh (if needed), and update block BOL heights - - Parameters - ---------- - assems: list - list of :py:class:`Assembly ` objects to be thermally expanded - dbLoad: bool - boolean to determine if Core::processLoading is loading a database or constructing a Core - referenceAssembly: optional, :py:class:`Assembly ` - is the thermally expanded assembly whose axial mesh is used to snap the - blueprints assemblies axial mesh to - """ - axialExpChngr = AxialExpansionChanger(self._detailedAxialExpansion) - for a in assems: - if not a.hasFlags(Flags.CONTROL): - axialExpChngr.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) - axialExpChngr.applyColdHeightMassIncrease() - axialExpChngr.expansionData.computeThermalExpansionFactors() - axialExpChngr.axiallyExpandAssembly(thermal=True) - # resolve axially disjoint mesh (if needed) - if not dbLoad: - axialExpChngr.manageCoreMesh(self.parent) - elif not self._detailedAxialExpansion: - for a in assems: - if not a.hasFlags(Flags.CONTROL): - 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() From 09cf47b4d417da145ee16473ca024de27dca3b4c Mon Sep 17 00:00:00 2001 From: Mark Onufer Date: Wed, 21 Dec 2022 17:20:03 -0800 Subject: [PATCH 02/11] minor updates --- armi/reactor/blueprints/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/armi/reactor/blueprints/__init__.py b/armi/reactor/blueprints/__init__.py index 9c93d1fb2..ebeb209d5 100644 --- a/armi/reactor/blueprints/__init__.py +++ b/armi/reactor/blueprints/__init__.py @@ -353,11 +353,11 @@ def _applyThermalExpansion( runLog.header( "=========== Axially expanding all assemblies (except control) from Tinput to Thot ===========" ) - assems = (self.assemblies.values(),) + assems = list(self.assemblies.values()) # assume finest mesh is reference referenceAssembly = sorted( - self.parent.blueprints.assemblies.values(), + assems, key=lambda a: len(a), reverse=True, )[0] From 9d8d4f5e213f48235002d5607da0f164eca46dcd Mon Sep 17 00:00:00 2001 From: onufer Date: Tue, 27 Dec 2022 15:18:19 -0800 Subject: [PATCH 03/11] move snap list into construction, fix tests --- armi/reactor/blueprints/__init__.py | 41 ++++++++++----- armi/reactor/reactors.py | 30 ++--------- armi/reactor/tests/test_reactors.py | 79 +++++++++++++---------------- 3 files changed, 66 insertions(+), 84 deletions(-) diff --git a/armi/reactor/blueprints/__init__.py b/armi/reactor/blueprints/__init__.py index ebeb209d5..0c90f7ea4 100644 --- a/armi/reactor/blueprints/__init__.py +++ b/armi/reactor/blueprints/__init__.py @@ -324,12 +324,30 @@ def _prepConstruction(self, cs): self.assemblies[aDesign.name] = a if currentCount != 0: assemblies.setAssemNumCounter(currentCount) - + runLog.header("=========== Verifying Assembly Configurations ===========") self._checkAssemblyAreaConsistency(cs) - runLog.header("=========== Verifying Assembly Configurations ===========") + # assume finest mesh is reference + referenceAssembly = sorted( + self.assemblies.values(), + key=lambda a: len(a), + reverse=True, + )[0] + if not cs["detailedAxialExpansion"]: + # make the snap lists so assems know how to expand + nonUniformAssems = [ + Flags.fromStringIgnoreErrors(t) for t in cs["nonUniformAssemFlags"] + ] + # prepare core for mesh snapping during axial expansion + for a in self.assemblies.values(): + if any(a.hasFlags(f) for f in nonUniformAssems): + continue + a.makeAxialSnapList(referenceAssembly) if not cs["inputHeightsConsideredHot"]: - self._applyThermalExpansion(cs["detailedAxialExpansion"]) + # expand axial heights from cold to hot + self._applyThermalExpansion( + referenceAssembly, cs["detailedAxialExpansion"] + ) # pylint: disable=no-member getPluginManagerOrFail().hook.afterConstructionOfAssemblies( @@ -340,6 +358,7 @@ def _prepConstruction(self, cs): def _applyThermalExpansion( self, + referenceAssembly, isDetailedAxialExpansion, ): """ @@ -355,24 +374,18 @@ def _applyThermalExpansion( ) assems = list(self.assemblies.values()) - # assume finest mesh is reference - referenceAssembly = sorted( - assems, - key=lambda a: len(a), - reverse=True, - )[0] - axialExpChngr = AxialExpansionChanger(isDetailedAxialExpansion) + axialExpChanger = AxialExpansionChanger(isDetailedAxialExpansion) for a in assems: if not a.hasFlags(Flags.CONTROL): - axialExpChngr.setAssembly(a) + 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) - axialExpChngr.applyColdHeightMassIncrease() - axialExpChngr.expansionData.computeThermalExpansionFactors() - axialExpChngr.axiallyExpandAssembly(thermal=True) + axialExpChanger.applyColdHeightMassIncrease() + axialExpChanger.expansionData.computeThermalExpansionFactors() + axialExpChanger.axiallyExpandAssembly(thermal=True) if not isDetailedAxialExpansion: for a in assems: if not a.hasFlags(Flags.CONTROL): diff --git a/armi/reactor/reactors.py b/armi/reactor/reactors.py index 0469a6dd6..03748a5dd 100644 --- a/armi/reactor/reactors.py +++ b/armi/reactor/reactors.py @@ -2239,39 +2239,17 @@ def processLoading(self, cs, dbLoad: bool = False): "Please make sure that this is intended and not a input error." ) - nonUniformAssems = [ - Flags.fromStringIgnoreErrors(t) for t in cs["nonUniformAssemFlags"] - ] if dbLoad: # reactor.blueprints.assemblies need to be populated # this normally happens during armi/reactor/blueprints/__init__.py::constructAssem # but for DB load, this is not called so it must be here. # pylint: disable=protected-access self.parent.blueprints._prepConstruction(cs) - if not cs["detailedAxialExpansion"]: - # Apply mesh snapping for self.parent.blueprints.assemblies - # This is stored as a param for assemblies in-core, so only blueprints assemblies are - # considered here. To guarantee mesh snapping will function, makeAxialSnapList - # should be in reference to the assembly with the finest mesh as defined in the blueprints. - finestMeshAssembly = sorted( - self.parent.blueprints.assemblies.values(), - key=lambda a: len(a), - reverse=True, - )[0] - for a in self.parent.blueprints.assemblies.values(): - if any(a.hasFlags(f) for f in nonUniformAssems): - continue - a.makeAxialSnapList(refAssem=finestMeshAssembly) - self.updateAxialMesh() - else: - if not cs["detailedAxialExpansion"]: - # prepare core for mesh snapping during axial expansion - for a in self.getAssemblies(includeAll=True): - if any(a.hasFlags(f) for f in nonUniformAssems): - continue - a.makeAxialSnapList(self.refAssem) - + # set reactor level meshing params + nonUniformAssems = [ + Flags.fromStringIgnoreErrors(t) for t in cs["nonUniformAssemFlags"] + ] # some assemblies, like control assemblies, have a non-conforming mesh # and should not be included in self.p.referenceBlockAxialMesh and self.p.axialMesh uniformAssems = [ diff --git a/armi/reactor/tests/test_reactors.py b/armi/reactor/tests/test_reactors.py index fcad1e57f..acde2a3e0 100644 --- a/armi/reactor/tests/test_reactors.py +++ b/armi/reactor/tests/test_reactors.py @@ -880,15 +880,13 @@ def test_nonUniformAssems(self): self.assertEqual(originalHeights, heights) def test_applyThermalExpansion_CoreConstruct(self): - """test Core::_applyThermalExpansion for core construction + """test that assemblies in core are correctly expanded. Notes: ------ - all assertions skip the first block as it has no $\Delta T$ and does not expand - - to maintain code coverage, _applyThermalExpansion is called via processLoading """ - self.o.cs["inputHeightsConsideredHot"] = False - assemsToChange = self.r.core.getAssemblies() + originalAssems = self.r.core.getAssemblies() # stash original axial mesh info oldRefBlockAxialMesh = self.r.core.p.referenceBlockAxialMesh oldAxialMesh = self.r.core.p.axialMesh @@ -896,70 +894,63 @@ def test_applyThermalExpansion_CoreConstruct(self): nonEqualParameters = ["heightBOL", "molesHmBOL", "massHmBOL"] equalParameters = ["smearDensity", "nHMAtBOL", "enrichmentBOL"] - oldBlockParameters = {} - for param in nonEqualParameters + equalParameters: - oldBlockParameters[param] = {} - for a in assemsToChange: - for b in a[1:]: - oldBlockParameters[param][b] = b.p[param] + print("loading cold") + _o, coldHeightR = loadTestReactor( + self.directoryChanger.destination, + customSettings={"inputHeightsConsideredHot": False}, + ) - self.r.core.processLoading(self.o.cs, dbLoad=False) for i, val in enumerate(oldRefBlockAxialMesh[1:]): - self.assertNotEqual(val, self.r.core.p.referenceBlockAxialMesh[i]) + self.assertNotEqual(val, coldHeightR.core.p.referenceBlockAxialMesh[i]) for i, val in enumerate(oldAxialMesh[1:]): - self.assertNotEqual(val, self.r.core.p.axialMesh[i]) + self.assertNotEqual(val, coldHeightR.core.p.axialMesh[i]) - for a in assemsToChange: + coldHeightAssems = coldHeightR.core.getAssemblies() + for a, coldHeightA in zip(originalAssems, coldHeightAssems): + print(a, coldHeightA) if not a.hasFlags(Flags.CONTROL): - for b in a[1:]: + for b, coldHeightB in zip(a[1:], coldHeightA[1:]): + print(b, coldHeightB) for param in nonEqualParameters: - if oldBlockParameters[param][b] and b.p[param]: + p, coldHeightP = b.p[param], coldHeightB.p[param] + if p and coldHeightP: self.assertNotEqual( - oldBlockParameters[param][b], b.p[param] + p, coldHeightP, f"{param} {p} {coldHeightP}" ) else: - self.assertAlmostEqual( - oldBlockParameters[param][b], b.p[param] - ) + self.assertAlmostEqual(p, coldHeightP) for param in equalParameters: - self.assertAlmostEqual(oldBlockParameters[param][b], b.p[param]) + p, coldHeightP = b.p[param], coldHeightB.p[param] + self.assertAlmostEqual(p, coldHeightP) def test_updateBlockBOLHeights_DBLoad(self): - """test Core::_applyThermalExpansion for db load + """Test that blueprints assemblies are expanded in DB load. Notes: ------ - all assertions skip the first block as it has no $\Delta T$ and does not expand - - to maintain code coverage, _applyThermalExpansion is called via processLoading """ - self.o.cs["inputHeightsConsideredHot"] = False - assemsToChange = [a for a in self.r.blueprints.assemblies.values()] - # stash original blueprint assemblies axial mesh info + originalAssems = sorted(a for a in self.r.blueprints.assemblies.values()) nonEqualParameters = ["heightBOL", "molesHmBOL", "massHmBOL"] equalParameters = ["smearDensity", "nHMAtBOL", "enrichmentBOL"] - oldBlockParameters = {} - for param in nonEqualParameters + equalParameters: - oldBlockParameters[param] = {} - for a in assemsToChange: - for b in a[1:]: - oldBlockParameters[param][b] = b.p[param] - self.r.core.processLoading(self.o.cs, dbLoad=True) - - for a in assemsToChange: + _o, coldHeightR = loadTestReactor( + self.directoryChanger.destination, + customSettings={"inputHeightsConsideredHot": False}, + ) + coldHeightAssems = sorted(a for a in coldHeightR.blueprints.assemblies.values()) + for a, coldHeightA in zip(originalAssems, coldHeightAssems): if not a.hasFlags(Flags.CONTROL): - for b in a[1:]: + for b, coldHeightB in zip(a[1:], coldHeightA[1:]): for param in nonEqualParameters: - if oldBlockParameters[param][b] and b.p[param]: - self.assertNotEqual( - oldBlockParameters[param][b], b.p[param] - ) + p, coldHeightP = b.p[param], coldHeightB.p[param] + if p and coldHeightP: + self.assertNotEqual(p, coldHeightP) else: - self.assertAlmostEqual( - oldBlockParameters[param][b], b.p[param] - ) + self.assertAlmostEqual(p, coldHeightP) for param in equalParameters: - self.assertAlmostEqual(oldBlockParameters[param][b], b.p[param]) + p, coldHeightP = b.p[param], coldHeightB.p[param] + self.assertAlmostEqual(p, coldHeightP) def test_buildManualZones(self): # define some manual zones in the settings From 4441c0a7c820686835bd165874cb13440efb149f Mon Sep 17 00:00:00 2001 From: onufer Date: Tue, 27 Dec 2022 15:57:46 -0800 Subject: [PATCH 04/11] fix empty blueprints testing --- .../tests/test_inputModifiers.py | 1 + armi/reactor/blueprints/__init__.py | 45 ++++++++++--------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/armi/cases/inputModifiers/tests/test_inputModifiers.py b/armi/cases/inputModifiers/tests/test_inputModifiers.py index bdc85db5c..a8bf6011b 100644 --- a/armi/cases/inputModifiers/tests/test_inputModifiers.py +++ b/armi/cases/inputModifiers/tests/test_inputModifiers.py @@ -121,6 +121,7 @@ def setUpClass(cls): geom.readGeomFromStream(GEOM_INPUT) bp = blueprints.Blueprints.load(BLUEPRINT_INPUT_LINKS) cs = settings.Settings() + cs["detailedAxialExpansion"] = True bp._prepConstruction(cs) cls.baseCase = cases.Case(cs=cs, bp=bp, geom=geom) diff --git a/armi/reactor/blueprints/__init__.py b/armi/reactor/blueprints/__init__.py index 0c90f7ea4..f8d14f5a0 100644 --- a/armi/reactor/blueprints/__init__.py +++ b/armi/reactor/blueprints/__init__.py @@ -327,27 +327,28 @@ def _prepConstruction(self, cs): runLog.header("=========== Verifying Assembly Configurations ===========") self._checkAssemblyAreaConsistency(cs) - # assume finest mesh is reference - referenceAssembly = sorted( - self.assemblies.values(), - key=lambda a: len(a), - reverse=True, - )[0] - if not cs["detailedAxialExpansion"]: - # make the snap lists so assems know how to expand - nonUniformAssems = [ - Flags.fromStringIgnoreErrors(t) for t in cs["nonUniformAssemFlags"] - ] - # prepare core for mesh snapping during axial expansion - for a in self.assemblies.values(): - if any(a.hasFlags(f) for f in nonUniformAssems): - continue - a.makeAxialSnapList(referenceAssembly) - if not cs["inputHeightsConsideredHot"]: - # expand axial heights from cold to hot - self._applyThermalExpansion( - referenceAssembly, cs["detailedAxialExpansion"] - ) + if self.assemblies.values(): + # if assemblies are defined in blueprints, handle meshing + # assume finest mesh is reference + referenceAssembly = sorted( + self.assemblies.values(), + key=lambda a: len(a), + reverse=True, + )[0] + if not cs["detailedAxialExpansion"]: + # make the snap lists so assems know how to expand + nonUniformAssems = [ + Flags.fromStringIgnoreErrors(t) + for t in cs["nonUniformAssemFlags"] + ] + # prepare core for mesh snapping during axial expansion + for a in self.assemblies.values(): + if any(a.hasFlags(f) for f in nonUniformAssems): + continue + a.makeAxialSnapList(referenceAssembly) + if not cs["inputHeightsConsideredHot"]: + # expand axial heights from cold to hot + self._coldDimsToHot(referenceAssembly, cs["detailedAxialExpansion"]) # pylint: disable=no-member getPluginManagerOrFail().hook.afterConstructionOfAssemblies( @@ -356,7 +357,7 @@ def _prepConstruction(self, cs): self._prepped = True - def _applyThermalExpansion( + def _coldDimsToHot( self, referenceAssembly, isDetailedAxialExpansion, From e638b1cb004d3d9eb3f5de34581d8301267e5270 Mon Sep 17 00:00:00 2001 From: onufer Date: Tue, 27 Dec 2022 16:00:24 -0800 Subject: [PATCH 05/11] rm setting change --- armi/cases/inputModifiers/tests/test_inputModifiers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/armi/cases/inputModifiers/tests/test_inputModifiers.py b/armi/cases/inputModifiers/tests/test_inputModifiers.py index a8bf6011b..bdc85db5c 100644 --- a/armi/cases/inputModifiers/tests/test_inputModifiers.py +++ b/armi/cases/inputModifiers/tests/test_inputModifiers.py @@ -121,7 +121,6 @@ def setUpClass(cls): geom.readGeomFromStream(GEOM_INPUT) bp = blueprints.Blueprints.load(BLUEPRINT_INPUT_LINKS) cs = settings.Settings() - cs["detailedAxialExpansion"] = True bp._prepConstruction(cs) cls.baseCase = cases.Case(cs=cs, bp=bp, geom=geom) From 5e7de3ce7da877452544dc9c46b430b80393eea8 Mon Sep 17 00:00:00 2001 From: onufer Date: Tue, 27 Dec 2022 17:07:22 -0800 Subject: [PATCH 06/11] delete prints in test --- armi/reactor/tests/test_reactors.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/armi/reactor/tests/test_reactors.py b/armi/reactor/tests/test_reactors.py index acde2a3e0..8036b124f 100644 --- a/armi/reactor/tests/test_reactors.py +++ b/armi/reactor/tests/test_reactors.py @@ -894,7 +894,6 @@ def test_applyThermalExpansion_CoreConstruct(self): nonEqualParameters = ["heightBOL", "molesHmBOL", "massHmBOL"] equalParameters = ["smearDensity", "nHMAtBOL", "enrichmentBOL"] - print("loading cold") _o, coldHeightR = loadTestReactor( self.directoryChanger.destination, customSettings={"inputHeightsConsideredHot": False}, @@ -907,10 +906,8 @@ def test_applyThermalExpansion_CoreConstruct(self): coldHeightAssems = coldHeightR.core.getAssemblies() for a, coldHeightA in zip(originalAssems, coldHeightAssems): - print(a, coldHeightA) if not a.hasFlags(Flags.CONTROL): for b, coldHeightB in zip(a[1:], coldHeightA[1:]): - print(b, coldHeightB) for param in nonEqualParameters: p, coldHeightP = b.p[param], coldHeightB.p[param] if p and coldHeightP: From 3b9c048b2ef3e53d446df6a72f3ae9e61617c2f3 Mon Sep 17 00:00:00 2001 From: onufer Date: Tue, 27 Dec 2022 18:38:06 -0800 Subject: [PATCH 07/11] simplify assemCheck --- armi/reactor/blueprints/__init__.py | 52 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/armi/reactor/blueprints/__init__.py b/armi/reactor/blueprints/__init__.py index f8d14f5a0..06f190965 100644 --- a/armi/reactor/blueprints/__init__.py +++ b/armi/reactor/blueprints/__init__.py @@ -327,33 +327,33 @@ def _prepConstruction(self, cs): runLog.header("=========== Verifying Assembly Configurations ===========") self._checkAssemblyAreaConsistency(cs) - if self.assemblies.values(): - # if assemblies are defined in blueprints, handle meshing - # assume finest mesh is reference - referenceAssembly = sorted( - self.assemblies.values(), - key=lambda a: len(a), - reverse=True, - )[0] - if not cs["detailedAxialExpansion"]: - # make the snap lists so assems know how to expand - nonUniformAssems = [ - Flags.fromStringIgnoreErrors(t) - for t in cs["nonUniformAssemFlags"] - ] - # prepare core for mesh snapping during axial expansion - for a in self.assemblies.values(): - if any(a.hasFlags(f) for f in nonUniformAssems): - continue - a.makeAxialSnapList(referenceAssembly) - if not cs["inputHeightsConsideredHot"]: - # expand axial heights from cold to hot - self._coldDimsToHot(referenceAssembly, cs["detailedAxialExpansion"]) - - # pylint: disable=no-member - getPluginManagerOrFail().hook.afterConstructionOfAssemblies( - assemblies=self.assemblies.values(), cs=cs + # if assemblies are defined in blueprints, handle meshing + # assume finest mesh is reference + assemsByNumBlocks = sorted( + self.assemblies.values(), + key=lambda a: len(a), + reverse=True, ) + referenceAssembly = assemsByNumBlocks[0] if assemsByNumBlocks else None + + if not cs["detailedAxialExpansion"]: + # make the snap lists so assems know how to expand + nonUniformAssems = [ + Flags.fromStringIgnoreErrors(t) for t in cs["nonUniformAssemFlags"] + ] + # prepare core for mesh snapping during axial expansion + for a in self.assemblies.values(): + if any(a.hasFlags(f) for f in nonUniformAssems): + continue + a.makeAxialSnapList(referenceAssembly) + if not cs["inputHeightsConsideredHot"]: + # expand axial heights from cold to hot + self._coldDimsToHot(referenceAssembly, cs["detailedAxialExpansion"]) + + # pylint: disable=no-member + getPluginManagerOrFail().hook.afterConstructionOfAssemblies( + assemblies=self.assemblies.values(), cs=cs + ) self._prepped = True From b65394649d2e98c5ac04f0b7c704bd78b0153588 Mon Sep 17 00:00:00 2001 From: onufer Date: Wed, 28 Dec 2022 17:01:33 -0800 Subject: [PATCH 08/11] move more logic outside blueprints --- armi/reactor/blueprints/__init__.py | 82 ++++--------------- .../converters/axialExpansionChanger.py | 62 ++++++++++++++ 2 files changed, 78 insertions(+), 66 deletions(-) diff --git a/armi/reactor/blueprints/__init__.py b/armi/reactor/blueprints/__init__.py index 06f190965..19267404b 100644 --- a/armi/reactor/blueprints/__init__.py +++ b/armi/reactor/blueprints/__init__.py @@ -104,7 +104,7 @@ from armi.reactor.blueprints.componentBlueprint import ComponentGroups from armi.reactor.blueprints import isotopicOptions from armi.reactor.blueprints.gridBlueprint import Grids, Triplet -from armi.reactor.converters.axialExpansionChanger import AxialExpansionChanger +from armi.reactor.converters import axialExpansionChanger context.BLUEPRINTS_IMPORTED = True context.BLUEPRINTS_IMPORT_CONTEXT = "".join(traceback.format_stack()) @@ -327,77 +327,27 @@ def _prepConstruction(self, cs): runLog.header("=========== Verifying Assembly Configurations ===========") self._checkAssemblyAreaConsistency(cs) - # if assemblies are defined in blueprints, handle meshing - # assume finest mesh is reference - assemsByNumBlocks = sorted( - self.assemblies.values(), - key=lambda a: len(a), - reverse=True, - ) - referenceAssembly = assemsByNumBlocks[0] if assemsByNumBlocks else None - if not cs["detailedAxialExpansion"]: - # make the snap lists so assems know how to expand - nonUniformAssems = [ - Flags.fromStringIgnoreErrors(t) for t in cs["nonUniformAssemFlags"] - ] - # prepare core for mesh snapping during axial expansion - for a in self.assemblies.values(): - if any(a.hasFlags(f) for f in nonUniformAssems): - continue - a.makeAxialSnapList(referenceAssembly) + axialExpansionChanger.makeAssemsAbleToSnapToUniformMesh( + self.assemblies.values(), cs["nonUniformAssemFlags"] + ) if not cs["inputHeightsConsideredHot"]: - # expand axial heights from cold to hot - self._coldDimsToHot(referenceAssembly, cs["detailedAxialExpansion"]) + runLog.header( + "=========== Axially expanding all assemblies (except control) from Tinput to Thot ===========" + ) + # expand axial heights from cold to hot so dims and masses are consistent + # with specified component hot temperatures. + axialExpansionChanger.expandColdDimsToHot( + self.assemblies.values(), cs["detailedAxialExpansion"] + ) - # pylint: disable=no-member - getPluginManagerOrFail().hook.afterConstructionOfAssemblies( - assemblies=self.assemblies.values(), cs=cs - ) + # pylint: disable=no-member + getPluginManagerOrFail().hook.afterConstructionOfAssemblies( + assemblies=self.assemblies.values(), cs=cs + ) self._prepped = True - def _coldDimsToHot( - self, - referenceAssembly, - isDetailedAxialExpansion, - ): - """ - Expand BOL assemblies, resolve disjoint axial mesh (if needed), and update block BOL heights - - Parameters - ---------- - assems: list - list of :py:class:`Assembly ` objects to be thermally expanded - """ - runLog.header( - "=========== Axially expanding all assemblies (except control) from Tinput to Thot ===========" - ) - assems = list(self.assemblies.values()) - - 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(thermal=True) - if not isDetailedAxialExpansion: - for a in assems: - if not a.hasFlags(Flags.CONTROL): - 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() - def _assignTypeNums(self): if self.blockDesigns is None: # this happens when directly defining assemblies. diff --git a/armi/reactor/converters/axialExpansionChanger.py b/armi/reactor/converters/axialExpansionChanger.py index d98031c73..bc2337c2f 100644 --- a/armi/reactor/converters/axialExpansionChanger.py +++ b/armi/reactor/converters/axialExpansionChanger.py @@ -30,6 +30,68 @@ ] +def getDefaultReferenceAssem(assems): + """Return a default reference assembly.""" + # if assemblies are defined in blueprints, handle meshing + # assume finest mesh is reference + assemsByNumBlocks = sorted( + assems, + key=lambda a: len(a), + reverse=True, + ) + return assemsByNumBlocks[0] if assemsByNumBlocks else None + + +def makeAssemsAbleToSnapToUniformMesh( + assems, nonUniformAssemFlags, referenceAssembly=None +): + """Make this set of assemblies aware of the reference mesh so they can stay uniform as they axially expand.""" + if not referenceAssembly: + referenceAssembly = getDefaultReferenceAssem(assems) + # make the snap lists so assems know how to expand + nonUniformAssems = [Flags.fromStringIgnoreErrors(t) for t in nonUniformAssemFlags] + for a in assems: + if any(a.hasFlags(f) for f in nonUniformAssems): + continue + a.makeAxialSnapList(referenceAssembly) + + +def expandColdDimsToHot(assems, isDetailedAxialExpansion, referenceAssembly=None): + """ + Expand BOL assemblies, resolve disjoint axial mesh (if needed), and update block BOL heights + + Parameters + ---------- + assems: list + list of :py:class:`Assembly ` objects to be thermally expanded + """ + assems = list(assems) + if not referenceAssembly: + 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(thermal=True) + if not isDetailedAxialExpansion: + for a in assems: + if not a.hasFlags(Flags.CONTROL): + 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() + + class AxialExpansionChanger: """ Axially expand or contract assemblies or an entire core. From 5009175c943cd82c0d280c7c6fc4c405d83f1248 Mon Sep 17 00:00:00 2001 From: onufer Date: Wed, 28 Dec 2022 17:03:19 -0800 Subject: [PATCH 09/11] comment --- armi/reactor/blueprints/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/armi/reactor/blueprints/__init__.py b/armi/reactor/blueprints/__init__.py index 19267404b..cec733d44 100644 --- a/armi/reactor/blueprints/__init__.py +++ b/armi/reactor/blueprints/__init__.py @@ -328,6 +328,9 @@ def _prepConstruction(self, cs): self._checkAssemblyAreaConsistency(cs) if not cs["detailedAxialExpansion"]: + # this is required to set up assemblies so they know how to snap + # to the reference mesh. They wont know the mesh to conform to + # otherwise.... axialExpansionChanger.makeAssemsAbleToSnapToUniformMesh( self.assemblies.values(), cs["nonUniformAssemFlags"] ) From 35811a0ed3e9f25f9d7a3a2dc02d33dfb8ab9651 Mon Sep 17 00:00:00 2001 From: onufer Date: Wed, 28 Dec 2022 17:04:45 -0800 Subject: [PATCH 10/11] remove unused import --- armi/reactor/blueprints/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/armi/reactor/blueprints/__init__.py b/armi/reactor/blueprints/__init__.py index cec733d44..4b10770f3 100644 --- a/armi/reactor/blueprints/__init__.py +++ b/armi/reactor/blueprints/__init__.py @@ -89,7 +89,6 @@ from armi.reactor import assemblies from armi.reactor import geometry from armi.reactor import systemLayoutInput -from armi.reactor.flags import Flags from armi.scripts import migration from armi.utils import textProcessors From fdbf6f9b40e0f978ebdac6eb4a0f3d2ea989f137 Mon Sep 17 00:00:00 2001 From: onufer Date: Thu, 29 Dec 2022 09:44:23 -0800 Subject: [PATCH 11/11] better doc string --- armi/reactor/converters/axialExpansionChanger.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/armi/reactor/converters/axialExpansionChanger.py b/armi/reactor/converters/axialExpansionChanger.py index bc2337c2f..acea0183a 100644 --- a/armi/reactor/converters/axialExpansionChanger.py +++ b/armi/reactor/converters/axialExpansionChanger.py @@ -64,6 +64,11 @@ def expandColdDimsToHot(assems, isDetailedAxialExpansion, referenceAssembly=None ---------- assems: list list of :py:class:`Assembly ` objects to be thermally expanded + isDetailedAxialExpansion: bool + If true assemblies will be forced to conform to the reference mesh after expansion + referenceAssembly: assembly, optional + Assembly whose mesh other meshes wil conform to if isDetailedAxialExpansion is true. + If not provided, will assume the finest mesh assembly which is typically fuel. """ assems = list(assems) if not referenceAssembly: