Exemple #1
0
    def _get_coord_no_sites_chrg(self, site):
        """
        Compute the coordination number and coordination charge

        Args:
            site:
                pymatgen.core.sites.Site
        """
        struct = self._structure.copy()
        struct.append(site.species_string, site.frac_coords)
        coord_finder = VoronoiCoordFinder(struct)
        coord_no = coord_finder.get_coordination_number(-1)
        coord_sites = coord_finder.get_coordinated_sites(-1)

        # In some cases coordination sites to interstitials include
        # interstitials also.
        sites_to_be_deleted = []
        for i in range(len(coord_sites)):
            if coord_sites[i].species_string == 'X':
                sites_to_be_deleted.append(i)
        sites_to_be_deleted.reverse()  # So index won't change in between
        for ind in sites_to_be_deleted:
            del coord_sites[ind]

        coord_chrg = 0
        for site, weight in coord_finder.get_voronoi_polyhedra(-1).items():
            if not site.species_string == 'X':
                coord_chrg += weight * self._valence_dict[site.species_string]

        return coord_no, coord_sites, coord_chrg
Exemple #2
0
    def get_atom_ofms(self, struct, symm=False):
        """
        Calls get_single_ofm for every site in struct. If symm=True,
        get_single_ofm is called for symmetrically distinct sites, and
        counts is constructed such that ofms[i] occurs counts[i] times
        in the structure

        Args:
            struct (Structure): structure for find ofms for
            symm (bool): whether to calculate ofm for only symmetrically
                    distinct sites

        Returns:
            ofms ([32 X 32 matrix] X len(struct)): ofms for struct
            if symm:
                ofms ([32 X 32 matrix] X number of symmetrically distinct sites):
                    ofms for struct
                counts: number of identical sites for each ofm
        """
        ofms = []
        vcf = VCF(struct, allow_pathological=True)
        if symm:
            symm_struct = SpacegroupAnalyzer(struct).get_symmetrized_structure()
            indices = [lst[0] for lst in symm_struct.equivalent_indices]
            counts = [len(lst) for lst in symm_struct.equivalent_indices]
        else:
            indices = [i for i in range(len(struct.sites))]
        for index in indices:
            ofms.append(self.get_single_ofm(struct.sites[index], \
                                            vcf.get_voronoi_polyhedra(index)))
        if symm:
            return ofms, counts
        return ofms
Exemple #3
0
    def _get_coord_no_sites_chrg(self, site):
        """
        Compute the coordination number and coordination charge

        Args:
            site:
                pymatgen.core.sites.Site
        """
        struct = self._structure.copy()
        struct.append(site.species_string, site.frac_coords)
        coord_finder = VoronoiCoordFinder(struct)
        coord_no = coord_finder.get_coordination_number(-1)
        coord_sites = coord_finder.get_coordinated_sites(-1)

        # In some cases coordination sites to interstitials include 
        # interstitials also. 
        sites_to_be_deleted = []
        for i in range(len(coord_sites)):
            if coord_sites[i].species_string == 'X':
                sites_to_be_deleted.append(i)
        sites_to_be_deleted.reverse()  # So index won't change in between
        for ind in sites_to_be_deleted:
            del coord_sites[ind]

        coord_chrg = 0
        for site, weight in coord_finder.get_voronoi_polyhedra(-1).items():
            if not site.species_string == 'X':
                coord_chrg += weight * self._valence_dict[site.species_string]

        return coord_no, coord_sites, coord_chrg
    def _get_coord_no_sites_chrg(self, site):
        """
        Compute the coordination number and coordination charge

        Args:
            site:
                pymatgen.core.sites.Site
        """
        struct = self._structure.copy()
        struct.append(site.specie.symbol, site.frac_coords)
        coord_finder = VoronoiCoordFinder(struct)
        coord_no = coord_finder.get_coordination_number(-1)
        coord_sites = coord_finder.get_coordinated_sites(-1)

        # In some cases coordination sites to interstitials include
        # interstitials also. Filtering them.
        def no_inter(site):
            return not site.specie.symbol == 'X'

        coord_sites = filter(no_inter, coord_sites)

        coord_chrg = 0
        for site, weight in coord_finder.get_voronoi_polyhedra(-1).items():
            if not site.specie.symbol == 'X':
                coord_chrg += weight * self._valence_dict[site.species_string]

        return coord_no, coord_sites, coord_chrg
    def _get_coord_no_sites_chrg(self, site):
        """
        Compute the coordination number and coordination charge

        Args:
            site:
                pymatgen.core.sites.Site
        """
        struct = self._structure.copy()
        struct.append(site.specie.symbol, site.frac_coords)
        coord_finder = VoronoiCoordFinder(struct)
        coord_no = coord_finder.get_coordination_number(-1)
        coord_sites = coord_finder.get_coordinated_sites(-1)

        # In some cases coordination sites to interstitials include
        # interstitials also. Filtering them.
        def no_inter(site):
            return not site.specie.symbol == 'X'
        coord_sites = filter(no_inter, coord_sites)

        coord_chrg = 0
        for site, weight in coord_finder.get_voronoi_polyhedra(-1).items():
            if not site.specie.symbol == 'X':
                coord_chrg += weight * self._valence_dict[site.species_string]

        return coord_no, coord_sites, coord_chrg
