示例#1
0
    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
示例#2
0
    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])
示例#3
0
文件: gulp.py 项目: gipfeli/PyXtal
    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
示例#4
0
    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)
示例#5
0
 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
示例#6
0
    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
示例#7
0
    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)
示例#8
0
    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
示例#9
0
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
示例#10
0
    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)]
示例#11
0
    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
示例#12
0
    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)
示例#13
0
    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
示例#14
0
    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)
示例#15
0
    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
示例#16
0
    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")
示例#17
0
    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
示例#18
0
文件: io.py 项目: gipfeli/PyXtal
    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")
示例#19
0
    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
示例#20
0
文件: test_all.py 项目: Virts/PyXtal
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)