Пример #1
0
    def add_snl(self, snl, force_new=False, snlgroup_guess=None):
        try:
            self.lock_db()
            snl_id = self._get_next_snl_id()

            spstruc = snl.structure.copy()
            spstruc.remove_oxidation_states()
            sf = SpacegroupAnalyzer(spstruc, SPACEGROUP_TOLERANCE)
            sf.get_space_group_operations()
            sgnum = sf.get_space_group_number() if sf.get_space_group_number() \
                else -1
            sgsym = sf.get_space_group_symbol() if sf.get_space_group_symbol() \
                else 'unknown'
            sghall = sf.get_hall() if sf.get_hall() else 'unknown'
            sgxtal = sf.get_crystal_system() if sf.get_crystal_system() \
                else 'unknown'
            sglatt = sf.get_lattice_type() if sf.get_lattice_type(
            ) else 'unknown'
            sgpoint = sf.get_point_group_symbol()

            mpsnl = MPStructureNL.from_snl(snl, snl_id, sgnum, sgsym, sghall,
                                           sgxtal, sglatt, sgpoint)
            snlgroup, add_new, spec_group = self.add_mpsnl(
                mpsnl, force_new, snlgroup_guess)
            self.release_lock()
            return mpsnl, snlgroup.snlgroup_id, spec_group
        except:
            self.release_lock()
            traceback.print_exc()
            raise ValueError("Error while adding SNL!")
Пример #2
0
    def add_snl(self, snl, force_new=False, snlgroup_guess=None):
        try:
            self.lock_db()
            snl_id = self._get_next_snl_id()

            spstruc = snl.structure.copy()
            spstruc.remove_oxidation_states()
            sf = SpacegroupAnalyzer(spstruc, SPACEGROUP_TOLERANCE)
            sf.get_space_group_operations()
            sgnum = sf.get_space_group_number() if sf.get_space_group_number() \
                else -1
            sgsym = sf.get_space_group_symbol() if sf.get_space_group_symbol() \
                else 'unknown'
            sghall = sf.get_hall() if sf.get_hall() else 'unknown'
            sgxtal = sf.get_crystal_system() if sf.get_crystal_system() \
                else 'unknown'
            sglatt = sf.get_lattice_type() if sf.get_lattice_type() else 'unknown'
            sgpoint = sf.get_point_group_symbol()

            mpsnl = MPStructureNL.from_snl(snl, snl_id, sgnum, sgsym, sghall,
                                           sgxtal, sglatt, sgpoint)
            snlgroup, add_new, spec_group = self.add_mpsnl(mpsnl, force_new, snlgroup_guess)
            self.release_lock()
            return mpsnl, snlgroup.snlgroup_id, spec_group
        except:
            self.release_lock()
            traceback.print_exc()
            raise ValueError("Error while adding SNL!")
Пример #3
0
    def _complete_ordering(self, structure, num_remove_dict):
        self.logger.debug("Performing complete ordering...")
        all_structures = []
        symprec = 0.2
        s = SpacegroupAnalyzer(structure, symprec=symprec)
        self.logger.debug(
            "Symmetry of structure is determined to be {}.".format(
                s.get_space_group_symbol()))
        sg = s.get_space_group_operations()
        tested_sites = []
        starttime = time.time()
        self.logger.debug("Performing initial ewald sum...")
        ewaldsum = EwaldSummation(structure)
        self.logger.debug("Ewald sum took {} seconds.".format(time.time() -
                                                              starttime))
        starttime = time.time()

        allcombis = []
        for ind, num in num_remove_dict.items():
            allcombis.append(itertools.combinations(ind, num))

        count = 0
        for allindices in itertools.product(*allcombis):
            sites_to_remove = []
            indices_list = []
            for indices in allindices:
                sites_to_remove.extend([structure[i] for i in indices])
                indices_list.extend(indices)
            s_new = structure.copy()
            s_new.remove_sites(indices_list)
            energy = ewaldsum.compute_partial_energy(indices_list)
            already_tested = False
            for i, tsites in enumerate(tested_sites):
                tenergy = all_structures[i]["energy"]
                if abs((energy - tenergy) / len(s_new)) < 1e-5 and \
                        sg.are_symmetrically_equivalent(sites_to_remove,
                                                        tsites,
                                                        symm_prec=symprec):
                    already_tested = True

            if not already_tested:
                tested_sites.append(sites_to_remove)
                all_structures.append({"structure": s_new, "energy": energy})

            count += 1
            if count % 10 == 0:
                timenow = time.time()
                self.logger.debug("{} structures, {:.2f} seconds.".format(
                    count, timenow - starttime))
                self.logger.debug("Average time per combi = {} seconds".format(
                    (timenow - starttime) / count))
                self.logger.debug(
                    "{} symmetrically distinct structures found.".format(
                        len(all_structures)))

        self.logger.debug(
            "Total symmetrically distinct structures found = {}".format(
                len(all_structures)))
        all_structures = sorted(all_structures, key=lambda s: s["energy"])
        return all_structures
