def plotConvertedBlock(self):
     """Render an image of the converted block."""
     figName = self._sourceBlock.name + "_1D_cylinder.svg"
     runLog.extra(
         "Plotting equivalent cylindrical block of {} as {}".format(
             self._sourceBlock, figName
         )
     )
     fig, ax = plt.subplots()
     fig.patch.set_visible(False)
     ax.patch.set_visible(False)
     ax.axis("off")
     patches = []
     colors = []
     for circleComp in self.convertedBlock:
         innerR, outerR = (
             circleComp.getDimension("id") / 2.0,
             circleComp.getDimension("od") / 2.0,
         )
         runLog.debug(
             "Plotting {:40s} with {:10.3f} {:10.3f} ".format(
                 circleComp, innerR, outerR
             )
         )
         circle = Wedge((0.0, 0.0), outerR, 0, 360.0, outerR - innerR)
         patches.append(circle)
         colors.append(circleComp.density())
     colorMap = matplotlib.cm
     p = PatchCollection(patches, alpha=1.0, linewidths=0.1, cmap=colorMap.rainbow)
     p.set_array(numpy.array(colors))
     p.set_clim(0, 20)
     ax.add_collection(p)
     ax.autoscale_view(True, True, True)
     plt.savefig(figName)
     return figName
Exemple #2
0
    def run(self):
        """
        Run the executer steps.

        .. warning::
                If a calculation requires anything different from what this method does,
                do not update this method with new complexity! Instead, simply make your own
                run sequence and/or class. This pattern is useful only in that it is fairly simple.
                By all means, do use ``DirectoryChanger`` and ``ExecuterOptions``
                and other utilities. 
        """
        self.options.resolveDerivedOptions()
        runLog.debug(self.options.describe())
        if self.options.executablePath and not os.path.exists(
                self.options.executablePath):
            raise IOError(
                f"Required executable `{self.options.executablePath}` not found for {self}"
            )
        self._performGeometryTransformations()
        inputs, outputs = self._collectInputsAndOutputs()
        # must either write input to CWD for analysis and then copy to runDir
        # or not list it in inputs (for optimization)
        self.writeInput()
        with directoryChangers.ForcedCreationDirectoryChanger(
                self.options.runDir, filesToMove=inputs,
                filesToRetrieve=outputs) as dc:
            self.options.workingDir = dc.initial
            self._execute()
            output = self._readOutput()
            if self.options.applyResultsToReactor:
                output.apply(self.r)
        self._undoGeometryTransformations()
        return output
Exemple #3
0
    def addNewOutputFileToRetrieve(self, sourceName, destinationName=None):
        """
        Add a new file to the list of files that will be copied to the shared drive after a run.

        These should be names only, not paths.

        Parameters
        ----------
        sourceName : str
            The name of the file in the source location, e.g. `FT06`

        destinationName : str or None
            The name of the file in the destination location, e.g. `caseName.dif3d.out`

        See Also
        --------
        armi.utils.directoryChangers.DirectoryChanger.retrieveFiles : should be used instead of this.
        """
        if destinationName is None:
            # no name change
            destinationName = sourceName
        runLog.debug(
            "Adding output file {} to list of files to retrieve".format(
                sourceName))
        self._outputFilesNamesToRetrieve.append((sourceName, destinationName))
Exemple #4
0
    def _updateReactorParams(self, reactor, dbTimeStep):
        """Update reactor-/core-level parameters from the database"""
        # These are the names present in the reactors section of the DB
        dbParamNames = self.getReactorParamNames()

        reactorNames = set(
            pDef.name for pDef in parameters.ALL_DEFINITIONS.forType(reactors.Reactor)
        )
        coreNames = set(
            pDef.name for pDef in parameters.ALL_DEFINITIONS.forType(reactors.Core)
        )

        for paramName in dbParamNames:
            if paramName == "TimeStep":
                continue
            runLog.debug("Reading scalar {0}".format(paramName))
            # get time-ordered list of scalar vals. Pick the relevant one.
            val = self.readReactorParam(paramName, dbTimeStep)[0]
            if val is parameters.NoDefault:
                continue
            if paramName in ["cycle", "timeNode"]:
                # int(float('0.000E+00')) works, but int('0.00E+00')
                val = int(float(val))
            if paramName in reactorNames:
                reactor.p[paramName] = val
            elif paramName in coreNames:
                reactor.core.p[paramName] = val
            else:
                runLog.warning(
                    'The parameter "{}" was present in the database, but is not '
                    "recognized as a Reactor or Core parameter".format(paramName)
                )
