overreact/coords.py
def _get_proper_axes(
    atomcoords, groups, axes, rotor_class, rtol=0.0, atol=1.0e-2, slack=0.735
):
    """Get proper symmetry axes and their orders.

    Parameters
    ----------
    atomcoords : array-like
        Atomic coordinates centered at the center of mass.
    groups : sequence of sequence of int
        Groups of symmetry equivalent atoms, in ascending order of size.
    axes : array-like
        Normalized principal axes of inertia.
    rotor_class : tuple of str
        Rigid rotor classification.
    rtol : float, optional
        The relative tolerance parameter (see `numpy.isclose`).
    atol : float, optional
        The absolute tolerance parameter (see `numpy.isclose`).
    slack : float, optional
        Number to multiply rtol and atol prior comparisons.

    Returns
    -------
    sequence of tuples of int, array-like
        Ordered sequence of tuples in the format ``(order, (x, y, z))``.

    Notes
    -----
    This function has some limitations. First, no C1 axis is never returned.
    Second, an empty list is returned if the structure has a single atom. And
    third, the largest symmetry axis found has order not greater than the
    maximum number of symmetry equivalent atoms, which particularly impacts
    linear rotors (for instance, the largest axis found for the hydrogen
    molecule is twofold, while no axis is found for hydrogen chloride).
    Furthermore, linear rotors are considered symmetric prolate tops, which
    means a single axis is returned (no perpendicular axes). Either way, this
    should be of little impact, as only cases whose groups can easily be
    inferred with no or little knowledge of symmetry axis are affected.

    Examples
    --------
    >>> from overreact import _datasets as datasets

    >>> data = datasets.logfiles["symmetries"]["diborane"]
    >>> groups = _equivalent_atoms(data.atommasses, data.atomcoords)
    >>> moments, axes, atomcoords = inertia(data.atommasses, data.atomcoords)
    >>> rotor_class = _classify_rotor(moments)
    >>> _get_proper_axes(atomcoords, groups, axes, rotor_class)
    [(2, (1.0, ...)),
     (2, (...)),
     (2, (...))]
    """
    rtol, atol = slack * rtol, slack * atol

    if rotor_class[1] == "atomic" or len(atomcoords) == 1:
        return list()

    axes = np.asarray(axes)
    atomcoords = np.asarray(atomcoords)
    orders = _guess_orders(groups, rotor_class)

    found_axes = list()
    nondeg_axes = list()
    if rotor_class[0] == "symmetric prolate":
        nondeg_axes = [axes[:, 0]]
        found_axes, order = _update_proper_axes(
            axes[:, 0],
            found_axes,
            atomcoords=atomcoords,
            groups=groups,
            orders=orders,
            rtol=rtol,
            atol=atol,
        )
    elif rotor_class[0] == "symmetric oblate":
        nondeg_axes = [axes[:, 2]]
        found_axes, order = _update_proper_axes(
            axes[:, 2],
            found_axes,
            atomcoords=atomcoords,
            groups=groups,
            orders=orders,
            rtol=rtol,
            atol=atol,
        )
    elif rotor_class[0] == "asymmetric":
        for ax in axes.T:
            found_axes, order = _update_proper_axes(
                ax,
                found_axes,
                atomcoords=atomcoords,
                groups=groups,
                orders=orders,
                rtol=rtol,
                atol=atol,
            )
        return sorted(found_axes, reverse=True)

    for group in groups:
        for i, a in enumerate(group):
            through_ax = atomcoords[a]
            found_axes, order = _update_proper_axes(
                through_ax,
                found_axes,
                atomcoords=atomcoords,
                groups=groups,
                orders=orders,
                rtol=rtol,
                atol=atol,
                nondeg_axes=nondeg_axes,
                normalize=True,
            )
            if rotor_class[0] == "spheric" and order == 5:
                return sorted(found_axes, reverse=True)

            for b in group[:i]:
                midpoint_ax = atomcoords[a] + atomcoords[b]
                found_axes, order = _update_proper_axes(
                    midpoint_ax,
                    found_axes,
                    atomcoords=atomcoords,
                    groups=groups,
                    orders=orders,
                    rtol=rtol,
                    atol=atol,
                    nondeg_axes=nondeg_axes,
                    normalize=True,
                )
                if rotor_class[0] == "spheric" and order == 5:
                    return sorted(found_axes, reverse=True)

    if rotor_class[0] == "spheric":
        twofold_axes = [ax for o, ax in found_axes if o == 2]
        for i, ax_a in enumerate(twofold_axes):
            for ax_b in twofold_axes[:i]:
                ax = np.cross(ax_a, ax_b)
                found_axes, order = _update_proper_axes(
                    ax,
                    found_axes,
                    atomcoords=atomcoords,
                    groups=groups,
                    orders=orders,
                    rtol=rtol,
                    atol=atol,
                    nondeg_axes=nondeg_axes,
                    normalize=True,
                )
                if order == 5:
                    return sorted(found_axes, reverse=True)

    return sorted(found_axes, reverse=True)