Пример #4
0
    def test_tensor(self):
        """Initialize Tensor"""

        lattice = Lattice.hexagonal(4, 6)
        #rprimd = np.array([[0,0.5,0.5],[0.5,0,0.5],[0.5,0.5,0]])
        #rprimd = rprimd*10
        #lattice = Lattice(rprimd)
        structure = Structure(lattice, ["Ga", "As"],
                              [[0, 0, 0], [0.5, 0.5, 0.5]])

        #finder = SymmetryFinder(structure)
        finder = SpacegroupAnalyzer(structure)

        spacegroup = finder.get_space_group_operations()
        pointgroup = finder.get_point_group_symbol()

        cartesian_tensor = [[2, 3, 1.2], [3, 4, 1.0], [1.2, 1.0, 6]]

        tensor = Tensor.from_cartesian_tensor(cartesian_tensor,
                                              lattice.reciprocal_lattice,
                                              space="g")
        red_tensor = tensor.reduced_tensor
        tensor2 = Tensor(red_tensor, lattice.reciprocal_lattice, space="g")
        assert (((np.abs(tensor2.cartesian_tensor) - np.abs(cartesian_tensor))
                 < 1E-8).all())

        self.assertTrue(tensor == tensor2)
        print(tensor)

        #print("non-symmetrized cartesian_tensor = ",tensor2.cartesian_tensor)
        tensor2.symmetrize(structure)

        #print("symmetrized_cartesian_tensor = ",tensor2.cartesian_tensor)

        self.serialize_with_pickle(tensor)
Пример #5
0
def get_all_sym_sites(ent,
                      base_struct_entry,
                      migrating_specie,
                      symprec=2.0,
                      angle_tol=10):
    """
    Return all of the symmetry equivalent sites by applying the symmetry operation of the empty structure

    Args:
        ent(ComputedStructureEntry): that contains cation
        migrating_species(string or Elment):

    Returns:
        Structure: containing all of the symmetry equivalent sites

    """
    migrating_specie_el = get_el_sp(migrating_specie)
    sa = SpacegroupAnalyzer(base_struct_entry.structure,
                            symprec=symprec,
                            angle_tolerance=angle_tol)
    # start with the base structure but empty
    host_allsites = base_struct_entry.structure.copy()
    host_allsites.remove_species(host_allsites.species)
    pos_Li = list(
        filter(
            lambda isite: isite.species_string == migrating_specie_el.name,
            ent.structure.sites,
        ))

    # energy difference per site
    inserted_energy = (ent.energy - base_struct_entry.energy) / len(pos_Li)

    for isite in pos_Li:
        host_allsites.insert(
            0,
            migrating_specie_el.name,
            np.mod(isite.frac_coords, 1),
            properties=dict(inserted_energy=inserted_energy, magmom=0),
        )
    # base_ops = sa.get_space_group_operations()
    # all_ops = generate_full_symmops(base_ops, tol=1.0)
    for op in sa.get_space_group_operations():
        logger.debug(f"{op}")
        struct_tmp = host_allsites.copy()
        struct_tmp.apply_operation(symmop=op, fractional=True)
        for isite in struct_tmp.sites:
            if isite.species_string == migrating_specie_el.name:
                logger.debug(f"{op}")
                host_allsites.insert(
                    0,
                    migrating_specie_el.name,
                    np.mod(isite.frac_coords, 1),
                    properties=dict(inserted_energy=inserted_energy, magmom=0),
                )
                host_allsites.merge_sites(
                    tol=SITE_MERGE_R,
                    mode="delete")  # keeps only remove duplicates
    return host_allsites
Пример #6
0
    def complete_ordering(self, structure, num_remove_dict):
        self.logger.debug("Performing complete ordering...")
        all_structures = []
        symprec = 0.2
        s = SpacegroupAnalyzer(structure, symprec=symprec)
        self.logger.debug("Symmetry of structure is determined to be {}."
                          .format(s.get_space_group_symbol()))
        sg = s.get_space_group_operations()
        tested_sites = []
        starttime = time.time()
        self.logger.debug("Performing initial ewald sum...")
        ewaldsum = EwaldSummation(structure)
        self.logger.debug("Ewald sum took {} seconds."
                          .format(time.time() - starttime))
        starttime = time.time()

        allcombis = []
        for ind, num in num_remove_dict.items():
            allcombis.append(itertools.combinations(ind, num))

        count = 0
        for allindices in itertools.product(*allcombis):
            sites_to_remove = []
            indices_list = []
            for indices in allindices:
                sites_to_remove.extend([structure[i] for i in indices])
                indices_list.extend(indices)
            s_new = structure.copy()
            s_new.remove_sites(indices_list)
            energy = ewaldsum.compute_partial_energy(indices_list)
            already_tested = False
            for i, tsites in enumerate(tested_sites):
                tenergy = all_structures[i]["energy"]
                if abs((energy - tenergy) / len(s_new)) < 1e-5 and \
                        sg.are_symmetrically_equivalent(sites_to_remove,
                                                        tsites,
                                                        symm_prec=symprec):
                    already_tested = True

            if not already_tested:
                tested_sites.append(sites_to_remove)
                all_structures.append({"structure": s_new, "energy": energy})

            count += 1
            if count % 10 == 0:
                timenow = time.time()
                self.logger.debug("{} structures, {:.2f} seconds."
                                  .format(count, timenow - starttime))
                self.logger.debug("Average time per combi = {} seconds"
                                  .format((timenow - starttime) / count))
                self.logger.debug("{} symmetrically distinct structures found."
                                  .format(len(all_structures)))

        self.logger.debug("Total symmetrically distinct structures found = {}"
                          .format(len(all_structures)))
        all_structures = sorted(all_structures, key=lambda s: s["energy"])
        return all_structures