Exemple #5
0
    def _updateAssemblyParams(self, reactor, dbTimeStep):
        """Update assembly-level parameters (and name) from the database"""
        runLog.debug("Reading assembly-level params of all assemblies")
        # an assembly-level param table exists. Load from it.
        loadAssemParamsFromDB = True
        assemParams = self.getAssemblyParamNames()
        for removal in ["RowID", "spatialLocator.indices"]:
            if removal in assemParams:
                assemParams.remove(
                    removal
                )  # take out the DB primary so as to not set it.

        assemParamData = self.readMultipleAssemblyParam(
            dbTimeStep, assemParams
        )  # pylint: disable=no-member
        for a in reactor.core.getAssemblies():
            if loadAssemParamsFromDB:
                ring, pos = a.spatialLocator.getRingPos()
                for assemParamName in assemParams:
                    val = assemParamData[assemParamName, ring, pos]
                    setParameterWithRenaming(a, assemParamName, val)

                    if assemParamName == "assemNum" and val:
                        # update assembly name based on assemNum
                        name = a.makeNameFromAssemNum(val)
                        a.name = name
                        a.renameBlocksAccordingToAssemblyNum()
Exemple #6
0
 def construct(self, blueprint, matMods):
     """Construct a component"""
     runLog.debug("Constructing component {}".format(self.name))
     kwargs = self._conformKwargs(blueprint, matMods)
     component = components.factory(self.shape.strip().lower(), [], kwargs)
     _insertDepletableNuclideKeys(component, blueprint)
     return component
Exemple #7
0
 def plotConvertedBlock(self, fName=None):
     """Render an image of the converted block."""
     runLog.extra("Plotting equivalent cylindrical block of {}".format(
         self._sourceBlock))
     fig, ax = plt.subplots()
     fig.patch.set_visible(False)
     ax.patch.set_visible(False)
     ax.axis("off")
     patches = []
     colors = []
     for circleComp in self.convertedBlock:
         innerR, outerR = (
             circleComp.getDimension("id") / 2.0,
             circleComp.getDimension("od") / 2.0,
         )
         runLog.debug("Plotting {:40s} with {:10.3f} {:10.3f} ".format(
             circleComp, innerR, outerR))
         circle = Wedge((0.0, 0.0), outerR, 0, 360.0, outerR - innerR)
         patches.append(circle)
         colors.append(circleComp.density())
     colorMap = matplotlib.cm
     p = PatchCollection(patches,
                         alpha=1.0,
                         linewidths=0.1,
                         cmap=colorMap.YlGn)
     p.set_array(numpy.array(colors))
     ax.add_collection(p)
     ax.autoscale_view(True, True, True)
     ax.set_aspect("equal")
     fig.tight_layout()
     if fName:
         plt.savefig(fName)
     else:
         plt.show()
     return fName
    def _updateBurnupGroups(self, blockList):
        """
        Update the burnup group of each block based on its burnup

        If only one burnup group exists, then this is skipped so as to accomodate the possibility
        of 2-character xsGroup values (useful for detailed V&V models w/o depletion).

        See Also
        --------
        armi.reactor.blocks.Block.getMicroSuffix
        """
        if self._buGroupUpdatesEnabled and len(self._upperBuGroupBounds) > 1:
            runLog.debug("Updating burnup groups of {0} blocks".format(len(blockList)))
            for block in blockList:
                bu = block.p.percentBu
                for buGroupIndex, upperBu in enumerate(self._upperBuGroupBounds):
                    if bu <= upperBu:
                        block.p.buGroupNum = buGroupIndex
                        break
                else:
                    raise ValueError("no bu group found for bu={0}".format(bu))
        else:
            runLog.debug(
                "Skipping burnup group update of {0} blocks because it is disabled"
                "".format(len(blockList))
            )
    def createRepresentativeBlocks(self):
        """
        Get a representative block from each cross section ID managed here.
        """
        representativeBlocks = {}
        self.avgNucTemperatures = {}
        self._unrepresentedXSIDs = []
        runLog.extra("Generating representative blocks for XS")
        blockCollectionsByXsGroup = self.makeCrossSectionGroups()
        for xsID, collection in blockCollectionsByXsGroup.items():
            numCandidateBlocks = len(collection.getCandidateBlocks())
            if self.xsTypeIsPregenerated(xsID):
                self._copyPregeneratedXSFile(xsID)
                continue
            if numCandidateBlocks > 0:
                runLog.debug("Creating representative block for {}".format(xsID))
                reprBlock = collection.createRepresentativeBlock()
                representativeBlocks[xsID] = reprBlock
                self.avgNucTemperatures[xsID] = collection.avgNucTemperatures
            else:
                runLog.debug(
                    "No candidate blocks for {} will apply different burnup group".format(
                        xsID
                    )
                )
                self._unrepresentedXSIDs.append(xsID)

        self.representativeBlocks = collections.OrderedDict(
            sorted(representativeBlocks.items())
        )
        self._modifyUnrepresentedXSIDs(blockCollectionsByXsGroup)
        self._summarizeGroups(blockCollectionsByXsGroup)
