<commit_before># coding: utf-8
# Copyright (c) Pymatgen Development Team.
# Distributed under the terms of the MIT License.

from __future__ import division, unicode_literals, print_function
import itertools
import logging
from collections import defaultdict

import math
from math import cos
from math import sin
from fractions import Fraction

import numpy as np

from six.moves import filter, map, zip
from monty.dev import deprecated
import spglib

from pymatgen.core.structure import Structure
from pymatgen.symmetry.structure import SymmetrizedStructure
from pymatgen.core.lattice import Lattice
from pymatgen.core.structure import PeriodicSite
from pymatgen.core.operations import SymmOp
from pymatgen.util.coord_utils import find_in_coord_list, pbc_diff

"""
An interface to the excellent spglib library by <NAME>
(http://spglib.sourceforge.net/) for pymatgen.

v1.0 - Now works with both ordered and disordered structure.
v2.0 - Updated for spglib 1.6.
v3.0 - pymatgen no longer ships with spglib. Instead, spglib (the python
       version) is now a dependency and the SpacegroupAnalyzer merely serves
       as an interface to spglib for pymatgen Structures.
"""

__author__ = "<NAME>"
__copyright__ = "Copyright 2012, The Materials Project"
__version__ = "3.0"
__maintainer__ = "<NAME>"
__email__ = "<EMAIL>"
__date__ = "May 14, 2016"


logger = logging.getLogger(__name__)