Exemple #6
0
    def from_bulk_and_miller(cls, structure, miller_index, min_slab_size=5.0,
                             min_vacuum_size=10.0, max_normal_search=None, 
                             center_slab = True, selective_dynamics=False,
                             undercoord_threshold = 0.09):
        """
        This method constructs the adsorbate site finder from a bulk 
        structure and a miller index, which allows the surface sites 
        to be determined from the difference in bulk and slab coordination, 
        as opposed to the height threshold.
        
        Args:
            structure (Structure): structure from which slab
                input to the ASF is constructed
            miller_index (3-tuple or list): miller index to be used
            min_slab_size (float): min slab size for slab generation
            min_vacuum_size (float): min vacuum size for slab generation
            max_normal_search (int): max normal search for slab generation
            center_slab (bool): whether to center slab in slab generation
            selective dynamics (bool): whether to assign surface sites
                to selective dynamics
            undercoord_threshold (float): threshold of "undercoordation"
                to use for the assignment of surface sites.  Default is
                0.1, for which surface sites will be designated if they
                are 10% less coordinated than their bulk counterpart
        """
        # TODO: for some reason this works poorly with primitive cells
        vcf_bulk = VoronoiCoordFinder(structure)
        bulk_coords = [len(vcf_bulk.get_coordinated_sites(n))
                       for n in range(len(structure))]
        struct = structure.copy(site_properties = {'bulk_coordinations':bulk_coords})
        slabs = generate_all_slabs(struct, max_index=max(miller_index), 
                                   min_slab_size=min_slab_size,
                                   min_vacuum_size=min_vacuum_size,
                                   max_normal_search = max_normal_search,
                                   center_slab = center_slab)

        slab_dict = {slab.miller_index:slab for slab in slabs}
        
        if miller_index not in slab_dict:
            raise ValueError("Miller index not in slab dict")

        this_slab = slab_dict[miller_index]

        vcf_surface = VoronoiCoordFinder(this_slab, allow_pathological=True)
        surf_props = []
        this_mi_vec = get_mi_vec(this_slab.miller_index)
        mi_mags = [np.dot(this_mi_vec, site.coords) for site in this_slab]
        average_mi_mag = np.average(mi_mags)
        for n, site in enumerate(this_slab):
            bulk_coord = this_slab.site_properties['bulk_coordinations'][n]
            slab_coord = len(vcf_surface.get_coordinated_sites(n))
            mi_mag = np.dot(this_mi_vec, site.coords)
            undercoord = (bulk_coord - slab_coord)/bulk_coord
            if undercoord > undercoord_threshold and mi_mag > average_mi_mag:
                surf_props += ['surface']
            else:
                surf_props += ['subsurface']
        new_site_properties = {'surface_properties':surf_props}
        new_slab = this_slab.copy(site_properties=new_site_properties)
        return cls(new_slab, selective_dynamics)
    def _get_ionic_radii(self):
        """
        Computes ionic radii of elements for all sites in the structure.
        If valence is zero, atomic radius is used.
        """
        radii = []
        coord_finder = VoronoiCoordFinder(self._structure)

        def nearest_key(sorted_vals, key):
            i = bisect_left(sorted_vals, key)
            if i == len(sorted_vals):
                i = -1
            return sorted_vals[i]

        for i in range(len(self._structure.sites)):
            site = self._structure.sites[i]
            el = site.specie.symbol
            oxi_state = int(round(site.specie.oxi_state))
            coord_no = int(round(coord_finder.get_coordination_number(i)))
            try:
                tab_oxi_states = map(int, _ion_radii[el].keys())
                tab_oxi_states.sort()
                oxi_state = nearest_key(tab_oxi_states, oxi_state)
                radius = _ion_radii[el][str(oxi_state)][str(coord_no)]
            except KeyError:
                if coord_finder.get_coordination_number(i)-coord_no > 0:
                    new_coord_no = coord_no + 1
                else:
                    new_coord_no = coord_no - 1
                try:
                    radius = _ion_radii[el][str(oxi_state)][str(new_coord_no)]
                    coord_no = new_coord_no
                except:
                    tab_coords = map(int, _ion_radii[el][str(oxi_state)].keys())
                    tab_coords.sort()
                    new_coord_no = nearest_key(tab_coords, coord_no)
                    i = 0
                    for val in tab_coords:
                        if  val > coord_no:
                            break
                        i = i + 1
                    if i == len(tab_coords):
                        key = str(tab_coords[-1])
                        radius = _ion_radii[el][str(oxi_state)][key]
                    elif i == 0:
                        key = str(tab_coords[0])
                        radius = _ion_radii[el][str(oxi_state)][key]
                    else:
                        key = str(tab_coords[i-1])
                        radius1 = _ion_radii[el][str(oxi_state)][key]
                        key = str(tab_coords[i])
                        radius2 = _ion_radii[el][str(oxi_state)][key]
                        radius = (radius1+radius2)/2

            #implement complex checks later
            radii.append(radius)
        return radii
Exemple #8
0
 def compute(self, structure, n):
     params = self._params
     vor = VoronoiCoordFinder(structure, **params)
     vorp = vor.get_voronoi_polyhedra(n)
     cdict = {}
     for i in vorp:
         if i.species_string not in cdict:
             cdict[i.species_string] = vorp[i]
         else:
             cdict[i.species_string] += vorp[i]
     return cdict
Exemple #9
0
 def get_voronoi(self, poscars, diff_sp):
     """
     Known bug, solid_angle_tol = 0.5 works for Mn2O4 spinels. Needs to be checked for others.
     solid_angle_tol is the tolerance on the solid angle, bigger the angle- closer the atoms.
     """
     solid_angle_tol = 0.5
     sites = []
     n = int(diff_sp.values()[0][0])-1
     for i in range(0, len(poscars)):
         voronoi = VoronoiCoordFinder(poscars[i].structure)
         sites.append(voronoi.get_coordinated_sites(n, solid_angle_tol))
     return sites
Exemple #10
0
    def get_cn(self, poscars, diff_sp):
        """
        A function to compute the coordinations numbers not rounded

        Output: cn
        """
        n = int(diff_sp.values()[0][0])-1
        cn=[]
        for i in range(0,len(poscars)):
            coord_no = VoronoiCoordFinder(poscars[i].structure)
            cn.append(coord_no.get_coordination_number(n))
        return cn
