Example #1
0
    def from_pymatgen(self, structure):
        """
        Load the seed structure from Pymatgen/ASE/POSCAR/CIFs
        """
        from pymatgen.symmetry.analyzer import SpacegroupAnalyzer as sga
        try:
            # needs to do it twice in order to get the conventional cell
            s = sga(structure)
            structure = s.get_refined_structure()
            s = sga(structure)
            sym_struc = s.get_symmetrized_structure()
            number = s.get_space_group_number()
        except:
            print("Failed to load the Pymatgen structure")
            self.valid = False

        if self.valid:
            d = sym_struc.composition.as_dict()
            species = [key for key in d.keys()]
            numIons = []
            for ele in species:
                numIons.append(int(d[ele]))
            self.numIons = numIons
            self.species = species
            self.group = Group(number)
            atom_sites = []
            for i, site in enumerate(sym_struc.equivalent_sites):
                pos = site[0].frac_coords
                wp = Wyckoff_position.from_group_and_index(
                    number, sym_struc.wyckoff_symbols[i])
                specie = site[0].specie.number
                atom_sites.append(atom_site(wp, pos, specie))
            self.atom_sites = atom_sites
            self.lattice = Lattice.from_matrix(sym_struc.lattice.matrix,
                                               ltype=self.group.lattice_type)
Example #2
0
def plotBZ():

    from pymatgen.electronic_structure.plotter import \
    plot_brillouin_zone_from_kpath as pltBZ

    st = bands.structure
    st_sym = sga(st)
    prim = sga(st).get_primitive_standard_structure(
        international_monoclinic=False)

    t = '{0} - {1} ({2})'.format(st_sym.get_lattice_type().capitalize(),
                                 st_sym.get_space_group_symbol(),
                                 st_sym.get_space_group_number())

    pltBZ(HighSymmKpath(prim), title=t)
Example #3
0
def get_site_symmetries(struc, precision=0.1):
    """
    Get all the point group operations centered on each atomic site
    in the form [[point operations of site index 1]...[[point operations of site index N]]]

    Args:
        struc: Pymatgen structure
        precision (float): tolerance to find symmetry operaitons

    Return:
        list of lists of point operations for each atomic site
    """

    pointops = []

    # Point symmetries of each atom
    for site1 in range(len(struc.sites)):
        tempstruc = struc.copy()

        # Place the origin of the cell at each atomic site
        pointops.append([])

        for site2 in range(len(struc.sites)):
            tempstruc.replace(
                site2,
                tempstruc.sites[site2].specie,
                tempstruc.frac_coords[site2] - struc.frac_coords[site1],
            )

        sgastruc = sga(tempstruc, symprec=precision)
        ops = sgastruc.get_symmetry_operations(cartesian=True)
        for site2, op in enumerate(ops):
            if all(op.translation_vector == [0, 0, 0]):
                pointops[site1].append(op)
    return pointops
Example #4
0
    def get_BEC_operations(self, eigtol=1e-05, opstol=1e-03):
        """
        Returns the symmetry operations which maps the tensors
        belonging to equivalent sites onto each other in the form
        [site index 1, site index 2, [Symmops mapping from site
        index 1 to site index 2]]


        Args:
            eigtol (float): tolerance for determining if two sites are
            related by symmetry
            opstol (float): tolerance for determining if a symmetry
            operation relates two sites

        Return:
            list of symmetry operations mapping equivalent sites and
            the indexes of those sites.
        """
        bec = self.bec
        struc = self.structure
        ops = sga(struc).get_symmetry_operations(cartesian=True)
        uniquepointops = []
        for op in ops:
            uniquepointops.append(op)

        for atom in range(len(self.pointops)):
            for op in self.pointops[atom]:
                if op not in uniquepointops:
                    uniquepointops.append(op)

        passed = []
        relations = []
        for site in range(len(bec)):
            unique = 1
            eig1, vecs1 = np.linalg.eig(bec[site])
            index = np.argsort(eig1)
            neweig = np.real([eig1[index[0]], eig1[index[1]], eig1[index[2]]])
            for index in range(len(passed)):

                if np.allclose(neweig, passed[index][1], atol=eigtol):
                    relations.append([site, index])
                    unique = 0
                    passed.append([site, passed[index][0], neweig])
                    break
            if unique == 1:
                relations.append([site, site])
                passed.append([site, neweig])
        BEC_operations = []
        for atom in range(len(relations)):
            BEC_operations.append(relations[atom])
            BEC_operations[atom].append([])

            for op in uniquepointops:
                new = op.transform_tensor(self.bec[relations[atom][1]])

                # Check the matrix it references
                if np.allclose(new, self.bec[relations[atom][0]], atol=opstol):
                    BEC_operations[atom][2].append(op)

        self.BEC_operations = BEC_operations