class SpacegroupAnalyzer(object):
    """
    Takes a pymatgen.core.structure.Structure object and a symprec.
    Uses pyspglib to perform various symmetry finding operations.

    Args:
        structure (Structure/IStructure): Structure to find symmetry
        symprec (float): Tolerance for symmetry finding. Defaults to 1e-3,
            which is fairly strict and works well for properly refined
            structures with atoms in the proper symmetry coordinates. For
            structures with slight deviations from their proper atomic
            positions (e.g., structures relaxed with electronic structure
            codes), a looser tolerance of 0.1 (the value used in Materials
            Project) is often needed.
        angle_tolerance (float): Angle tolerance for symmetry finding.
    """

    def __init__(self, structure, symprec=1e-3, angle_tolerance=5):
        self._symprec = symprec
        self._angle_tol = angle_tolerance
        self._structure = structure
        latt = structure.lattice.matrix
        positions = structure.frac_coords
        unique_species = []
        zs = []
        magmoms = []

        for species, g in itertools.groupby(structure,
                                            key=lambda s: s.species_and_occu):
            if species in unique_species:
                ind = unique_species.index(species)
                zs.extend([ind + 1] * len(tuple(g)))
            else:
                unique_species.append(species)
                zs.extend([len(unique_species)] * len(tuple(g)))

        for site in structure:
            if hasattr(site, 'magmom'):
                magmoms.append(site.magmom)
            elif site.is_ordered and hasattr(site.specie, 'spin'):
                magmoms.append(site.specie.spin)
            else:
                magmoms.append(0)

        self._unique_species = unique_species
        self._numbers = zs
        # For now, we are setting magmom to zero.
        self._cell = latt, positions, zs, magmoms

        self._space_group_data = spglib.get_symmetry_dataset(
            self._cell, symprec=self._symprec, angle_tolerance=angle_tolerance)

    @deprecated(message="get_spacegroup has been renamed "
                        "get_space_group_operations. Will be removed in "
                        "pymatgen 5.0.")
    def get_space_group(self):
        """
        Get the SpacegroupOperations for the Structure.

        Returns:
            SpacgroupOperations object.
        """
        return self.get_space_group_operations()

    @deprecated(message="get_spacegroup_symbol has been renamed "
                        "get_space_group_symbol. Will be removed in "
                        "pymatgen 5.0.")
    def get_space_group_symbol(self):
        """
        Get the spacegroup symbol (e.g., Pnma) for structure.

        Returns:
            (str): Spacegroup symbol for structure.
        """
        return self._space_group_data["international"]

    @deprecated(message="get_spacegroup_number has been renamed "
                        "get_space_group_number. Will be removed in "
                        "pymatgen 5.0.")
    def get_space_group_number(self):
        """
        Get the international spacegroup number (e.g., 62) for structure.

        Returns:
            (int): International spacegroup number for structure.
        """
        return int(self._space_group_data["number"])

    def get_space_group_operations(self):
        """
        Get the SpacegroupOperations for the Structure.

        Returns:
            SpacgroupOperations object.
        """
        return SpacegroupOperations(self.get_space_group_symbol(),
                                    self.get_space_group_number(),
                                    self.get_symmetry_operations())

    def get_space_group_symbol(self):
        """
        Get the spacegroup symbol (e.g., Pnma) for structure.

        Returns:
            (str): Spacegroup symbol for structure.
        """
        return self._space_group_data["international"]

    def get_space_group_number(self):
        """
        Get the international spacegroup number (e.g., 62) for structure.

        Returns:
            (int): International spacegroup number for structure.
        """
        return int(self._space_group_data["number"])

    def get_hall(self):
        """
        Returns Hall symbol for structure.

        Returns:
            (str): Hall symbol
        """
        return self._space_group_data["hall"]

    @deprecated(message="get_point_group has been renamed "
                        "get_point_group_symbol. Will be removed in "
                        "pymatgen 5.0.")
    def get_point_group(self):
        return self.get_point_group_symbol()

    def get_point_group_symbol(self):
        """
        Get the point group associated with the structure.

        Returns:
            (Pointgroup): Point group for structure.
        """
        rotations = self._space_group_data["rotations"]
        # passing a 0-length rotations list to spglib can segfault
        if len(rotations) == 0:
            return '1'
        return spglib.get_pointgroup(rotations)[0].strip()

    def get_crystal_system(self):
        """
        Get the crystal system for the structure, e.g., (triclinic,
        orthorhombic, cubic, etc.).

        Returns:
            (str): Crystal system for structure.
        """
        n = self._space_group_data["number"]

        f = lambda i, j: i <= n <= j
        cs = {"triclinic": (1, 2), "monoclinic": (3, 15),
              "orthorhombic": (16, 74), "tetragonal": (75, 142),
              "trigonal": (143, 167), "hexagonal": (168, 194),
              "cubic": (195, 230)}

        crystal_sytem = None

        for k, v in cs.items():
            if f(*v):
                crystal_sytem = k
                break
        return crystal_sytem

    def get_lattice_type(self):
        """
        Get the lattice for the structure, e.g., (triclinic,
        orthorhombic, cubic, etc.).This is the same than the
        crystal system with the exception of the hexagonal/rhombohedral
        lattice

        Returns:
            (str): Lattice type for structure.
        """
        n = self._space_group_data["number"]
        system = self.get_crystal_system()
        if n in [146, 148, 155, 160, 161, 166, 167]:
            return "rhombohedral"
        elif system == "trigonal":
            return "hexagonal"
        else:
            return system

    def get_symmetry_dataset(self):
        """
        Returns the symmetry dataset as a dict.

        Returns:
            (dict): With the following properties:
            number: International space group number
            international: International symbol
            hall: Hall symbol
            transformation_matrix: Transformation matrix from lattice of
            input cell to Bravais lattice L^bravais = L^original * Tmat
            origin shift: Origin shift in the setting of "Bravais lattice"
            rotations, translations: Rotation matrices and translation
            vectors. Space group operations are obtained by
            [(r,t) for r, t in zip(rotations, translations)]
            wyckoffs: Wyckoff letters
        """
        return self._space_group_data

    def _get_symmetry(self):
        """
        Get the symmetry operations associated with the structure.

        Returns:
            Symmetry operations as a tuple of two equal length sequences.
            (rotations, translations). "rotations" is the numpy integer array
            of the rotation matrices for scaled positions
            "translations" gives the numpy float64 array of the translation
            vectors in scaled positions.
        """
        d = spglib.get_symmetry(self._cell, symprec=self._symprec,
                                angle_tolerance=self._angle_tol)
        # Sometimes spglib returns small translation vectors, e.g. [1e-4, 2e-4, 1e-4]
        # (these are in fractional coordinates, so should be small denominator fractions)
        trans = []
        for t in d["translations"]:
            trans.append([float(Fraction.from_float(c).limit_denominator(1000)) for c in t])
        trans = np.array(trans)
        return d["rotations"], trans

    def get_symmetry_operations(self, cartesian=False):
        """
        Return symmetry operations as a list of SymmOp objects.
        By default returns fractional coord symmops.
        But cartesian can be returned too.<commit_msg>Simplify translational symmetry vectors<commit_after># coding: utf-8
# Copyright (c) Pymatgen Development Team.
# Distributed under the terms of the MIT License.

from __future__ import division, unicode_literals, print_function
import itertools
import logging
from collections import defaultdict

import math
from math import cos
from math import sin
from fractions import Fraction

import numpy as np

from six.moves import filter, map, zip
from monty.dev import deprecated
import spglib

from pymatgen.core.structure import Structure
from pymatgen.symmetry.structure import SymmetrizedStructure
from pymatgen.core.lattice import Lattice
from pymatgen.core.structure import PeriodicSite
from pymatgen.core.operations import SymmOp
from pymatgen.util.coord_utils import find_in_coord_list, pbc_diff

"""
An interface to the excellent spglib library by <NAME>
(http://spglib.sourceforge.net/) for pymatgen.

v1.0 - Now works with both ordered and disordered structure.
v2.0 - Updated for spglib 1.6.
v3.0 - pymatgen no longer ships with spglib. Instead, spglib (the python
       version) is now a dependency and the SpacegroupAnalyzer merely serves
       as an interface to spglib for pymatgen Structures.
"""

__author__ = "<NAME>"
__copyright__ = "Copyright 2012, The Materials Project"
__version__ = "3.0"
__maintainer__ = "<NAME>"
__email__ = "<EMAIL>"
__date__ = "May 14, 2016"


logger = logging.getLogger(__name__)


class SpacegroupAnalyzer(object):
    """
    Takes a pymatgen.core.structure.Structure object and a symprec.
    Uses pyspglib to perform various symmetry finding operations.

    Args:
        structure (Structure/IStructure): Structure to find symmetry
        symprec (float): Tolerance for symmetry finding. Defaults to 1e-3,
            which is fairly strict and works well for properly refined
            structures with atoms in the proper symmetry coordinates. For
            structures with slight deviations from their proper atomic
            positions (e.g., structures relaxed with electronic structure
            codes), a looser tolerance of 0.1 (the value used in Materials
            Project) is often needed.
        angle_tolerance (float): Angle tolerance for symmetry finding.
    """

    def __init__(self, structure, symprec=1e-3, angle_tolerance=5):
        self._symprec = symprec
        self._angle_tol = angle_tolerance
        self._structure = structure
        latt = structure.lattice.matrix
        positions = structure.frac_coords
        unique_species = []
        zs = []
        magmoms = []

        for species, g in itertools.groupby(structure,
                                            key=lambda s: s.species_and_occu):
            if species in unique_species:
                ind = unique_species.index(species)
                zs.extend([ind + 1] * len(tuple(g)))
            else:
                unique_species.append(species)
                zs.extend([len(unique_species)] * len(tuple(g)))

        for site in structure:
            if hasattr(site, 'magmom'):
                magmoms.append(site.magmom)
            elif site.is_ordered and hasattr(site.specie, 'spin'):
                magmoms.append(site.specie.spin)
            else:
                magmoms.append(0)

        self._unique_species = unique_species
        self._numbers = zs
        # For now, we are setting magmom to zero.
        self._cell = latt, positions, zs, magmoms

        self._space_group_data = spglib.get_symmetry_dataset(
            self._cell, symprec=self._symprec, angle_tolerance=angle_tolerance)

    @deprecated(message="get_spacegroup has been renamed "
                        "get_space_group_operations. Will be removed in "
                        "pymatgen 5.0.")
    def get_space_group(self):
        """
        Get the SpacegroupOperations for the Structure.

        Returns:
            SpacgroupOperations object.
        """
        return self.get_space_group_operations()

    @deprecated(message="get_spacegroup_symbol has been renamed "
                        "get_space_group_symbol. Will be removed in "
                        "pymatgen 5.0.")
    def get_space_group_symbol(self):
        """
        Get the spacegroup symbol (e.g., Pnma) for structure.

        Returns:
            (str): Spacegroup symbol for structure.
        """
        return self._space_group_data["international"]

    @deprecated(message="get_spacegroup_number has been renamed "
                        "get_space_group_number. Will be removed in "
                        "pymatgen 5.0.")
    def get_space_group_number(self):
        """
        Get the international spacegroup number (e.g., 62) for structure.

        Returns:
            (int): International spacegroup number for structure.
        """
        return int(self._space_group_data["number"])

    def get_space_group_operations(self):
        """
        Get the SpacegroupOperations for the Structure.

        Returns:
            SpacgroupOperations object.
        """
        return SpacegroupOperations(self.get_space_group_symbol(),
                                    self.get_space_group_number(),
                                    self.get_symmetry_operations())

    def get_space_group_symbol(self):
        """
        Get the spacegroup symbol (e.g., Pnma) for structure.

        Returns:
            (str): Spacegroup symbol for structure.
        """
        return self._space_group_data["international"]

    def get_space_group_number(self):
        """
        Get the international spacegroup number (e.g., 62) for structure.

        Returns:
            (int): International spacegroup number for structure.
        """
        return int(self._space_group_data["number"])

    def get_hall(self):
        """
        Returns Hall symbol for structure.

        Returns:
            (str): Hall symbol
        """
        return self._space_group_data["hall"]

    @deprecated(message="get_point_group has been renamed "
                        "get_point_group_symbol. Will be removed in "
                        "pymatgen 5.0.")
    def get_point_group(self):
        return self.get_point_group_symbol()

    def get_point_group_symbol(self):
        """
        Get the point group associated with the structure.

        Returns:
            (Pointgroup): Point group for structure.
        """
        rotations = self._space_group_data["rotations"]
        # passing a 0-length rotations list to spglib can segfault
        if len(rotations) == 0:
            return '1'
        return spglib.get_pointgroup(rotations)[0].strip()

    def get_crystal_system(self):
        """
        Get the crystal system for the structure, e.g., (triclinic,
        orthorhombic, cubic, etc.).

        Returns:
            (str): Crystal system for structure.
        """
        n = self._space_group_data["number"]

        f = lambda i, j: i <= n <= j
        cs = {"triclinic": (1, 2), "monoclinic": (3, 15),
              "orthorhombic": (16, 74), "tetragonal": (75, 142),
              "trigonal": (143, 167), "hexagonal": (168, 194),
              "cubic": (195, 230)}

        crystal_sytem = None

        for k, v in cs.items():
            if f(*v):
                crystal_sytem = k
                break
        return crystal_sytem

    def get_lattice_type(self):
        """
        Get the lattice for the structure, e.g., (triclinic,
        orthorhombic, cubic, etc.).This is the same than the
        crystal system with the exception of the hexagonal/rhombohedral
        lattice

        Returns:
            (str): Lattice type for structure.
        """
        n = self._space_group_data["number"]
        system = self.get_crystal_system()
        if n in [146, 148, 155, 160, 161, 166, 167]:
            return "rhombohedral"
        elif system == "trigonal":
            return "hexagonal"
        else:
            return system

    def get_symmetry_dataset(self):
        """
        Returns the symmetry dataset as a dict.

        Returns:
            (dict): With the following properties:
            number: International space group number
            international: International symbol
            hall: Hall symbol
            transformation_matrix: Transformation matrix from lattice of
            input cell to Bravais lattice L^bravais = L^original * Tmat
            origin shift: Origin shift in the setting of "Bravais lattice"
            rotations, translations: Rotation matrices and translation
            vectors. Space group operations are obtained by
            [(r,t) for r, t in zip(rotations, translations)]
            wyckoffs: Wyckoff letters
        """
        return self._space_group_data

    def _get_symmetry(self):
        """
        Get the symmetry operations associated with the structure.

        Returns:
            Symmetry operations as a tuple of two equal length sequences.
            (rotations, translations). "rotations" is the numpy integer array
            of the rotation matrices for scaled positions
            "translations" gives the numpy float64 array of the translation
            vectors in scaled positions.
        """
        d = spglib.get_symmetry(self._cell, symprec=self._symprec,
                                angle_tolerance=self._angle_tol)
        # Sometimes spglib returns small translation vectors, e.g. [1e-4, 2e-4, 1e-4]
        # (these are in fractional coordinates, so should be small denominator fractions)
        trans = []
        for t in d["translations"]:
            trans.append([float(Fraction.from_float(c).limit_denominator(1000)) for c in t])
        trans = np.array(trans)
        trans[np.abs(trans) == 1] = 0  # fractional translations of 1 are more simply 0
        return d["rotations"], trans

    def get_symmetry_operations(self, cartesian=False):
        """
        Return symmetry operations as a list of SymmOp objects.
        By default returns fractional coord symmops.
        But cartesian can be returned too.
{'fraction_non_alphanumeric': 0.08017025538307461, 'fraction_numerical': 0.011316975463194792, 'mean_word_length': 2.1649762282091918, 'pattern_counts': {'":': 16, '<': 15, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5911820', 'n_tokens_mistral': 5611, 'n_tokens_neox': 5251, 'n_words': 1683}