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_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
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 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
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
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
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
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)
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)
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)
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
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
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
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
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()
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")])
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