Exemple #11
0
 def _coord_find(self):
     """
     calls VoronoiCoordFinder to compute the coordination number,
     coordination charge
     """
     for i in range(self.defect_count()):
         struct = self._structs[i].copy()
         coord_finder = VoronoiCoordFinder(struct)
         self._coord_no.append(coord_finder.get_coordination_number(-1))
         self._coord_sites.append(coord_finder.get_coordinated_sites(-1))
         coord_chrg = 0
         for site, weight in coord_finder.get_voronoi_polyhedra(-1).items():
             coord_chrg += weight * self._valence_dict[site.species_string]
         self._coord_charge_no.append(coord_chrg)
Exemple #12
0
class VoronoiCoordFinderTest(PymatgenTest):
    def setUp(self):
        s = self.get_structure('LiFePO4')
        self.finder = VoronoiCoordFinder(s, [Element("O")])

    def test_get_voronoi_polyhedra(self):
        self.assertEqual(len(self.finder.get_voronoi_polyhedra(0).items()), 8)

    def test_get_coordination_number(self):
        self.assertAlmostEqual(self.finder.get_coordination_number(0),
                               5.809265748999465, 7)

    def test_get_coordinated_sites(self):
        self.assertEqual(len(self.finder.get_coordinated_sites(0)), 8)
 def _coord_find(self):
     """
     calls VoronoiCoordFinder to compute the coordination number,
     coordination charge
     """
     for i in range(self.defect_count()):
         struct = self._structs[i].copy()
         coord_finder = VoronoiCoordFinder(struct)
         self._coord_no.append(coord_finder.get_coordination_number(-1))
         self._coord_sites.append(coord_finder.get_coordinated_sites(-1))
         coord_chrg = 0
         for site, weight in coord_finder.get_voronoi_polyhedra(-1).items():
             coord_chrg += weight * self._valence_dict[site.species_string]
         self._coord_charge_no.append(coord_chrg)
class VoronoiCoordFinderTest(PymatgenTest):
    def setUp(self):
        s = self.get_structure('LiFePO4')
        self.finder = VoronoiCoordFinder(s, [Element("O")])

    def test_get_voronoi_polyhedra(self):
        self.assertEqual(len(self.finder.get_voronoi_polyhedra(0).items()), 8)

    def test_get_coordination_number(self):
        self.assertAlmostEqual(self.finder.get_coordination_number(0),
                               5.809265748999465, 7)

    def test_get_coordinated_sites(self):
        self.assertEqual(len(self.finder.get_coordinated_sites(0)), 8)
Exemple #15
0
    def __init__(self, structure, valences, radii):
        """
        Args:
            structure:
                pymatgen.core.structure.Structure
            valences:
                valences of elements as a dictionary 
            radii:
                Radii of elements as a dictionary
        """

        self._structure = structure
        self._valence_dict = valences
        self._rad_dict = radii
        # Store symmetrically distinct sites, their coordination numbers
        # coordinated_sites, effective charge
        symm_finder = SymmetryFinder(self._structure)
        symm_structure = symm_finder.get_symmetrized_structure()
        equiv_site_seq = symm_structure.equivalent_sites

        self._defect_sites = []
        for equiv_sites in equiv_site_seq:
            self._defect_sites.append(equiv_sites[0])

        self._vac_site_indices = []
        for site in self._defect_sites:
            for i in range(len(self._structure.sites)):
                if site == self._structure[i]:
                    self._vac_site_indices.append(i)

        coord_finder = VoronoiCoordFinder(self._structure)
        self._defectsite_coord_no = []
        self._defect_coord_sites = []
        for i in self._vac_site_indices:
            self._defectsite_coord_no.append(
                coord_finder.get_coordination_number(i)
            )
            self._defect_coord_sites.append(
                coord_finder.get_coordinated_sites(i)
            )

        # Store the ionic radii for the elements in the structure
        # (Used to  computing the surface are and volume)
        # Computed based on valence of each element

        self._vac_eff_charges = None
        self._vol = None
        self._sa = None
class VoronoiCoordFinderTest(unittest.TestCase):

    def setUp(self):
        filepath = os.path.join(test_dir, 'vasprun.xml')
        reader = Vasprun(filepath)
        s = reader.final_structure
        self.finder = VoronoiCoordFinder(s,[Element("O")])
    
    def test_get_voronoi_polyhedra(self):
        self.assertEqual(len(self.finder.get_voronoi_polyhedra(0).items()),10, "Incorrect number of results returned for get_voronoi_polyhedra")
        
    def test_get_coordination_number(self):
        print self.finder.get_coordination_number(0)
        self.assertAlmostEqual(self.finder.get_coordination_number(0), 5.60588600732, 7, "Incorrect coordination number returned!")                

    def test_get_coordinated_sites(self):
        self.assertEqual(len(self.finder.get_coordinated_sites(0)), 10)
Exemple #17
0
    def __init__(self, structure, valences, radii):
        """
        Args:
            structure:
                pymatgen.core.structure.Structure
            valences:
                valences of elements as a dictionary 
            radii:
                Radii of elements as a dictionary
        """

        self._structure = structure
        self._valence_dict = valences
        self._rad_dict = radii
        # Store symmetrically distinct sites, their coordination numbers
        # coordinated_sites, effective charge
        symm_finder = SymmetryFinder(self._structure)
        symm_structure = symm_finder.get_symmetrized_structure()
        equiv_site_seq = symm_structure.equivalent_sites

        self._defect_sites = []
        for equiv_sites in equiv_site_seq:
            self._defect_sites.append(equiv_sites[0])

        self._vac_site_indices = []
        for site in self._defect_sites:
            for i in range(len(self._structure.sites)):
                if site == self._structure[i]:
                    self._vac_site_indices.append(i)

        coord_finder = VoronoiCoordFinder(self._structure)
        self._defectsite_coord_no = []
        self._defect_coord_sites = []
        for i in self._vac_site_indices:
            self._defectsite_coord_no.append(
                coord_finder.get_coordination_number(i))
            self._defect_coord_sites.append(
                coord_finder.get_coordinated_sites(i))

        # Store the ionic radii for the elements in the structure
        # (Used to  computing the surface are and volume)
        # Computed based on valence of each element

        self._vac_eff_charges = None
        self._vol = None
        self._sa = None