Get proper symmetry axes and their orders.

    Parameters
    ----------
    atomcoords : array-like
        Atomic coordinates centered at the center of mass.
    groups : sequence of sequence of int
        Groups of symmetry equivalent atoms, in ascending order of size.
    axes : array-like
        Normalized principal axes of inertia.
    rotor_class : tuple of str
        Rigid rotor classification.
    rtol : float, optional
        The relative tolerance parameter (see `numpy.isclose`).
    atol : float, optional
        The absolute tolerance parameter (see `numpy.isclose`).
    slack : float, optional
        Number to multiply rtol and atol prior comparisons.

    Returns
    -------
    sequence of tuples of int, array-like
        Ordered sequence of tuples in the format ``(order, (x, y, z))``.

    Notes
    -----
    This function has some limitations. First, no C1 axis is never returned.
    Second, an empty list is returned if the structure has a single atom. And
    third, the largest symmetry axis found has order not greater than the
    maximum number of symmetry equivalent atoms, which particularly impacts
    linear rotors (for instance, the largest axis found for the hydrogen
    molecule is twofold, while no axis is found for hydrogen chloride).
    Furthermore, linear rotors are considered symmetric prolate tops, which
    means a single axis is returned (no perpendicular axes). Either way, this
    should be of little impact, as only cases whose groups can easily be
    inferred with no or little knowledge of symmetry axis are affected.

    Examples
    --------
    >>> from overreact import _datasets as datasets

    >>> data = datasets.logfiles["symmetries"]["diborane"]
    >>> groups = _equivalent_atoms(data.atommasses, data.atomcoords)
    >>> moments, axes, atomcoords = inertia(data.atommasses, data.atomcoords)
    >>> rotor_class = _classify_rotor(moments)
    >>> _get_proper_axes(atomcoords, groups, axes, rotor_class)
    [(2, (1.0, ...)),
     (2, (...)),
     (2, (...))]
    
Get proper symmetry axes and their orders.
Parameters

atomcoords : array-like
Atomic coordinates centered at the center of mass.
groups : sequence of sequence of int
Groups of symmetry equivalent atoms, in ascending order of size.
axes : array-like
Normalized principal axes of inertia.
rotor_class : tuple of str
Rigid rotor classification.
rtol : float, optional
The relative tolerance parameter .
atol : float, optional
The absolute tolerance parameter .
slack : float, optional
Number to multiply rtol and atol prior comparisons.

Returns

sequence of tuples of int, array-like
Ordered sequence of tuples in the format ``(order, (x, y, z))``.

Notes

This function has some limitations. First, no C1 axis is never returned.
Second, an empty list is returned if the structure has a single atom. And
third, the largest symmetry axis found has order not greater than the
maximum number of symmetry equivalent atoms, which particularly impacts
linear rotors (for instance, the largest axis found for the hydrogen
molecule is twofold, while no axis is found for hydrogen chloride).
Furthermore, linear rotors are considered symmetric prolate tops, which
means a single axis is returned (no perpendicular axes). Either way, this
should be of little impact, as only cases whose groups can easily be
inferred with no or little knowledge of symmetry axis are affected.

Examples

>>> from overreact import _datasets as datasets