Пример #7
0
def get_sym_migration_ion_sites(
    base_struct: Structure,
    inserted_struct: Structure,
    migrating_ion: str,
    symprec: float = 0.01,
    angle_tol: float = 5.0,
) -> Structure:
    """
    Take one inserted entry then map out all symmetry equivalent copies of the cation sites in base entry.
    Each site is decorated with the insertion energy calculated from the base and inserted entries.

    Args:
        inserted_entry: entry that contains cation
        base_struct_entry: the entry containing the base structure
        migrating_ion_entry: the name of the migrating species
        symprec: the symprec tolerance for the space group analysis
        angle_tol: the angle tolerance for the space group analysis

    Returns:
        Structure with only the migrating ion sites decorated with insertion energies.
    """
    wi_ = migrating_ion

    sa = SpacegroupAnalyzer(base_struct,
                            symprec=symprec,
                            angle_tolerance=angle_tol)
    # start with the base structure but empty
    sym_migration_ion_sites = list(
        filter(
            lambda isite: isite.species_string == wi_,
            inserted_struct.sites,
        ))

    sym_migration_struct = Structure.from_sites(sym_migration_ion_sites)
    for op in sa.get_space_group_operations():
        struct_tmp = sym_migration_struct.copy()
        struct_tmp.apply_operation(symmop=op, fractional=True)
        for isite in struct_tmp.sites:
            if isite.species_string == wi_:
                sym_migration_struct.insert(
                    0,
                    wi_,
                    coords=np.mod(isite.frac_coords, 1.0),
                    properties=isite.properties,
                )

            # must clean up as you go or the number of sites explodes
            if len(sym_migration_struct) > 1:
                sym_migration_struct.merge_sites(
                    tol=SITE_MERGE_R,
                    mode="average")  # keeps removing duplicates
    return sym_migration_struct
Пример #8
0
def get_all_sym_sites(ent,
                      base_struct_entry,
                      migrating_specie,
                      stol=1.0,
                      atol=10):
    """
    Return all of the symmetry equivalent sites by applying the symmetry operation of the empty structure

    Args:
        ent(ComputedStructureEntry): that contains cation
        migrating_species(string or Elment):

    Returns:
        Structure: containing all of the symmetry equivalent sites

    """
    migrating_specie_el = get_el_sp(migrating_specie)
    sa = SpacegroupAnalyzer(base_struct_entry.structure,
                            symprec=stol,
                            angle_tolerance=atol)
    # start with the base structure but empty
    host_allsites = base_struct_entry.structure.copy()
    host_allsites.remove_species(host_allsites.species)
    pos_Li = list(
        filter(lambda isite: isite.species_string == migrating_specie_el.name,
               ent.structure.sites))
    for isite in pos_Li:
        host_allsites.insert(0,
                             migrating_specie_el.name,
                             np.mod(isite.frac_coords, 1),
                             properties={'inserted_energy': ent.energy})
    # base_ops = sa.get_space_group_operations()
    # all_ops = generate_full_symmops(base_ops, tol=1.0)
    for op in sa.get_space_group_operations():
        logger.debug(f'{op}')
        struct_tmp = host_allsites.copy()
        struct_tmp.apply_operation(symmop=op, fractional=True)
        for isite in struct_tmp.sites:
            if isite.species_string == migrating_specie_el.name:
                logger.debug(f'{op}')
                host_allsites.insert(
                    0,
                    migrating_specie_el.name,
                    np.mod(isite.frac_coords, 1),
                    properties={'inserted_energy': ent.energy})
                host_allsites.merge_sites(
                    mode='average'
                )  # keeps only one position but average the properties

    return host_allsites