def substructures_from_structure(structure, weight_cutoff=1e-2):
    """
    Helper method to calculate substructural components from a
    pymatgen structure object.

    Args:
        structure (Structure): Input structure
        weight_cutoff (float): minimum solid angle weight for
                               inclusion in a substructure

    Returns:
            A list of Substructure objects for the given structure
    """

    vcf = VoronoiCoordFinder(structure)
    substructures = []

    for i, site in enumerate(structure):

        charge = sum([getattr(specie, "oxi_state", 0) * amt
                      for specie, amt in site.species_and_occu.items()])

        central_subspecies = SubStructureSpecie(site.specie.symbol,
                                         oxidation_state=charge,
                                         properties=site.properties,
                                         weight=1.0)

        peripheral_subspecies = []
        for peripheral_site, weight in vcf.get_voronoi_polyhedra(i).iteritems():

            if weight > weight_cutoff:
                charge = sum([getattr(specie, "oxi_state", 0) * amt
                              for specie, amt in
                              peripheral_site.species_and_occu.items()])

                peripheral_subspecies.append(
                    SubStructureSpecie(peripheral_site.specie.symbol,
                                       oxidation_state=charge,
                                       properties=peripheral_site.properties,
                                       weight=weight))

        substructures.append(SubStructure(peripheral_subspecies, central_sub_species=central_subspecies))

    return substructures
class VoronoiCoordFinderTest(unittest.TestCase):
    def setUp(self):
        filepath = os.path.join(test_dir, 'LiFePO4.cif')
        parser = CifParser(filepath)
        s = parser.get_structures()[0]
        self.finder = VoronoiCoordFinder(s, [Element("O")])

    def test_get_voronoi_polyhedra(self):
        self.assertEqual(
            len(self.finder.get_voronoi_polyhedra(0).items()), 8,
            "Incorrect number of results returned for " +
            "get_voronoi_polyhedra")

    def test_get_coordination_number(self):
        self.assertAlmostEqual(self.finder.get_coordination_number(0),
                               5.809265748999465, 7)

    def test_get_coordinated_sites(self):
        self.assertEqual(len(self.finder.get_coordinated_sites(0)), 8)
class VoronoiCoordFinderTest(unittest.TestCase):

    def setUp(self):
        filepath = os.path.join(test_dir, 'LiFePO4.cif')
        parser = CifParser(filepath)
        s = parser.get_structures()[0]
        self.finder = VoronoiCoordFinder(s, [Element("O")])

    def test_get_voronoi_polyhedra(self):
        self.assertEqual(len(self.finder.get_voronoi_polyhedra(0).items()), 8,
                         "Incorrect number of results returned for " +
                         "get_voronoi_polyhedra")

    def test_get_coordination_number(self):
        self.assertAlmostEqual(self.finder.get_coordination_number(0),
                               5.809265748999465, 7)

    def test_get_coordinated_sites(self):
        self.assertEqual(len(self.finder.get_coordinated_sites(0)), 8)
Exemple #21
0
    def get_cns(self):
        siteno = self.n
        try:
            vor = VoronoiCoordFinder(
                self._structure).get_voronoi_polyhedra(siteno)
            weights = VoronoiCoordFinder(
                self._structure).get_voronoi_polyhedra(siteno).values()
        except RuntimeError as e:
            print e

        coordination = {}
        max_weight = max(weights)
        for v in vor:
            el = v.species_string
            if vor[v] > 0.50 * max_weight:
                if el in coordination:
                    coordination[el] += 1
                else:
                    coordination[el] = 1
        return coordination
Exemple #22
0
def get_coordination_numbers(d):
    """
    Helper method to get the coordination number of all sites in the final
    structure from a run.

    Args:
        d:
            Run dict generated by VaspToDbTaskDrone.

    Returns:
        Coordination numbers as a list of dict of [{"site": site_dict,
        "coordination": number}, ...].
    """
    structure = Structure.from_dict(d["output"]["crystal"])
    f = VoronoiCoordFinder(structure)
    cn = []
    for i, s in enumerate(structure.sites):
        try:
            n = f.get_coordination_number(i)
            number = int(round(n))
            cn.append({"site": s.to_dict, "coordination": number})
        except Exception:
            logger.error("Unable to parse coordination errors")
    return cn
