def __init__(self, struc, label="_", path='tmp', ff='reax', \ opt='conp', steps=1000, exe='gulp',\ input='gulp.in', output='gulp.log', dump=None): if isinstance(struc, Atoms): self.lattice = Lattice.from_matrix(struc.cell) self.frac_coords = struc.get_scaled_positions() self.sites = struc.get_chemical_symbols() else: raise NotImplementedError("only support ASE atoms object") self.structure = struc self.label = label self.ff = ff self.opt = opt self.exe = exe self.steps = steps self.folder = path if not os.path.exists(self.folder): os.makedirs(self.folder) self.input = self.folder + '/' + self.label + input self.output = self.folder + '/' + self.label + output self.dump = dump self.iter = 0 self.energy = None self.stress = None self.forces = None self.positions = None self.optimized = False self.cputime = 0 self.error = False
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 __init__(self, struc, label="_", ff='reax', \ opt='conp', steps=1000, exe='gulp',\ input='gulp.in', output='gulp.log', dump='opt.cif'): if isinstance(struc, random_crystal): self.lattice = struc.lattice self.frac_coords, self.sites = struc._get_coords_and_species( absolute=False) elif isinstance(struc, Atoms): self.lattice = Lattice.from_matrix(struc.cell) self.frac_coords = struc.get_scaled_positions() self.sites = struc.get_chemical_symbols() self.structure = struc self.label = label self.ff = ff self.opt = opt self.exe = exe self.steps = steps self.input = self.label + input self.output = self.label + output self.dump = dump self.iter = 0 self.energy = None self.stress = None self.forces = None self.positions = None self.optimized = False self.cputime = 0 self.error = False self.clean_up = False #True
def from_1D_dicts(cls, dicts): from pyxtal.molecule import pyxtal_molecule, Orientation mol = pyxtal_molecule(mol=dicts['smile'] + '.smi') rdkit_mol = mol.rdkit_mol(mol.smile) conf = rdkit_mol.GetConformer(0) #print("try") #print(conf.GetPositions()[:3]) #print(dicts["rotor"]) if dicts['reflect']: mol.set_torsion_angles(conf, dicts["rotor"], False) # print(mol.set_torsion_angles(conf, dicts["rotor"], True)) # #import sys; sys.exit() xyz = mol.set_torsion_angles(conf, dicts["rotor"], dicts['reflect']) mol.reset_positions(xyz) g = dicts["number"] index = dicts["index"] dim = dicts["dim"] matrix = R.from_euler('zxy', dicts["orientation"], degrees=True).as_matrix() orientation = Orientation(matrix) #if dicts['reflect']: # print('load'); print(xyz[:3]) # print("aaaaaaaaaaaaaa"); print(xyz[:3].dot(orientation.matrix.T)) # print("matrix"); print(orientation.matrix) wp = Wyckoff_position.from_group_and_index(g, index, dim) diag = dicts["diag"] lattice = Lattice.from_matrix(dicts["lattice"], ltype=dicts["lattice_type"]) position = dicts[ "center"] #np.dot(dicts["center"], lattice.inv_matrix) return cls(mol, position, orientation, wp, lattice, diag)
def load_dict(self, dict0): """ load the structure from a dictionary """ self.group = Group(dict0["group"]) self.lattice = Lattice.from_matrix(dict0["lattice"], ltype=self.group.lattice_type) self.molecular = dict0["molecular"] self.number = self.group.number self.factor = dict0["factor"] self.source = dict0["source"] self.dim = dict0["dim"] self.PBC = dict0["PBC"] self.numIons = dict0["numIons"] self.numMols = dict0["numMols"] self.valid = dict0["valid"] self.formula = dict0["formula"] sites = [] if dict0["molecular"]: for site in dict0["sites"]: sites.append(mol_site.load_dict(site)) self.mol_sites = sites else: for site in dict0["sites"]: sites.append(atom_site.load_dict(site)) self.atom_sites = sites
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 from_pymatgen(self, structure): """ Load the seed structure from Pymatgen/ASE/POSCAR/CIFs """ from pymatgen.symmetry.analyzer import SpacegroupAnalyzer as sga try: # needs to do it twice in order to get the conventional cell s = sga(structure) structure = s.get_refined_structure() s = sga(structure) sym_struc = s.get_symmetrized_structure() number = s.get_space_group_number() except: print("Failed to load the Pymatgen structure") self.valid = False if self.valid: d = sym_struc.composition.as_dict() species = [key for key in d.keys()] numIons = [] for ele in species: numIons.append(int(d[ele])) self.numIons = numIons self.species = species self.group = Group(number) atom_sites = [] for i, site in enumerate(sym_struc.equivalent_sites): pos = site[0].frac_coords wp = Wyckoff_position.from_group_and_index( number, sym_struc.wyckoff_symbols[i]) specie = site[0].specie.number atom_sites.append(atom_site(wp, pos, specie)) self.atom_sites = atom_sites self.lattice = Lattice.from_matrix(sym_struc.lattice.matrix, ltype=self.group.lattice_type)
def make_supergroup(self, solutions, once=False, show_detail=True): """ create supergroup structures based on the list of solutions Args: solutions: list of tuples (splitter, mapping, disp) show_detail (bool): print out the detail Returns: list of pyxtal structures """ G_strucs = [] if len(solutions) > 0: if once: disps = np.array([sol[-1] for sol in solutions]) ID = np.argmin(disps) solutions = [solutions[ID]] for solution in solutions: (sp, mapping, disp, mae) = solution lat1 = np.dot(np.linalg.inv(sp.R[:3,:3]).T, self.struc.lattice.matrix) lattice = Lattice.from_matrix(lat1, ltype=sp.G.lattice_type) details = self.symmetrize(sp, mapping, disp) coords_G1, coords_G2, coords_H1, elements = details G_struc = self.struc.copy() G_struc.group = sp.G G_sites = [] for i, wp in enumerate(sp.wp1_lists): pos = coords_G1[i] pos -= np.floor(pos) pos1 = sym.search_matched_position(sp.G, wp, pos) if pos1 is not None: site = atom_site(wp, pos1, sp.elements[i]) G_sites.append(site) else: print(">>>>>>>>>>>>>>") print(self.struc.group.number) print(pos) print(wp) print(">>>>>>>>>>>>>>") raise RuntimeError("cannot assign the right wp") G_struc.atom_sites = G_sites G_struc.source = 'supergroup {:6.3f}'.format(mae) G_struc.lattice = lattice G_struc.numIons *= round(np.abs(np.linalg.det(sp.R[:3,:3]))) G_struc._get_formula() G_struc.disp = mae if new_structure(G_struc, G_strucs): G_strucs.append(G_struc) if show_detail: G = sp.G.number self.print_detail(G, coords_H1, coords_G2, elements, disp) print(G_struc) return G_strucs
def check_lattice(G, trans, struc, tol=1.0, a_tol=10): """ check if the lattice mismatch is big used to save some computational cost """ matrix = np.dot(trans.T, struc.lattice.get_matrix()) l1 = Lattice.from_matrix(matrix) l2 = Lattice.from_matrix(matrix, ltype=G.lattice_type) (a1,b1,c1,alpha1,beta1,gamma1)=l1.get_para(degree=True) (a2,b2,c2,alpha2,beta2,gamma2)=l2.get_para(degree=True) abc_diff = np.abs(np.array([a2-a1, b2-b1, c2-c1])).max() ang_diff = np.abs(np.array([alpha2-alpha1, beta2-beta1, gamma2-gamma1])).max() #print(l1, l2) if abc_diff > tol or ang_diff > a_tol: return False else: return True
def __init__(self, struc, ref_mols, 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_mols: a list of reference molecule (xyz file or Pyxtal molecule) tol: scale factor for covalent bond distance relax_h: whether or not relax the position for hydrogen atoms in structure """ for ref_mol in ref_mols: if isinstance(ref_mol, str): ref_mol = pyxtal_molecule(ref_mol) elif isinstance(ref_mol, pyxtal_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.ref_mols = ref_mols self.tol = tol self.diag = False self.relax_h = relax_h sym_struc, number = get_symmetrized_pmg(pmg_struc) group = Group(number) self.group = group self.wyc = group[0] self.perm = [0,1,2] molecules = search_molecules_in_crystal(sym_struc, self.tol) if self.relax_h: molecules = self.addh(molecules) self.pmg_struc = sym_struc self.lattice = Lattice.from_matrix(sym_struc.lattice.matrix, ltype=group.lattice_type) self.resort(molecules) self.numMols = [len(self.wyc)]
def make_supergroup(self, solutions, show_detail=True): """ create supergroup structures based on the list of solutions Args: solutions: list of tuples (splitter, mapping, disp) Returns: list of pyxtal structures """ G_strucs = [] for solution in solutions: (sp, mapping, disp, mae) = solution G = sp.G.number #print(disp) details = self.symmetrize(sp, mapping, disp) coords_G1, coords_G2, coords_H1, elements, mults = details G_struc = self.struc.copy() G_struc.group = sp.G G_sites = [] for i, wp in enumerate(sp.wp1_lists): site = atom_site(wp, coords_G1[i], sp.elements[i]) G_sites.append(site) G_struc.atom_sites = G_sites G_struc.source = 'supergroup {:6.3f}'.format(mae) lat1 = np.dot(sp.inv_R[:3, :3].T, self.struc.lattice.matrix) lattice = Lattice.from_matrix(lat1, ltype=sp.G.lattice_type) #lattice.reset_matrix() #make it has a regular shape #G_struc.lattice = self.struc.lattice.supergroup(sp.G.lattice_type) G_struc.lattice = lattice G_struc.numIons *= round(np.abs(np.linalg.det(sp.R[:3, :3]))) G_struc._get_formula() if new_structure(G_struc, G_strucs): G_strucs.append(G_struc) if show_detail: details = self.symmetrize(sp, mapping, disp) _, coords_G2, coords_H1, elements, mults = details self.print_detail(G, coords_H1, coords_G2, elements, mults, disp) print(G_struc) #print(sp.R) return G_strucs
def load_dict(cls, dicts): """ load the sites from a dictionary """ from pyxtal.molecule import pyxtal_molecule, Orientation g = dicts["number"] index = dicts["index"] dim = dicts["dim"] mol = pyxtal_molecule.load_dict(dicts["molecule"]) position = dicts["position"] orientation = Orientation.load_dict(dicts['orientation']) wp = Wyckoff_position.from_group_and_index(g, index, dim) diag = dicts["diag"] lattice = Lattice.from_matrix(dicts["lattice"]) return cls(mol, position, orientation, wp, lattice, diag)
def _from_pymatgen(self, struc, tol=1e-3, a_tol=5.0): """ Load structure from Pymatgen should not be used directly """ from pyxtal.util import get_symmetrized_pmg #import pymatgen.analysis.structure_matcher as sm self.valid = True try: sym_struc, number = get_symmetrized_pmg(struc, tol, a_tol) #print(sym_struc) #import sys; sys.exit() except TypeError: print("Failed to load the Pymatgen structure") # print(struc) # self.valid = False if self.valid: d = sym_struc.composition.as_dict() species = [key for key in d.keys()] numIons = [] for ele in species: numIons.append(int(d[ele])) self.numIons = numIons self.species = species self.group = Group(number) matrix, ltype = sym_struc.lattice.matrix, self.group.lattice_type self.lattice = Lattice.from_matrix(matrix, ltype=ltype) atom_sites = [] for i, site in enumerate(sym_struc.equivalent_sites): pos = site[0].frac_coords wp = Wyckoff_position.from_group_and_index(number, sym_struc.wyckoff_symbols[i]) specie = site[0].specie.number pos1 = search_matched_position(self.group, wp, pos) if pos1 is not None: atom_sites.append(atom_site(wp, pos1, specie)) else: break if len(atom_sites) != len(sym_struc.equivalent_sites): raise RuntimeError("Cannot extract the right mapping from spglib") else: self.atom_sites = atom_sites
def _from_pymatgen(self, struc, tol=1e-3): """ Load structure from Pymatgen should not be used directly """ from pymatgen.symmetry.analyzer import SpacegroupAnalyzer as sga from pyxtal.util import symmetrize #import pymatgen.analysis.structure_matcher as sm self.valid = True try: # needs to do it twice in order to get the conventional cell pmg = symmetrize(struc, tol) s = sga(pmg, symprec=tol) sym_struc = s.get_symmetrized_structure() number = s.get_space_group_number() #print(sym_struc) except: print("Failed to load the Pymatgen structure") self.valid = False if self.valid: d = sym_struc.composition.as_dict() species = [key for key in d.keys()] numIons = [] for ele in species: numIons.append(int(d[ele])) self.numIons = numIons self.species = species self.group = Group(number) atom_sites = [] for i, site in enumerate(sym_struc.equivalent_sites): pos = site[0].frac_coords wp = Wyckoff_position.from_group_and_index( number, sym_struc.wyckoff_symbols[i]) specie = site[0].specie.number atom_sites.append(atom_site(wp, pos, specie, search=True)) self.atom_sites = atom_sites matrix, ltype = sym_struc.lattice.matrix, self.group.lattice_type self.lattice = Lattice.from_matrix(matrix, ltype=ltype)
def _get_alternative(self, wyc_set, index): """ get alternative structure representations Args: tran: affine matrix index: the list of transformed wps Returns: a new pyxtal structure after transformation """ new_struc = self.copy() # xyz_string like 'x+1/4,y+1/4,z+1/4' xyz_string = wyc_set['Coset Representative'][index] op = get_inverse(SymmOp.from_xyz_string(xyz_string)) #op = SymmOp.from_xyz_string(xyz_string) ids = [] for i, site in enumerate(new_struc.atom_sites): id = len(self.group) - site.wp.index - 1 letter = wyc_set['Transformed WP'][index].split()[id] ids.append(letters.index(letter)) wp = Wyckoff_position.from_group_and_index(self.group.number, letter) pos = op.operate(site.position) pos1 = search_matched_position(self.group, wp, pos) if pos1 is not None: new_struc.atom_sites[i] = atom_site(wp, pos1, site.specie) else: print(pos) print(wp) raise RuntimeError("Cannot find the right pos") # switch lattice R = op.affine_matrix[:3,:3] #rotation matrix = np.dot(R, self.lattice.matrix) new_struc.lattice = Lattice.from_matrix(matrix, ltype=self.group.lattice_type) new_struc.source = "Alt. Wyckoff Set: " + xyz_string return new_struc, ids
def read(self): with open(self.output, 'r') as f: lines = f.readlines() try: for i, line in enumerate(lines): if self.pstress is None or self.pstress == 0: m = re.match(r'\s*Total lattice energy\s*=\s*(\S+)\s*eV', line) else: m = re.match(r'\s*Total lattice enthalpy\s*=\s*(\S+)\s*eV', line) #print(line.find('Final asymmetric unit coord'), line) if m: self.energy = float(m.group(1)) self.energy_per_atom = self.energy / len(self.frac_coords) elif line.find('Job Finished') != -1: self.optimized = True elif line.find('Total CPU time') != -1: self.cputime = float(line.split()[-1]) elif line.find('Final stress tensor components') != -1: stress = np.zeros([6]) for j in range(3): var = lines[i + j + 3].split()[1] stress[j] = float(var) var = lines[i + j + 3].split()[3] stress[j + 3] = float(var) self.stress = stress # Forces, QZ copied from https://gitlab.com/ase/ase/-/blob/master/ase/calculators/gulp.py elif line.find('Final internal derivatives') != -1: s = i + 5 forces = [] while (True): s = s + 1 if lines[s].find("------------") != -1: break g = lines[s].split()[3:6] for t in range(3 - len(g)): g.append(' ') for j in range(2): min_index = [ i + 1 for i, e in enumerate(g[j][1:]) if e == '-' ] if j == 0 and len(min_index) != 0: if len(min_index) == 1: g[2] = g[1] g[1] = g[0][min_index[0]:] g[0] = g[0][:min_index[0]] else: g[2] = g[0][min_index[1]:] g[1] = g[0][min_index[0]:min_index[1]] g[0] = g[0][:min_index[0]] break if j == 1 and len(min_index) != 0: g[2] = g[1][min_index[0]:] g[1] = g[1][:min_index[0]] G = [-float(x) * eV / Ang for x in g] forces.append(G) forces = np.array(forces) self.forces = forces elif line.find(' Cycle: ') != -1: self.iter = int(line.split()[1]) elif line.find('Final fractional coordinates of atoms') != -1: s = i + 5 positions = [] species = [] while True: s = s + 1 if lines[s].find("------------") != -1: break xyz = lines[s].split()[3:6] XYZ = [float(x) for x in xyz] positions.append(XYZ) species.append(lines[s].split()[1]) #if len(species) != len(self.sites): # print("Warning", len(species), len(self.sites)) self.frac_coords = np.array(positions) elif line.find('Final Cartesian lattice vectors') != -1: lattice_vectors = np.zeros((3, 3)) s = i + 2 for j in range(s, s + 3): temp = lines[j].split() for k in range(3): lattice_vectors[j - s][k] = float(temp[k]) self.lattice = Lattice.from_matrix(lattice_vectors) if np.isnan(self.energy): self.error = True self.energy = None print("GULP calculation is wrong, reading------") except: self.error = True self.energy = None print("GULP calculation is wrong")
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
import numpy as np from pkg_resources import resource_filename from pymatgen.core.structure import Molecule import pymatgen.analysis.structure_matcher as sm from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.core.operations import SymmOp from pyxtal import pyxtal from pyxtal.lattice import Lattice from pyxtal.symmetry import Group, Wyckoff_position, get_wyckoffs from pyxtal.wyckoff_site import WP_merge from pyxtal.XRD import Similarity from pyxtal.operations import get_inverse cif_path = resource_filename("pyxtal", "database/cifs/") l0 = Lattice.from_matrix([[4.08, 0, 0], [0, 9.13, 0], [0, 0, 5.50]]) l1 = Lattice.from_matrix([[4.08, 0, 0], [0, 9.13, 0], [0, 0, 5.50]]) l2 = Lattice.from_para(4.08, 9.13, 5.50, 90, 90, 90) l3 = Lattice.from_para(4.08, 7.13, 5.50, 90, 38, 90, ltype="monoclinic") wp1 = Wyckoff_position.from_group_and_index(36, 0) wp2 = Wyckoff_position.from_group_and_index(36, "4a") class TestGroup(unittest.TestCase): def test_list_wyckoff_combinations(self): g = Group(64) a1, _ = g.list_wyckoff_combinations([4, 2]) self.assertTrue(a1 is None) a2, _ = g.list_wyckoff_combinations([4, 8], quick=False) self.assertTrue(len(a2) == 8)