Example #5
0
    def get_rand_BEC(self, max_charge=1):
        """
        Generate a random born effective charge tensor which obeys a structure's
        symmetry and the acoustic sum rule

        Args:
            max_charge (float): maximum born effective charge value

        Return:
            np.array Born effective charge tensor
        """

        struc = self.structure
        symstruc = sga(struc)
        symstruc = symstruc.get_symmetrized_structure()

        l = len(struc)
        BEC = np.zeros((l, 3, 3))
        for atom, ops in enumerate(self.BEC_operations):
            if ops[0] == ops[1]:
                temp_tensor = Tensor(np.random.rand(3, 3) - 0.5)
                temp_tensor = sum(temp_tensor.transform(symm_op) for symm_op in self.pointops[atom]) / len(
                    self.pointops[atom]
                )
                BEC[atom] = temp_tensor
            else:
                tempfcm = np.zeros([3, 3])
                for op in ops[2]:

                    tempfcm += op.transform_tensor(BEC[self.BEC_operations[atom][1]])
                BEC[ops[0]] = tempfcm
                if len(ops[2]) != 0:
                    BEC[ops[0]] = BEC[ops[0]] / len(ops[2])

        #     Enforce Acoustic Sum
        disp_charge = np.einsum("ijk->jk", BEC) / l
        add = np.zeros([l, 3, 3])

        for atom, ops in enumerate(self.BEC_operations):

            if ops[0] == ops[1]:
                temp_tensor = Tensor(disp_charge)
                temp_tensor = sum(temp_tensor.transform(symm_op) for symm_op in self.pointops[atom]) / len(
                    self.pointops[atom]
                )
                add[ops[0]] = temp_tensor
            else:
                temp_tensor = np.zeros([3, 3])
                for op in ops[2]:

                    temp_tensor += op.transform_tensor(add[self.BEC_operations[atom][1]])

                add[ops[0]] = temp_tensor

                if len(ops) != 0:
                    add[ops[0]] = add[ops[0]] / len(ops[2])

        BEC = BEC - add

        return BEC * max_charge
Example #6
0
def symmetrize_cell(struc, mode='C'):
    """
    symmetrize structure from pymatgen, and return the struc in conventional/primitive setting

    Args:
        struc: ase type
        mode: output conventional or primitive cell
    """
    P_struc = ase2pymatgen(struc)
    finder = sga(P_struc, symprec=0.06)
    if mode == 'C':
        P_struc = finder.get_conventional_standard_structure()
    else:
        P_struc = finder.get_primitive_standard_structure()

    return pymatgen2ase(P_struc)
Example #7
0
    def _from_pymatgen(self, struc, tol=1e-3):
        """
        Load structure from Pymatgen
        should not be used directly
        """
        from pymatgen.symmetry.analyzer import SpacegroupAnalyzer as sga
        from pyxtal.util import symmetrize
        #import pymatgen.analysis.structure_matcher as sm

        self.valid = True
        try:
            # needs to do it twice in order to get the conventional cell
            pmg = symmetrize(struc, tol)
            s = sga(pmg, symprec=tol)
            sym_struc = s.get_symmetrized_structure()
            number = s.get_space_group_number()
            #print(sym_struc)

        except:
            print("Failed to load the Pymatgen structure")
            self.valid = False

        if self.valid:
            d = sym_struc.composition.as_dict()
            species = [key for key in d.keys()]
            numIons = []
            for ele in species:
                numIons.append(int(d[ele]))
            self.numIons = numIons
            self.species = species
            self.group = Group(number)
            atom_sites = []
            for i, site in enumerate(sym_struc.equivalent_sites):
                pos = site[0].frac_coords
                wp = Wyckoff_position.from_group_and_index(
                    number, sym_struc.wyckoff_symbols[i])
                specie = site[0].specie.number
                atom_sites.append(atom_site(wp, pos, specie, search=True))
            self.atom_sites = atom_sites
            matrix, ltype = sym_struc.lattice.matrix, self.group.lattice_type
            self.lattice = Lattice.from_matrix(matrix, ltype=ltype)