Exemple #10
0
    def _getAllFissionProductNames(self):
        """
        Find all fission product names in the problem

        Considers all LFP collections, whether they be global,
        block-level, or a mix of these.

       sets fissionProductNames, a list of nuclide names of all the
       fission products
        """
        runLog.debug("  Gathering all possible FPs")
        fissionProductNames = []
        lfpCollections = []
        # get all possible lfp collections (global + block-level)
        for b in self.r.core.getBlocks(Flags.FUEL, includeAll=True):
            lfpCollection = b.getLumpedFissionProductCollection()
            if lfpCollection and lfpCollection not in lfpCollections:
                lfpCollections.append(lfpCollection)

        # get all possible FP names in each LFP collection
        for lfpCollection in lfpCollections:
            for fpName in lfpCollection.getAllFissionProductNames():
                if fpName not in fissionProductNames:
                    fissionProductNames.append(fpName)

        self.fissionProductNames = fissionProductNames
Exemple #11
0
def copyOrWarn(fileDescription, sourcePath, destinationPath):
    """Copy a file, or warn if the file doesn't exist.

    Parameters
    ----------
    fileDescription : str
        a description of the file and/or operation being performed.

    sourcePath : str
        Path of the file to be copied.

    destinationPath : str
        Path for the copied file.
    """
    try:
        shutil.copy(sourcePath, destinationPath)
        runLog.debug(
            "Copied {}: {} -> {}".format(fileDescription, sourcePath, destinationPath)
        )
    except Exception as e:
        runLog.warning(
            "Could not copy {} from {} to {}\nError was: {}".format(
                fileDescription, sourcePath, destinationPath, e
            )
        )
Exemple #12
0
    def getRingZoneRings(self):
        """
        Get rings in each ring zone as a list of lists.

        Returns
        -------
        ringZones : list
            List of lists. Each entry is the ring numbers in a ring zone.
            If there are no ring zones defined, returns a list of all rings.
        """
        core = self.core
        if not self.cs["ringZones"]:
            # no ring zones defined. Return all rings.
            return [range(1, core.getNumRings() + 1)]

        # ringZones are upper limits, defining ring zones from the center. so if they're
        #  [3, 5, 8, 90] then the ring zones are from 1 to 3, 4 to 5, 6 to 8, etc.
        # AKA, the upper bound is included in that particular zone.

        # check validity of ringZones. Increasing order and integers.
        ring0 = 0
        for i, ring in enumerate(self.cs["ringZones"]):
            if ring <= ring0 or not isinstance(ring, int):
                runLog.warning(
                    "ring zones {0} are invalid. Must be integers, increasing in order. "
                    "Can not return ring zone rings.".format(self.cs["ringZones"])
                )
                return
            ring0 = ring
            if i == len(self.cs["ringZones"]) - 1:
                # this is the final ring zone
                if ring < (core.getNumRings() + 1):
                    finalRing = core.getNumRings()
                else:
                    finalRing = None

        # modify the ringZones to definitely include all assemblies
        if finalRing:
            runLog.debug(
                "Modifying final ring zone definition to include all assemblies. New max: {0}".format(
                    finalRing
                ),
                single=True,
                label="Modified ring zone definition",
            )
            self.cs["ringZones"][-1] = finalRing

        # build the ringZone list
        ring0 = 0
        ringZones = []
        for upperRing in self.cs["ringZones"]:
            ringsInThisZone = range(
                ring0 + 1, upperRing + 1
            )  # the rings in this ring zone as defined above.

            ringZones.append(ringsInThisZone)
            ring0 = upperRing

        return ringZones
