Exemplo n.º 1
0
    def test_inverse(self):
        coord0 = [0.35, 0.1, 0.4]
        coords = np.array([
            [0.350, 0.100, 0.400],
            [0.350, 0.100, 0.000],
            [0.350, 0.100, 0.000],
            [0.350, 0.000, 0.667],
            [0.350, 0.000, 0.250],
            [0.350, 0.350, 0.400],
            [0.350, 0.350, 0.500],
            [0.350, 0.350, 0.000],
            [0.350, 0.350, 0.350],
            [0.100, 0.100, 0.100],
            [0.400, 0.400, 0.400],
            [0.350, 0.000, 0.000],
            [0.000, 0.100, 0.400],
            [0.350, 0.000, 0.400],
        ])
        xyzs = [
            'x,y,z',
            'x,y,0',
            'y,x,0',
            'x,0,2/3',
            '0,x,1/4',
            'x,x,z',
            'x,-x,1/2',
            '2x,x,0',
            '-2x,-0.5x,-x+1/4',
            '-2y,-0.5y,-y+1/4',
            '-2z,-0.5z,-z+1/4',
            '0,0,x',
            '-y/2+1/2,-z,0',
            '-z,-x/2+1/2,0',
        ]

        for i, xyz in enumerate(xyzs):
            op = SymmOp.from_xyz_string(xyz)
            inv_op = get_inverse(op)
            coord1 = op.operate(coord0)
            coord2 = inv_op.operate(coord1)
            self.assertTrue(np.allclose(coord2, coords[i], rtol=1e-2))
Exemplo n.º 2
0
    def _get_alternative(self, wyc_set, index):
        """
        get alternative structure representations

        Args:
            tran: affine matrix
            index: the list of transformed wps

        Returns:
            a new pyxtal structure after transformation
        """
        new_struc = self.copy()

        # xyz_string like 'x+1/4,y+1/4,z+1/4'
        xyz_string = wyc_set['Coset Representative'][index]
        op = get_inverse(SymmOp.from_xyz_string(xyz_string))
        #op = SymmOp.from_xyz_string(xyz_string)

        ids = []
        for i, site in enumerate(new_struc.atom_sites):
            id = len(self.group) - site.wp.index - 1
            letter = wyc_set['Transformed WP'][index].split()[id]
            ids.append(letters.index(letter))
            wp = Wyckoff_position.from_group_and_index(self.group.number, letter)
            pos = op.operate(site.position)
            pos1 = search_matched_position(self.group, wp, pos)
            if pos1 is not None:
                new_struc.atom_sites[i] = atom_site(wp, pos1, site.specie)
            else:
                print(pos)
                print(wp)
                raise RuntimeError("Cannot find the right pos")

        # switch lattice
        R = op.affine_matrix[:3,:3] #rotation
        matrix = np.dot(R, self.lattice.matrix)
        new_struc.lattice = Lattice.from_matrix(matrix, ltype=self.group.lattice_type)
        new_struc.source = "Alt. Wyckoff Set: " + xyz_string
        return new_struc, ids
