Returns a symmetrized molecule

    The equivalent atoms obtained via
    :meth:`~pymatgen.symmetry.analyzer.PointGroupAnalyzer.get_equivalent_atoms`
    are rotated, mirrored... unto one position.
    Then the average position is calculated.
    The average position is rotated, mirrored... back with the inverse
    of the previous symmetry operations, which gives the
    symmetrized molecule

    Args:
        mol (Molecule): A pymatgen Molecule instance.
        max_n (int): Maximum number of iterations.
        tolerance (float): Tolerance for detecting symmetry.
            Gets passed as Argument into
            :class:`~pymatgen.analyzer.symmetry.PointGroupAnalyzer`.
        epsilon (float): If the elementwise absolute difference of two
            subsequently symmetrized structures is smaller epsilon,
            the iteration stops before ``max_n`` is reached.


    Returns:
        dict: The returned dictionary has three possible keys:

        ``sym_mol``:
        A symmetrized molecule instance.

        ``eq_sets``:
        A dictionary of indices mapping to sets of indices,
        each key maps to indices of all equivalent atoms.
        The keys are guaranteed to be not equivalent.

        ``sym_ops``:
        Twofold nested dictionary.
        ``operations[i][j]`` gives the symmetry operation
        that maps atom ``i`` unto ``j``.

def iterative_symmetrize(mol, max_n=10, tolerance=0.3, epsilon=1e-2):
    """Returns a symmetrized molecule

    The equivalent atoms obtained via
    :meth:`~pymatgen.symmetry.analyzer.PointGroupAnalyzer.get_equivalent_atoms`
    are rotated, mirrored... unto one position.
    Then the average position is calculated.
    The average position is rotated, mirrored... back with the inverse
    of the previous symmetry operations, which gives the
    symmetrized molecule

    Args:
        mol (Molecule): A pymatgen Molecule instance.
        max_n (int): Maximum number of iterations.
        tolerance (float): Tolerance for detecting symmetry.
            Gets passed as Argument into
            :class:`~pymatgen.analyzer.symmetry.PointGroupAnalyzer`.
        epsilon (float): If the elementwise absolute difference of two
            subsequently symmetrized structures is smaller epsilon,
            the iteration stops before ``max_n`` is reached.


    Returns:
        dict: The returned dictionary has three possible keys:

        ``sym_mol``:
        A symmetrized molecule instance.

        ``eq_sets``:
        A dictionary of indices mapping to sets of indices,
        each key maps to indices of all equivalent atoms.
        The keys are guaranteed to be not equivalent.

        ``sym_ops``:
        Twofold nested dictionary.
        ``operations[i][j]`` gives the symmetry operation
        that maps atom ``i`` unto ``j``.
    """
    new = mol
    n = 0
    finished = False
    while not finished and n <= max_n:
        previous = new
        PA = PointGroupAnalyzer(previous, tolerance=tolerance)
        eq = PA.symmetrize_molecule()
        new = eq['sym_mol']
        finished = np.allclose(new.cart_coords, previous.cart_coords,
                               atol=epsilon)
        n += 1
    return eq