Exemple #23
0
def get_coordination_numbers(d):
    """
    Helper method to get the coordination number of all sites in the final
    structure from a run.

    Args:
        d:
            Run dict generated by VaspToDbTaskDrone.

    Returns:
        Coordination numbers as a list of dict of [{"site": site_dict,
        "coordination": number}, ...].
    """
    structure = Structure.from_dict(d["output"]["crystal"])
    f = VoronoiCoordFinder(structure)
    cn = []
    for i, s in enumerate(structure.sites):
        try:
            n = f.get_coordination_number(i)
            number = int(round(n))
            cn.append({"site": s.as_dict(), "coordination": number})
        except Exception:
            logger.error("Unable to parse coordination errors")
    return cn
    def get_bondlengths(self, structure):
        """
        Get minimum bond lengths in a structure by bond type.

        :param structure: pymatgen structure object
        :return: (dict) with bond types as keys in the format 'A-B', and values as namedtuples that contain the
        minimum bond distance and number of bonds
        """
        bondlengths = defaultdict(list)
        min_bondlengths = {}
        minbond_details = namedtuple('minbond_details', 'min_dist no_of_bonds')
        for site_idx, site in enumerate(structure.sites):
            site_element_str = ''.join(re.findall(r'[A-Za-z]', site.species_string))
            try:
                voronoi_sites = VoronoiCoordFinder(structure).get_coordinated_sites(site_idx, tol=0.5)
            except RuntimeError as r:
                print 'Error for site {} in {}: {}'.format(site, structure.composition, r)
                continue
            except ValueError as v:
                print 'Error for site {} in {}: {}'.format(site, structure.composition, v)
                continue
            for vsite in voronoi_sites:
                # s_dist = np.linalg.norm(vsite.coords - site.coords)
                # s_dist = site.distance(vsite)
                if site.distance(vsite) != 0:
                    s_dist = site.distance(vsite)
                else:
                    s_dist = np.linalg.norm(vsite.coords - site.coords)
                # print np.linalg.norm(vsite.coords - site.coords)
                # TODO: avoid "continue" statements if possible-it is dead simply to write this code without "continue"
                if s_dist < 0.1:
                    continue
                vsite_element_str = ''.join(re.findall(r'[A-Za-z]', vsite.species_string))
                bond = '-'.join(sorted([site_element_str, vsite_element_str]))
                bondlengths[bond].append(s_dist)
        for bondtype in bondlengths:
            min_length = bondlengths[bondtype][0]
            for length in bondlengths[bondtype]:
                if length < min_length:
                    min_length = length
            min_bondlengths[bondtype] = minbond_details(min_dist=min_length, no_of_bonds=len(bondlengths[bondtype]))
        return min_bondlengths
 def get_cns(self):
     species = []
     species_coord_dictlst = defaultdict(list)
     struct_dict = self._structure.as_dict()
     no_of_sites = len(struct_dict['sites'])
     for specie in struct_dict['sites']:
         species.append(specie['label'])
     for siteno in range(no_of_sites):
         coordination = 0
         try:
             weights = VoronoiCoordFinder(
                 self._structure).get_voronoi_polyhedra(siteno).values()
         except RuntimeError as e:
             print e
             continue
         max_weight = max(weights)
         for weight in weights:
             if weight > 0.50 * max_weight:
                 coordination += 1
         species_coord_dictlst[species[siteno]].append(coordination)
     return species_coord_dictlst
Exemple #26
0
    def get_surface_sites(self, tag=False):
        """
        Returns the surface sites and their site indices in a dictionary. The oriented unit
        cell of the slab will determine the coordination number of a typical site. We use
        VoronoiCoordFinder to determine the coordination number of bulk sites and slab
        sites. Due to the pathological error resulting from some surface sites in the
        VoronoiCoordFinder, we assume any site that has this error is a surface site as well.
        This will work for elemental systems only for now. Useful for analysis involving
        broken bonds and for finding adsorption sites.

            Args:
                tag (bool): Option to adds site attribute "is_surfsite" (bool) to
                    all sites of slab. Defaults to False

            Returns:
                A dictionary grouping sites on top and bottom of the slab together.
                    {"top": [sites with indices], "bottom": [sites with indices}

        TODO:
            Is there a way to determine site equivalence between sites in a slab and sites
            in a bulk system? This would allow us get the coordination number of a specific
            site for multi-elemental systems or systems with more than one unequivalent sites
            This will allow us to use this for compound systems.
        """

        from pymatgen.analysis.structure_analyzer import VoronoiCoordFinder

        # Get a dictionary of coordination numbers for each distinct site in the structure
        a = SpacegroupAnalyzer(self.oriented_unit_cell)
        ucell = a.get_symmetrized_structure()
        cn_dict = {}
        v = VoronoiCoordFinder(ucell)
        unique_indices = [equ[0] for equ in ucell.equivalent_indices]

        for i in unique_indices:
            el = ucell[i].species_string
            if el not in cn_dict.keys():
                cn_dict[el] = []
            # Since this will get the cn as a result of the weighted polyhedra, the
            # slightest difference in cn will indicate a different environment for a
            # species, eg. bond distance of each neighbor or neighbor species. The
            # decimal place to get some cn to be equal.
            cn = v.get_coordination_number(i)
            cn = float('%.5f' %(round(cn, 5)))
            if cn not in cn_dict[el]:
                cn_dict[el].append(cn)

        v = VoronoiCoordFinder(self)

        surf_sites_dict, properties = {"top": [], "bottom": []}, []
        for i, site in enumerate(self):
            # Determine if site is closer to the top or bottom of the slab
            top = True if site.frac_coords[2] > self.center_of_mass[2] else False

            try:
                # A site is a surface site, if its environment does
                # not fit the environment of other sites
                cn = float('%.5f' %(round(v.get_coordination_number(i), 5)))
                if cn < min(cn_dict[site.species_string]):
                    properties.append(True)
                    key = "top" if top else "bottom"
                    surf_sites_dict[key].append([site, i])
                else:
                    properties.append(False)
            except RuntimeError:
                # or if pathological error is returned, indicating a surface site
                properties.append(True)
                key = "top" if top else "bottom"
                surf_sites_dict[key].append([site, i])

        if tag:
            self.add_site_property("is_surf_site", properties)
        return surf_sites_dict