Exemplo n.º 3
0
    def symmetrize(self, splitter, solution, disp):
        """
        For a given solution, search for the possbile supergroup structure

        Args: 
            splitter: splitter object to specify the relation between G and H
            disp: an overall shift from H to G, None or 3 vector
            d_tol: the tolerance in angstrom

        Returns:
            coords_G1
            coords_G2
            coords_H1
            elements
            mults
        """

        atom_sites_H = self.struc.atom_sites
        coords_G1 = []  # position in G
        coords_G2 = []  # position in G on the subgroup bais
        coords_H1 = []  # position in H
        elements = []
        mults = []
        inv_R = splitter.inv_R  # inverse coordinate transformation
        # wp1 stores the wyckoff position object of ['2c', '6h', '12i']
        for i, wp1 in enumerate(splitter.wp1_lists):

            if len(splitter.wp2_lists[i]) == 1:
                # symmetry info
                ops_H = splitter.H_orbits[i][0]  # ops for H
                ops_G2 = splitter.G2_orbits[i][0]  # ops for G2

                # refine coord1 to find the best match on coord2
                coord = atom_sites_H[solution[i][0]].position
                coord0s = apply_ops(coord, ops_H)  # possible coords in H
                dists = []
                for coord0 in coord0s:
                    coord2 = coord0.copy()
                    coord2 += disp
                    coord1 = apply_ops(coord2, ops_G2)[0]  # coord in G
                    dist = coord1 - coord2
                    dist -= np.round(dist)
                    dist = np.dot(dist, self.cell)
                    dists.append(np.linalg.norm(dist))
                min_ID = np.argmin(np.array(dists))

                coord2 = coord0s[min_ID].copy()
                coord1 = ops_G2[0].operate(coord2 + disp)
                if round(np.trace(ops_G2[0].rotation_matrix)) in [1, 2]:

                    def fun(x, pt, ref, op):
                        pt[0] = x[0]
                        y = op.operate(pt)
                        diff = y - ref
                        diff -= np.round(diff)
                        diff = np.dot(diff, self.cell)
                        return np.linalg.norm(diff)

                    # optimize the distance by changing coord1
                    res = minimize(fun,
                                   coord1[0],
                                   args=(coord1, coord2, ops_G2[0]),
                                   method='Nelder-Mead',
                                   options={'maxiter': 20})
                    coord1[0] = res.x[0]
                    coord1 = ops_G2[0].operate(coord1)

                coords_G1.append(
                    np.dot(inv_R[:3, :3], coord1).T + inv_R[:3, 3].T)
                #coords_G1.append(coord1)
                coords_G2.append(coord1)
                coords_H1.append(coord2)
                elements.append(splitter.elements[i])
                mults.append(len(ops_G2))

            else:
                # symmetry operations
                ops_H1 = splitter.H_orbits[i][0]
                ops_G22 = splitter.G2_orbits[i][1]

                coord1 = atom_sites_H[solution[i][0]].position.copy()
                coord2 = atom_sites_H[solution[i][1]].position.copy()
                # refine coord1 to find the best match on coord2
                coord11 = coord1 + disp
                coord22 = coord2 + disp
                coords11 = apply_ops(coord11, ops_H1)

                # transform coords1 by symmetry operation
                op = ops_G22[0]
                for m, coord11 in enumerate(coords11):
                    coords11[m] = op.operate(coord11)
                tmp, dist = get_best_match(coords11, coord22, self.cell)

                # recover the original position
                inv_op = get_inverse(op)
                coord1 = inv_op.operate(tmp)
                coord1 -= disp
                d = coord22 - tmp
                d -= np.round(d)

                coord22 -= d / 2  #final coord2 after disp
                # recover the displaced position
                coord11 = inv_op.operate(coord22)

                coords_G1.append(
                    np.dot(inv_R[:3, :3], coord11).T + inv_R[:3, 3].T)
                coords_G2.append(coord11)
                coords_G2.append(coord22)
                coords_H1.append(coord1)
                coords_H1.append(coord2)
                elements.extend([splitter.elements[i]] * 2)
                mults.append(len(ops_H1))
                mults.append(len(ops_G22))
        return coords_G1, coords_G2, coords_H1, elements, mults