Exemple #13
0
def setMasterCs(cs):
    """
    Set the master Cs to be the one that is passed in.

    These are kept track of independently on a PID basis to allow independent multiprocessing.
    """
    Settings.instance = cs
    runLog.debug("Master cs set to {} with ID: {}".format(cs, id(cs)))
Exemple #14
0
    def _constructSpatialGrid(self):
        """
        Build spatial grid.

        If you do not enter latticeDimensions, a unit grid will be produced which must
        be adjusted to the proper dimensions (often by inspection of children) at a
        later time.
        """
        geom = self.geom
        maxIndex = self._getMaxIndex()
        runLog.extra("Creating the spatial grid")
        if geom in (geometry.RZT, geometry.RZ):
            if self.gridBounds is None:
                # This check is regrattably late. It would be nice if we could validate
                # that bounds are provided if R-Theta mesh is being used.
                raise InputError(
                    "Grid bounds must be provided for `{}` to specify a grid with "
                    "r-theta components.".format(self.name))
            for key in ("theta", "r"):
                if key not in self.gridBounds:
                    raise InputError(
                        "{} grid bounds were not provided for `{}`.".format(
                            key, self.name))

            # convert to list, otherwise it is a CommentedSeq
            theta = numpy.array(self.gridBounds["theta"])
            radii = numpy.array(self.gridBounds["r"])
            for l, name in ((theta, "theta"), (radii, "radii")):
                if not _isMonotonicUnique(l):
                    raise InputError(
                        "Grid bounds for {}:{} is not sorted or contains "
                        "duplicates. Check blueprints.".format(
                            self.name, name))
            spatialGrid = grids.ThetaRZGrid(bounds=(theta, radii, (0.0, 0.0)))
        if geom == geometry.HEX:
            pitch = self.latticeDimensions.x if self.latticeDimensions else 1.0
            # add 2 for potential dummy assems
            spatialGrid = grids.HexGrid.fromPitch(pitch, numRings=maxIndex + 2)
        elif geom == geometry.CARTESIAN:
            # if full core or not cut-off, bump the first assembly from the center of
            # the mesh into the positive values.
            xw, yw = ((self.latticeDimensions.x,
                       self.latticeDimensions.y) if self.latticeDimensions else
                      (1.0, 1.0))
            isOffset = (self.symmetry and geometry.THROUGH_CENTER_ASSEMBLY
                        not in self.symmetry)
            spatialGrid = grids.CartesianGrid.fromRectangle(xw,
                                                            yw,
                                                            numRings=maxIndex +
                                                            1,
                                                            isOffset=isOffset)
        runLog.debug("Built grid: {}".format(spatialGrid))
        # set geometric metadata on spatialGrid. This information is needed in various
        # parts of the code and is best encapsulated on the grid itself rather than on
        # the container state.
        spatialGrid.geomType = self.geom
        spatialGrid.symmetry = self.symmetry
        return spatialGrid
Exemple #15
0
 def _cacheLFPDensities(self, blockList):
     # pass 2: Cache all LFP densities for all blocks (so they aren't read
     # for each FP)
     runLog.debug("  Caching LFP densities of all blocks")
     lfpDensities = {}
     for b in blockList:
         for lfpName in self._globalLFPs:
             lfpDensities[lfpName, b.getName()] = b.getNumberDensity(lfpName)
     return lfpDensities
Exemple #16
0
    def writeHTML(self):
        """Renders this report as a standalone HTML file"""
        filename = "{}.html".format(self.title)
        runLog.debug("Writing HTML document {}.".format(filename))

        with html.HTMLFile(filename, "w") as f:
            html.writeStandardReportTemplate(f, self)

        runLog.info("HTML document {} written".format(filename))
