Skip to content

imporved error message when findingtally #80

@shimwell

Description

@shimwell

update statepoint to

    def get_tally(self, scores=[], filters=[], nuclides=[],
                  name=None, id=None, estimator=None, exact_filters=False,
                  exact_nuclides=False, exact_scores=False,
                  multiply_density=None, derivative=None, filter_type=None):
        """Finds and returns a Tally object with certain properties.

        This routine searches the list of Tallies and returns the first Tally
        found which satisfies all of the input parameters.

        NOTE: If any of the "exact" parameters are False (default), the input
        parameters do not need to match the complete Tally specification and may
        only represent a subset of the Tally's properties. If an "exact"
        parameter is True then number of scores, filters, or nuclides in the
        parameters must precisely match those of any matching Tally.

        Parameters
        ----------
        scores : list, optional
            A list of one or more score strings (default is []).
        filters : list, optional
            A list of Filter objects (default is []).
        nuclides : list, optional
            A list of Nuclide objects (default is []).
        name : str, optional
            The name specified for the Tally (default is None).
        id : Integral, optional
            The id specified for the Tally (default is None).
        estimator: str, optional
            The type of estimator ('tracklength', 'analog'; default is None).
        exact_filters : bool
            If True, the number of filters in the parameters must be identical
            to those in the matching Tally. If False (default), the filters in
            the parameters may be a subset of those in the matching Tally.
        exact_nuclides : bool
            If True, the number of nuclides in the parameters must be identical
            to those in the matching Tally. If False (default), the nuclides in
            the parameters may be a subset of those in the matching Tally.
        exact_scores : bool
            If True, the number of scores in the parameters must be identical to
            those in the matching Tally. If False (default), the scores in the
            parameters may be a subset of those in the matching Tally. Default
            is None (no check).
        multiply_density : bool, optional
            If not None, the Tally must have the multiply_density attribute set
            to the same value as this parameter.
        derivative : openmc.TallyDerivative, optional
            TallyDerivative object to match.
        filter_type : type, optional
            If not None, the Tally must have at least one Filter that is an
            instance of this type. For example `openmc.MeshFilter`.

        Returns
        -------
        tally : openmc.Tally
            A tally matching the specified criteria

        Raises
        ------
        LookupError
            If a Tally meeting all of the input parameters cannot be found in
            the statepoint.

        """

        tally = None

        # Iterate over all tallies to find the appropriate one
        for test_tally in self.tallies.values():

            # Determine if Tally has queried name
            if name and name != test_tally.name:
                continue

            # Determine if Tally has queried id
            if id and id != test_tally.id:
                continue

            # Determine if Tally has queried estimator, only move on to next tally
            # if the estimator is both specified and the tally estimtor does not
            # match
            if estimator is not None and estimator != test_tally.estimator:
                continue

            # The number of filters, nuclides and scores must exactly match
            if exact_scores and len(scores) != test_tally.num_scores:
                continue
            if exact_nuclides and nuclides and len(nuclides) != test_tally.num_nuclides:
                continue
            if exact_nuclides and not nuclides and test_tally.nuclides != ['total']:
                continue
            if exact_filters and len(filters) != test_tally.num_filters:
                continue
            if derivative is not None and derivative != test_tally.derivative:
                continue
            if multiply_density is not None and multiply_density != test_tally.multiply_density:
                continue

            # Determine if Tally has the queried score(s)
            if scores:
                if not all(score in test_tally.scores for score in scores):
                    continue

            # Determine if Tally has the queried Filter(s)
            if filters:
                contains_filters = True

                # Iterate over the Filters requested by the user
                for outer_filter in filters:
                    contains_filters = False

                    # Test if requested filter is a subset of any of the test
                    # tally's filters and if so continue to next filter
                    for inner_filter in test_tally.filters:
                        if inner_filter.is_subset(outer_filter):
                            contains_filters = True
                            break

                    if not contains_filters:
                        break

                if not contains_filters:
                    continue

            if filter_type is not None:
                if not any(isinstance(f, filter_type) for f in test_tally.filters):
                    continue

            # Determine if Tally has the queried Nuclide(s)
            if nuclides:
                if not all(nuclide in test_tally.nuclides for nuclide in nuclides):
                    continue

            # If the current Tally met user's request, break loop and return it
            tally = test_tally
            break

        # If we did not find the Tally, return an error message
        if tally is None:
            # Build a detailed error message with search criteria
            criteria = []
            if name is not None:
                criteria.append(f"name='{name}'")
            if id is not None:
                criteria.append(f"id={id}")
            if estimator is not None:
                criteria.append(f"estimator='{estimator}'")
            if scores:
                criteria.append(f"scores={scores}")
            if filters:
                criteria.append(f"filters={filters}")
            if nuclides:
                criteria.append(f"nuclides={nuclides}")
            if filter_type is not None:
                criteria.append(f"filter_type={filter_type}")
            if multiply_density is not None:
                criteria.append(f"multiply_density={multiply_density}")
            if derivative is not None:
                criteria.append(f"derivative={derivative}")

            criteria_str = ", ".join(criteria) if criteria else "no criteria specified"

            # Get list of available values for each search criterion
            available_parts = []

            if name is not None:
                available_names = [f"'{t.name}'" for t in self.tallies.values() if t.name]
                names_str = ", ".join(available_names) if available_names else "no named tallies"
                available_parts.append(f"names: {names_str}")

            if id is not None:
                available_ids = [str(t.id) for t in self.tallies.values()]
                ids_str = ", ".join(available_ids) if available_ids else "no IDs"
                available_parts.append(f"IDs: {ids_str}")

            if estimator is not None:
                available_estimators = list(set(t.estimator for t in self.tallies.values()))
                estimators_str = ", ".join(f"'{e}'" for e in available_estimators) if available_estimators else "none"
                available_parts.append(f"estimators: {estimators_str}")

            if scores:
                available_scores = list(set(score for t in self.tallies.values() for score in t.scores))
                scores_str = ", ".join(available_scores) if available_scores else "none"
                available_parts.append(f"scores: {scores_str}")

            if nuclides:
                available_nuclides = list(set(nuc for t in self.tallies.values() for nuc in t.nuclides))
                nuclides_str = ", ".join(available_nuclides) if available_nuclides else "none"
                available_parts.append(f"nuclides: {nuclides_str}")

            if filter_type is not None:
                available_filter_types = list(set(type(f).__name__ for t in self.tallies.values() for f in t.filters))
                filter_types_str = ", ".join(available_filter_types) if available_filter_types else "none"
                available_parts.append(f"filter types: {filter_types_str}")

            if multiply_density is not None:
                available_multiply_density = list(set(str(t.multiply_density) for t in self.tallies.values()))
                multiply_density_str = ", ".join(available_multiply_density)
                available_parts.append(f"multiply_density values: {multiply_density_str}")

            available_str = "; ".join(available_parts) if available_parts else "no tallies available"

            error_msg = (f"Unable to get Tally with {criteria_str}. "
                        f"Available: {available_str}")
            raise LookupError(error_msg)

        return tally

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions