def gen_atom_map(self, basis, vectol): images = self._images DG = self._distortion_group.matrices numIm = len(images) numAtoms = len(images[0].frac_coords) num_unstar = self._distortion_group.num_unstar a_map = np.zeros((len(DG), numIm, numAtoms, numAtoms)) for i in range(len(DG)): for j in range(1, numIm - 1): atoms1 = images[j].frac_coords num1 = images[j].species for k in range(0, numAtoms): t_coord = np.dot(DG[i].rotation_matrix, atoms1[k]) t_coord = (t_coord + DG[i].translation_vector) % 1.0 if i < num_unstar: atoms2 = images[j].frac_coords num2 = images[j].species else: atoms2 = images[numIm - 1 - j].frac_coords num2 = images[numIm - 1 - j].species for l in range(0, numAtoms): if closewrapped( t_coord, atoms2[l], vectol ) and num1[k] == num2[l] and basis[k] == 1: a_map[i, j, k, l] = 1 return a_map
def obtain_tmat(self, images, vectol, symprec, angle_tolerance): # -- Generate lattice with DG in input basis numIm = len(images) DG = self.matrices cp_pos = images[0].frac_coords cp_labels = images[0].species newimage = images[int(numIm / 2)].copy() for i in [int(numIm / 2), numIm - 1]: cp_pos = np.append(cp_pos, images[i].frac_coords, axis=0) cp_labels = np.append(cp_labels, images[i].species) newimage.remove_sites(range(len(newimage.species))) new_pos = [cp_pos[0]] new_labels = [cp_labels[0]] for j in range(len(cp_pos[:, 0])): atom = cp_pos[j] for i in range(len(DG)): t_coord = np.dot(atom, np.transpose(DG[i].rotation_matrix)) t_coord = (t_coord + DG[i].translation_vector) % 1.0 if not any([closewrapped(t_coord, m, vectol) for m in new_pos]): new_pos = np.append(new_pos, [t_coord], axis=0) new_labels = np.append(new_labels, cp_labels[j]) for k in range(len(new_labels)): newimage.append(new_labels[k], new_pos[k]) dataset2 = SpacegroupAnalyzer( newimage, symprec=symprec, angle_tolerance=angle_tolerance).get_symmetry_dataset() return dataset2["transformation_matrix"], dataset2["origin_shift"]
def from_images( cls, images: List, symprec: float, angle_tolerance: float, gentol: float, vectol: List[float], vectol2: List[float], ): dataset = cls.get_img_sym_data(images, symprec, angle_tolerance, gentol) # -- Finds H, the symmetry operations common for each image # H[0] is the list of rotations, H[1] is the list of translations numIm = len(images) H = [[], []] # type: List[List] rotH = dataset[0]["rotations"] tranH = dataset[0]["translations"] for i in range(0, len(rotH)): add = True for data in dataset: found = False for j in range(0, len(data["rotations"])): if np.allclose( data["rotations"][j], rotH[i], atol=gentol, rtol=0.0) and closewrapped( data["translations"][j], tranH[i], vectol2): found = True break if not found: add = False break if add: H[0].append(rotH[i]) H[1].append(tranH[i]) # -- Finds A*, the starred symmetry operations that map all images to their opposite image # Astar[0] is the list of rotations, Astar[1] is the list of translations Astar = [[], []] # type: List[List] rotA = dataset[int(numIm / 2)]["rotations"] tranA = dataset[int(numIm / 2)]["translations"] for i in range(0, len(rotA)): add = True for j in range(0, int(numIm / 2)): positions = images[j].frac_coords positions = np.dot(positions, np.transpose(rotA[i])) positions = positions + findtranslation( images[int(numIm / 2)], rotA[i], tranA[i], gentol, vectol2, symprec, angle_tolerance) newimage = images[j].copy() new_species = newimage.species for k in range(len(new_species)): newimage.replace(k, new_species[k], positions[k]) if not atomsequal(newimage, images[numIm - 1 - j], vectol): add = False break if add: Astar[0].append(rotA[i]) Astar[1].append(tranA[i]) # -- Finds the general distortion group with a direct product of H and A* # DG[0] is the list of rotations, DG[1] is the list of translations # The first len(H[0]) operations are unstarred operations; the rest are starred. DG = [] for i in range(0, len(H[0])): DG.append(SymmOp.from_rotation_and_translation(H[0][i], H[1][i])) for i in range(0, len(H[0])): for j in range(0, len(Astar[0])): add = True testrot = np.dot(H[0][i], Astar[0][j]) testtran = standardize( np.dot(Astar[1][j], np.transpose(H[0][i])) + H[1][i], gentol) for k in range(len(H[0]), len(DG)): if np.allclose(testrot, DG[k].rotation_matrix, atol=gentol) and closewrapped( testtran, DG[k].translation_vector, vectol): add = False break if add: DG.append( SymmOp.from_rotation_and_translation( testrot, testtran)) return cls(matrices=DG, num_unstar=len(H[0]), img_sym_dataset=dataset)
def gen_perturb(path, irrep, io): images = path.images DG = path.distortion_group.matrices num_unstar = path.distortion_group.num_unstar numIm = len(images) irrep_tools = IrrepTools() # -- Generate starting basis atoms1 = images[0].frac_coords numAtoms = len(atoms1) basis = np.zeros(numAtoms) atoms2 = images[numIm - 1].frac_coords m_vec = np.dot(np.linalg.inv(images[0].lattice.matrix), [io.min_move, io.min_move, io.min_move]) for i in range(0, numAtoms): if not closewrapped(atoms1[i, :], atoms2[i, :], m_vec): basis[i] = 1 # -- Generate matrix showing atom mapping for each operations a_map = path.gen_atom_map(basis=basis, vectol=io.vectol) # -- Generate and apply modes for an irrep of arbitrary dimension pt = np.zeros((numIm, numAtoms, 3)) pt_mode_init = irrep_tools.projection_diag(images=images, symmop_list=DG, irrep=irrep, num_unstar=num_unstar, a_map=a_map, basis=basis) if not np.any(pt_mode_init): raise RuntimeError( f"Uniform random initial perturbation does not contain any non-zero contributions from irrep {irrep.stokes_number}.\ Try a different irrep.") pt_mode_init = pt_mode_init / np.linalg.norm( np.ndarray.flatten(pt_mode_init)) pt += io.m_co[0] * pt_mode_init if io.irr_dim > 1: for i in range(1, io.irr_dim): pt_mode = irrep_tools.projection_odiag( images=images, symmop_list=DG, num_unstar=num_unstar, irrep=irrep, vec=pt_mode_init, index=i, a_map=a_map, basis=basis, ) pt += io.m_co[i] * (pt_mode / np.linalg.norm(np.ndarray.flatten(pt_mode))) p_vec = [ io.p_mag / np.linalg.norm(images[0].lattice.matrix[m, :]) for m in range(0, 3) ] # print pt[3]/np.amax(abs(pt[3])) # print(np.amax(p_vec*(pt[:,:,:]/np.amax(abs(pt))))) images_alt = [] for i in range(numIm): image_copy = images[i].copy() alt_species = image_copy.species perturbed_coords = image_copy.frac_coords + p_vec * (pt[i, :, :] / np.amax(abs(pt))) for j in range(numAtoms): image_copy.replace(j, alt_species[j], perturbed_coords[j]) images_alt.append(image_copy) return images_alt, basis
def gen_perturb(path, irrep, io): images = path.images DG = path.distortion_group.matrices num_unstar = path.distortion_group.num_unstar numIm = len(images) irrep_tools = IrrepTools() io.print("\n\n\n\n" + ("=" * 27 + "\n") * 2 + "\nGenerating perturbations...\n\n" + ("=" * 27 + "\n") * 2 + "\n") io.print("***THE FOLLOWING DATA IS FOR THE PERTURBED IMAGES***\n\n") # -- Generate starting basis atoms1 = images[0].frac_coords numAtoms = len(atoms1) basis = np.zeros(numAtoms) atoms2 = images[numIm - 1].frac_coords m_vec = np.dot(np.linalg.inv(images[0].lattice.matrix), [io.min_move, io.min_move, io.min_move]) for i in range(0, numAtoms): if not closewrapped(atoms1[i, :], atoms2[i, :], m_vec): basis[i] = 1 io.print("------- Atoms included in basis:") symbols = images[0].species for i in range(0, numAtoms): if basis[i] == 0: io.print(str(symbols[i]) + " No") else: io.print(str(symbols[i]) + " Yes") # -- Generate matrix showing atom mapping for each operations a_map = path.gen_atom_map(basis=basis, vectol=io.vectol) # -- Generate and apply modes for an irrep of arbitrary dimension pt = np.zeros((numIm, numAtoms, 3)) pt_mode_init = irrep_tools.projection_diag(images=images, symmop_list=DG, irrep=irrep, num_unstar=num_unstar, a_map=a_map, basis=basis) pt_mode_init = pt_mode_init / \ np.linalg.norm(np.ndarray.flatten(pt_mode_init)) pt += io.m_co[0] * pt_mode_init if io.irr_dim > 1: for i in range(1, io.irr_dim): pt_mode = irrep_tools.projection_odiag(images=images, symmop_list=DG, num_unstar=num_unstar, irrep=irrep, vec=pt_mode_init, index=i, a_map=a_map, basis=basis) pt += io.m_co[i] * \ (pt_mode/np.linalg.norm(np.ndarray.flatten(pt_mode))) p_vec = [ io.p_mag / np.linalg.norm(images[0].lattice.matrix[m, :]) for m in range(0, 3) ] # print pt[3]/np.amax(abs(pt[3])) # print(np.amax(p_vec*(pt[:,:,:]/np.amax(abs(pt))))) images_alt = [] for i in range(numIm): image_copy = images[i].copy() alt_species = image_copy.species perturbed_coords = image_copy.frac_coords + p_vec * (pt[i, :, :] / np.amax(abs(pt))) for j in range(numAtoms): image_copy.replace(j, alt_species[j], perturbed_coords[j]) images_alt.append(image_copy) return images_alt