Exemple #27
0
    def run_task(self, fw_spec):

        MyDB.db_access().connect()
        collection = MyDB.db_access().collection(fw_spec['collection'])

        with open('vasp.out', 'r') as output:
            content = output.readlines()
        examine = content[-2].strip()
        if examine[0:3] == '1 F':
            chgcar = Chgcar.from_file('CHGCAR')
            s1 = self['struct_s1']
            s2 = self['struct_s2']
            relax_sites = []
            for site_i, site in enumerate(s1.sites):
                if site.specie == fw_spec['moving_cation']:
                    relax_sites.append(site_i)

            pf = NEBPathfinder(s1,
                               s2,
                               relax_sites=relax_sites,
                               v=ChgcarPotential(chgcar).get_v(),
                               n_images=8)
            images = pf.images

            doc = collection.find_one({
                'mp-id': fw_spec["mp-id"],
                'pair_index': fw_spec["pair_index"]
            })
            path = []
            coordination_number = []
            for image in images:
                struct = Structure.from_dict(doc["gamma_structure"])
                site_index = image.species.index(fw_spec['moving_cation'])
                cation_site = image[site_index]
                struct.insert(0,
                              cation_site.specie,
                              cation_site.frac_coords,
                              properties=cation_site.properties)
                path.append(image[site_index].as_dict())
                voro_cn = VoronoiCoordFinder(struct)
                cn = voro_cn.get_coordination_number(0)
                coordination_number.append(cn)

            collection.update(
                {
                    "mp-id": fw_spec["mp-id"],
                    "pair_index": fw_spec["pair_index"]
                }, {
                    "$set": {
                        "status": "success",
                        "CN_path": coordination_number,
                        "path": path
                    }
                })
        else:
            collection.update(
                {
                    "mp-id": fw_spec["mp-id"],
                    "pair_index": fw_spec["pair_index"]
                }, {"$set": {
                    "status": "error"
                }})

        MyDB.db_access().close()

        return FWAction()
 def setUp(self):
     s = self.get_structure('LiFePO4')
     self.finder = VoronoiCoordFinder(s, [Element("O")])
    def _get_ionic_radii(self):
        """
        Computes ionic radii of elements for all sites in the structure.
        If valence is zero, atomic radius is used.
        """
        radii = []
        coord_finder = VoronoiCoordFinder(self._structure)

        def nearest_key(sorted_vals, key):
            i = bisect_left(sorted_vals, key)
            if i == len(sorted_vals):
                return sorted_vals[-1]
            if i == 0:
                return sorted_vals[0]
            before = sorted_vals[i - 1]
            after = sorted_vals[i]
            if after - key < key - before:
                return after
            else:
                return before

        for i in range(len(self._structure.sites)):
            site = self._structure.sites[i]
            if isinstance(site.specie, Element):
                radius = site.specie.atomic_radius
                radii.append(radius)
                continue

            el = site.specie.symbol
            oxi_state = int(round(site.specie.oxi_state))
            coord_no = int(round(coord_finder.get_coordination_number(i)))
            try:
                tab_oxi_states = map(int, _ion_radii[el].keys())
                tab_oxi_states.sort()
                oxi_state = nearest_key(tab_oxi_states, oxi_state)
                radius = _ion_radii[el][str(oxi_state)][str(coord_no)]
            except KeyError:
                if coord_finder.get_coordination_number(i) - coord_no > 0:
                    new_coord_no = coord_no + 1
                else:
                    new_coord_no = coord_no - 1
                try:
                    radius = _ion_radii[el][str(oxi_state)][str(new_coord_no)]
                    coord_no = new_coord_no
                except:
                    tab_coords = map(int,
                                     _ion_radii[el][str(oxi_state)].keys())
                    tab_coords.sort()
                    new_coord_no = nearest_key(tab_coords, coord_no)
                    i = 0
                    for val in tab_coords:
                        if val > coord_no:
                            break
                        i = i + 1
                    if i == len(tab_coords):
                        key = str(tab_coords[-1])
                        radius = _ion_radii[el][str(oxi_state)][key]
                    elif i == 0:
                        key = str(tab_coords[0])
                        radius = _ion_radii[el][str(oxi_state)][key]
                    else:
                        key = str(tab_coords[i - 1])
                        radius1 = _ion_radii[el][str(oxi_state)][key]
                        key = str(tab_coords[i])
                        radius2 = _ion_radii[el][str(oxi_state)][key]
                        radius = (radius1 + radius2) / 2

            #implement complex checks later
            radii.append(radius)
        return radii
    return True


s1 = Structure.from_file('POSCAR.orig')
s2 = Structure.from_file('CONTCAR.relax2')
spacegroup1 = SpacegroupAnalyzer(s1)
spacegroup2 = SpacegroupAnalyzer(s2)
print('space group symbol of structure1:',
      spacegroup1.get_space_group_symbol())
print('space group number of structure1:',
      spacegroup1.get_space_group_number())
print('space group symbol of structure2:',
      spacegroup2.get_space_group_symbol())
print('space group number of structure2:',
      spacegroup2.get_space_group_number())
