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
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