Exemplo n.º 4
0
    def symmetrize_dist(self,
                        splitter,
                        solution,
                        disp=None,
                        mask=None,
                        d_tol=1.2):
        """
        For a given solution, search for the possbile supergroup structure

        Args: 
            splitter: splitter object to specify the relation between G and H
            solution: list of sites in H, e.g., ['4a', '8b']
            disp: an overall shift from H to G, None or 3 vector
            d_tol: the tolerance in angstrom
        Returns:
            distortion
            cell translation
        """
        max_disps = []
        atom_sites_H = self.struc.atom_sites
        n_atoms = sum([site.wp.multiplicity for site in atom_sites_H])
        #print("checking solution-----------------", solution)
        # wp1 stores the wyckoff position object of ['2c', '6h', '12i']
        for i, wp1 in enumerate(splitter.wp1_lists):

            if len(splitter.wp2_lists[i]) == 1:
                # one to one splitting, e.g., 2c->2d
                # this usually involves increase of site symmetry

                # symmetry info
                ops_H = splitter.H_orbits[i][0]  # ops for H
                ops_G2 = splitter.G2_orbits[i][0]  # ops for G2

                # refine coord1 to find the best match on coord2
                coord = atom_sites_H[solution[i][0]].position
                coord0s = apply_ops(coord, ops_H)  # possible coords in H
                dists = []
                for coord0 in coord0s:
                    coord2 = coord0.copy()
                    if disp is not None:
                        coord2 += disp
                    coord1 = apply_ops(coord2, ops_G2)[0]  # coord in G
                    #print(coord1, coord2)
                    dist = coord1 - coord2
                    dist -= np.round(dist)
                    dist = np.dot(dist, self.cell)
                    dists.append(np.linalg.norm(dist))
                min_ID = np.argmin(np.array(dists))

                dist = dists[min_ID]
                coord2 = coord0s[min_ID].copy()

                #print("---------", wp1.letter, coord1, coord2, disp, dist)
                #print(splitter)
                #print(splitter.R)
                if disp is None:
                    coord1 = apply_ops(coord2, ops_G2)[0]
                    disp = (coord1 - coord2).copy()
                    # temporary fix
                    xyz1 = ops_H[0].as_xyz_string().split(',')
                    xyz2 = ops_G2[0].as_xyz_string().split(',')
                    mask = [m for m in range(3) if xyz1[m] == xyz2[m]]
                    #disp[mask] = 0
                    #if abs(disp[0] + disp[1]) < 1e-2:
                    #    disp[:2] = 0
                    #print(disp, coord1, coord2)
                elif dist < d_tol:
                    coord1 = ops_G2[0].operate(coord2 + disp)
                    if round(np.trace(ops_G2[0].rotation_matrix)) in [1, 2]:

                        def fun(x, pt, ref, op):
                            pt[0] = x[0]
                            y = op.operate(pt)
                            diff = y - ref
                            diff -= np.round(diff)
                            diff = np.dot(diff, self.cell)
                            return np.linalg.norm(diff)

                        # optimize the distance by changing coord1
                        res = minimize(fun,
                                       coord1[0],
                                       args=(coord1, coord2, ops_G2[0]),
                                       method='Nelder-Mead',
                                       options={'maxiter': 20})
                        coord1[0] = res.x[0]
                        coord1 = ops_G2[0].operate(coord1)
                else:
                    return 10000, None, None
                if mask is not None:
                    disp[mask] = 0
                diff = coord1 - (coord2 + disp)
                diff -= np.round(diff)
                max_disps.append(np.linalg.norm(np.dot(diff, self.cell)))

            else:
                # symmetry operations
                ops_H1 = splitter.H_orbits[i][0]
                ops_G22 = splitter.G2_orbits[i][1]

                coord1 = atom_sites_H[solution[i][0]].position.copy()
                coord2 = atom_sites_H[solution[i][1]].position.copy()
                # refine coord1 to find the best match on coord2
                if disp is not None:
                    coord11 = coord1 + disp
                    coord22 = coord2 + disp
                else:
                    coord11 = coord1
                    coord22 = coord2
                coords11 = apply_ops(coord11, ops_H1)

                # transform coords1 by symmetry operation
                op = ops_G22[0]
                for m, coord11 in enumerate(coords11):
                    coords11[m] = op.operate(coord11)
                tmp, dist = get_best_match(coords11, coord22, self.cell)
                if dist > np.sqrt(2) * d_tol:
                    return 10000, None, mask

                # recover the original position
                try:
                    inv_op = get_inverse(op)
                except:
                    print("Error in getting the inverse")
                    print(op)
                    print(op.as_xyz_string())
                    import sys
                    sys.exit()
                coord1 = inv_op.operate(tmp)
                if disp is not None:
                    coord1 -= disp
                d = coord22 - tmp
                d -= np.round(d)

                coord22 -= d / 2  #final coord2 after disp
                # recover the displaced position
                coord11 = inv_op.operate(coord22)

                max_disps.append(np.linalg.norm(np.dot(d / 2, self.cell)))

        return max(max_disps), disp, mask