Exemplo n.º 1
0
    def _expandElementMassFracs(self):
        """
        Expand the custom isotopics input entries that are elementals to isotopics.

        This is necessary when the element name is not a elemental nuclide.
        Most everywhere else expects Nuclide objects (or nuclide names). This input allows a
        user to enter "U" which would expand to the naturally occurring uranium isotopics.

        This is different than the isotopic expansion done for meeting user-specified
        modeling options (such as an MC**2, or MCNP expecting elements or isotopes),
        because it translates the user input into something that can be used later on.
        """
        elementsToExpand = []
        for nucName in self.massFracs:
            if nucName not in nuclideBases.byName:
                element = elements.bySymbol.get(nucName)
                if element is not None:
                    runLog.info(
                        "Expanding custom isotopic `{}` element `{}` to natural isotopics"
                        .format(self.name, nucName))
                    # include all natural isotopes with None flag
                    elementsToExpand.append((element, None))
                else:
                    raise InputError(
                        "Unrecognized nuclide/isotope/element in input: {}".
                        format(nucName))

        densityTools.expandElementalMassFracsToNuclides(
            self.massFracs, elementsToExpand)
Exemplo n.º 2
0
    def _moveFiles(self):
        # check for orificed flow bounds files. These will often be named based on the
        # case that this one is dependent upon, but not always. For example, testSassys
        # is dependent on the safety case but requires physics bounds files.  now copy
        # the files over
        copyFilesFrom = [
            filePath for possiblePat in self.cs["copyFilesFrom"]
            for filePath in glob.glob(possiblePat)
        ]
        copyFilesTo = self.cs["copyFilesTo"]

        if len(copyFilesTo) in (len(copyFilesFrom), 0, 1):
            # if any files to copy, then use the first as the default, i.e. len() == 1,
            # otherwise assume '.'
            default = copyFilesTo[0] if any(copyFilesTo) else "."
            for filename, dest in itertools.zip_longest(copyFilesFrom,
                                                        copyFilesTo,
                                                        fillvalue=default):
                pathTools.copyOrWarn("copyFilesFrom", filename, dest)
        else:
            runLog.error(
                "cs['copyFilesTo'] must either be length 1, 0, or have the same number of entries as "
                "cs['copyFilesFrom']. Actual values:\n"
                "    copyFilesTo   : {}\n"
                "    copyFilesFrom : {}".format(copyFilesTo, copyFilesFrom))
            raise InputError("Failed to process copyFilesTo/copyFilesFrom")
Exemplo n.º 3
0
 def gridContents(self, value):  # pylint: disable=method-hidden
     if value is None:
         return True
     if not all(isinstance(key, tuple) for key in value.keys()):
         raise InputError(
             "Keys need to be presented as [i, j]. Check the blueprints."
         )
Exemplo n.º 4
0
    def _checkAssemblyAreaConsistency(self, cs):
        references = None
        for a in self.assemblies.values():
            if references is None:
                references = (a, a.getArea())
                continue

            assemblyArea = a.getArea()
            if isinstance(a, assemblies.RZAssembly):
                # R-Z assemblies by definition have different areas, so skip the check
                continue
            if abs(references[1] - assemblyArea) > 1e-9:
                runLog.error("REFERENCE COMPARISON ASSEMBLY:")
                references[0][0].printContents()
                runLog.error("CURRENT COMPARISON ASSEMBLY:")
                a[0].printContents()
                raise InputError(
                    "Assembly {} has a different area {} than assembly {} {}.  Check inputs for accuracy".format(
                        a, assemblyArea, references[0], references[1]
                    )
                )

            blockArea = a[0].getArea()
            for b in a[1:]:
                if (
                    abs(b.getArea() - blockArea) / blockArea
                    > cs["acceptableBlockAreaError"]
                ):
                    runLog.error("REFERENCE COMPARISON BLOCK:")
                    a[0].printContents(includeNuclides=False)
                    runLog.error("CURRENT COMPARISON BLOCK:")
                    b.printContents(includeNuclides=False)

                    for c in b.getChildren():
                        runLog.error(
                            "{0} area {1} effective area {2}"
                            "".format(c, c.getArea(), c.getVolume() / b.getHeight())
                        )

                    raise InputError(
                        "Block {} has a different area {} than block {} {}. Check inputs for accuracy".format(
                            b, b.getArea(), a[0], blockArea
                        )
                    )
Exemplo n.º 5
0
    def _initializeMassFracs(self):
        self.massFracs = dict()  # defaults to 0.0, __init__ is not called

        if any(v < 0.0 for v in self.values()):
            raise ValueError("Custom isotopic input for {} is negative".format(
                self.name))

        valSum = sum(self.values())
        if not abs(valSum - 1.0) < 1e-5 and "fractions" in self.inputFormat:
            raise ValueError(
                "Fractional custom isotopic input values must sum to 1.0 in: {}"
                .format(self.name))

        if self.inputFormat == "number fractions":
            sumNjAj = 0.0

            for nuc, nj in self.items():
                if nj:
                    sumNjAj += nj * nucDir.getAtomicWeight(nuc)

            for nuc, value in self.items():
                massFrac = value * nucDir.getAtomicWeight(nuc) / sumNjAj
                self.massFracs[nuc] = massFrac

        elif self.inputFormat == "number densities":
            if self._density is not None:
                raise InputError(
                    "Custom isotopic `{}` is over-specified. It was provided as number "
                    "densities, and but density ({}) was also provided. Is the input format "
                    "correct?".format(self.name, self.density))

            M = {
                nuc: Ni / units.MOLES_PER_CC_TO_ATOMS_PER_BARN_CM *
                nucDir.getAtomicWeight(nuc)
                for nuc, Ni in self.items()
            }
            densityTotal = sum(M.values())
            if densityTotal < 0:
                raise ValueError("Computed density is negative")

            for nuc, Mi in M.items():
                self.massFracs[nuc] = Mi / densityTotal

            self._computedDensity = densityTotal

        elif self.inputFormat == "mass fractions":
            self.massFracs = dict(self)  # as input

        else:
            raise ValueError(
                "Unrecognized custom isotopics input format {}.".format(
                    self.inputFormat))