Get proper symmetry axes and their orders . Parameters atomcoords : array - like Atomic coordinates centered at the center of mass . groups : sequence of sequence of int Groups of symmetry equivalent atoms in ascending order of size . axes : array - like Normalized principal axes of inertia . rotor_class : tuple of str Rigid rotor classification . rtol : float optional The relative tolerance parameter . atol : float optional The absolute tolerance parameter . slack : float optional Number to multiply rtol and atol prior comparisons . Returns sequence of tuples of int array - like Ordered sequence of tuples in the format ` ` ( order ( x y z )) ` ` . Notes This function has some limitations . First no C1 axis is never returned . Second an empty list is returned if the structure has a single atom . And third the largest symmetry axis found has order not greater than the maximum number of symmetry equivalent atoms which particularly impacts linear rotors ( for instance the largest axis found for the hydrogen molecule is twofold while no axis is found for hydrogen chloride ) . Furthermore linear rotors are considered symmetric prolate tops which means a single axis is returned ( no perpendicular axes ) . Either way this should be of little impact as only cases whose groups can easily be inferred with no or little knowledge of symmetry axis are affected . Examples >>> from overreact import _datasets as datasets
def _get_proper_axes(
    atomcoords, groups, axes, rotor_class, rtol=0.0, atol=1.0e-2, slack=0.735
):
    rtol, atol = slack * rtol, slack * atol
    if rotor_class[1] == "atomic" or len(atomcoords) == 1:
        return list()
    axes = np.asarray(axes)
    atomcoords = np.asarray(atomcoords)
    orders = _guess_orders(groups, rotor_class)
    found_axes = list()
    nondeg_axes = list()
    if rotor_class[0] == "symmetric prolate":
        nondeg_axes = [axes[:, 0]]
        found_axes, order = _update_proper_axes(
            axes[:, 0],
            found_axes,
            atomcoords=atomcoords,
            groups=groups,
            orders=orders,
            rtol=rtol,
            atol=atol,
        )
    elif rotor_class[0] == "symmetric oblate":
        nondeg_axes = [axes[:, 2]]
        found_axes, order = _update_proper_axes(
            axes[:, 2],
            found_axes,
            atomcoords=atomcoords,
            groups=groups,
            orders=orders,
            rtol=rtol,
            atol=atol,
        )
    elif rotor_class[0] == "asymmetric":
        for ax in axes.T:
            found_axes, order = _update_proper_axes(
                ax,
                found_axes,
                atomcoords=atomcoords,
                groups=groups,
                orders=orders,
                rtol=rtol,
                atol=atol,
            )
        return sorted(found_axes, reverse=True)
    for group in groups:
        for i, a in enumerate(group):
            through_ax = atomcoords[a]
            found_axes, order = _update_proper_axes(
                through_ax,
                found_axes,
                atomcoords=atomcoords,
                groups=groups,
                orders=orders,
                rtol=rtol,
                atol=atol,
                nondeg_axes=nondeg_axes,
                normalize=True,
            )
            if rotor_class[0] == "spheric" and order == 5:
                return sorted(found_axes, reverse=True)
            for b in group[:i]:
                midpoint_ax = atomcoords[a] + atomcoords[b]
                found_axes, order = _update_proper_axes(
                    midpoint_ax,
                    found_axes,
                    atomcoords=atomcoords,
                    groups=groups,
                    orders=orders,
                    rtol=rtol,
                    atol=atol,
                    nondeg_axes=nondeg_axes,
                    normalize=True,
                )
                if rotor_class[0] == "spheric" and order == 5:
                    return sorted(found_axes, reverse=True)
    if rotor_class[0] == "spheric":
        twofold_axes = [ax for o, ax in found_axes if o == 2]
        for i, ax_a in enumerate(twofold_axes):
            for ax_b in twofold_axes[:i]:
                ax = np.cross(ax_a, ax_b)
                found_axes, order = _update_proper_axes(
                    ax,
                    found_axes,
                    atomcoords=atomcoords,
                    groups=groups,
                    orders=orders,
                    rtol=rtol,
                    atol=atol,
                    nondeg_axes=nondeg_axes,
                    normalize=True,
                )
                if order == 5:
                    return sorted(found_axes, reverse=True)
    return sorted(found_axes, reverse=True)