Voronoi = VoronoiCoordFinder(s2, target=None)
site = s2.num_sites
#print("s2[0]:",s2.sites)
print("s2_cart_coords[0]", s2.cart_coords[0])
#print("s2_distance_(0,1)",s2.get_distance(0,1))
polyhedra = Voronoi.get_voronoi_polyhedra(1)
coordinate = Voronoi.get_coordination_number(1)
coordinate_sites = Voronoi.get_coordinated_sites(1)
Voronoi_Analyzer = VoronoiAnalyzer()
anay = Voronoi_Analyzer.analyze(s1, n=0)
strucs = [s1, s2]
anays = Voronoi_Analyzer.analyze_structures(strucs)
print("Voronoi_Analyzer.analyze(s1,n=0):", anay)
#plt = Voronoi_Analyzer.plot_vor_analysis(anays)
relax = RelaxationAnalyzer(s1, s2)
volume_change = relax.get_percentage_volume_change()
Exemple #31
0
    def from_bulk_and_miller(cls,
                             structure,
                             miller_index,
                             min_slab_size=8.0,
                             min_vacuum_size=10.0,
                             max_normal_search=None,
                             center_slab=True,
                             selective_dynamics=False,
                             undercoord_threshold=0.09):
        """
        This method constructs the adsorbate site finder from a bulk 
        structure and a miller index, which allows the surface sites 
        to be determined from the difference in bulk and slab coordination, 
        as opposed to the height threshold.
        
        Args:
            structure (Structure): structure from which slab
                input to the ASF is constructed
            miller_index (3-tuple or list): miller index to be used
            min_slab_size (float): min slab size for slab generation
            min_vacuum_size (float): min vacuum size for slab generation
            max_normal_search (int): max normal search for slab generation
            center_slab (bool): whether to center slab in slab generation
            selective dynamics (bool): whether to assign surface sites
                to selective dynamics
            undercoord_threshold (float): threshold of "undercoordation"
                to use for the assignment of surface sites.  Default is
                0.1, for which surface sites will be designated if they
                are 10% less coordinated than their bulk counterpart
        """
        # TODO: for some reason this works poorly with primitive cells
        #       may want to switch the coordination algorithm eventually
        vcf_bulk = VoronoiCoordFinder(structure)
        bulk_coords = [
            len(vcf_bulk.get_coordinated_sites(n, tol=0.05))
            for n in range(len(structure))
        ]
        struct = structure.copy(
            site_properties={'bulk_coordinations': bulk_coords})
        slabs = generate_all_slabs(struct,
                                   max_index=max(miller_index),
                                   min_slab_size=min_slab_size,
                                   min_vacuum_size=min_vacuum_size,
                                   max_normal_search=max_normal_search,
                                   center_slab=center_slab)

        slab_dict = {slab.miller_index: slab for slab in slabs}

        if miller_index not in slab_dict:
            raise ValueError("Miller index not in slab dict")

        this_slab = slab_dict[miller_index]

        vcf_surface = VoronoiCoordFinder(this_slab, allow_pathological=True)

        surf_props, undercoords = [], []
        this_mi_vec = get_mi_vec(this_slab)
        mi_mags = [np.dot(this_mi_vec, site.coords) for site in this_slab]
        average_mi_mag = np.average(mi_mags)
        for n, site in enumerate(this_slab):
            bulk_coord = this_slab.site_properties['bulk_coordinations'][n]
            slab_coord = len(vcf_surface.get_coordinated_sites(n, tol=0.05))
            mi_mag = np.dot(this_mi_vec, site.coords)
            undercoord = (bulk_coord - slab_coord) / bulk_coord
            undercoords += [undercoord]
            if undercoord > undercoord_threshold and mi_mag > average_mi_mag:
                surf_props += ['surface']
            else:
                surf_props += ['subsurface']
        new_site_properties = {
            'surface_properties': surf_props,
            'undercoords': undercoords
        }
        new_slab = this_slab.copy(site_properties=new_site_properties)
        return cls(new_slab, selective_dynamics)
 def setUp(self):
     filepath = os.path.join(test_dir, 'LiFePO4.cif')
     parser = CifParser(filepath)
     s = parser.get_structures()[0]
     self.finder = VoronoiCoordFinder(s, [Element("O")])