Пример #9
0
    def analyze_symmetry(self, tol):
        s = Structure.from_sites(self.framework)
        site_to_vindex = {}
        for i, v in enumerate(self.vnodes):
            s.append("Li", v.frac_coords)
            site_to_vindex[s[-1]] = i

        print(len(s))
        finder = SpacegroupAnalyzer(s, tol)
        print(finder.get_space_group_operations())
        symm_structure = finder.get_symmetrized_structure()
        print(len(symm_structure.equivalent_sites))
        return [[site_to_vindex[site]
                 for site in sites]
                for sites in symm_structure.equivalent_sites
                if sites[0].specie.symbol == "Li"]
    def get_all_sym_sites(self, ent):
        """
        Return all of the symmetry equivalent sites by applying the symmetry operation of the empty structure

        Args:
          ent(ComputedStructureEntry):  that contains cation

        Returns:
          Structure: containing all of the symmetry equivalent sites

        """

        sa = SpacegroupAnalyzer(
            self.base_struct_entry.structure, symprec=0.3, angle_tolerance=10)
        host_allsites = self.base_struct_entry.structure.copy()
        host_allsites.remove_species(host_allsites.species)
        pos_Li = list(
            filter(lambda isite: isite.species_string == 'Li',
                   ent.structure.sites))

        for isite in pos_Li:
            host_allsites.insert(
                0,
                'Li',
                isite.frac_coords,
                properties={'inserted_energy': ent.energy})

        for op in sa.get_space_group_operations():
            struct_tmp = host_allsites.copy()
            struct_tmp.apply_operation(symmop=op, fractional=True)
            for isite in struct_tmp.sites:
                if isite.species_string == "Li":
                    host_allsites.insert(
                        0,
                        'Li',
                        isite.frac_coords,
                        properties={'inserted_energy': ent.energy})

        host_allsites.merge_sites(
            mode='average'
        )  # keeps only one position but average the properties

        return host_allsites
Пример #11
0
def symmetry_order_2d(structure, point_group_list):
    '''
    Find symmetry order in a two dimensional structure

    Args:
        structure: pymatgen structure
        point_group_list
    Return:
        Number of symmetry operations, and
        site symmetry order of the input structure
    '''
    s = SpacegroupAnalyzer(structure, symprec=0.1)
    point_group_symbols = s.get_symmetry_dataset()['site_symmetry_symbols']
    point_group_order = 0
    for point_group_symbol in point_group_symbols :
        point_group_order += point_group_list[point_group_symbol.replace('.', '')]
    # return s.get_space_group_symbol(), s.get_space_group_number(),
    return len(s.get_space_group_operations()), point_group_order/len(point_group_symbols)


             
Пример #12
0
    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")
Пример #13
0
    def enumerate_ads_config(self,
                             adsorbate,
                             loading,
                             site='all',
                             symmetry_tol=0.01,
                             custom_sites_only=False,
                             csite_name='bcc_octa',
                             save_voronoi=False):

        vor, mesh = self.Voronoi_tessalate()
        vcoords = vor.vertices
        base_coords = [a.position for a in self.bulk_ase]
        repeat_unit_cell = self.bulk_ase.get_cell().T
        inv_ruc = np.linalg.inv(repeat_unit_cell)
        custom_sites = np.dot(repeat_unit_cell,
                              self.custom_sites.positions.T).T

        corrected_vcoords = []
        used = []
        for coord in vcoords:
            if np.linalg.norm(coord) < 1e3:
                fcoord = np.dot(inv_ruc, coord)
                zero_threshold_indices = abs(fcoord) < 1e-6
                fcoord[zero_threshold_indices] = 0.0
                one_threshold_indices = abs(fcoord - 1.0) < 1e-6
                fcoord[one_threshold_indices] = 0.0

                advance = True
                for u in used:
                    if np.allclose(fcoord, u):
                        advance = False
                        break

                for dim in fcoord:
                    if dim > 1.0 or dim < 0.0:
                        advance = False

                if advance:
                    corrected_vcoords.append(np.dot(repeat_unit_cell, fcoord))
                    used.append(fcoord)

        if save_voronoi:
            with open('voronoi_sites.xyz', 'w') as out:
                out.write(str(len(base_coords) + len(corrected_vcoords)))
                out.write('\n')
                out.write('check')
                out.write('\n')
                for vec in base_coords:
                    out.write('{:4} {:>10} {:>10} {:>10}'.format(*['C'] +
                                                                 list(vec)))
                    out.write('\n')
                for vec in corrected_vcoords:
                    out.write('{:4} {:>10} {:>10} {:>10}'.format(*['H'] +
                                                                 list(vec)))
                    out.write('\n')

        vtypes = []
        if not custom_sites_only:
            for vert in corrected_vcoords:
                used = []
                spectrum = []
                for atom_coord in base_coords:
                    fvert = np.dot(inv_ruc, vert)
                    fatom_coord = np.dot(inv_ruc, atom_coord)
                    fdist_vec, sym = PBC3DF_sym(fvert, fatom_coord)
                    dist = np.linalg.norm(np.dot(repeat_unit_cell, fdist_vec))
                    sym_atom_coord = np.dot(repeat_unit_cell,
                                            fatom_coord - sym)
                    spectrum.append((dist, sym_atom_coord))

                spectrum.sort(key=lambda x: x[0])
                min_dist = spectrum[0][0]
                spectrum = [
                    s[1] for s in spectrum if s[0] - min_dist < symmetry_tol
                ] + [vert]

                spectrum_atoms = Atoms()
                for s in spectrum:
                    spectrum_atoms.append(Atom('H', s))

                spectrum_atoms.set_cell(repeat_unit_cell.T)
                spectrum_struct = AseAtomsAdaptor.get_structure(spectrum_atoms)
                sga = SpacegroupAnalyzer(spectrum_struct, symprec=symmetry_tol)
                symmetry_order = len(sga.get_space_group_operations())
                symmetry_type = 'PG' + str(symmetry_order)

                vtypes.append((symmetry_type, vert))

        if len(custom_sites) > 0:
            for custom_site in custom_sites:
                vtypes.append((csite_name, custom_site))

        if site in ('all', 'single_site'):
            max_loading = float(len(vtypes))
        else:
            max_loading = float(len([ty for ty in vtypes if ty[0] == site]))

        all_combinations = [s for s in itertools.combinations(vtypes, loading)]
        max_sgn = 0

        if site == 'single_site':
            site_dict = dict((k, []) for k in set(ty[0] for ty in vtypes))
            for entry in vtypes:
                ty, coord = entry
                site_dict[ty].append(coord)
        elif site == 'all':
            site_dict = {'all': []}
            for entry in vtypes:
                site_dict['all'].append(entry)
        else:
            site_dict = {site: []}
            for entry in vtypes:
                ty, coord = entry
                if ty == site:
                    site_dict[ty].append(coord)

        ads_dict = {}

        fingerprints = dict((len(s), dict((k, []) for k in range(1, 231)))
                            for s in all_combinations)
        sg_counts = dict((k, 0) for k in range(1, 231))

        for subset in all_combinations:

            types = [s[0] for s in subset]
            type_string = ''
            for ty in set(types):
                type_string += ty + '_'

            if len(set(types)) > 1 and site == 'single_site':
                continue
            elif site not in ('all', 'single_site'):
                if len(set(types)) > 1:
                    continue
                else:
                    if list(set(types))[0] != site:
                        continue

            ld = len(subset)
            fp_dict = fingerprints[ld]

            fractional_loading = ld / max_loading
            adsorbate_combinations = itertools.product(adsorbate, repeat=ld)

            for ads_comb in adsorbate_combinations:

                adsonly_positions = []
                bulk_coords = [(sl.symbol, sl.position)
                               for sl in self.bulk_ase]

                for s, a in zip(subset, ads_comb):

                    ads, shift_ind = a
                    ty, site_coord = s

                    elems = []
                    positions = []
                    for atom in ads:
                        elems.append(atom.symbol)
                        positions.append(atom.position)
                    elems = np.asarray(elems)
                    positions = np.asarray(positions)
                    positions -= positions[shift_ind]
                    positions += site_coord

                    for e, c in zip(elems, positions):
                        bulk_coords.append((e, c))
                        adsonly_positions.append(c)

                advance, index, sgs, sgn, dists, atoms, formula = redundancy_check(
                    bulk_coords, adsonly_positions, fp_dict, repeat_unit_cell,
                    symmetry_tol)

                if sgn > max_sgn:
                    max_sgn = sgn
                elif sgn < max_sgn:
                    continue

                if advance:
                    sg_counts[sgn] += 1
                    fp_dict[sgn].append(dists)

                ads_dict[formula + '_' + str(int(fractional_loading * 1000)) +
                         '_' + type_string + str(sgn) + '_' + index] = atoms

        self.adsorbate_configuration_dict = ads_dict