def _get_proper_axes ( atomcoords , groups , axes , rotor_class , rtol = 0.0 , atol = 1.0e-2 , slack = 0.735 ) : rtol , atol = slack * rtol , slack * atol if rotor_class [ 1 ] == "atomic" or len ( atomcoords ) == 1 : return list ( ) axes = np . asarray ( axes ) atomcoords = np . asarray ( atomcoords ) orders = _guess_orders ( groups , rotor_class ) found_axes = list ( ) nondeg_axes = list ( ) if rotor_class [ 0 ] == "symmetric prolate" : nondeg_axes = [ axes [ : , 0 ] ] found_axes , order = _update_proper_axes ( axes [ : , 0 ] , found_axes , atomcoords = atomcoords , groups = groups , orders = orders , rtol = rtol , atol = atol , ) elif rotor_class [ 0 ] == "symmetric oblate" : nondeg_axes = [ axes [ : , 2 ] ] found_axes , order = _update_proper_axes ( axes [ : , 2 ] , found_axes , atomcoords = atomcoords , groups = groups , orders = orders , rtol = rtol , atol = atol , ) elif rotor_class [ 0 ] == "asymmetric" : for ax in axes . T : found_axes , order = _update_proper_axes ( ax , found_axes , atomcoords = atomcoords , groups = groups , orders = orders , rtol = rtol , atol = atol , ) return sorted ( found_axes , reverse = True ) for group in groups : for i , a in enumerate ( group ) : through_ax = atomcoords [ a ] found_axes , order = _update_proper_axes ( through_ax , found_axes , atomcoords = atomcoords , groups = groups , orders = orders , rtol = rtol , atol = atol , nondeg_axes = nondeg_axes , normalize = True , ) if rotor_class [ 0 ] == "spheric" and order == 5 : return sorted ( found_axes , reverse = True ) for b in group [ : i ] : midpoint_ax = atomcoords [ a ] + atomcoords [ b ] found_axes , order = _update_proper_axes ( midpoint_ax , found_axes , atomcoords = atomcoords , groups = groups , orders = orders , rtol = rtol , atol = atol , nondeg_axes = nondeg_axes , normalize = True , ) if rotor_class [ 0 ] == "spheric" and order == 5 : return sorted ( found_axes , reverse = True ) if rotor_class [ 0 ] == "spheric" : twofold_axes = [ ax for o , ax in found_axes if o == 2 ] for i , ax_a in enumerate ( twofold_axes ) : for ax_b in twofold_axes [ : i ] : ax = np . cross ( ax_a , ax_b ) found_axes , order = _update_proper_axes ( ax , found_axes , atomcoords = atomcoords , groups = groups , orders = orders , rtol = rtol , atol = atol , nondeg_axes = nondeg_axes , normalize = True , ) if order == 5 : return sorted ( found_axes , reverse = True ) return sorted ( found_axes , reverse = True )
Get proper symmetry axes and their orders.
Get proper symmetry axes and their orders .
"""Get proper symmetry axes and their orders.

    Parameters
    ----------
    atomcoords : array-like
        Atomic coordinates centered at the center of mass.
    groups : sequence of sequence of int
        Groups of symmetry equivalent atoms, in ascending order of size.
    axes : array-like
        Normalized principal axes of inertia.
    rotor_class : tuple of str
        Rigid rotor classification.
    rtol : float, optional
        The relative tolerance parameter (see `numpy.isclose`).
    atol : float, optional
        The absolute tolerance parameter (see `numpy.isclose`).
    slack : float, optional
        Number to multiply rtol and atol prior comparisons.

    Returns
    -------
    sequence of tuples of int, array-like
        Ordered sequence of tuples in the format ``(order, (x, y, z))``.

    Notes
    -----
    This function has some limitations. First, no C1 axis is never returned.
    Second, an empty list is returned if the structure has a single atom. And
    third, the largest symmetry axis found has order not greater than the
    maximum number of symmetry equivalent atoms, which particularly impacts
    linear rotors (for instance, the largest axis found for the hydrogen
    molecule is twofold, while no axis is found for hydrogen chloride).
    Furthermore, linear rotors are considered symmetric prolate tops, which
    means a single axis is returned (no perpendicular axes). Either way, this
    should be of little impact, as only cases whose groups can easily be
    inferred with no or little knowledge of symmetry axis are affected.

    Examples
    --------
    >>> from overreact import _datasets as datasets

    >>> data = datasets.logfiles["symmetries"]["diborane"]
    >>> groups = _equivalent_atoms(data.atommasses, data.atomcoords)
    >>> moments, axes, atomcoords = inertia(data.atommasses, data.atomcoords)
    >>> rotor_class = _classify_rotor(moments)
    >>> _get_proper_axes(atomcoords, groups, axes, rotor_class)
    [(2, (1.0, ...)),
     (2, (...)),
     (2, (...))]
    """
atomcoords
groups
axes
rotor_class
rtol
atol
slack