def subgroup_by_splitter(self, splitter, eps=0.05): lat1 = np.dot(splitter.R[:3, :3].T, self.lattice.matrix) multiples = np.linalg.det(splitter.R[:3, :3]) split_sites = [] for i, site in enumerate(self.atom_sites): pos = site.position for ops1, ops2 in zip(splitter.G2_orbits[i], splitter.H_orbits[i]): pos0 = apply_ops(pos, ops1)[0] pos0 -= np.floor(pos0) pos0 += eps * (np.random.sample(3) - 0.5) wp, _ = Wyckoff_position.from_symops(ops2, group=splitter.H.number, permutation=False) split_sites.append(atom_site(wp, pos0, site.specie)) new_struc = deepcopy(self) new_struc.group = splitter.H lattice = Lattice.from_matrix(lat1, ltype=new_struc.group.lattice_type) new_struc.lattice = lattice.mutate(degree=0.01, frozen=True) new_struc.atom_sites = split_sites new_struc.numIons = [ int(multiples * numIon) for numIon in self.numIons ] new_struc.source = 'Wyckoff Split' return new_struc
def optimize_lattice(self): """ optimize the lattice if the cell has a bad inclination angles """ for i in range(5): lattice, trans, opt = self.lattice.optimize() if opt: for site in self.mol_sites: pos_absolute = np.dot(site.position, self.lattice.matrix) pos_frac = pos_absolute.dot(lattice.inv_matrix) site.position = pos_frac - np.floor(pos_frac) site.lattice = lattice # for P21/c, Pc, C2/c, check if opt the inclination angle if self.group.number in [7, 14, 15]: for j, op in enumerate(site.wp.ops): vec = op.translation_vector.dot(trans) vec -= np.floor(vec) op1 = op.from_rotation_and_translation( op.rotation_matrix, vec) site.wp.ops[j] = op1 #to do needs to update diag if necessary _, perm = Wyckoff_position.from_symops(site.wp.ops, self.group.number) if not isinstance(perm, list): self.diag = True else: self.diag = False self.lattice = lattice else: break
def test_P21n(self): strs = [ "x, y, z", "-x, -y, -z", "-x+1/2, y+1/2, -z+1/2", "x+1/2, -y+1/2, z+1/2", ] wyc, perm = Wyckoff_position.from_symops(strs) self.assertTrue(wyc.number == 14)
def optimize_lattice(self, iterations=3): """ optimize the lattice if the cell has a bad inclination angles """ #if self.molecular: count = 0 for i in range(iterations): lattice, trans, opt = self.lattice.optimize() #print(self.lattice, "->", lattice) if opt: if self.molecular: sites = self.mol_sites else: sites = self.atom_sites for j, site in enumerate(sites): count += 1 pos_abs = np.dot(site.position, self.lattice.matrix) pos_frac = pos_abs.dot(lattice.inv_matrix) pos_frac -= np.floor(pos_frac) if self.molecular: site.lattice = lattice # for P21/c, Pc, C2/c, check if opt the inclination angle ops = site.wp.ops.copy() diag = False if self.group.number in [7, 14, 15]: for k, op in enumerate(ops): vec = op.translation_vector.dot(trans) #print(vec) vec -= np.floor(vec) op1 = op.from_rotation_and_translation( op.rotation_matrix, vec) ops[k] = op1 wp, perm = Wyckoff_position.from_symops( ops, self.group.number) if not isinstance(perm, list): diag = True else: diag = False pos_frac = pos_frac[perm] sites[j] = atom_site(wp, pos_frac, site.specie, diag) #print(sites[j].wp) #site.update() self.lattice = lattice self.diag = diag else: break
def subgroup_by_splitter(self, splitter, eps=0.05): """ transform the crystal to subgroup symmetry from a splitter object """ lat1 = np.dot(splitter.R[:3, :3].T, self.lattice.matrix) multiples = np.linalg.det(splitter.R[:3, :3]) new_struc = deepcopy(self) new_struc.group = splitter.H lattice = Lattice.from_matrix(lat1, ltype=new_struc.group.lattice_type) lattice = lattice.mutate(degree=eps, frozen=True) h = splitter.H.number split_sites = [] if self.molecular: # below only works when the cell does not change for i, site in enumerate(self.mol_sites): pos = site.position mol = site.molecule ori = site.orientation coord0 = mol.mol.cart_coords.dot(ori.matrix.T) wp1 = site.wp ori.reset_matrix(np.eye(3)) for ops1, ops2 in zip(splitter.G2_orbits[i], splitter.H_orbits[i]): #reset molecule coord1 = np.dot(coord0, ops1[0].affine_matrix[:3, :3].T) _mol = mol.copy() _mol.reset_positions(coord1) pos0 = apply_ops(pos, ops1)[0] pos0 -= np.floor(pos0) pos0 += eps * (np.random.sample(3) - 0.5) wp, _ = Wyckoff_position.from_symops(ops2, h, permutation=False) split_sites.append(mol_site(_mol, pos0, ori, wp, lattice)) new_struc.mol_sites = split_sites new_struc.numMols = [ int(multiples * numMol) for numMol in self.numMols ] else: for i, site in enumerate(self.atom_sites): pos = site.position for ops1, ops2 in zip(splitter.G2_orbits[i], splitter.H_orbits[i]): pos0 = apply_ops(pos, ops1)[0] pos0 -= np.floor(pos0) pos0 += eps * (np.random.sample(3) - 0.5) wp, _ = Wyckoff_position.from_symops(ops2, h, permutation=False) split_sites.append(atom_site(wp, pos0, site.specie)) new_struc.atom_sites = split_sites new_struc.numIons = [ int(multiples * numIon) for numIon in self.numIons ] new_struc.lattice = lattice new_struc.source = 'Wyckoff Split' return new_struc
def __init__(self, struc, ref_mol=None, tol=0.2, relax_h=False): """ extract the mol_site information from the give cif file and reference molecule Args: struc: cif/poscar file or a Pymatgen Structure object ref_mol: xyz file or a reference Pymatgen molecule object tol: scale factor for covalent bond distance relax_h: whether or not relax the position for hydrogen atoms in structure """ if isinstance(ref_mol, str): ref_mol = Molecule.from_file(ref_mol) elif isinstance(ref_mol, Molecule): ref_mol = ref_mol else: print(type(ref_mol)) raise NameError("reference molecule cannot be defined") if isinstance(struc, str): pmg_struc = Structure.from_file(struc) elif isinstance(struc, Structure): pmg_struc = struc else: print(type(struc)) raise NameError("input structure cannot be intepretted") self.props = ref_mol.site_properties self.ref_mol = ref_mol.get_centered_molecule() self.tol = tol self.diag = False self.relax_h = relax_h sga = SpacegroupAnalyzer(pmg_struc) ops = sga.get_space_group_operations() self.wyc, perm = Wyckoff_position.from_symops( ops, sga.get_space_group_number()) if self.wyc is not None: self.group = Group(self.wyc.number) if isinstance(perm, list): if perm != [0, 1, 2]: lattice = Lattice.from_matrix(pmg_struc.lattice.matrix, self.group.lattice_type) latt = lattice.swap_axis(ids=perm, random=False).get_matrix() coor = pmg_struc.frac_coords[:, perm] pmg_struc = Structure(latt, pmg_struc.atomic_numbers, coor) else: self.diag = True self.perm = perm coords, numbers = search_molecule_in_crystal(pmg_struc, self.tol) #coords -= np.mean(coords, axis=0) if self.relax_h: self.molecule = self.addh(Molecule(numbers, coords)) else: self.molecule = Molecule(numbers, coords) self.pmg_struc = pmg_struc self.lattice = Lattice.from_matrix(pmg_struc.lattice.matrix, self.group.lattice_type) else: raise ValueError( "Cannot find the space group matching the symmetry operation")
def _subgroup_by_splitter(self, splitter, eps=0.05, mut_lat=True): """ transform the crystal to subgroup symmetry from a splitter object Args: splitter: wyckoff splitter object eps (float): maximum atomic displacement in Angstrom mut_lat (bool): whether or not mutate the lattice """ lat1 = np.dot(splitter.R[:3,:3].T, self.lattice.matrix) multiples = np.linalg.det(splitter.R[:3,:3]) new_struc = self.copy() new_struc.group = splitter.H lattice = Lattice.from_matrix(lat1, ltype=new_struc.group.lattice_type) if mut_lat: lattice=lattice.mutate(degree=eps, frozen=True) h = splitter.H.number split_sites = [] if self.molecular: # below only works when the cell does not change for i, site in enumerate(self.mol_sites): pos = site.position mol = site.molecule ori = site.orientation coord0 = mol.mol.cart_coords.dot(ori.matrix.T) coord0 = np.dot(coord0, splitter.R[:3,:3]) wp1 = site.wp ori.reset_matrix(np.eye(3)) id = 0 for ops1, ops2 in zip(splitter.G2_orbits[i], splitter.H_orbits[i]): #reset molecule rot = wp1.generators_m[id].affine_matrix[:3,:3].T coord1 = np.dot(coord0, rot) _mol = mol.copy() _mol.reset_positions(coord1) pos0 = apply_ops(pos, ops1)[0] pos0 -= np.floor(pos0) dis = (np.random.sample(3) - 0.5).dot(self.lattice.matrix) dis /= np.linalg.norm(dis) pos0 += eps*dis*(np.random.random()-0.5) wp, _ = Wyckoff_position.from_symops(ops2, h, permutation=False) if h in [7, 14] and self.group.number == 31: diag = True else: diag = self.diag split_sites.append(mol_site(_mol, pos0, ori, wp, lattice, diag)) id += wp.multiplicity new_struc.mol_sites = split_sites new_struc.numMols = [int(multiples*numMol) for numMol in self.numMols] else: for i, site in enumerate(self.atom_sites): pos = site.position for ops1, ops2 in zip(splitter.G2_orbits[i], splitter.H_orbits[i]): pos0 = apply_ops(pos, ops1)[0] pos0 -= np.floor(pos0) dis = (np.random.sample(3) - 0.5).dot(self.lattice.matrix) dis /= np.linalg.norm(dis) pos0 += np.dot(eps*dis*(np.random.random()-0.5), self.lattice.inv_matrix) wp, _ = Wyckoff_position.from_symops(ops2, h, permutation=False) split_sites.append(atom_site(wp, pos0, site.specie)) new_struc.atom_sites = split_sites new_struc.numIons = [int(multiples*numIon) for numIon in self.numIons] new_struc.lattice = lattice new_struc.source = 'subgroup' return new_struc
def test_Pmn21(self): strs = ["x, y, z", "-x+1/2, -y, z+1/2", "-x, y, z", "x+1/2, -y, z+1/2"] wyc, perm = Wyckoff_position.from_symops(strs) self.assertTrue(wyc.number == 31)
def test_P21(self): strs = ["x, y, z", "-x, y+1/2, -z"] wyc, perm = Wyckoff_position.from_symops(strs) self.assertTrue(wyc.number == 4)