Iterator over selected atomic angles.

        Angles are in degrees as with :meth:`angle_single`.

        See `above <toc-generators_>`_ for more information on
        calling options.

        Parameters
        ----------
        g_nums
            |int| or iterable |int| or |None| --
            Index of the desired geometry

        ats_1
            |int| or iterable |int| or |None| --
            Index of the first atom

        ats_2
            |int| or iterable |int| or |None| --
            Index of the second atom

        ats_3
            |int| or iterable |int| or |None| --
            Index of the third atom

        invalid_error
            |bool|, optional --
            If |False| (the default), |None| values are returned for
            results corresponding to invalid indices. If |True|,
            exceptions are raised per normal.

        Yields
        ------
        angle
            |npfloat_| --
            Spanning angles in degrees between corresponding |br|
            `ats_1`-`ats_2`-`ats_3`, from geometry/geometries `g_nums`

        Raises
        ------
        ~exceptions.IndexError
            If an invalid (out-of-range) `g_num` or `at_#` is provided.

        ~exceptions.ValueError
            If all iterable objects are not the same length.

        ~exceptions.ValueError
            If any `ats_2` element is equal to either the corresponding `ats_1`
            or `ats_3` element.

def angle_iter(self, g_nums, ats_1, ats_2, ats_3, invalid_error=False):
        """ Iterator over selected atomic angles.

        Angles are in degrees as with :meth:`angle_single`.

        See `above <toc-generators_>`_ for more information on
        calling options.

        Parameters
        ----------
        g_nums
            |int| or iterable |int| or |None| --
            Index of the desired geometry

        ats_1
            |int| or iterable |int| or |None| --
            Index of the first atom

        ats_2
            |int| or iterable |int| or |None| --
            Index of the second atom

        ats_3
            |int| or iterable |int| or |None| --
            Index of the third atom

        invalid_error
            |bool|, optional --
            If |False| (the default), |None| values are returned for
            results corresponding to invalid indices. If |True|,
            exceptions are raised per normal.

        Yields
        ------
        angle
            |npfloat_| --
            Spanning angles in degrees between corresponding |br|
            `ats_1`-`ats_2`-`ats_3`, from geometry/geometries `g_nums`

        Raises
        ------
        ~exceptions.IndexError
            If an invalid (out-of-range) `g_num` or `at_#` is provided.

        ~exceptions.ValueError
            If all iterable objects are not the same length.

        ~exceptions.ValueError
            If any `ats_2` element is equal to either the corresponding `ats_1`
            or `ats_3` element.

        """
        # Suitability of ats_n indices will be checked within the
        #  self.angle_single() calls and thus no check is needed here.

        # Import the tuple-generating function
        from .utils import pack_tups

        # Print the function inputs if debug mode is on
        if _DEBUG:   # pragma: no cover
            print("g_nums = {0}".format(g_nums))
            print("ats_1 = {0}".format(ats_1))
            print("ats_2 = {0}".format(ats_2))
            print("ats_3 = {0}".format(ats_3))
        ## end if

        # Perform the None substitution
        arglist = self._none_subst(g_nums, ats_1, ats_2, ats_3)

        # Expand/pack the tuples from the inputs
        tups = pack_tups(*arglist)

        # Dump the results if debug mode is on
        if _DEBUG:  # pragma: no cover
            print(tups)
        ## end if

        # Construct the generator using the packed tuples.
        for tup in tups:
            if _DEBUG: # pragma: no cover
                print(tup)
            ## end if

            yield self._iter_return(tup, self.angle_single, invalid_error)