Пример #14
0
def find_unique_structures(base_crystal_path, directory):
    """
    DESCRIPTION: Given a base crystal structure along with a directory containing CIF structures, determine how many unique configurations are in the directory,
                 and assign a configuration ID number to each CIF structure in the directory. Results can be seen in the outputted unique.csv file. The function
                 uses the base crystal structure provided to find the space group and associated symmetry operations, which are then used to see whether the CIF
                 structures in the directory are equivalent. 
                 
    PARAMETERS:
        base_crystal_path: string
            The path to the base crystal CIF structure. Note that this structure should contain the space group that you want to check all other structures to.
            If the symmetry of this structure is incorrect, you will get unexpected results. 
        directory: string
            The path to the directory containing the CIF structures to compare. Note that this function assumes that the CIF names are all prefixed by a dash
            and followed by an interger, ie "LiCoO2-1", "LiCoO2-2", "LiCoO2-3"..., ect.
    RETURNS: None
    """
    # convert all files (they should all be cif files) in the directory into Structures and place them into a list
    cif_file_names = [
        os.path.splitext(os.path.basename(path))[0]
        for path in os.listdir(directory) if path.endswith('.cif')
    ]
    cif_files = [
        directory + path for path in os.listdir(directory)
        if path.endswith('.cif')
    ]
    cif_files = sorted(cif_files,
                       key=lambda x: int(x.split("-")[-1].replace(".cif", "")))
    cif_file_names = sorted(cif_file_names,
                            key=lambda x: int(x.split("-")[-1]))
    structures = [IStructure.from_file(path) for path in cif_files]
    print(cif_file_names)
    # Get the base crystal structure
    base_crystal = IStructure.from_file(base_crystal_path)

    # Get the space group of the base crystal
    analyzer = SpacegroupAnalyzer(base_crystal)
    spacegroup = analyzer.get_space_group_operations()
    print(spacegroup)

    id = 1
    num_structures = len(structures)
    config_dict = {}
    unique_structs = []
    for i in range(num_structures):
        config1 = structures[i]
        unique_cif_name = cif_file_names[i]
        if not unique_cif_name in config_dict:
            config_dict[unique_cif_name] = id
            print("%s Unique Structures Found..." % (id))
            unique_structs.append(config1)
            id += 1
            for j in range(num_structures):
                cif_name = cif_file_names[j]
                config2 = structures[j]
                if not cif_name in config_dict:
                    isEquivalent = spacegroup.are_symmetrically_equivalent(
                        config1, config2)
                    if isEquivalent:
                        config_dict[cif_name] = config_dict[unique_cif_name]
    print("----------------------------------------------")
    print("%s Total Unique Structures Found" % (id - 1))
    print("----------------------------------------------")
    with open('unique.csv', 'w') as f:
        for key in config_dict.keys():
            f.write("%s,%s\n" % (key, config_dict[key]))
    crystal_name = os.path.splitext(os.path.basename(base_crystal_path))[0]
    directory_path = "{}-unique".format(crystal_name)
    if not os.path.isdir(directory_path):
        os.mkdir(directory_path)

    num_unique_structures = len(unique_structs)
    for i in range(num_unique_structures):
        config = unique_structs[i]
        #Save to CIF
        filename = crystal_name + '_ewald' + '_{}'.format(i + 1)
        w = CifWriter(config)
        w.write_file(directory_path + '/' + filename + '.cif')
        # print("Cif file saved to {}.cif".format(filename))

        #Save to POSCAR
        poscar = Poscar(config)
        poscar.write_file(directory_path + '/' + filename)