Exemplo n.º 6
0
def autoSelectElementsToKeepFromSettings(cs):
    """
    Intelligently choose elements to expand based on settings.

    If settings point to a particular code and library and we know
    that combo requires certain elementals to be expanded, we
    flag them here to make the user input as simple as possible.

    This determines both which elementals to keep and which
    specific expansion subsets to use.

    Notes
    -----
    This logic is expected to be moved to respective plugins in time.

    Returns
    -------
    elementalsToKeep : set
        Set of NaturalNuclideBase instances to not expand into
        natural isotopics.
    expansions : dict
        Element to list of nuclides for expansion.
        For example: {oxygen: [oxygen16]} indicates that all
        oxygen should be expanded to O16, ignoring natural
        O17 and O18. (variables are Natural/NuclideBases)

    """
    elementalsToKeep = set()
    oxygenElementals = [nuclideBases.byName["O"]]
    hydrogenElementals = [nuclideBases.byName[name] for name in ["H"]]
    endf70Elementals = [nuclideBases.byName[name] for name in ["C", "V", "ZN"]]
    endf71Elementals = [nuclideBases.byName[name] for name in ["C"]]
    endf80Elementals = []
    elementalsInMC2 = set()
    expansionStrings = {}
    mc2Expansions = {
        "HE": ["HE4"],  # neglect HE3
        "O": ["O16"],  # neglect O17 and O18
        "W": ["W182", "W183", "W184", "W186"],  # neglect W180
    }
    mcnpExpansions = {"O": ["O16"]}

    for element in elements.byName.values():
        # any NaturalNuclideBase that's available in MC2 libs
        nnb = nuclideBases.byName.get(element.symbol)
        if nnb and nnb.mc2id:
            elementalsInMC2.add(nnb)

    if "MCNP" in cs["neutronicsKernel"]:
        expansionStrings.update(mcnpExpansions)
        if int(cs["mcnpLibrary"]) == 50:
            elementalsToKeep.update(nuclideBases.instances)  # skip expansion
        # ENDF/B VII.0
        elif 70 <= int(cs["mcnpLibrary"]) <= 79:
            elementalsToKeep.update(endf70Elementals)
        # ENDF/B VII.1
        elif 80 <= int(cs["mcnpLibrary"]) <= 89:
            elementalsToKeep.update(endf71Elementals)
        else:
            raise InputError(
                "Failed to determine nuclides for modeling. "
                "The `mcnpLibrary` setting value ({}) is not supported.".
                format(cs["mcnpLibrary"]))

    elif cs["xsKernel"] in ["", "SERPENT", "MC2v3", "MC2v3-PARTISN"]:
        elementalsToKeep.update(endf70Elementals)
        expansionStrings.update(mc2Expansions)

    elif cs["xsKernel"] == "DRAGON":
        # Users need to use default nuclear lib name. This is documented.
        dragLib = cs["dragonDataPath"]
        # only supports ENDF/B VII/VIII at the moment.
        if "7r0" in dragLib:
            elementalsToKeep.update(endf70Elementals)
        elif "7r1" in dragLib:
            elementalsToKeep.update(endf71Elementals)
        elif "8r0" in dragLib:
            elementalsToKeep.update(endf80Elementals)
            elementalsToKeep.update(hydrogenElementals)
            elementalsToKeep.update(oxygenElementals)
        else:
            raise ValueError(
                f"Unrecognized DRAGLIB name: {dragLib} Use default file name.")

    elif cs["xsKernel"] == "MC2v2":
        # strip out any NaturalNuclideBase with no mc2id (not on mc2Nuclides.yaml)
        elementalsToKeep.update(elementalsInMC2)
        expansionStrings.update(mc2Expansions)

    # convert convenient string notation to actual NuclideBase objects
    expansions = {}
    for nnb, nbs in expansionStrings.items():
        expansions[nuclideBases.byName[nnb]] = [
            nuclideBases.byName[nb] for nb in nbs
        ]

    return elementalsToKeep, expansions
Exemplo n.º 7
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.
        """
        symmetry = (
            geometry.SymmetryType.fromStr(self.symmetry) if self.symmetry else None
        )
        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 regrettably 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 in (geometry.HEX, geometry.HEX_CORNERS_UP):
            pitch = self.latticeDimensions.x if self.latticeDimensions else 1.0
            # add 2 for potential dummy assems
            spatialGrid = grids.HexGrid.fromPitch(
                pitch,
                numRings=maxIndex + 2,
                pointedEndUp=geom == geometry.HEX_CORNERS_UP,
            )
        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)
            )

            # Specifically in the case of grid blueprints, where we have grid contents
            # available, we can also infer "through center" based on the contents.
            # Note that the "through center" symmetry check cannot be performed when
            # the grid contents has not been provided (i.e., None or empty).
            if self.gridContents and symmetry.domain == geometry.DomainType.FULL_CORE:
                nx, ny = _getGridSize(self.gridContents.keys())
                if nx == ny and nx % 2 == 1:
                    symmetry.isThroughCenterAssembly = True

            isOffset = symmetry is not None and not symmetry.isThroughCenterAssembly

            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: str = str(self.geom)
        self.symmetry = str(symmetry)
        spatialGrid._symmetry: str = self.symmetry
        return spatialGrid