def _generate_orientation(self, pyxtal_mol, pt, oris, wp): # Use a Wyckoff_site object for the current site self.numattempts += 1 #ensure that the orientation is good count = 0 while count < 100: ori = random.choice(oris).copy() ori.change_orientation(flip=True) if self._check_ori_dist(ori): #print("===good orientation", count, self._check_ori_dist(ori)) break count += 1 #print(count, self.molecules[0].axes.T[0].dot(ori.r.as_matrix().T)) #print(ori.r.as_matrix()) ms0 = mol_site(pyxtal_mol, pt, ori, wp, self.lattice, self.diag) # Check distances within the WP if ms0.check_distances(): return ms0 else: # Maximize the smallest distance for the general # positions if needed if len(pyxtal_mol.mol) > 1 and ori.degrees > 0: # bisection method def fun_dist(angle, ori, mo, pt): ori0 = ori.copy() ori.change_orientation(angle) ms0 = mol_site( mo, pt, ori, wp, self.lattice, self.diag, ) d = ms0.compute_distances() return d angle_lo = ori.angle angle_hi = angle_lo + np.pi fun_lo = fun_dist(angle_lo, ori, pyxtal_mol, pt) fun_hi = fun_dist(angle_hi, ori, pyxtal_mol, pt) fun = fun_hi for it in range(self.ori_attempts): self.numattempts += 1 if (fun > 0.8) & (ms0.check_distances()): return ms0 angle = (angle_lo + angle_hi) / 2 fun = fun_dist(angle, ori, pyxtal_mol, pt) #print('Bisection: ', it, fun) if fun_lo > fun_hi: angle_hi, fun_hi = angle, fun else: angle_lo, fun_lo = angle, fun return None
def make_mol_site(self, ref=False): if ref: mol = self.ref_mol ori = self.ori else: mol = self.molecule ori = Orientation(np.eye(3)) mol = self.add_site_props(mol) pmol = pyxtal_molecule(mol, symmetrize=False) site = mol_site(pmol, self.position, ori, self.wyc, self.lattice, self.diag) return site
def fun_dist(angle, ori, mo, pt): ori0 = ori.copy() ori.change_orientation(angle) ms0 = mol_site( mo, pt, ori, wp, self.lattice, self.diag, ) d = ms0.compute_distances() return d
def make_mol_sites(self): """ generate the molecular wyckoff sites """ ori = Orientation(np.eye(3)) sites = [] for mol, pos, wp in zip(self.p_mols, self.positions, self.wps): site = mol_site(mol, pos, ori, wp, self.lattice, self.diag) #print(pos) #print(self.lattice.matrix) #print([a.value for a in site.molecule.mol.species]) #print(site.molecule.mol.cart_coords) #print(site._get_coords_and_species(absolute=True)[0][:10]) sites.append(site) return sites
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 _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