Пример #15
0
def get_interstitial_sites(structure, octahedra=False, unique=False):
    """
    Use a Delaunay triangulation of all atomic sites in the crystal
    structure to define tetrahedra of open volumes (interstitial
    sites). Each interstitial site is ranked according to the maximum
    radius of an atom that could fit in that site without overlapping
    one of the existing neighboring atoms' radii.

    The default behavior is to stop there, but by setting `octahedra`
    to True, the tetrahedra which share faces are combined to form
    bipyramids (hexahedra) and then points are added to these
    bipyramids to formoctahedra, in order to identify the largest 5-
    and 6-fold coordinated sites as well. This takes a little longer
    since it requires combining tetrahedra.

    Args:
        structure (Structure): Pymatgen Structure object
        octahedra (Boolean): Whether or not to search also for
            octahedral interstitial sites.
        unique (Boolean): Whether or not to enforce that only
            symmetrically inequivalent sites are returned.
            Determining the symmetry-equivalence is usually
            by far the slowest task in the algorithm.
    Returns:
        interstitials (dict): dictionary of the form
            {"tetrahedral": [(coordinates, max_radius), ...],
             "hexahedral": [(coordinates, max_radius), ...],
             "octahedral": [(coordinates, max_radius), ...]}
            storing lists of each interstitial site for both
            coordination types, sorted by largest radius first.
            Coordinates are given as cartesian.
    """

    # Preserve the original structure
    st = structure.copy()

    # Small unit cells make the triangulation unreliable
    n_sites = structure.num_sites
    if n_sites < 4:
        st.make_supercell(3)
    m_0 = st.lattice._matrix

    # Make a 3x3x3 supercell so that the center unit cell
    # is surrounded by its images- i.e. it has no "boundaries",
    # which can erroneously create tetrahedra of infinite volumes.
    st.make_supercell(3)
    m = st.lattice._matrix

    # These are the vertices of only the center cell
    cell_vertices = np.array([
        np.add(np.add(m[0] / 3., m[1] / 3.), m[2] / 3.),
        np.add(np.add(m[0] / 1.5, m[1] / 3.), m[2] / 3.),
        np.add(np.add(m[0] / 3., m[1] / 1.5), m[2] / 3.),
        np.add(np.add(m[0] / 1.5, m[1] / 1.5), m[2] / 3.),
        np.add(np.add(m[0] / 3., m[1] / 3.), m[2] / 1.5),
        np.add(np.add(m[0] / 1.5, m[1] / 3.), m[2] / 1.5),
        np.add(np.add(m[0] / 3., m[1] / 1.5), m[2] / 1.5),
        np.add(np.add(m[0] / 1.5, m[1] / 1.5), m[2] / 1.5)
    ])
    cell_center = np.mean(cell_vertices, axis=0)
    other_cell_centers = []
    for i in range(-1, 2):
        for j in range(-1, 2):
            for k in range(-1, 2):
                c = np.add(cell_center, np.multiply(i, m_0[0]))
                c = np.add(c, np.multiply(j, m_0[1]))
                c = np.add(c, np.multiply(k, m_0[2]))
                other_cell_centers.append(c)

    max_distance_in_cell = sq_dist(cell_vertices[0], cell_center)

    points = [s.coords for s in st.sites]
    radii = [float(s.specie.atomic_radius) for s in st.sites]

    # Create the initial Delaunay triangulation of all sites in the
    # supercell.
    delaunay = Delaunay(points)
    all_simplices = delaunay.simplices.copy()

    # Now filter those Delaunay simplices to only those with
    # at least one vertex lying within the center unit cell.
    simplices = []
    center_cell = ConvexHull(cell_vertices)
    if not octahedra:
        for simplex in all_simplices:
            for vertex in simplex:
                if sq_dist(cell_center, points[vertex]) <= max_distance_in_cell\
                        and sq_dist(cell_center, points[vertex]) ==\
                        min([sq_dist(points[vertex], pt) for pt in
                             other_cell_centers]):
                    simplices.append(simplex)
                    break
    else:
        for simplex in all_simplices:
            n = 0
            for vertex in simplex:
                if sq_dist(cell_center, points[vertex]) <= max_distance_in_cell\
                        and sq_dist(cell_center, points[vertex]) ==\
                        min([sq_dist(points[vertex], pt) for pt in
                             other_cell_centers]):
                    n += 1
            if n == 4:
                simplices.append(simplex)

    # Calculate the maximum interstitial
    # radius for all the relevant tetrahedra.
    tetrahedra = []
    for simplex in simplices:
        a = points[simplex[0]]
        r_a = radii[simplex[0]]
        b = points[simplex[1]]
        r_b = radii[simplex[1]]
        c = points[simplex[2]]
        r_c = radii[simplex[2]]
        d = points[simplex[3]]
        r_d = radii[simplex[3]]
        centroid = np.mean([a, b, c, d], axis=0)

        # Add the atomic radii to the nuclei loactions to find
        # their "true" extrema, then use these to find the
        # "true" centroid.
        move = 1
        while move > 0.01:
            true_a = pt_btwn(a, centroid, r_a)
            true_b = pt_btwn(b, centroid, r_b)
            true_c = pt_btwn(c, centroid, r_c)
            true_d = pt_btwn(d, centroid, r_d)
            true_centroid = np.mean([true_a, true_b, true_c, true_d], axis=0)
            move = sq_dist(true_centroid, centroid)
            centroid = true_centroid

        max_radius = sqrt(
            min([
                sq_dist(true_centroid, pt)
                for pt in [true_a, true_b, true_c, true_d]
            ]))

        tetrahedra.append((true_centroid, [tuple(x) for x in [a, b, c, d]],
                           [r_a, r_b, r_c, r_d], 4, max_radius))

    interstitials = {"tetrahedral": []}
    if octahedra:
        tet_pts = [i[1] for i in tetrahedra]
        tet_pts = list(set([coords for pt in tet_pts for coords in pt]))
        interstitials.update({"hexahedral": [], "octahedral": []})
        for i in range(len(tetrahedra)):
            for j in range(i, len(tetrahedra)):
                # If 3 vertices are shared then the tetrahedra
                # share a face and form a bipyramid.
                shared = list(set(tetrahedra[i][1]) & set(tetrahedra[j][1]))
                if len(shared) == 3:
                    # Vertices of the bipyramid
                    a = tetrahedra[i][1][0]
                    r_a = tetrahedra[i][2][0]
                    b = tetrahedra[i][1][1]
                    r_b = tetrahedra[i][2][1]
                    c = tetrahedra[i][1][2]
                    r_c = tetrahedra[i][2][2]
                    d = tetrahedra[i][1][3]
                    r_d = tetrahedra[i][2][3]
                    # Fifth point to define trigonal bipyramid
                    e, r_e = [(s, tetrahedra[j][2][k])
                              for k, s in enumerate(tetrahedra[j][1])
                              if s not in tetrahedra[i][1]][0]

                    h_centroid = np.mean([a, b, c, d, e], axis=0)
                    move = 1
                    while move > 0.01:
                        true_a = pt_btwn(a, h_centroid, r_a)
                        true_b = pt_btwn(b, h_centroid, r_b)
                        true_c = pt_btwn(c, h_centroid, r_c)
                        true_d = pt_btwn(d, h_centroid, r_d)
                        true_e = pt_btwn(e, h_centroid, r_e)

                        true_h_centroid = np.mean(
                            [true_a, true_b, true_c, true_d, true_e], axis=0)
                        move = sq_dist(true_h_centroid, h_centroid)
                        h_centroid = true_h_centroid

                    r_h = sqrt(
                        min([
                            sq_dist(true_h_centroid, pt)
                            for pt in [true_a, true_b, true_c, true_d, true_e]
                        ]))

                    # Add the bipyramid to the final list
                    # of interstitials.
                    interstitials["hexahedral"].append(
                        (tuple(h_centroid), r_h))

                    # Enlarge the bipyramid by one point to create
                    # octahedra.
                    v1 = np.subtract(shared[0], shared[1])
                    v2 = np.subtract(shared[0], shared[2])
                    tol = max([
                        sq_dist(shared[0], shared[1]),
                        sq_dist(shared[0], shared[2]),
                        sq_dist(shared[1], shared[2])
                    ]) * 1.1
                    for index, f in enumerate(tet_pts):
                        v3 = np.subtract(shared[0], f)
                        distances = [sq_dist(f, p) for p in shared]
                        distances.sort()
                        if 0 < distances[0] < tol and 0 < distances[1] < tol\
                                and np.dot(v3, (np.cross(v1, v2))) == 0:
                            r_f = radii[index]
                            o_centroid = np.mean([a, b, c, d, e, f], axis=0)

                            move = 1
                            while move > 0.01:
                                true_a = pt_btwn(a, o_centroid, r_a)
                                true_b = pt_btwn(b, o_centroid, r_b)
                                true_c = pt_btwn(c, o_centroid, r_c)
                                true_d = pt_btwn(d, o_centroid, r_d)
                                true_e = pt_btwn(e, o_centroid, r_e)
                                true_f = pt_btwn(f, o_centroid, r_f)

                                true_o_centroid = np.mean([
                                    true_a, true_b, true_c, true_d, true_e,
                                    true_f
                                ],
                                                          axis=0)
                                move = sq_dist(true_o_centroid, o_centroid)
                                o_centroid = true_o_centroid

                            r_o = sqrt(
                                min([
                                    sq_dist(true_o_centroid, pt) for pt in [
                                        true_a, true_b, true_c, true_d, true_e,
                                        true_f
                                    ]
                                ]))

                            # Add the octahedron to the final
                            # list of interstitials.
                            interstitials["octahedral"].append(
                                (tuple(o_centroid), r_o))
        interstitials["hexahedral"] = list(set(interstitials["hexahedral"]))
        interstitials["octahedral"] = list(set(interstitials["octahedral"]))

    interstitials["tetrahedral"] = [(i[0], i[4]) for i in tetrahedra]

    # Since the centroid coordinates were given in the center
    # cell of the supercell, bring them back into the original
    # unit cell.
    if n_sites < 4:
        f = 1. / 3.
    else:
        f = 1.
    for c in interstitials:
        for i in range(len(interstitials[c])):
            for r in m_0:
                interstitials[c][i] = (np.multiply(
                    np.subtract(np.array(interstitials[c][i][0]), r),
                    f), interstitials[c][i][1])

    # Sort by the maximum radii
    for c in interstitials:
        interstitials[c].sort(key=operator.itemgetter(1))
        interstitials[c].reverse()

    if unique:
        sga = SpacegroupAnalyzer(structure)
        sop = sga.get_space_group_operations()
        l = structure.lattice
        for c in interstitials:
            remove = []
            for i in range(len(interstitials[c])):
                if i not in remove:
                    site_i = PeriodicSite("C", interstitials[c][i][0], l)
                    for j in range(i + 1, len(interstitials[c])):
                        if interstitials[c][i][1] == interstitials[c][j][1] and\
                                sop.are_symmetrically_equivalent(
                                    [site_i],
                                    [PeriodicSite("C",interstitials[c][j][0],l)]
                                ):
                            remove.append(j)
            interstitials[c] = [
                interstitials[c][x] for x in range(len(interstitials[c]))
                if x not in remove
            ]

    return interstitials
