Ejemplo n.º 1
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
    def get_rand_IST(self, max_force=1):
        """
        Generate a random internal strain tensor which obeys a structure's
        symmetry and the acoustic sum rule

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

        Return:
            InternalStrainTensor object
        """

        l = len(self.structure)
        IST = np.zeros((l, 3, 3, 3))
        for atom, ops in enumerate(self.IST_operations):
            temp_tensor = np.zeros([3, 3, 3])
            for op in ops:
                temp_tensor += op[1].transform_tensor(IST[op[0]])

            if len(ops) == 0:
                temp_tensor = Tensor(np.random.rand(3, 3, 3) - 0.5)
                for dim in range(3):
                    temp_tensor[dim] = (temp_tensor[dim] +
                                        temp_tensor[dim].T) / 2
                temp_tensor = sum([
                    temp_tensor.transform(symm_op)
                    for symm_op in self.pointops[atom]
                ]) / len(self.pointops[atom])
            IST[atom] = temp_tensor
            if len(ops) != 0:
                IST[atom] = IST[atom] / len(ops)

        return IST * max_force
Ejemplo n.º 3
0
    def test_transform(self):
        # Rank 3
        tensor = Tensor(np.arange(0, 27).reshape(3, 3, 3))
        symm_op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 30, False,
                                                         [0, 0, 1])
        new_tensor = tensor.transform(symm_op)

        self.assertArrayAlmostEqual(
            new_tensor,
            [
                [
                    [-0.871, -2.884, -1.928],
                    [-2.152, -6.665, -4.196],
                    [-1.026, -2.830, -1.572],
                ],
                [
                    [0.044, 1.531, 1.804],
                    [4.263, 21.008, 17.928],
                    [5.170, 23.026, 18.722],
                ],
                [
                    [1.679, 7.268, 5.821],
                    [9.268, 38.321, 29.919],
                    [8.285, 33.651, 26.000],
                ],
            ],
            3,
        )
    def get_asum_FCM(self, fcm, numiter=15):
        """
        Generate a symmeterized force constant matrix that obeys the objects symmetry
        constraints and obeys the acoustic sum rule through an iterative procedure

        Args:
            fcm (numpy array): 3Nx3N unsymmeterized force constant matrix
            numiter (int): number of iterations to attempt to obey the acoustic sum
                rule

        Return:
            numpy array representing the force constant matrix
        """

        # set max force in reciprocal space
        operations = self.FCM_operations
        numsites = len(self.structure)

        D = np.ones([numsites * 3, numsites * 3])
        for num in range(numiter):
            X = np.real(fcm)

            # symmetry operations
            pastrow = 0
            total = np.zeros([3, 3])
            for col in range(numsites):
                total = total + X[0:3, col * 3:col * 3 + 3]

            total = total / (numsites)
            for op in operations:
                same = 0
                transpose = 0
                if op[0] == op[1] and op[0] == op[2] and op[0] == op[3]:
                    same = 1
                if op[0] == op[3] and op[1] == op[2]:
                    transpose = 1
                if transpose == 0 and same == 0:
                    D[3 * op[0]:3 * op[0] + 3,
                      3 * op[1]:3 * op[1] + 3] = np.zeros([3, 3])

                    for symop in op[4]:

                        tempfcm = D[3 * op[2]:3 * op[2] + 3,
                                    3 * op[3]:3 * op[3] + 3]
                        tempfcm = symop.transform_tensor(tempfcm)

                        D[3 * op[0]:3 * op[0] + 3,
                          3 * op[1]:3 * op[1] + 3] += tempfcm

                    if len(op[4]) != 0:
                        D[3 * op[0]:3 * op[0] + 3, 3 * op[1]:3 * op[1] +
                          3] = D[3 * op[0]:3 * op[0] + 3,
                                 3 * op[1]:3 * op[1] + 3] / len(op[4])
                    D[3 * op[1]:3 * op[1] + 3,
                      3 * op[0]:3 * op[0] + 3] = D[3 * op[0]:3 * op[0] + 3,
                                                   3 * op[1]:3 * op[1] + 3].T
                    continue
                # Get the difference in the sum up to this point
                currrow = op[0]
                if currrow != pastrow:
                    total = np.zeros([3, 3])
                    for col in range(numsites):
                        total = total + X[currrow * 3:currrow * 3 + 3,
                                          col * 3:col * 3 + 3]
                    for col in range(currrow):
                        total = total - D[currrow * 3:currrow * 3 + 3,
                                          col * 3:col * 3 + 3]
                    total = total / (numsites - currrow)
                pastrow = currrow

                # Apply the point symmetry operations of the site
                temp_tensor = Tensor(total)
                temp_tensor_sum = sum([
                    temp_tensor.transform(symm_op)
                    for symm_op in self.sharedops[op[0]][op[1]]
                ])

                if len(self.sharedops[op[0]][op[1]]) != 0:
                    temp_tensor_sum = temp_tensor_sum / (len(
                        self.sharedops[op[0]][op[1]]))

                # Apply the proper transformation if there is an equivalent already
                if op[0] != op[1]:

                    for pair in range(len(op[4])):

                        temp_tensor2 = temp_tensor_sum.T
                        temp_tensor2 = op[4][pair].transform_tensor(
                            temp_tensor2)
                        temp_tensor_sum = (temp_tensor_sum + temp_tensor2) / 2

                else:
                    temp_tensor_sum = (temp_tensor_sum + temp_tensor_sum.T) / 2

                D[3 * op[0]:3 * op[0] + 3,
                  3 * op[1]:3 * op[1] + 3] = temp_tensor_sum
                D[3 * op[1]:3 * op[1] + 3,
                  3 * op[0]:3 * op[0] + 3] = temp_tensor_sum.T
            fcm = fcm - D

        return fcm
    def get_symmetrized_FCM(self, unsymmetrized_fcm, max_force=1):
        """
        Generate a symmeterized force constant matrix from an unsymmeterized matrix

        Args:
            unsymmetrized_fcm (numpy array): unsymmeterized force constant matrix
            max_charge (float): maximum born effective charge value

        Return:
            3Nx3N numpy array representing the force constant matrix
        """

        operations = self.FCM_operations
        D = unsymmetrized_fcm
        for op in operations:
            same = 0
            transpose = 0
            if op[0] == op[1] and op[0] == operations[2] and op[0] == op[3]:
                same = 1
            if op[0] == op[3] and op[1] == op[2]:
                transpose = 1
            if transpose == 0 and same == 0:
                D[3 * op[0]:3 * op[0] + 3,
                  3 * op[1]:3 * op[1] + 3] = np.zeros([3, 3])

                for symop in op[4]:

                    tempfcm = D[3 * op[2]:3 * op[2] + 3,
                                3 * op[3]:3 * op[3] + 3]
                    tempfcm = symop.transform_tensor(tempfcm)

                    D[3 * op[0]:3 * op[0] + 3,
                      3 * op[1]:3 * op[1] + 3] += tempfcm

                if len(op[4]) != 0:
                    D[3 * op[0]:3 * op[0] + 3, 3 * op[1]:3 * op[1] +
                      3] = D[3 * op[0]:3 * op[0] + 3,
                             3 * op[1]:3 * op[1] + 3] / len(op[4])
                D[3 * op[1]:3 * op[1] + 3,
                  3 * op[0]:3 * op[0] + 3] = D[3 * op[0]:3 * op[0] + 3,
                                               3 * op[1]:3 * op[1] + 3].T
                continue

            temp_tensor = Tensor(D[3 * op[0]:3 * op[0] + 3,
                                   3 * op[1]:3 * op[1] + 3])
            temp_tensor_sum = sum([
                temp_tensor.transform(symm_op)
                for symm_op in self.sharedops[op[0]][op[1]]
            ])
            if len(self.sharedops[op[0]][op[1]]) != 0:
                temp_tensor_sum = temp_tensor_sum / (len(
                    self.sharedops[op[0]][op[1]]))

            # Apply the proper transformation if there is an equivalent already
            if op[0] != op[1]:

                for pair in range(len(op[4])):

                    temp_tensor2 = temp_tensor_sum.T
                    temp_tensor2 = op[4][pair].transform_tensor(temp_tensor2)
                    temp_tensor_sum = (temp_tensor_sum + temp_tensor2) / 2

            else:
                temp_tensor_sum = (temp_tensor_sum + temp_tensor_sum.T) / 2

            D[3 * op[0]:3 * op[0] + 3,
              3 * op[1]:3 * op[1] + 3] = temp_tensor_sum
            D[3 * op[1]:3 * op[1] + 3,
              3 * op[0]:3 * op[0] + 3] = temp_tensor_sum.T

        return D
    def get_unstable_FCM(self, max_force=1):
        """
        Generate an unsymmeterized force constant matrix

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

        Return:
            numpy array representing the force constant matrix
        """

        struc = self.structure
        operations = self.FCM_operations
        # set max force in reciprocal space
        numsites = len(struc.sites)
        D = (1 / max_force) * 2 * (np.ones([numsites * 3, numsites * 3]))
        for op in operations:
            same = 0
            transpose = 0
            if op[0] == op[1] and op[0] == op[2] and op[0] == op[3]:
                same = 1
            if op[0] == op[3] and op[1] == op[2]:
                transpose = 1
            if transpose == 0 and same == 0:
                D[3 * op[0]:3 * op[0] + 3,
                  3 * op[1]:3 * op[1] + 3] = np.zeros([3, 3])
                D[3 * op[1]:3 * op[1] + 3,
                  3 * op[0]:3 * op[0] + 3] = np.zeros([3, 3])

                for symop in op[4]:

                    tempfcm = D[3 * op[2]:3 * op[2] + 3,
                                3 * op[3]:3 * op[3] + 3]
                    tempfcm = symop.transform_tensor(tempfcm)
                    D[3 * op[0]:3 * op[0] + 3,
                      3 * op[1]:3 * op[1] + 3] += tempfcm

                if len(op[4]) != 0:
                    D[3 * op[0]:3 * op[0] + 3, 3 * op[1]:3 * op[1] +
                      3] = D[3 * op[0]:3 * op[0] + 3,
                             3 * op[1]:3 * op[1] + 3] / len(op[4])

                D[3 * op[1]:3 * op[1] + 3,
                  3 * op[0]:3 * op[0] + 3] = D[3 * op[0]:3 * op[0] + 3,
                                               3 * op[1]:3 * op[1] + 3].T
                continue

            temp_tensor = Tensor(np.random.rand(3, 3) - 0.5) * max_force

            temp_tensor_sum = sum([
                temp_tensor.transform(symm_op)
                for symm_op in self.sharedops[op[0]][op[1]]
            ])
            temp_tensor_sum = temp_tensor_sum / (len(
                self.sharedops[op[0]][op[1]]))
            if op[0] != op[1]:
                for pair in range(len(op[4])):

                    temp_tensor2 = temp_tensor_sum.T
                    temp_tensor2 = op[4][pair].transform_tensor(temp_tensor2)
                    temp_tensor_sum = (temp_tensor_sum + temp_tensor2) / 2

            else:
                temp_tensor_sum = (temp_tensor_sum + temp_tensor_sum.T) / 2

            D[3 * op[0]:3 * op[0] + 3,
              3 * op[1]:3 * op[1] + 3] = temp_tensor_sum
            D[3 * op[1]:3 * op[1] + 3,
              3 * op[0]:3 * op[0] + 3] = temp_tensor_sum.T

        return D