Skip to content

Commit

Permalink
Parameter mapping fix in the uniform mesh converter. (#799)
Browse files Browse the repository at this point in the history
* This adds a fix to the uniform mesh converter where data was being
removed from the source reactor if it was not set on the converted
reactor. This allows us to preserve the existing data on the blocks and
reactor core if it is unchanged from any work that is performed on
the converted reactor core.

Additionally, this moves the `axialMesh` parameter on the core outside
of a "neutronics" parameter category, as we want to avoid changing this
during the transfer of data from the uniform mesh reactor core back to
the non-uniform mesh reactor core. This is a significant bug that would
lead the reactor core state on non-uniform mesh to be inconsistent with
the core assembly meshes.

* Update the initialization of a new reactor to get name from the previous reactor passed in. This allows for two things: (1) further decoupling of the reactor and the operator for future refactoring and ease of running analyses without requiring an operator, and (2) not all reactors contain an instance of the operator so this can lead to failures.

* Fix getting case settings when operator is not available on the reactor.

* Improving the reporting of the cross section library that is attached
to the reactor core state.
  • Loading branch information
jakehader committed Aug 21, 2022
1 parent 44f287d commit 86dc4d8
Show file tree
Hide file tree
Showing 10 changed files with 348 additions and 136 deletions.
54 changes: 43 additions & 11 deletions armi/nuclearDataIO/xsLibraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from armi.utils.properties import ImmutablePropertyError

"""
Cross section library objects.
Expand Down Expand Up @@ -364,12 +365,38 @@ def __init__(self):
@property
def numGroups(self):
"""Get the number of neutron energy groups"""
return len(self.neutronEnergyUpperBounds)
# This unlocks the immutable property so that it can be
# read prior to not being set to check the number of groups
# that are defined. If the property is not unlocked before
# accessing when it has not yet been defined then an exception
# is thrown.
properties.unlockImmutableProperties(self)
if self.neutronEnergyUpperBounds is not None:
energyBounds = self.neutronEnergyUpperBounds
else:
energyBounds = []

# Make sure to re-lock the properties after we are done.
properties.lockImmutableProperties(self)
return len(energyBounds)

@property
def numGroupsGamma(self):
"""get the number of gamma energy groups"""
return len(self.gammaEnergyUpperBounds)
# This unlocks the immutable property so that it can be
# read prior to not being set to check the number of groups
# that are defined. If the property is not unlocked before
# accessing when it has not yet been defined then an exception
# is thrown.
properties.unlockImmutableProperties(self)
if self.gammaEnergyUpperBounds is not None:
energyBounds = self.gammaEnergyUpperBounds
else:
energyBounds = []

# Make sure to re-lock the properties after we are done.
properties.lockImmutableProperties(self)
return len(energyBounds)

@property
def xsIDs(self):
Expand All @@ -381,15 +408,20 @@ def xsIDs(self):
return list(set(getSuffixFromNuclideLabel(name) for name in self.nuclideLabels))

def __repr__(self):
files = (
self.isotxsMetadata.fileNames
+ self.pmatrxMetadata.fileNames
+ self.gamisoMetadata.fileNames
)
if not any(files):
return "<IsotxsLibrary empty>"
return "<IsotxsLibrary id:{} containing {} nuclides from {}>".format(
id(self), len(self), ", ".join(files)
isotxs = bool(self.isotxsMetadata.keys())
pmatrx = bool(self.pmatrxMetadata.keys())
gamiso = bool(self.gamisoMetadata.keys())
groups = ""
if self.numGroups:
groups += f"Neutron groups: {self.numGroups}, "
if self.numGroupsGamma:
groups += f"Gamma groups: {self.numGroupsGamma},"

return (
f"<IsotxsLibrary (id:{id(self)}), "
f"ISOTXS: {isotxs}, PMATRX: {pmatrx}, GAMISO: {gamiso}, "
f"{groups} containing {len(self)} nuclides with "
f"XS IDs: {sorted(self.xsIDs)}>"
)

def __setitem__(self, key, value):
Expand Down
26 changes: 16 additions & 10 deletions armi/physics/neutronics/globalFlux/globalFluxInterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,8 @@ def _performGeometryTransformations(self, makePlots=False):
None,
calcReactionRates=self.options.calcReactionRatesOnMeshConversion,
)
neutronicsReactor = converter.convert(self.r)
converter.convert(self.r)
neutronicsReactor = converter.convReactor
if makePlots:
converter.plotConvertedReactor()
self.geomConverters["axial"] = converter
Expand Down Expand Up @@ -443,21 +444,26 @@ def _undoGeometryTransformations(self):
geomConverter.scaleParamsRelatedToSymmetry(
self.r, paramsToScaleSubset=self.options.paramsToScaleSubset
)

# Resets the reactor core model to the correct symmetry and removes
# stored attributes on the converter to ensure that there is
# state data that is long-lived on the object in case the garbage
# collector does not remove it. Additionally, this will reset the
# global assembly counter.
geomConverter.removeEdgeAssemblies(self.r.core)

meshConverter = self.geomConverters.get("axial")
if meshConverter:
if self.options.applyResultsToReactor:
meshConverter.applyStateToOriginal()

self.r = meshConverter._sourceReactor # pylint: disable=protected-access;

nAssemsBeforeConversion = [
converter.getAssemblyModuleCounter()
for converter in (geomConverter, meshConverter)
if converter is not None
]
if nAssemsBeforeConversion:
assemblies.setAssemNumCounter(min(nAssemsBeforeConversion))
# Resets the stored attributes on the converter to
# ensure that there is state data that is long-lived on the
# object in case the garbage collector does not remove it.
# Additionally, this will reset the global assembly counter.
meshConverter.reset()

# clear the converters in case this function gets called twice
self.geomConverters = {}
Expand Down Expand Up @@ -1065,10 +1071,10 @@ def calcReactionRates(obj, keff, lib):

nucMc = lib.getNuclide(nucName, obj.getMicroSuffix())
micros = nucMc.micros
for g, groupGlux in enumerate(obj.getMgFlux()):
for g, groupFlux in enumerate(obj.getMgFlux()):

# dE = flux_e*dE
dphi = numberDensity * groupGlux
dphi = numberDensity * groupFlux

tot += micros.total[g, 0] * dphi
# absorption is fission + capture (no n2n here)
Expand Down
8 changes: 0 additions & 8 deletions armi/physics/neutronics/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -786,14 +786,6 @@ def _getNeutronicsCoreParams():
location=ParamLocation.AVERAGE,
)

pb.defParam(
"axialMesh",
units="cm",
description="Global axial mesh from bottom to top used in structured-mesh neutronics simulations.",
default=None,
location=ParamLocation.TOP,
)

pb.defParam(
"kInf",
units=None,
Expand Down
27 changes: 10 additions & 17 deletions armi/reactor/converters/blockConverters.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
class BlockConverter:
"""Converts a block."""

def __init__(self, sourceBlock, quiet=False):
def __init__(self, sourceBlock):
"""
Parameters
----------
Expand All @@ -45,7 +45,6 @@ def __init__(self, sourceBlock, quiet=False):
quite : boolean, optional
If True, less information is output in the runLog.
"""
self._quiet = quiet
self._sourceBlock = sourceBlock
self.convertedBlock = None # the new block that is created.

Expand Down Expand Up @@ -73,12 +72,11 @@ def dissolveComponentIntoComponent(self, soluteName, solventName, minID=0.0):
recommended that these blocks be made right before the physics calculation of interest and be immediately
discarded. Attaching them to the reactor is not recommended.
"""
if not self._quiet:
runLog.extra(
"Homogenizing the {} component into the {} component in block {}".format(
soluteName, solventName, self._sourceBlock
)
runLog.extra(
"Homogenizing the {} component into the {} component in block {}".format(
soluteName, solventName, self._sourceBlock
)
)
# break up dimension links since we will be messing with this block's components
newBlock = copy.deepcopy(self._sourceBlock)
# cannot pass components directly since the new block will have new components
Expand Down Expand Up @@ -213,7 +211,7 @@ def convert(self):
class ComponentMerger(BlockConverter):
"""For a provided block, merged the solute component into the solvent component."""

def __init__(self, sourceBlock, soluteName, solventName, quiet=False):
def __init__(self, sourceBlock, soluteName, solventName):
"""
Parameters
----------
Expand All @@ -226,7 +224,7 @@ def __init__(self, sourceBlock, soluteName, solventName, quiet=False):
quite : boolean, optional
If True, less information is output in the runLog.
"""
BlockConverter.__init__(self, sourceBlock, quiet=quiet)
BlockConverter.__init__(self, sourceBlock)
self.soluteName = soluteName
self.solventName = solventName

Expand All @@ -250,9 +248,7 @@ class MultipleComponentMerger(BlockConverter):
do single and multiple components with the same code.
"""

def __init__(
self, sourceBlock, soluteNames, solventName, specifiedMinID=0.0, quiet=False
):
def __init__(self, sourceBlock, soluteNames, solventName, specifiedMinID=0.0):
"""
Parameters
----------
Expand All @@ -268,7 +264,7 @@ def __init__(
quite : boolean, optional
If True, less information is output in the runLog.
"""
BlockConverter.__init__(self, sourceBlock, quiet=quiet)
BlockConverter.__init__(self, sourceBlock)
self.soluteNames = soluteNames
self.solventName = solventName
self.specifiedMinID = specifiedMinID
Expand Down Expand Up @@ -318,10 +314,9 @@ def __init__(
driverFuelBlock=None,
numInternalRings=1,
numExternalRings=None,
quiet=False,
):

BlockConverter.__init__(self, sourceBlock, quiet=quiet)
BlockConverter.__init__(self, sourceBlock)
self._driverFuelBlock = driverFuelBlock
self._numExternalRings = numExternalRings
self.convertedBlock = blocks.ThRZBlock(
Expand Down Expand Up @@ -489,14 +484,12 @@ def __init__(
driverFuelBlock=None,
numExternalRings=None,
mergeIntoClad=None,
quiet=False,
):
BlockAvgToCylConverter.__init__(
self,
sourceBlock,
driverFuelBlock=driverFuelBlock,
numExternalRings=numExternalRings,
quiet=quiet,
)
if not isinstance(sourceBlock, blocks.HexBlock):
raise TypeError(
Expand Down

0 comments on commit 86dc4d8

Please sign in to comment.