Example #8
0
def get_symmetrized_pmg(pmg, tol=1e-3, a_tol=5.0):
    """
    Symmetrized Pymatgen structure
    A slight modification to ensure that the structure adopts the
    standard setting used in interational crystallography table

    Args:
        pmg: input pymatgen structure
        tol: symmetry tolerance
    """

    pmg = symmetrize(pmg, tol, a_tol=a_tol)
    s = sga(pmg, symprec=tol, angle_tolerance=a_tol)
    hn = Group(s.get_space_group_number()).hall_number
    # make sure that the coordinates are in standard setting
    if hn != s._space_group_data["hall_number"]:
        s._space_group_data = get_symmetry_dataset(s._cell,
                                                   tol,
                                                   angle_tolerance=a_tol,
                                                   hall_number=hn)
    return s.get_symmetrized_structure(), s.get_space_group_number()
    def get_IST_operations(self, opstol=1e-03):
        """
        Returns the symmetry operations which maps the tensors
        belonging to equivalent sites onto each other in the form
        [site index 1, site index 2, [Symmops mapping from site
        index 1 to site index 2]]


        Args:
            opstol (float): tolerance for determining if a symmetry
            operation relates two sites

        Return:
            list of symmetry operations mapping equivalent sites and
            the indexes of those sites.
        """

        struc = self.structure
        ops = sga(struc).get_symmetry_operations(cartesian=True)
        uniquepointops = []
        for op in ops:
            uniquepointops.append(op)

        for ops in self.pointops:
            for op in ops:
                if op not in uniquepointops:
                    uniquepointops.append(op)

        IST_operations = []
        for atom in range(len(self.ist)):  # pylint: disable=C0200
            IST_operations.append([])
            for j in range(0, atom):
                for op in uniquepointops:
                    new = op.transform_tensor(self.ist[j])

                    # Check the matrix it references
                    if np.allclose(new, self.ist[atom], atol=opstol):
                        IST_operations[atom].append([j, op])

        self.IST_operations = IST_operations
    def get_FCM_operations(self, eigtol=1e-05, opstol=1e-05):
        """
        Returns the symmetry operations which maps the tensors
        belonging to equivalent sites onto each other in the form
        [site index 1a, site index 1b, site index 2a, site index 2b,
        [Symmops mapping from site index 1a, 1b to site index 2a, 2b]]


        Args:
            eigtol (float): tolerance for determining if two sites are
            related by symmetry
            opstol (float): tolerance for determining if a symmetry
            operation relates two sites

        Return:
            list of symmetry operations mapping equivalent sites and
            the indexes of those sites.
        """
        struc = self.structure
        ops = sga(struc).get_symmetry_operations(cartesian=True)
        uniquepointops = []
        for op in ops:
            uniquepointops.append(op)

        for ops in self.pointops:
            for op in ops:
                if op not in uniquepointops:
                    uniquepointops.append(op)

        passed = []
        relations = []
        for atom1 in range(len(self.fcm)):  # pylint: disable=C0200
            for atom2 in range(atom1, len(self.fcm)):
                unique = 1
                eig1, vecs1 = np.linalg.eig(self.fcm[atom1][atom2])
                index = np.argsort(eig1)
                neweig = np.real(
                    [eig1[index[0]], eig1[index[1]], eig1[index[2]]])

                for entry, p in enumerate(passed):
                    if np.allclose(neweig, p[2], atol=eigtol):
                        relations.append([atom1, atom2, p[0], p[1]])
                        unique = 0
                        break
                if unique == 1:
                    relations.append([atom1, atom2, atom2, atom1])
                    passed.append([atom1, atom2, np.real(neweig)])
        FCM_operations = []
        for entry, r in enumerate(relations):
            FCM_operations.append(r)
            FCM_operations[entry].append([])

            good = 0
            for op in uniquepointops:
                new = op.transform_tensor(self.fcm[r[2]][r[3]])

                if np.allclose(new, self.fcm[r[0]][r[1]], atol=opstol):
                    FCM_operations[entry][4].append(op)
                    good = 1
            if r[0] == r[3] and r[1] == r[2]:
                good = 1
            if r[0] == r[2] and r[1] == r[3]:
                good = 1
            if good == 0:
                FCM_operations[entry] = [
                    r[0],
                    r[1],
                    r[3],
                    r[2],
                ]
                FCM_operations[entry].append([])
                for op in uniquepointops:
                    new = op.transform_tensor(self.fcm[r[2]][r[3]])
                    if np.allclose(
                            new.T,
                            self.fcm[r[0]][r[1]],
                            atol=opstol,
                    ):
                        FCM_operations[entry][4].append(op)

        self.FCM_operations = FCM_operations
        return FCM_operations