def test_xyz(self): op = SymmOp([[1, -1, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]) s = op.as_xyz_string() self.assertEqual(s, 'x-y, -y, -z') self.assertEqual(op, SymmOp.from_xyz_string(s)) op2 = SymmOp([[0, -1, 0, 0.5], [1, 0, 0, 0.5], [0, 0, 1, 0.5 + 1e-7], [0, 0, 0, 1]]) s2 = op2.as_xyz_string() self.assertEqual(s2, '-y+1/2, x+1/2, z+1/2') self.assertEqual(op2, SymmOp.from_xyz_string(s2)) op2 = SymmOp([[3, -2, -1, 0.5], [-1, 0, 0, 12. / 13], [0, 0, 1, 0.5 + 1e-7], [0, 0, 0, 1]]) s2 = op2.as_xyz_string() self.assertEqual(s2, '3x-2y-z+1/2, -x+12/13, z+1/2') self.assertEqual(op2, SymmOp.from_xyz_string(s2)) op3 = SymmOp.from_xyz_string('3x - 2y - z+1 /2 , -x+12/ 13, z+1/2') self.assertEqual(op2, op3) # Ensure strings can be read in any order op4 = SymmOp.from_xyz_string('1 /2 + 3X - 2y - z , 12/ 13-x, z+1/2') op5 = SymmOp.from_xyz_string('+1 /2 + 3x - 2y - z , 12/ 13-x, +1/2+z') self.assertEqual(op4, op3) self.assertEqual(op4, op5) self.assertEqual(op3, op5) self.assertRaises(ValueError, self.op.as_xyz_string) o = SymmOp.from_xyz_string('0.5+x, 0.25+y, 0.75+z') self.assertArrayAlmostEqual(o.translation_vector, [0.5, 0.25, 0.75]) o = SymmOp.from_xyz_string('x + 0.5, y + 0.25, z + 0.75') self.assertArrayAlmostEqual(o.translation_vector, [0.5, 0.25, 0.75])
def site_symm(point, gen_pos, tol=1e-3, lattice=Euclidean_lattice): ''' Given gen_pos (a list of SymmOps), return the list of symmetry operations leaving a point (coordinate or SymmOp) invariant. ''' #Convert point into a SymmOp if type(point) != SymmOp: point = SymmOp.from_rotation_and_translation([[0,0,0],[0,0,0],[0,0,0]], point) symmetry = [] for op in gen_pos: is_symmetry = True #Calculate the effect of applying op to point difference = SymmOp(point.affine_matrix - (op*point).affine_matrix) #Check that the rotation matrix is unaltered by op if not np.allclose(difference.rotation_matrix, np.zeros((3,3)), rtol = 1e-3, atol = 1e-3): is_symmetry = False #Check that the displacement is less than tol displacement = difference.translation_vector if distance(displacement, lattice) > tol: is_symmetry = False if is_symmetry: '''The actual site symmetry's translation vector may vary from op by a factor of +1 or -1 (especially when op contains +-1/2). We record this to distinguish between special Wyckoff positions. As an example, consider the point (-x+1/2,-x,x+1/2) in position 16c of space group Ia-3(206). The site symmetry includes the operations (-z+1,x-1/2,-y+1/2) and (y+1/2,-z+1/2,-x+1). These operations are not listed in the general position, but correspond to the operations (-z,x+1/2,-y+1/2) and (y+1/2,-z+1/2,-x), respectively, just shifted by (+1,-1,0) and (0,0,+1), respectively. ''' el = SymmOp.from_rotation_and_translation(op.rotation_matrix, op.translation_vector + np.round(displacement)) symmetry.append(el) return symmetry
def get_inverse(op): """ Given a SymmOp object, returns its inverse. Args: op: a Symmop object Returns: the inverse """ matrix = op.affine_matrix.copy() # fill the matrix if it is ill conditioned # experimental if np.linalg.matrix_rank(matrix) < 4: for row in range(3): # fixed value if np.sum(matrix[row, :3]**2) < 1e-3: matrix[row, row] = 1 matrix[row, 3] = 0 if np.linalg.matrix_rank(matrix) == 3: # [-3x/2, -x/2, 1/4] # [0, x, 1/4] for rows in [[0, 1, 2], [1, 2, 0], [0, 2, 1]]: #m = (matrix[rows,:])[:,rows] #print(rows, m) if np.linalg.matrix_rank(matrix[rows[:2], :3]) != 2: break id0, id1, id2 = rows[0], rows[1], rows[2] if matrix[id0, id1] == 0: matrix[id0, id1], matrix[id0, id0] = matrix[id0, id0], matrix[id0, id1] if np.linalg.matrix_rank(matrix) == 3: matrix[id0, id1], matrix[id0, id2] = matrix[id0, id2], matrix[id0, id1] else: matrix[id1, id0], matrix[id1, id1] = matrix[id1, id1], matrix[id1, id0] if np.linalg.matrix_rank(matrix) == 3: matrix[id1, id0], matrix[id1, id2] = matrix[id1, id2], matrix[id1, id0] elif np.linalg.matrix_rank(matrix) == 2: # -3x/2, -x/2, -x+1/4 if np.sum(matrix[:, 0]**2) > 1e-3: matrix[1, 0], matrix[1, 1] = matrix[1, 1], matrix[1, 0] matrix[2, 0], matrix[2, 2] = matrix[2, 2], matrix[2, 0] elif np.sum(matrix[:, 1]**2) > 1e-3: matrix[0, 1], matrix[0, 0] = matrix[0, 0], matrix[0, 1] matrix[2, 1], matrix[2, 2] = matrix[2, 2], matrix[2, 1] else: matrix[0, 2], matrix[0, 0] = matrix[0, 0], matrix[0, 2] matrix[1, 2], matrix[1, 1] = matrix[1, 1], matrix[1, 2] return SymmOp(np.linalg.inv(matrix))
def test_to_from_dict(self): op = SymmOp([[3, -2, -1, 0.5], [-1, 0, 0, 12. / 13], [0, 0, 1, 0.5 + 1e-7], [0, 0, 0, 1]]) magop = MagSymmOp.from_symmop(op, -1) magop2 = MagSymmOp.from_dict(magop.as_dict()) self.assertEqual(magop2.time_reversal, -1) self.assertEqual(magop2.as_xyzt_string(), '3x-2y-z+1/2, -x+12/13, z+1/2, -1')
def generate_full_symmops(symmops, tol): """ Recursive algorithm to permute through all possible combinations of the initially supplied symmetry operations to arrive at a complete set of operations mapping a single atom to all other equivalent atoms in the point group. This assumes that the initial number already uniquely identifies all operations. Args: symmops ([SymmOp]): Initial set of symmetry operations. Returns: Full set of symmetry operations. """ a = [o.affine_matrix for o in symmops] if len(symmops) > 300: logger.debug("Generation of symmetry operations in infinite loop. " + "Possible error in initial operations or tolerance too " "low.") else: for op1, op2 in itertools.product(symmops, symmops): m = np.dot(op1.affine_matrix, op2.affine_matrix) d = np.abs(a - m) < tol if not np.any(np.all(np.all(d, axis=2), axis=1)): return generate_full_symmops(symmops + [SymmOp(m)], tol) return symmops
def symmetry_ops(self): """ Full set of symmetry operations as matrices. Lazily initialized as generation sometimes takes a bit of time. """ if self._symmetry_ops is None: self._symmetry_ops = [ SymmOp(m) for m in self._generate_full_symmetry_ops()] return self._symmetry_ops
def test_xyz(self): op = SymmOp([[1, -1, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]) s = op.as_xyz_string() self.assertEqual(s, "x-y, -y, -z") self.assertEqual(op, SymmOp.from_xyz_string(s)) op2 = SymmOp( [[0, -1, 0, 0.5], [1, 0, 0, 0.5], [0, 0, 1, 0.5 + 1e-7], [0, 0, 0, 1]] ) s2 = op2.as_xyz_string() self.assertEqual(s2, "-y+1/2, x+1/2, z+1/2") self.assertEqual(op2, SymmOp.from_xyz_string(s2)) op2 = SymmOp( [ [3, -2, -1, 0.5], [-1, 0, 0, 12.0 / 13], [0, 0, 1, 0.5 + 1e-7], [0, 0, 0, 1], ] ) s2 = op2.as_xyz_string() self.assertEqual(s2, "3x-2y-z+1/2, -x+12/13, z+1/2") self.assertEqual(op2, SymmOp.from_xyz_string(s2)) op3 = SymmOp.from_xyz_string("3x - 2y - z+1 /2 , -x+12/ 13, z+1/2") self.assertEqual(op2, op3) # Ensure strings can be read in any order op4 = SymmOp.from_xyz_string("1 /2 + 3X - 2y - z , 12/ 13-x, z+1/2") op5 = SymmOp.from_xyz_string("+1 /2 + 3x - 2y - z , 12/ 13-x, +1/2+z") self.assertEqual(op4, op3) self.assertEqual(op4, op5) self.assertEqual(op3, op5) # TODO: assertWarns not in Python 2.x unittest # update PymatgenTest for unittest2? # self.assertWarns(UserWarning, self.op.as_xyz_string) o = SymmOp.from_xyz_string("0.5+x, 0.25+y, 0.75+z") self.assertArrayAlmostEqual(o.translation_vector, [0.5, 0.25, 0.75]) o = SymmOp.from_xyz_string("x + 0.5, y + 0.25, z + 0.75") self.assertArrayAlmostEqual(o.translation_vector, [0.5, 0.25, 0.75])
def get_inverse(op): """ Given a SymmOp object, returns its inverse. Args: op: a Symmop object Returns: the inverse """ return SymmOp(np.linalg.inv(op.affine_matrix))
def symmetry_ops(self) -> set[SymmOp]: """ Full set of symmetry operations as matrices. Lazily initialized as generation sometimes takes a bit of time. """ from pymatgen.core.operations import SymmOp if self._symmetry_ops is None: self._symmetry_ops = { SymmOp(m) for m in self._generate_full_symmetry_ops() } return self._symmetry_ops
def rotate(self, frac=False): R = rotate_matrix() arr1 = np.zeros(3) R = np.vstack((R, arr1)) arr2 = np.array([0, 0, 0, 1]) R = np.vstack((R.T, arr2)) affine_matrix = R.T sym = SymmOp(affine_matrix) init_st = copy.copy(self.struct) if frac is True: init_st.apply_operation(sym, fractional=True) else: init_st.apply_operation(sym, fractional=False) self.rotate = init_st
def test_apply_operation(self): op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 90) s = self.structure.copy() s.apply_operation(op) self.assertArrayAlmostEqual( s.lattice.matrix, [[0.000000, 3.840198, 0.000000], [-3.325710, 1.920099, 0.000000], [2.217138, -0.000000, 3.135509]], 5) op = SymmOp([[1, 1, 0, 0.5], [1, 0, 0, 0.5], [0, 0, 1, 0.5], [0, 0, 0, 1]]) s = self.structure.copy() s.apply_operation(op, fractional=True) self.assertArrayAlmostEqual( s.lattice.matrix, [[5.760297, 3.325710, 0.000000], [3.840198, 0.000000, 0.000000], [0.000000, -2.217138, 3.135509]], 5)
def _analyze(self): if len(self.centered_mol) == 1: self.sch_symbol = "Kh" else: inertia_tensor = np.zeros((3, 3)) total_inertia = 0 for site in self.mol: c = site.coords wt = site.species_and_occu.weight for i in range(3): inertia_tensor[i, i] += wt * (c[(i + 1) % 3]**2 + c[(i + 2) % 3]**2) for i, j in itertools.combinations(list(range(3)), 2): inertia_tensor[i, j] += -wt * c[i] * c[j] inertia_tensor[j, i] += -wt * c[j] * c[i] total_inertia += wt * np.dot(c, c) # Normalize the inertia tensor so that it does not scale with size # of the system. This mitigates the problem of choosing a proper # comparison tolerance for the eigenvalues. inertia_tensor /= total_inertia eigvals, eigvecs = np.linalg.eig(inertia_tensor) self.principal_axes = eigvecs.T self.eigvals = eigvals v1, v2, v3 = eigvals eig_zero = abs(v1 * v2 * v3) < self.eig_tol**3 eig_all_same = abs(v1 - v2) < self.eig_tol and abs( v1 - v3) < self.eig_tol eig_all_diff = abs(v1 - v2) > self.eig_tol and abs( v1 - v3) > self.eig_tol and abs(v2 - v3) > self.eig_tol self.rot_sym = [] self.symmops = [SymmOp(np.eye(4))] if eig_zero: logger.debug("Linear molecule detected") self._proc_linear() elif eig_all_same: logger.debug("Spherical top molecule detected") self._proc_sph_top() elif eig_all_diff: logger.debug("Asymmetric top molecule detected") self._proc_asym_top() else: logger.debug("Symmetric top molecule detected") self._proc_sym_top()
def test_xyzt_string(self): xyzt_strings = [ 'x, y, z, +1', 'x, y, z, -1', '-y+1/2, x+1/2, x+1/2, +1' ] for xyzt_string in xyzt_strings: op = MagSymmOp.from_xyzt_string(xyzt_string) xyzt_string_out = op.as_xyzt_string() self.assertEqual(xyzt_string, xyzt_string_out) op = SymmOp([[3, -2, -1, 0.5], [-1, 0, 0, 12. / 13], [0, 0, 1, 0.5 + 1e-7], [0, 0, 0, 1]]) magop = MagSymmOp.from_symmop(op, -1) magop_str = magop.as_xyzt_string() self.assertEqual(magop.time_reversal, -1) self.assertEqual(magop_str, '3x-2y-z+1/2, -x+12/13, z+1/2, -1')
def split_k(self, wp1, wp2_lists): """ split the generators in w1 to different w2s for k-subgroup """ wp1_generators = [np.array(wp.as_dict()['matrix']) for wp in wp1] G1_orbits = [] G2_orbits = [] quadrant=deepcopy(self.inv_R[:3,3]) quadrant[np.abs(quadrant)<1e-5]=0 #finds the orientation of the subgroup_basis for i in range(3): if quadrant[i]>=0: quadrant[i]=1 else: quadrant[i]=-1 all_g2_orbits = [] translations = self.translation_generator() for translation in translations: #the translation generator provides all the possible ways to translate the starting positions, then they are shifted for gen in wp1_generators:#into the proper orientation orbit=np.matmul(self.inv_R,gen) orbit[np.abs(orbit)<1e-5]=0 orbit[np.abs(orbit-1)<1e-5]=1 orbit[np.abs(orbit+1)<1e-5]=-1 for i in range(3): if quadrant[i]==1: orbit[i][3]+=translation[i] orbit[i][3]=orbit[i][3]%1 if np.abs(orbit[i][3]-1)<1e-5: orbit[i][3]=0 else: orbit[i][3]+=(translation[i])%-1 orbit[i][3]=orbit[i][3]%-1 if np.abs(orbit[i][3])<1e-5: orbit[i][3]=-1 all_g2_orbits.append(orbit) for wp2 in wp2_lists: #final_G2=[] temp=np.array(deepcopy(all_g2_orbits)) temp[np.abs(temp) <1e-5] =0 temp=temp.tolist() for j, x in enumerate(temp): temp[j]=SymmOp(x) for orbit in temp: try_match=np.array([np.matmul(x.as_dict()['matrix'], orbit.as_dict()['matrix']) for x in wp2]) try_match[np.abs(try_match) <1e-5]=0 try_match[np.abs(try_match-1) <1e-5]=1 try_match[np.abs(try_match+1) <1e-5]=-1 for j in range(len(try_match)): for k in range(3): try_match[j][k][3]=try_match[j][k][3]%quadrant[k] if try_match[j][k][3]==0 and quadrant[k]==-1: try_match[j][k][3]=-1 try_match=try_match.tolist() for j,x in enumerate(try_match): try_match[j]=SymmOp(x) if np.any([try_match.count(x)>1 for x in try_match]): continue try: corresponding_positions=[temp.index(x) for x in try_match] except: continue for index in sorted(corresponding_positions,reverse=True): del all_g2_orbits[index] G2_orbits.append(try_match) break for position in G2_orbits: final_G1=[] for orbit in position: final_G1.append(SymmOp(np.matmul(self.R,orbit.as_dict()['matrix']))) G1_orbits.append(final_G1) if len(G1_orbits)!=len(wp2_lists): raise ValueError('inaccurate') else: return G1_orbits, G2_orbits
def split_t(self, wp1, wp2_lists, quadrant=None): """ split the generators in w1 to different w2s """ if self.counter == 0: self.proper_wp1 = [] [ self.proper_wp1.append(np.array(x.as_dict()['matrix'])) for x in wp1 ] self.original_tau_list = [x[:3, 3] for x in self.proper_wp1] for k, x in enumerate(self.original_tau_list): for j in range(3): self.original_tau_list[k][ j] = self.original_tau_list[k][j] % 1 self.original_tau_list[k] = self.original_tau_list[k].round(4) for k, x in enumerate(self.proper_wp1): self.proper_wp1[k][:3, 3] = self.original_tau_list[k] self.proper_wp1[k] = SymmOp(self.proper_wp1[k]) wp1_generators_visited = [] wp1_generators = [np.array(wp.as_dict()['matrix']) for wp in wp1] G1_orbits = [] G2_orbits = [] factor = max([1, np.linalg.det(self.R)]) if quadrant is None: quadrant = deepcopy(self.inv_R[:3, 3]) quadrant[np.abs(quadrant) < 1e-5] = 0 for i in range(3): if quadrant[i] >= 0: quadrant[i] = 1 else: quadrant[i] = -1 for wp2 in wp2_lists: for l, gen in enumerate(wp1_generators): good_generator = False trans_generator = np.matmul(self.inv_R, gen) trans_generator[np.abs(trans_generator) < 1e-5] = 0 for i in range(3): trans_generator[i][3] = trans_generator[i][3] % quadrant[i] if trans_generator[i][3] == 0 and quadrant[i] == -1: trans_generator[i][3] = -1 g1_orbits = [] g2_orbits = [] strs = [] for i, wp in enumerate(wp2): new_basis_orbit = np.matmul(wp.as_dict()['matrix'], trans_generator) new_basis_orbit[np.abs(new_basis_orbit) < 1e-5] = 0 for j in range(3): new_basis_orbit[j, 3] = new_basis_orbit[j, 3] % quadrant[j] if new_basis_orbit[j, 3] == 0 and quadrant[j] == -1: new_basis_orbit[j, 3] = -1 old_basis_orbit = np.matmul(self.R, new_basis_orbit) old_basis_orbit[np.abs(old_basis_orbit) < 1e-5] = 0 old_basis_orbit[np.abs(old_basis_orbit - 1) < 1e-5] = 1 old_basis_orbit[np.abs(old_basis_orbit + 1) < 1e-5] = -1 tmp = deepcopy(old_basis_orbit) tmp[3, :] = [0, 0, 0, 1] if i == 0: truth = True if self.counter != 0: tau = tmp[:3, 3] for j in range(3): tau[j] = tau[j] % 1 tau = tau.round(4) temporary = deepcopy(tmp) temporary[:3, 3] = tau temporary = SymmOp(temporary) truth = any( [temporary == x for x in self.proper_wp1]) if not in_lists(tmp, wp1_generators_visited) and in_lists( tmp, wp1_generators) and truth: good_generator = True else: break # to consider PBC g1_orbits.append(old_basis_orbit) if self.counter >= 1 and in_lists(new_basis_orbit, g2_orbits): good_generator = False break g2_orbits.append(new_basis_orbit) if good_generator: temp = [] for gen in g1_orbits: if not in_lists(gen, temp, PBC=False): temp.append(gen) if int(len(temp) * factor) >= len(wp2): wp1_generators_visited.extend(temp) g1_orbits = [SymmOp(orbit) for orbit in g1_orbits] g2_orbits = [SymmOp(orbit) for orbit in g2_orbits] G1_orbits.append(g1_orbits) G2_orbits.append(g2_orbits) break try: self.check_orbits(g1_orbits, wp1, wp2, wp2_lists) except: if self.counter != 0: quadrants = [[1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1], [-1, 1, 1], [-1, 1, -1], [-1, -1, 1], [-1, -1, -1]] quadrant = quadrants[self.counter - 1] wp1_generators = wp1_generators[:self.current_wp1_size] wp2_translations = [] for wp2 in wp2_lists: wp = [np.array(x.as_dict()['matrix']) for x in wp2] rot = [x[:3, :3] for x in wp] tau = [x[:3, 3] for x in wp] translations = [ np.array(tau[i]) for i, x in enumerate(rot) if np.array_equal(x, rot[0]) ] translations = [x - translations[0] for x in translations] wp2_translations.append(translations) new_wp1 = [] for translation_set in wp2_translations: for translation in translation_set: for gen in wp1_generators: orbit = np.matmul(self.inv_R, gen) orbit[np.abs(orbit) < 1e-5] = 0 orbit[np.abs(orbit - 1) < 1e-5] = 1 orbit[np.abs(orbit + 1) < 1e-5] = -1 for i in range(3): if quadrant[i] == 1: orbit[i][3] += (translation[i]) % 1 orbit[i][3] = orbit[i][3] % 1 else: orbit[i][3] += (translation[i]) % -1 orbit[np.abs(orbit) < 1e-5] = 0 orbit[np.abs(orbit - 1) < 1e-5] = 1 orbit[np.abs(orbit + 1) < 1e-5] = -1 if orbit[i][3] == 0: orbit[i][3] = -1 elif orbit[i][3] != -1: orbit[i][3] = orbit[i][3] % -1 orbit = np.matmul(self.R, orbit) orbit[np.abs(orbit) < 1e-5] = 0 orbit[np.abs(orbit - 1) < 1e-5] = 1 orbit[np.abs(orbit + 1) < 1e-5] = -1 orbit = SymmOp(orbit) if orbit not in new_wp1: new_wp1.append(orbit) self.counter += 1 if self.counter == 5: self.valid_split = False self.error = True return None, None return self.split_t(new_wp1, wp2_lists, quadrant=quadrant) return G1_orbits, G2_orbits
def k_split2(self, wp1, wp2_lists): """ split the generators in w1 to different w2 """ def add_g2_orbits(orbit, translations): new_g2 = [] for translation in translations: pos = deepcopy(orbit) pos[np.abs(pos) < 1e-5] = 0 for p in range(3): if pos[p][3] >= 0: pos[p][3] += translation[p] pos[p][3] = pos[p][3] % 1 else: pos[p][3] += (translation[p]) % -1 if pos[p][3] != -1: pos[p][3] = pos[p][3] % -1 new_g2.append(pos) return new_g2 wp1_generators = [np.array(wp.as_dict()['matrix']) for wp in wp1] G1_orbits = [] G2_orbits = [] all_g2_orbits = [] translations = self.translation_generator() for translation in translations: for gen in wp1_generators: orbit = np.matmul(self.inv_R, gen) orbit[np.abs(orbit) < 1e-5] = 0 for i in range(3): if orbit[i][3] >= 0.: orbit[i][3] += translation[i] orbit[i][3] = orbit[i][3] % 1 else: orbit[i][3] += (translation[i]) % -1 if orbit[i][3] != -1: orbit[i][3] = orbit[i][3] % -1 all_g2_orbits.append(orbit) for i in range(len(wp2_lists)): match = False ite = 0 while not match: final_G2 = [] temp = np.array(deepcopy(all_g2_orbits)) for j in range(len(temp)): temp[j][:3, 3] = temp[j][:3, 3] % 1 temp = temp.round(3).tolist() for orbit in temp: try_match = np.array([ np.matmul(x.as_dict()['matrix'], orbit) for x in wp2_lists[i] ]) for j in range(len(try_match)): try_match[j][:3, 3] = try_match[j][:3, 3] % 1 try_match = try_match.round(3).tolist() corresponding_positions = [] duplicates = [] for x in try_match: if x not in duplicates: duplicates.append(x) if len(duplicates) != len(try_match): continue try: corresponding_positions = [ temp.index(x) for x in try_match ] for index in sorted(corresponding_positions, reverse=True): final_G2.append(all_g2_orbits[index]) del all_g2_orbits[index] G2_orbits.append(final_G2) match = True break except: continue if len(corresponding_positions) == 0: trial = all_g2_orbits[0] quadrant = [0, 0, 0] for f in range(3): if trial[f][3] < 0.: quadrant[f] = -1 else: quadrant[f] = 1 try_match = np.array([ np.matmul(x.as_dict()['matrix'], trial) for x in wp2_lists[i] ]) for j in range(len(try_match)): for k in range(3): if try_match[j][k][3] >= 1: try_match[j][k][3] = try_match[j][k][3] % 1 temp_try_match = deepcopy(try_match) for j in range(len(try_match)): try_match[j][:3, 3] = try_match[j][:3, 3] % 1 try_match = try_match.round(3).tolist() temp = np.array(deepcopy(all_g2_orbits)) for j in range(len(temp)): temp[j][:3, 3] = temp[j][:3, 3] % 1 temp = temp.round(3).tolist() necessary_orbits = [x for x in try_match if x not in temp] necessary_index = [ try_match.index(x) for x in necessary_orbits ] trial_orbit = temp_try_match[choice(necessary_index)] for f in range(3): trial_orbit[f][3] = trial_orbit[f][3] % (quadrant[f]) all_g2_orbits.extend( add_g2_orbits(trial_orbit, translations)) ite += 1 for position in G2_orbits: final_G1 = [] for orbit in position: final_G1.append(SymmOp(np.matmul(self.R, orbit).round(3))) G1_orbits.append(final_G1) for i, position in enumerate(G2_orbits): for j, orbit in enumerate(position): G2_orbits[i][j] = SymmOp(orbit) return G1_orbits, G2_orbits
def split_t(self, wp1, wp2_lists, quadrant=None): """ split the generators in w1 to different w2s for t-subgroup """ if self.counter==0: self.proper_wp1=[] [self.proper_wp1.append(np.array(x.as_dict()['matrix'])) for x in wp1] self.original_tau_list=[x[:3,3] for x in self.proper_wp1] for k,x in enumerate(self.original_tau_list): for j in range(3): self.original_tau_list[k][j]=self.original_tau_list[k][j]%1 self.original_tau_list[k]=self.original_tau_list[k].round(4) for k,x in enumerate(self.proper_wp1): self.proper_wp1[k][:3,3]=self.original_tau_list[k] self.proper_wp1[k]=SymmOp(self.proper_wp1[k]) wp1_generators_visited = [] wp1_generators = [np.array(wp.as_dict()['matrix']) for wp in wp1] if np.all([wp2_lists[0]==x for x in wp2_lists]) and len(wp2_lists)==2: w1=deepcopy(wp1_generators[1]) w2=deepcopy(wp1_generators[2]) wp1_generators[1]=w2 wp1_generators[2]=w1 G1_orbits = [] G2_orbits = [] factor = max([1,np.linalg.det(self.R)]) if quadrant is None: quadrant=deepcopy(self.inv_R[:3,3]) quadrant[np.abs(quadrant)<1e-5]=0 for i in range(3): if quadrant[i]>=0: quadrant[i]=1 else: quadrant[i]=-1 for wp2 in wp2_lists: # [print(SymmOp(np.matmul(self.inv_R,x)).as_xyz_string()) for x in wp1_generators] for gen in wp1_generators: good_generator = False # inv=np.linalg.inv(self.R[:3,:3]) # t=self.R[:3,3] # trans_generator=np.zeros([4,4]) # trans_generator[:3,3]=gen[:3,3]-t # trans_generator[:3,:3]=np.matmul(inv,gen[:3,:3]) # # print("My new one",SymmOp(trans_generator).as_xyz_string()) trans_generator = np.matmul(self.inv_R, gen) trans_generator[np.abs(trans_generator)<1e-5]=0 for i in range(3): trans_generator[i][3]=trans_generator[i][3]%quadrant[i] if trans_generator[i][3]==0 and quadrant[i]==-1: trans_generator[i][3]=-1 g1_orbits = [] g2_orbits = [] for i, wp in enumerate(wp2): new_basis_orbit = np.matmul(wp.as_dict()['matrix'], trans_generator) new_basis_orbit[np.abs(new_basis_orbit)<1e-5]=0 for j in range(3): new_basis_orbit[j,3]=new_basis_orbit[j,3]%quadrant[j] if new_basis_orbit[j,3]==0 and quadrant[j]==-1: new_basis_orbit[j,3]=-1 old_basis_orbit = np.matmul(self.R, new_basis_orbit) old_basis_orbit[np.abs(old_basis_orbit)<1e-5]=0 old_basis_orbit[np.abs(old_basis_orbit-1)<1e-5]=1 old_basis_orbit[np.abs(old_basis_orbit+1)<1e-5]=-1 tmp = deepcopy(old_basis_orbit) tmp[3,:] = [0, 0, 0, 1] # print('tracking wp2 orbit',i,'newbasisorbit',SymmOp(new_basis_orbit).as_xyz_string(),'oldbasisorbit',SymmOp(old_basis_orbit).as_xyz_string(), 'chosenwyckoff',wp.as_xyz_string()) # print('transgenerator',SymmOp(trans_generator).as_xyz_string()) if i==0: truth=True if self.counter!=0: tau=tmp[:3,3] for j in range(3): tau[j]=tau[j]%1 tau=tau.round(4) temporary=deepcopy(tmp) temporary[:3,3]=tau temporary=SymmOp(temporary) truth=any([temporary==x for x in self.proper_wp1]) # print('current state') # print('wp1generated') # [print(SymmOp(x).as_xyz_string()) for x in wp1_generators_visited] # print('not in wp1 visited',not in_lists(tmp, wp1_generators_visited)) # print('in wp1 generators',in_lists(tmp, wp1_generators)) if not in_lists(tmp, wp1_generators_visited) and in_lists(tmp, wp1_generators) and truth: good_generator = True else: break # to consider PBC # print(SymmOp(old_basis_orbit).as_xyz_string(),' ',SymmOp(new_basis_orbit).as_xyz_string(),' ',wp.as_xyz_string()) g1_orbits.append(old_basis_orbit) if self.counter>=1 and in_lists(new_basis_orbit,g2_orbits): good_generator=False break g2_orbits.append(new_basis_orbit) if good_generator: temp=[] for gen in g1_orbits: if not in_lists(gen, temp, PBC=False): temp.append(gen) if int(len(temp)*factor) >= len(wp2): wp1_generators_visited.extend(temp) g1_orbits = [SymmOp(orbit) for orbit in g1_orbits] g2_orbits = [SymmOp(orbit) for orbit in g2_orbits] # print('G1=') # [print(x.as_xyz_string()) for x in g1_orbits] # print('G2=') # [print(x.as_xyz_string()) for x in g2_orbits] G1_orbits.append(g1_orbits) G2_orbits.append(g2_orbits) break try: self.check_orbits(g1_orbits, wp2, wp2_lists) except: if self.counter!=0: quadrants=[[1,1,1],[1,1,-1],[1,-1,1],[1,-1,-1],[-1,1,1],[-1,1,-1],[-1,-1,1],[-1,-1,-1]] quadrant=quadrants[self.counter-1] wp1_generators=wp1_generators[:self.current_wp1_size] wp2_translations=[] for wp2 in wp2_lists: wp=[np.array(x.as_dict()['matrix']) for x in wp2] rot=[x[:3,:3] for x in wp] tau=[x[:3,3] for x in wp] translations=[np.array(tau[i]) for i,x in enumerate(rot) if np.array_equal(x,rot[0])] translations=[x-translations[0] for x in translations] wp2_translations.append(translations) new_wp1=[] for translation_set in wp2_translations: for translation in translation_set: for gen in wp1_generators: orbit=np.matmul(self.inv_R,gen) orbit[np.abs(orbit)<1e-5]=0 orbit[np.abs(orbit-1)<1e-5]=1 orbit[np.abs(orbit+1)<1e-5]=-1 for i in range(3): if quadrant[i]==1: orbit[i][3]+=(translation[i])%1 orbit[i][3]=orbit[i][3]%1 else: orbit[i][3]+=(translation[i])%-1 orbit[np.abs(orbit)<1e-5]=0 orbit[np.abs(orbit-1)<1e-5]=1 orbit[np.abs(orbit+1)<1e-5]=-1 if orbit[i][3]==0: orbit[i][3]=-1 elif orbit[i][3]!=-1: orbit[i][3]=orbit[i][3]%-1 orbit=np.matmul(self.R,orbit) orbit[np.abs(orbit)<1e-5]=0 orbit[np.abs(orbit-1)<1e-5]=1 orbit[np.abs(orbit+1)<1e-5]=-1 orbit=SymmOp(orbit) if orbit not in new_wp1: new_wp1.append(orbit) self.counter += 1 if self.counter == 5: self.valid_split = False self.error = True return None, None return self.split_t(new_wp1, wp2_lists, quadrant=quadrant) return G1_orbits, G2_orbits
def split_t(self, wp1, wp2_lists): """ split the generators in w1 to different w2s """ #print(wp1) # wyckoff objects wp1_generators_visited = [] wp1_generators = [np.array(wp.as_dict()['matrix']) for wp in wp1] # convert them to numpy array for generator in wp1_generators: generator = np.array(generator) G1_orbits = [] G2_orbits = [] factor = max([1, np.linalg.det(self.R)]) for wp2 in wp2_lists: #print(wp2) #import sys; sys.exit() # try all generators here for gen in wp1_generators: good_generator = False #QZ: temporary solution, Needs to be fixed later if gen[0, 3] == 1 / 4 and gen[1, 3] == 3 / 4: gen[0, 3] -= 1 trans_generator = np.matmul(self.inv_R, gen) #print(trans_generator) g1_orbits = [] g2_orbits = [] strs = [] for i, wp in enumerate(wp2): new_basis_orbit = np.matmul(wp.as_dict()['matrix'], trans_generator) #print(wp.as_dict()['matrix']) #print(new_basis_orbit) #import sys; sys.exit() old_basis_orbit = np.matmul(self.R, new_basis_orbit).round(3) #old_basis_orbit[3,:] = [0, 0, 0, 1] tmp = deepcopy(old_basis_orbit) tmp[3, :] = [0, 0, 0, 1] if i == 0: #print("wp1", SymmOp(gen).as_xyz_string(), in_lists(tmp, wp1_generators_visited), in_lists(tmp, wp1_generators)) #print("sgb", SymmOp(new_basis_orbit).as_xyz_string()) #print("gb", SymmOp(tmp).as_xyz_string()) #for w in wp1_generators: # print(SymmOp(w).as_xyz_string()) if not in_lists(tmp, wp1_generators_visited) and in_lists( tmp, wp1_generators): #if in_lists(tmp, wp1_generators): good_generator = True #print("good_gener") else: break # to consider PBC g1_orbits.append(old_basis_orbit) g2_orbits.append(new_basis_orbit) #print(g1_orbits) if good_generator: temp = [] # remove duplicates due to peridoic boundary conditions for gen in g1_orbits: if not in_lists(gen, temp): temp.append(gen) if int(len(temp) * factor) >= len(wp2): wp1_generators_visited.extend(temp) g1_orbits = [SymmOp(orbit) for orbit in g1_orbits] g2_orbits = [SymmOp(orbit) for orbit in g2_orbits] G1_orbits.append(g1_orbits) G2_orbits.append(g2_orbits) #print("adding unique generators", len(g1_orbits), len(wp2), int(len(temp)*factor)) break #print("EEEEEE", len(g1_orbits), len(wp2)) self.check_orbits(g1_orbits, wp1, wp2, wp2_lists) return G1_orbits, G2_orbits
def split_k(self, wp1, wp2_lists): """ split the generators in w1 to different w2s """ wp1_generators = [np.array(wp.as_dict()['matrix']) for wp in wp1] G1_orbits = [] G2_orbits = [] # factor = max([1, np.linalg.det(self.R)]) all_g2_orbits = [] translations = self.translation_generator() for translation in translations: for gen in wp1_generators: orbit = np.matmul(self.inv_R, gen) for i in range(3): if orbit[i][3] >= 0: orbit[i][3] += translation[i] orbit[i][3] = orbit[i][3] % 1 else: orbit[i][3] -= translation[i] orbit[i][3] = orbit[i][3] % -1 all_g2_orbits.append(orbit) ######################################################################### for i in range(len(wp2_lists)): final_G2 = [] temp = np.array(deepcopy(all_g2_orbits)) for j in range(len(temp)): temp[j][:3, 3] = temp[j][:3, 3] % 1 temp = temp.round(3).tolist() for orbit in temp: try_match = np.array([ np.matmul(x.as_dict()['matrix'], orbit) for x in wp2_lists[i] ]) for j in range(len(try_match)): try_match[j][:3, 3] = try_match[j][:3, 3] % 1 try_match = try_match.round(3).tolist() try: corresponding_positions = [ temp.index(x) for x in try_match ] except: continue for index in sorted(corresponding_positions, reverse=True): final_G2.append(all_g2_orbits[index]) del all_g2_orbits[index] G2_orbits.append(final_G2) break for position in G2_orbits: final_G1 = [] for orbit in position: final_G1.append(SymmOp(np.matmul(self.R, orbit).round(3))) G1_orbits.append(final_G1) for i, position in enumerate(G2_orbits): for j, orbit in enumerate(position): G2_orbits[i][j] = SymmOp(orbit) return G1_orbits, G2_orbits