Source code for armi.reactor.blueprints.assemblyBlueprint

# Copyright 2019 TerraPower, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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.

"""
This module defines the blueprints input object for assemblies.

In addition to defining the input format, the ``AssemblyBlueprint`` class is responsible for
constructing ``Assembly`` objects. An attempt has been made to decouple ``Assembly`` construction
from the rest of ARMI as much as possible. For example, an assembly does not require a reactor to be
constructed, or a geometry file (but uses contained Block geometry type as a surrogate).
"""
import yamlize

from armi import getPluginManagerOrFail
from armi import runLog
from armi.reactor import assemblies
from armi.reactor.flags import Flags
from armi.reactor import parameters
from armi.reactor.blueprints import blockBlueprint
from armi.reactor import grids
from armi.settings.fwSettings.globalSettings import CONF_INPUT_HEIGHTS_HOT


def _configureAssemblyTypes():
    assemTypes = dict()
    pm = getPluginManagerOrFail()
    for pluginAssemTypes in pm.hook.defineAssemblyTypes():
        for blockType, assemType in pluginAssemTypes:
            assemTypes[blockType] = assemType

    return assemTypes


[docs]class Modifications(yamlize.Map): """ The names of material modifications and lists of the modification values for each block in the assembly. """ key_type = yamlize.Typed(str) value_type = yamlize.Sequence
[docs]class ByComponentModifications(yamlize.Map): """The name of a component within the block and an associated Modifications object.""" key_type = yamlize.Typed(str) value_type = Modifications
[docs]class MaterialModifications(yamlize.Map): """ A yamlize map for reading and holding material modifications. A user may specify material modifications directly as keys/values on this class, in which case these material modifications will be blanket applied to the entire block. If the user wishes to specify material modifications specific to a component within the block, they should use the `by component` attribute, specifying the keys/values underneath the name of a specific component in the block. .. impl:: User-impact on material definitions. :id: I_ARMI_MAT_USER_INPUT0 :implements: R_ARMI_MAT_USER_INPUT Defines a yaml map attribute for the assembly portion of the blueprints (see :py:class:`~armi.blueprints.assemblyBlueprint.AssemblyBlueprint`) that allows users to specify material attributes as lists corresponding to each axial block in the assembly. Two types of specifications can be made: 1. Key-value pairs can be specified directly, where the key is the name of the modification and the value is the list of block values. 2. The "by component" attribute can be used, in which case the user can specify material attributes that are specific to individual components in each block. This is enabled through the :py:class:`~armi.reactor.blueprints.assemblyBlueprint.ByComponentModifications` class, which basically just allows for one additional layer of attributes corresponding to the component names. These material attributes can be used during the resolution of material classes during core instantiation (see :py:meth:`~armi.reactor.blueprints.blockBlueprint.BlockBlueprint.construct` and :py:meth:`~armi.reactor.blueprints.componentBlueprint.ComponentBlueprint.construct`). """ key_type = yamlize.Typed(str) value_type = yamlize.Sequence byComponent = yamlize.Attribute( key="by component", type=ByComponentModifications, default=ByComponentModifications(), )
[docs]class AssemblyBlueprint(yamlize.Object): """ A data container for holding information needed to construct an ARMI assembly. This class utilizes ``yamlize`` to enable serialization to and from the blueprints YAML file. .. impl:: Create assembly from blueprint file. :id: I_ARMI_BP_ASSEM :implements: R_ARMI_BP_ASSEM Defines a yaml construct that allows the user to specify attributes of an assembly from within their blueprints file, including a name, flags, specifier for use in defining a core map, a list of blocks, a list of block heights, a list of axial mesh points in each block, a list of cross section identifiers for each block, and material options (see :need:`I_ARMI_MAT_USER_INPUT0`). Relies on the underlying infrastructure from the ``yamlize`` package for reading from text files, serialization, and internal storage of the data. Is implemented as part of a blueprints file by being imported and used as an attribute within the larger :py:class:`~armi.reactor.blueprints.Blueprints` class. Includes a ``construct`` method, which instantiates an instance of :py:class:`~armi.reactor.assemblies.Assembly` with the characteristics as specified in the blueprints. """ name = yamlize.Attribute(type=str) flags = yamlize.Attribute(type=str, default=None) specifier = yamlize.Attribute(type=str) blocks = yamlize.Attribute(type=blockBlueprint.BlockList) height = yamlize.Attribute(type=yamlize.FloatList) axialMeshPoints = yamlize.Attribute(key="axial mesh points", type=yamlize.IntList) radialMeshPoints = yamlize.Attribute( key="radial mesh points", type=int, default=None ) azimuthalMeshPoints = yamlize.Attribute( key="azimuthal mesh points", type=int, default=None ) materialModifications = yamlize.Attribute( key="material modifications", type=MaterialModifications, default=MaterialModifications(), ) xsTypes = yamlize.Attribute(key="xs types", type=yamlize.StrList) # note: yamlizable does not call an __init__ method, instead it uses __new__ and setattr _assemTypes = _configureAssemblyTypes()
[docs] @classmethod def getAssemClass(cls, blocks): """ Get the ARMI ``Assembly`` class for the specified blocks. Parameters ---------- blocks : list of Blocks Blocks for which to determine appropriate containing Assembly type """ blockClasses = {b.__class__ for b in blocks} for bType, aType in cls._assemTypes.items(): if bType in blockClasses: return aType raise ValueError( 'Unsupported block geometries in {}: "{}"'.format(cls.name, blocks) )
[docs] def construct(self, cs, blueprint): """ Construct an instance of this specific assembly blueprint. Parameters ---------- cs : Settings Settings object which containing relevant modeling options. blueprint : Blueprint Root blueprint object containing relevant modeling options. """ runLog.info("Constructing assembly `{}`".format(self.name)) self._checkParamConsistency() a = self._constructAssembly(cs, blueprint) a.calculateZCoords() return a
def _constructAssembly(self, cs, blueprint): """Construct the current assembly.""" blocks = [] for axialIndex, bDesign in enumerate(self.blocks): b = self._createBlock(cs, blueprint, bDesign, axialIndex) blocks.append(b) assemblyClass = self.getAssemClass(blocks) a = assemblyClass(self.name) flags = None if self.flags is not None: flags = Flags.fromString(self.flags) a.p.flags = flags # set a basic grid with the right number of blocks with bounds to be adjusted. a.spatialGrid = grids.axialUnitGrid(len(blocks)) a.spatialGrid.armiObject = a # TODO: Remove mesh points from blueprints entirely. Submeshing should be # handled by specific physics interfaces radMeshPoints = self.radialMeshPoints or 1 a.p.RadMesh = radMeshPoints aziMeshPoints = self.azimuthalMeshPoints or 1 a.p.AziMesh = aziMeshPoints # loop a second time because we needed all the blocks before choosing the # assembly class. for axialIndex, b in enumerate(blocks): b.name = b.makeName(a.p.assemNum, axialIndex) a.add(b) # Assign values for the parameters if they are defined on the blueprints for paramDef in a.p.paramDefs.inCategory( parameters.Category.assignInBlueprints ): val = getattr(self, paramDef.name) if val is not None: a.p[paramDef.name] = val return a @staticmethod def _shouldMaterialModiferBeApplied(value) -> bool: """Determine if a material modifier entry is applicable. Two exceptions: 1. Modifiers that are empty strings are not applied. 2. Modifiers that are ``None`` are not applied Parameters ---------- value : object Entry in a material modifications array Returns ------- bool Result of the check """ if value != "" and value is not None: return True return False def _createBlock(self, cs, blueprint, bDesign, axialIndex): """Create a block based on the block design and the axial index.""" meshPoints = self.axialMeshPoints[axialIndex] height = self.height[axialIndex] xsType = self.xsTypes[axialIndex] materialInput = {} for key, mod in { "byBlock": {**self.materialModifications}, **self.materialModifications.byComponent, }.items(): materialInput[key] = { modName: modList[axialIndex] for modName, modList in mod.items() if self._shouldMaterialModiferBeApplied(modList[axialIndex]) } b = bDesign.construct( cs, blueprint, axialIndex, meshPoints, height, xsType, materialInput ) b.completeInitialLoading() # set b10 volume cc since its a cold dim param b.setB10VolParam(cs[CONF_INPUT_HEIGHTS_HOT]) return b def _checkParamConsistency(self): """Check that the number of block params specified is equal to the number of blocks specified.""" paramsToCheck = { "mesh points": self.axialMeshPoints, "heights": self.height, "xs types": self.xsTypes, } for mod in [self.materialModifications] + list( self.materialModifications.byComponent.values() ): for modName, modList in mod.items(): paramName = "material modifications for {}".format(modName) paramsToCheck[paramName] = modList for paramName, blockVals in paramsToCheck.items(): if len(self.blocks) != len(blockVals): raise ValueError( "Assembly {} had {} blocks, but {} {}. These numbers should be equal. " "Check input for errors.".format( self.name, len(self.blocks), len(blockVals), paramName ) )
for paramDef in parameters.forType(assemblies.Assembly).inCategory( parameters.Category.assignInBlueprints ): setattr( AssemblyBlueprint, paramDef.name, yamlize.Attribute(name=paramDef.name, default=None), )
[docs]class AssemblyKeyedList(yamlize.KeyedList): """ Effectively and OrderedDict of assembly items, keyed on the assembly name. This uses yamlize KeyedList for YAML serialization. """ item_type = AssemblyBlueprint key_attr = AssemblyBlueprint.name heights = yamlize.Attribute(type=yamlize.FloatList, default=None) axialMeshPoints = yamlize.Attribute( key="axial mesh points", type=yamlize.IntList, default=None ) # note: yamlize does not call an __init__ method, instead it uses __new__ and setattr @property def bySpecifier(self): """Used by the reactor to ``_loadAssembliesIntoCore`` later, specifiers are two character strings.""" return {aDesign.specifier: aDesign for aDesign in self}