Recursively extract the dict described in override_options().

    In particular, this searches for selected options in the override_opts dict. It stores only
    the override options that are selected.

    Args:
        selected_options: The options selected for this analysis, in the order defined used
            with ``override_options()`` and in the configuration file.
        override_opts: dict-like object returned by ruamel.yaml which contains the options that
            should be used to override the configuration options.
        set_of_possible_options (tuple of enums): Possible options for the override value categories.

def determine_override_options(selected_options: tuple, override_opts: DictLike, set_of_possible_options: tuple = ()) -> Dict[str, Any]:
    """ Recursively extract the dict described in override_options().

    In particular, this searches for selected options in the override_opts dict. It stores only
    the override options that are selected.

    Args:
        selected_options: The options selected for this analysis, in the order defined used
            with ``override_options()`` and in the configuration file.
        override_opts: dict-like object returned by ruamel.yaml which contains the options that
            should be used to override the configuration options.
        set_of_possible_options (tuple of enums): Possible options for the override value categories.
    """
    override_dict: Dict[str, Any] = {}
    for option in override_opts:
        # We need to cast the option to a string to effectively compare to the selected option,
        # since only some of the options will already be strings
        if str(option) in list(map(lambda opt: str(opt), selected_options)):
            override_dict.update(determine_override_options(selected_options, override_opts[option], set_of_possible_options))
        else:
            logger.debug(f"override_opts: {override_opts}")
            # Look for whether the key is one of the possible but unselected options.
            # If so, we haven't selected it for this analysis, and therefore they should be ignored.
            # NOTE: We compare both the names and value because sometimes the name is not sufficient,
            #       such as in the case of the energy (because a number is not allowed to be a field name.)
            found_as_possible_option = False
            for possible_options in set_of_possible_options:
                # Same type of comparison as above, but for all possible options instead of the selected
                # options.
                if str(option) in list(map(lambda opt: str(opt), possible_options)):
                    found_as_possible_option = True
                # Below is more or less equivalent to the above (although .str() hides the details or
                # whether we should compare to the name or the value in the enum and only compares against
                # the designated value).
                #for possible_opt in possible_options:
                    #if possible_opt.name == option or possible_opt.value == option:
                    #    found_as_possible_option = True

            if not found_as_possible_option:
                # Store the override value, since it doesn't correspond with a selected option or a possible
                # option and therefore must be an option that we want to override.
                logger.debug(f"Storing override option \"{option}\", with value \"{override_opts[option]}\"")
                override_dict[option] = override_opts[option]
            else:
                logger.debug(f"Found option \"{option}\" as possible option, so skipping!")

    return override_dict