Пример #16
0
    def __init__(
        self,
        uuid,
        structure,  # pristine unit cell
        sc_size="1 1 1",
        signicant_figures=4,
        muon_threshold=1e-2,
        if_pristine=False,
        if_with_distortions=True
        #         reduced_sites = False,
        #         reduced_distance = 10.0
    ):
        """
        
        Parameters
        -----------
        uuid : int
            The PK for relax structure              
        structure : Structure object
            Pymatgen Structure Object                   
        sc_size : str
            Supercell size in a, b and c direction eg "3 3 3"
        signicant_figures : int
            Round positios to significant figures. Default=4
        muon_threshold : float
            Absolute threshold for checking distance.      
        if_pristine : bool
            If True, to generate symmetry pristine structure for muon equivalent sites. 
            Default=False
        if_with_distortions : bool
            If True, to generate structure with distortions for each muon equivalent sites.
            Default=True
            
        Returns
        -------
        """
        #
        """
#         reduced_sites: Bool
#             If True, (planning for something). 
#             Default=False
#         reduced_distance : float
#             planning for something        
        """
        #

        self.relax_uuid = uuid
        self.structure = structure
        self.sc_size = sc_size
        self.signicant_figures = signicant_figures
        self.muon_threshold = muon_threshold
        self.if_pristine = if_pristine
        self.if_with_distortions = if_with_distortions

        # This parameters for future
        #         self.reduced_sites = reduced_sites
        #         self.reduced_distance = reduced_distance

        if sc_size is None or sc_size == "":
            self.sc_size = "1 1 1"
        else:
            self.sc_size = sc_size

        self.pristine_structure_supercell = self.structure.copy()
        self.pristine_structure_supercell.make_supercell(
            [int(x) for x in self.sc_size.split()])

        # Check if pritine structure is compatible to supercell size
        if len(self.pristine_structure_supercell) != self.n_atoms - 1:
            raise SiteDistortions('Invalid inputs Structure or supercell size')

        SA = SpacegroupAnalyzer(self.pristine_structure_supercell,
                                symprec=self.muon_threshold)
        self._SA = SA

        self._SG = SA.get_space_group_operations()
        self._PG = SA.get_point_group_operations()
        self._SGO = SpacegroupOperations(
            SA.get_space_group_number(), SA.get_space_group_symbol(),
            SA.get_symmetry_operations(cartesian=False))
Пример #17
0
 def get_num_sym_ops(ent):
     sga = SpacegroupAnalyzer(ent.structure)
     return len(sga.get_space_group_operations())