Exemple #17
0
    def createMacrosFromMicros(self,
                               microLibrary,
                               block,
                               nucNames=None,
                               libType="micros"):
        """
        Creates a macroscopic cross section set based on a microscopic XS library using a block object

        Micro libraries have lots of nuclides, but macros only have 1.

        Parameters
        ----------
        microLibrary : xsCollection.XSCollection
            Input micros

        block : Block
            Object whos number densities should be used to generate macros

        nucNames : list, optional
            List of nuclides to include in the macros. Defaults to all in block.

        libType : str, optional
            The block attribute containing the desired microscopic XS for this block:
            either "micros" for neutron XS or "gammaXS" for gamma XS.

        Returns
        -------
        macros : xsCollection.XSCollection
            A new XSCollection full of macroscopic cross sections

        """
        runLog.debug(
            "Building macroscopic cross sections for {0}".format(block))
        if nucNames is None:
            nucNames = block.getNuclides()

        self.microLibrary = microLibrary
        self.block = block
        self.xsSuffix = block.getMicroSuffix()
        self.macros = XSCollection(parent=block)
        self.densities = dict(
            zip(nucNames, block.getNuclideNumberDensities(nucNames)))
        self.ng = getattr(self.microLibrary,
                          "numGroups" + _getLibTypeSuffix(libType))

        self._initializeMacros()
        self._convertBasicXS(libType=libType)
        self._computeAbsorptionXS()
        self._convertScatterMatrices(libType=libType)
        self._computeDiffusionConstants()
        self._buildTotalScatterMatrix()
        self._computeRemovalXS()
        self.macros.chi = computeBlockAverageChi(b=self.block,
                                                 isotxsLib=self.microLibrary)

        return self.macros
 def __exit__(self, exc_type, exc_value, traceback):
     """At the termination of a with command, navigate back to the original directory."""
     runLog.debug("Returning to directory {}".format(self.initial))
     if exc_type is not None and self._dumpOnException:
         runLog.info("An exception was raised within a DirectoryChanger. "
                     "Retrieving entire folder for debugging.")
         self._retrieveEntireFolder()
     else:
         self.retrieveFiles()
     self.close()
Exemple #19
0
    def validate(self):
        """
        Performs validation checks on the inputs and provides warnings for option inconsistencies.

        Raises
        ------
        ValueError
            When the mutually exclusive ``fileLocation`` and ``geometry`` attributes
            are provided or when neither are provided.
        """
        # Check for valid inputs when the file location is supplied.
        if self.fileLocation is None and self.geometry is None:
            raise ValueError(f"{self} is missing a geometry input or a file location.")

        if self.fileLocation is not None and self.geometry is not None:
            runLog.warning(
                f"Either file location or geometry inputs in {self} should be given, but not both. "
                "The file location setting will take precedence over the geometry inputs. "
                "Remove one or the other in the `crossSectionSettings` input to fix this warning."
            )

        invalids = []
        if self.fileLocation is not None:
            for var, val in self:
                # Skip these attributes since they are valid options
                # when the ``fileLocation`` attribute`` is set.
                if var in [CONF_XSID, CONF_FILE_LOCATION, CONF_BLOCK_REPRESENTATION]:
                    continue
                if val is not None:
                    invalids.append((var, val))

        if invalids:
            runLog.debug(
                f"The following inputs in {self} are not valid when the file location is set:"
            )
            for var, val in invalids:
                runLog.debug(f"\tAttribute: {var}, Value: {val}")

        # Check for valid inputs when the geometry is supplied.
        invalids = []
        if self.geometry is not None:
            validOptions = _VALID_INPUTS_BY_GEOMETRY_TYPE[self.geometry]
            for var, val in self:
                if var not in validOptions and val is not None:
                    invalids.append((var, val))

        if invalids:
            runLog.debug(
                f"The following inputs in {self} are not valid when `{self.geometry}` geometry type is set:"
            )
            for var, val in invalids:
                runLog.debug(f"\tAttribute: {var}, Value: {val}")
            runLog.debug(
                f"The valid options for the `{self.geometry}` geometry are: {validOptions}"
            )