Exemple #33
0
    def featurize(self, struct, idx):
        """
        Get crystal fingerprint of site with given index in input
        structure.
        Args:
            struct (Structure): Pymatgen Structure object.
            idx (int): index of target site in structure.
        Returns:
            list of weighted order parameters of target site.
        """

        cn_fingerprint_array = defaultdict(
            list
        )  # dict where key = CN, val is array that contains each OP for that CN
        total_weight = math.pi / 4  # 1/4 unit circle area

        target = None
        if self.cation_anion:
            target = []
            m_oxi = struct[idx].specie.oxi_state
            for site in struct:
                if site.specie.oxi_state * m_oxi <= 0:  # opposite charge
                    target.append(site.specie)
            if not target:
                raise ValueError(
                    "No valid targets for site within cation_anion constraint!"
                )

        vcf = VoronoiCoordFinder(struct,
                                 cutoff=self.cutoff_radius,
                                 target=target)
        n_w = vcf.get_voronoi_polyhedra(idx)

        dist_sorted = (sorted(n_w.values(), reverse=True))

        if self.override_cn1:
            cn1 = 1
            for d in dist_sorted[1:]:
                cn1 = cn1 * (dist_sorted[0]**2 - d**2) / dist_sorted[0]**2
            cn_fingerprint_array[1] = [round(cn1, 6)]
            dist_sorted[0] = dist_sorted[1]

        dist_norm = [d / dist_sorted[0] for d in dist_sorted if d > 0]

        dist_bins = []  # bin numerical tolerances (~error bar of measurement)
        for d in dist_norm:
            if not dist_bins or (d > self.tol and dist_bins[-1] /
                                 (1 + self.tol) > d):
                dist_bins.append(d)

        for dist_idx, dist in enumerate(dist_bins):
            neigh_sites = [
                n for n, w in n_w.items()
                if w > 0 and w / dist_sorted[0] >= dist / (1 + self.tol)
            ]
            cn = len(neigh_sites)
            if cn in self.ops:
                for opidx, op in enumerate(self.ops[cn]):
                    if self.optypes[cn][opidx] == "wt":
                        opval = 1
                    else:
                        opval = \
                        op.get_order_parameters([struct[idx]] + neigh_sites, 0,
                                                indices_neighs=[i for i in
                                                                range(1, len(
                                                                    neigh_sites) + 1)])[
                            0]

                    opval = opval or 0  # handles None

                    if self.optypes[cn][
                            opidx] == 'bcc':  # TODO: remove after pymatgen update
                        opval = opval / 0.976

                    # figure out the weight for this opval based on semicircle integration method
                    x1 = 1 - dist
                    x2 = 1 if dist_idx == len(dist_bins) - 1 else \
                        1 - dist_bins[dist_idx + 1]
                    weight = self._semicircle_integral(x2) - \
                             self._semicircle_integral(x1)

                    opval = opval * weight / total_weight

                    cn_fingerprint_array[cn].append(opval)

        # convert dict to list
        cn_fingerprint = []
        for cn in sorted(self.optypes):
            for op_idx, _ in enumerate(self.optypes[cn]):
                try:
                    cn_fingerprint.append(cn_fingerprint_array[cn][op_idx])
                except IndexError:  # no OP value computed
                    cn_fingerprint.append(0)

        return cn_fingerprint
 def setUp(self):
     filepath = os.path.join(test_dir, 'vasprun.xml')
     reader = Vasprun(filepath)
     s = reader.final_structure
     self.finder = VoronoiCoordFinder(s,[Element("O")])
    def calculate_layers_angles_and_stuff(self, G, dim):
        #obtain information based on the bonding network. Dont mess with this function unless you really have to.

        layer_anions = {}  #all the anions present in the layer
        valence = {}  #valency of the magnetic metal atoms in the layer
        bonded_anions = {}  #anions bonded directly to the magnetic metal atom
        bonded_coordination = {}  #number of anions bonded
        bonded_rank = {}
        pymatgen_coordination = {
        }  #coordination number {fraction} obtained from pymatgen
        #voronoi_anions = {} #anions bonded to the magnetic metal atom according to voronoi polyhedra
        #voronoi_coordination = {} #number of anions from above
        #voronoi_rank = {}
        bonded_vectors = {}  #vectors of the neighbors using bonds
        #voronoi_vectors = {} #vectors of the neighbors using voronoi construction
        metal_neigh_angle = {
        }  #angle formed by magnetic metal atoms with each other in the same network
        metal_neigh_vectors = {}
        metal_neigh_coordination = {}
        metal_neigh_rank = {}
        #str_formulas = []

        for loc in np.arange(0, len(self.structure), 8):
            specie = self.structure.species_and_occu[loc]
            if self.check_3Dmetal(
                    specie
            ):  #determine is the species is magnetic metal or not
                rank_nodes = []
                accepted_nodes = np.arange(8) + loc

                try:
                    connected_nodes = nx.node_connected_component(G, loc)
                    for node in connected_nodes:
                        if (node in accepted_nodes):
                            rank_nodes.append(node)
                except KeyError:
                    continue

                rank_matrix = np.array([
                    np.round(self.frac_coords[node], decimals=3)
                    for node in rank_nodes
                ])
                rank_matrix = rank_matrix - rank_matrix[0]
                rank = np.linalg.matrix_rank(rank_matrix)
                str_formula = str(loc / 8) + str(
                    self.structure.species_and_occu[loc].formula)
                if rank == dim:  # and str_formula not in str_formulas: #only proceed if the magnetic metal is of the same dim
                    #str_formulas.append(str_formula)

                    #get all the non-metals in the rod or layer and valence info
                    layer_anions[str_formula] = self.layer(connected_nodes)
                    valence[str_formula] = self.valence(loc)

                    # get metal angles, metal neighbor coords and rank
                    formula = str(self.structure.species_and_occu[loc].formula)
                    if formula not in metal_neigh_angle:
                        metal_neigh_angle[formula], metal_neigh_vectors[
                            formula] = self.angle_calculation(
                                connected_nodes, loc)
                        metal_neigh_coordination[formula] = len(
                            metal_neigh_vectors[formula])
                        metal_neigh_rank[formula] = np.linalg.matrix_rank(
                            metal_neigh_vectors[formula])

                    #bonded anions, coordination, coords and rank
                    neigh = np.array([
                        n for n in G[loc] if not self.check_3Dmetal(
                            self.structure.species_and_occu[n])
                    ])
                    coords = [
                        self.get_nearest_cartesian_coords(n, loc)
                        for n in neigh
                    ]

                    bonded_anions[str_formula] = [
                        str(self.structure.species_and_occu[n].formula)
                        for n in neigh
                    ]
                    bonded_coordination[str_formula] = len(
                        bonded_anions[str_formula])
                    bonded_vectors[str_formula] = [
                        coords[n] -
                        self.structure.lattice.get_cartesian_coords(
                            self.frac_coords[loc]) for n in range(len(coords))
                    ]
                    bonded_rank[str_formula] = np.linalg.matrix_rank(
                        bonded_vectors[str_formula])

                    vcf = VoronoiCoordFinder(self.structure)

                    # paymetgen coordination
                    pymatgen_coordination[
                        str_formula] = self.pymatgen_coordination(vcf, loc)

                    # voronoi anions, coordination, coords and rank
                    """try:
                        vor_neigh = [n for n in vcf.get_coordinated_sites(loc) \
                            if self.check_bonds(n.species_and_occu) and not self.check_3Dmetal(n.species_and_occu)]
                        coords = [n.coords for n in vor_neigh]
                        voronoi_anions[str_formula] = self.voronoi_anions(vor_neigh)
                        voronoi_coordination[str_formula] = len(voronoi_anions[str_formula]) 
                    except Exception:
                        coords = []
                        voronoi_anions[str_formula] = -1
                        voronoi_coordination[str_formula] = -1

                    voronoi_vectors[str_formula] = [coords[n] - self.structure.cart_coords[loc] for n in range(len(coords))]
                    voronoi_rank[str_formula] = np.linalg.matrix_rank(voronoi_vectors[str_formula])"""

        return layer_anions, valence, bonded_anions, bonded_coordination, bonded_rank,  pymatgen_coordination, \
                    metal_neigh_angle, metal_neigh_coordination, metal_neigh_rank, bonded_vectors, metal_neigh_vectors
Exemple #36
0
 def setUp(self):
     s = self.get_structure('LiFePO4')
     self.finder = VoronoiCoordFinder(s, [Element("O")])
 def setUp(self):
     filepath = os.path.join(test_dir, 'LiFePO4.cif')
     parser = CifParser(filepath)
     s = parser.get_structures()[0]
     self.finder = VoronoiCoordFinder(s, [Element("O")])