def __init__(self, mol, position, orientation, wp, lattice=None, diag=False): # describe the molecule self.molecule = mol self.diag = diag self.wp = wp self.position = position # fractional coordinate of molecular center self.orientation = orientation #pyxtal.molecule.orientation object if isinstance(lattice, Lattice): self.lattice = lattice else: self.lattice = Lattice.from_matrix(lattice) self.PBC = self.wp.PBC self.mol = mol.mol # A Pymatgen molecule object self.site_props = mol.props self.symbols = mol.symbols #[site.specie.value for site in self.mol.sites] self.numbers = self.mol.atomic_numbers self.tols_matrix = mol.tols_matrix self.radius = mol.radius if self.diag: self.wp.diagonalize_symops() self.position = project_point(self.position, wp[0])
def translate(self, disp=np.array([0.0, 0.0, 0.0]), absolute=False): """ To translate the molecule """ disp = np.array(disp) if absolute: disp = disp.dot(self.lattice.inv_matrix) position = self.position + disp self.position = project_point(position, self.wp[0])
def WP_merge(pt, lattice, wp, tol, orientations=None): """ Given a list of fractional coordinates, merges them within a given tolerance, and checks if the merged coordinates satisfy a Wyckoff position. Used for merging general Wyckoff positions into special Wyckoff positions within the random_crystal (and its derivative) classes. Args: pt: the originl point (3-vector) lattice: a 3x3 matrix representing the unit cell wp: a `Wyckoff_position <pyxtal.symmetry.Wyckoff_position.html> object after merge tol: the cutoff distance for merging coordinates orientations: the valid orientations for a given molecule. Obtained from get_sg_orientations, which is called within molecular_crystal Returns: pt: 3-vector after merge wp: a `pyxtal.symmetry.Wyckoff_position` object, If no matching WP, returns False. valid_ori: the valid orientations after merge """ index = wp.index PBC = wp.PBC group = Group(wp.number, wp.dim) pt = project_point(pt, wp[0], lattice, PBC) coor = apply_ops(pt, wp) if orientations is None: valid_ori = None else: j, k = jk_from_i(index, orientations) valid_ori = orientations[j][k] # Main loop for merging multiple times while True: # Check distances of current WP. If too small, merge dm = distance_matrix([coor[0]], coor, lattice, PBC=PBC) passed_distance_check = True x = np.argwhere(dm < tol) for y in x: # Ignore distance from atom to itself if y[0] == 0 and y[1] == 0: pass else: passed_distance_check = False break # for molecular crystal, one more check if check_images([coor[0]], [6], lattice, PBC=PBC, tol=tol) is False: passed_distance_check = False if not passed_distance_check: mult1 = group[index].multiplicity # Find possible wp's to merge into possible = [] for i, wp0 in enumerate(group): mult2 = wp0.multiplicity # Check that a valid orientation exists if orientations is not None: j, k = jk_from_i(i, orientations) if orientations[j][k] == []: continue else: valid_ori = orientations[j][k] # factor = mult2 / mult1 if (mult2 < mult1) and (mult1 % mult2 == 0): possible.append(i) if possible == []: return None, False, valid_ori # Calculate minimum separation for each WP distances = [] for i in possible: wp = group[i] projected_point = project_point(pt, wp[0], lattice=lattice, PBC=PBC) d = distance(pt - projected_point, lattice, PBC=PBC) distances.append(np.min(d)) # Choose wp with shortest translation for generating point tmpindex = np.argmin(distances) index = possible[tmpindex] wp = group[index] pt = project_point(pt, wp[0], lattice=lattice, PBC=PBC) coor = apply_ops(pt, wp) # Distances were not too small; return True else: return pt, wp, valid_ori