Exemple #20
0
    def _getLinkedComponents(self, b, c):
        """retrieve the axial linkage for component c

        Parameters
        ----------
        b : :py:class:`Block <armi.reactor.blocks.Block>` object
            key to access blocks containing linked components
        c : :py:class:`Component <armi.reactor.components.component.Component>` object
            component to determine axial linkage for

        Raises
        ------
        RuntimeError
            multiple candidate components are found to be axially linked to a component
        """
        lstLinkedC = [None, None]
        for ib, linkdBlk in enumerate(self.linkedBlocks[b]):
            if linkdBlk is not None:
                for otherC in linkdBlk.getChildren():
                    if _determineLinked(c, otherC):
                        if lstLinkedC[ib] is not None:
                            errMsg = (
                                "Multiple component axial linkages have been found for "
                                "Component {0}; Block {1}; Assembly {2}."
                                " This is indicative of an error in the blueprints! Linked components found are"
                                "{3} and {4}".format(
                                    c, b, b.parent, lstLinkedC[ib], otherC
                                )
                            )
                            runLog.error(msg=errMsg)
                            raise RuntimeError(errMsg)
                        lstLinkedC[ib] = otherC

        self.linkedComponents[c] = lstLinkedC

        if lstLinkedC[0] is None:
            runLog.debug(
                "Assembly {0:22s} at location {1:22s}, Block {2:22s}, Component {3:22s} "
                "has nothing linked below it!".format(
                    str(self.a.getName()),
                    str(self.a.getLocation()),
                    str(b.p.flags),
                    str(c.p.flags),
                )
            )
        if lstLinkedC[1] is None:
            runLog.debug(
                "Assembly {0:22s} at location {1:22s}, Block {2:22s}, Component {3:22s} "
                "has nothing linked above it!".format(
                    str(self.a.getName()),
                    str(self.a.getLocation()),
                    str(b.p.flags),
                    str(c.p.flags),
                )
            )
Exemple #21
0
    def _getLinkedBlocks(self, b):
        """retrieve the axial linkage for block b

        Parameters
        ----------
        b : :py:class:`Block <armi.reactor.blocks.Block>` object
            block to determine axial linkage for

        NOTES
        -----
        - block linkage is determined by matching ztop/zbottom (see below)
        - block linkage is stored in self.linkedBlocks[b]
         _ _
        |   |
        | 2 |  Block 2 is linked to block 1.
        |_ _|
        |   |
        | 1 |  Block 1 is linked to both block 0 and 1.
        |_ _|
        |   |
        | 0 |  Block 0 is linked to block 1.
        |_ _|
        """
        lowerLinkedBlock = None
        upperLinkedBlock = None
        block_list = self.a.getChildren()
        for otherBlk in block_list:
            if b.name != otherBlk.name:
                if b.p.zbottom == otherBlk.p.ztop:
                    lowerLinkedBlock = otherBlk
                elif b.p.ztop == otherBlk.p.zbottom:
                    upperLinkedBlock = otherBlk

        self.linkedBlocks[b] = [lowerLinkedBlock, upperLinkedBlock]

        if lowerLinkedBlock is None:
            runLog.debug(
                "Assembly {0:22s} at location {1:22s}, Block {2:22s}"
                "is not linked to a block below!".format(
                    str(self.a.getName()),
                    str(self.a.getLocation()),
                    str(b.p.flags),
                )
            )
        if upperLinkedBlock is None:
            runLog.debug(
                "Assembly {0:22s} at location {1:22s}, Block {2:22s}"
                "is not linked to a block above!".format(
                    str(self.a.getName()),
                    str(self.a.getLocation()),
                    str(b.p.flags),
                )
            )
Exemple #22
0
    def _clearStateOnSourceReactor(self):
        """Delete existing state that will be updated so they don't increment."""
        runLog.debug(
            "Clearing params from source reactor that will be converted.")
        for rp in self.reactorParamNames:
            self._sourceReactor.core.p[rp] = 0.0

        for b in self._sourceReactor.core.getBlocks():
            b.p.mgFlux = []
            b.p.adjMgFlux = []
            for bp in self.blockParamNames:
                b.p[bp] = 0.0
Exemple #23
0
def expandFissionProducts(massFrac, lumpedFissionProducts):
    """
    expands lumped fission products in a massFrac vector

    Parameters
    ----------
    massFrac : dict

    lumpedFissionProducts : LumpedFissionProductCollection (acts like a dict)
        result of <fissionProductInterface>.getGlobalLumpedFissionProducts

    Returns
    -------
    newMassFracs : dict
    """
    lfpNbs = []
    elementalNbs = []
    newMassFrac = {}

    for nucName in massFrac.keys():
        nB = nuclideBases.byName[nucName]
        if isinstance(nB, nuclideBases.LumpNuclideBase):
            lfpNbs.append(nB)
        elif isinstance(nB, nuclideBases.NaturalNuclideBase):
            elementalNbs.append(nB)
        else:
            newMassFrac[nucName] = massFrac[nucName]

    for lfp in lfpNbs:
        if massFrac[lfp.name] != 0:
            try:
                lfpFP = lumpedFissionProducts[lfp.name]
            except KeyError:
                errorMessage = ["{}".format(lumpedFissionProducts)]
                errorMessage.append("\ntype {}".format(
                    type(lumpedFissionProducts)))
                errorMessage.append("\nmassFrac dict {}".format(massFrac))
                errorMessage.append("\nLumped Fission Product Name {}".format(
                    lfp.name))
                runLog.debug("".join(errorMessage))

            for nB in lfpFP.keys():
                newMassFrac[nB.name] = massFrac.get(
                    nB.name,
                    0) + massFrac[lfp.name] * lfpFP.getMassFrac(nuclideBase=nB)

    for element in elementalNbs:
        for nB in element.getNaturalIsotopics():
            newMassFrac[nB.name] = (massFrac.get(nB.name, 0) +
                                    massFrac[element.name] * nB.abundance *
                                    nB.weight / element.weight)
    return newMassFrac
Exemple #24
0
def addDummyNuclidesToLibrary(lib, dummyNuclides):
    """
    This method adds DUMMY nuclides to the current GAMISO library.

    Parameters
    ----------
    lib : obj
        GAMISO library object

    dummyNuclides: list
        List of DUMMY nuclide objects that will be copied and added to the GAMISO file

    Notes
    -----
    Since MC2-3 does not write DUMMY nuclide information for GAMISO files, this is necessary to provide a
    consistent set of nuclide-level data across all the nuclides in a
    :py:class:`~armi.nuclearDataIO.xsLibraries.XSLibrary`.
    """
    if not dummyNuclides:
        runLog.important(
            "No dummy nuclide data provided to be added to {}".format(lib))
        return False
    elif len(lib.xsIDs) > 1:
        runLog.warning(
            "Cannot add dummy nuclide data to GAMISO library {} containing data for more than 1 XS ID."
            .format(lib))
        return False

    dummyNuclideKeysAddedToLibrary = []
    for dummyNuclide in dummyNuclides:
        dummyKey = dummyNuclide.nucLabel
        if len(lib.xsIDs):
            dummyKey += lib.xsIDs[0]
        if dummyKey in lib:
            continue

        runLog.debug("Adding {} nuclide data to {}".format(dummyKey, lib))
        newDummy = xsNuclides.XSNuclide(lib, dummyKey)

        # Copy gamiso metadata from the isotxs metadata of the given dummy nuclide
        for kk, vv in dummyNuclide.isotxsMetadata.items():
            if kk in ["jj", "jband"]:
                newDummy.gamisoMetadata[kk] = {}
                for mm in vv:
                    newDummy.gamisoMetadata[kk][mm] = 1
            else:
                newDummy.gamisoMetadata[kk] = vv

        lib[dummyKey] = newDummy
        dummyNuclideKeysAddedToLibrary.append(dummyKey)

    return any(dummyNuclideKeysAddedToLibrary)
Exemple #25
0
def _checkBlockHeight(b):
    if b.p.height < 3.0:
        runLog.debug(
            "Block {0:s} ({1:s}) has a height less than 3.0 cm. ({2:.12e})".format(
                b.name, str(b.p.flags), b.p.height
            )
        )
    if b.p.height < 0.0:
        raise ArithmeticError(
            "Block {0:s} ({1:s}) has a negative height! ({2:.12e})".format(
                b.name, str(b.p.flags), b.p.height
            )
        )
Exemple #26
0
    def test_setVerbosityBeforeStartLog(self):
        """The user/dev my accidentally call setVerbosity() before startLog(), this should be mostly supportable"""
        with mockRunLogs.BufferLog() as mock:
            # we should start with a clean slate
            self.assertEqual("", mock._outputStream)
            runLog.LOG.setVerbosity(logging.DEBUG)
            runLog.LOG.startLog("test_setVerbosityBeforeStartLog")

            # we should start at info level, and that should be working correctly
            self.assertEqual(runLog.LOG.getVerbosity(), logging.DEBUG)
            runLog.debug("hi")
            self.assertIn("hi", mock._outputStream)
            mock._outputStream = ""
Exemple #27
0
    def _clearStateOnReactor(self, reactor):
        """
        Delete existing state that will be updated so they don't increment.

        The summations should start at zero but will happen for all overlaps.
        """
        runLog.debug("Clearing params from source reactor that will be converted.")
        for rp in self.reactorParamNames:
            reactor.core.p[rp] = 0.0

        for b in reactor.core.getBlocks():
            for bp in self.blockParamNames:
                b.p[bp] = 0.0
Exemple #28
0
    def _setParamsToUpdate(self):
        b = self._sourceReactor.core.getFirstBlock()

        self.blockParamNames = b.p.paramDefs.inCategory(
            "detailedAxialExpansion").names
        self.reactorParamNames = self._sourceReactor.core.p.paramDefs.inCategory(
            "neutronics").names

        runLog.debug("Block params that will be converted include: {0}".format(
            self.blockParamNames))
        runLog.debug(
            "Reactor params that will be converted include: {0}".format(
                self.reactorParamNames))
Exemple #29
0
    def _updateYieldVectorFromNumberDensities(self,
                                              numberDensities,
                                              fpFiltered=False):
        """
        This method updates the yield distribution of the first lfp to reflect
        whatever is on the massFrac vector

        Parameters
        ----------
        numberDensities : dict
            This is a <material>.p.massFrac format mass fraction vector indexed by
            nuclide name

        fpFiltered : bool
            This is a flag to let this method know whether it needs to filter
            the mass fraction vector for fission products

        """
        lfp = self.getFirstLfp()
        lfpNumberFrac = lfp.getNumberFracs()

        if fpFiltered:
            fpNumberDensities = numberDensities
        else:
            fpNumberDensities = {}
            # filter massFracs for only fission products
            lfpNumberDensity = numberDensities.get(self.getName(), 0)
            for nucName in sorted(self.getAllFissionProductNames()):
                nb = nuclideBases.byName[nucName]
                fpNumberDensities[nb] = numberDensities.get(
                    nucName, 0) + lfpNumberDensity * lfpNumberFrac.get(nb, 0)

        totalFPNumberDensity = sum(fpNumberDensities.values())
        if totalFPNumberDensity:
            # check to see that you want to update the yields AND that there is
            # a distribution of fission products -- at BOL this has a zero
            # division bc there are no fission products.
            for nb in lfp.keys():
                lfp[nb] = (fpNumberDensities.get(nb, 0) / totalFPNumberDensity
                           ) * 2.0  # part of ARMI task T331
        else:
            runLog.debug(
                "fpMassFrac vector should be populated -- not updating the yield vector"
            )
        # update the weight on the nuclide base object
        # This is a GLOBAL operation, which is a bit problematic if it
        # is being changed and should be upgraded accordingly.
        nb = nuclideBases.byName[lfp.name]
        nb.weight = (2 * sum([yld * nb.weight
                              for nb, yld in lfp.yld.items()]) /
                     sum([yld for yld in self.getFirstLfp().values()]))
Exemple #30
0
    def temporarilySet(self, settingName, temporaryValue):
        """
        Change a setting that you will restore later.

        Useful to change settings before doing a certain run and then reverting them

        See Also
        --------
        unsetTemporarySettings : reverts this
        """
        runLog.debug("Temporarily changing {} from {} to {}".format(
            settingName, self[settingName], temporaryValue))
        self._backedup[settingName] = self[settingName]
        self[settingName] = temporaryValue