class BVAnalyzerTest(PymatgenTest): def setUp(self): self.analyzer = BVAnalyzer() def test_get_valence(self): s = Structure.from_file(os.path.join(test_dir, "LiMn2O4.json")) ans = [1, 1, 3, 3, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2] self.assertEqual(self.analyzer.get_valences(s), ans) s = self.get_structure("LiFePO4") ans = [1, 1, 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, -2, -2, -2, -2, -2, -2, -2, - 2, -2, -2, -2, -2, -2, -2, -2, -2] self.assertEqual(self.analyzer.get_valences(s), ans) s = self.get_structure("Li3V2(PO4)3") ans = [1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, -2, -2, -2, -2, - 2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - 2, -2, -2, -2] self.assertEqual(self.analyzer.get_valences(s), ans) s = Structure.from_file(os.path.join(test_dir, "Li4Fe3Mn1(PO4)4.json")) ans = [1, 1, 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, -2, -2, -2, -2, -2, -2, -2, - 2, -2, -2, -2, -2, -2, -2, -2, -2] self.assertEqual(self.analyzer.get_valences(s), ans) s = self.get_structure("NaFePO4") ans = [1, 1, 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, -2, -2, -2, -2, -2, -2, -2, - 2, -2, -2, -2, -2, -2, -2, -2, -2] self.assertEqual(self.analyzer.get_valences(s), ans) def test_get_oxi_state_structure(self): s = Structure.from_file(os.path.join(test_dir, "LiMn2O4.json")) news = self.analyzer.get_oxi_state_decorated_structure(s) self.assertIn(Specie("Mn", 3), news.composition.elements) self.assertIn(Specie("Mn", 4), news.composition.elements)
def _get_valences(self): """ Computes ionic valences of elements for all sites in the structure. """ try: bv = BVAnalyzer() self._structure = bv.get_oxi_state_decorated_structure( self._structure) valences = bv.get_valences(self._structure) except: try: bv = BVAnalyzer(symm_tol=0.0) self._structure = bv.get_oxi_state_decorated_structure( self._structure) valences = bv.get_valences(self._structure) except: valences = [0] * self._structure.num_sites #raise #el = [site.specie.symbol for site in self._structure.sites] #el = [site.species_string for site in self._structure.sites] #el = [site.specie for site in self._structure.sites] #valence_dict = dict(zip(el, valences)) #print valence_dict return valences
def __init__(self, defect): """ Args: defect(Defect): pymatgen Defect object """ self.defect = defect try: bv = BVAnalyzer() struct_valences = bv.get_valences(self.defect.bulk_structure) site_index = self.defect.bulk_structure.get_sites_in_sphere( self.defect.site.coords, 0.1, include_index=True)[0][2] def_site_valence = struct_valences[site_index] except Exception: # sometimes valences cant be assigned def_site_valence = 0 if isinstance(defect, Vacancy): self.charges = [-1 * def_site_valence] elif isinstance(defect, Substitution): #(minimize difference with host site specie) probable_chgs = [ ox - def_site_valence for ox in self.defect.site.specie.oxidation_states ] self.charges = [min(probable_chgs, key=abs)] elif isinstance(defect, Interstitial): self.charges = [0] else: raise ValueError("Defect Type not recognized.")
def __init__(self, defect): """ Args: defect(Defect): pymatgen Defect object """ self.defect = defect try: bv = BVAnalyzer() struct_valences = bv.get_valences(self.defect.bulk_structure) site_index = self.defect.bulk_structure.get_sites_in_sphere( self.defect.site.coords, 0.1, include_index=True)[0][2] def_site_valence = struct_valences[site_index] except Exception: # sometimes valences cant be assigned def_site_valence = 0 if isinstance(defect, Vacancy): self.charges = [-1 * def_site_valence] elif isinstance(defect, Substitution): #(minimize difference with host site specie) probable_chgs = [ox - def_site_valence for ox in self.defect.site.specie.oxidation_states] self.charges = [min(probable_chgs, key=abs)] elif isinstance(defect, Interstitial): self.charges = [0] else: raise ValueError("Defect Type not recognized.")
def tersoff_potential(self, structure): """ Generate the species, tersoff potential lines for an oxide structure Args: structure: pymatgen.core.structure.Structure """ bv = BVAnalyzer() el = [site.species_string for site in structure.sites] valences = bv.get_valences(structure) el_val_dict = dict(zip(el, valences)) gin = "species \n" qerfstring = "qerfc\n" for key in el_val_dict.keys(): if key != "O" and el_val_dict[key] % 1 != 0: raise SystemError("Oxide has mixed valence on metal") specie_string = key + " core " + str(el_val_dict[key]) + "\n" gin += specie_string qerfstring += key + " " + key + " 0.6000 10.0000 \n" gin += "# noelectrostatics \n Morse \n" met_oxi_ters = Tersoff_pot().data for key in el_val_dict.keys(): if key != "O": metal = key + "(" + str(int(el_val_dict[key])) + ")" ters_pot_str = met_oxi_ters[metal] gin += ters_pot_str gin += qerfstring return gin
def calc(self, item): s = Structure.from_dict(item["structure"]) d = { "pymatgen_version": pymatgen_version, "successful": False, "bond_valence": { "structure": item["structure"], "method": None }, } try: bva = BVAnalyzer() valences = bva.get_valences(s) possible_species = { str(Specie(s[idx].specie, oxidation_state=valence)) for idx, valence in enumerate(valences) } d["successful"] = True s.add_oxidation_state_by_site(valences) d["bond_valence"] = { "possible_species": list(possible_species), "possible_valences": valences, "method": "BVAnalyzer", "structure": s.as_dict(), } except Exception as e: self.logger.error("BVAnalyzer failed with: {}".format(e)) try: first_oxi_state_guess = s.composition.oxi_state_guesses( max_sites=-50)[0] valences = [ first_oxi_state_guess[site.species_string] for site in s ] possible_species = { str(Specie(el, oxidation_state=valence)) for el, valence in first_oxi_state_guess.items() } d["successful"] = True s.add_oxidation_state_by_site(valences) d["bond_valence"] = { "possible_species": list(possible_species), "possible_valences": valences, "method": "oxi_state_guesses", "structure": s.as_dict(), } except Exception as e: self.logger.error( "Oxidation state guess failed with: {}".format(e)) return d
class BVAnalyzerTest(unittest.TestCase): def setUp(self): self.analyzer = BVAnalyzer() def test_get_valence(self): parser = CifParser(os.path.join(test_dir, "LiMn2O4.cif")) s = parser.get_structures()[0] ans = [1, 1, 3, 3, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2] self.assertEqual(self.analyzer.get_valences(s), ans) parser = CifParser(os.path.join(test_dir, "LiFePO4.cif")) s = parser.get_structures()[0] ans = [ 1, 1, 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 ] self.assertEqual(self.analyzer.get_valences(s), ans) parser = CifParser(os.path.join(test_dir, "Li3V2(PO4)3.cif")) s = parser.get_structures()[0] ans = [ 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 ] self.assertEqual(self.analyzer.get_valences(s), ans) parser = CifParser(os.path.join(test_dir, "Li4Fe3Mn1(PO4)4.cif")) s = parser.get_structures()[0] ans = [ 1, 1, 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 ] self.assertEqual(self.analyzer.get_valences(s), ans) parser = CifParser(os.path.join(test_dir, "NaFePO4.cif")) s = parser.get_structures()[0] ans = [ 1, 1, 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 ] self.assertEqual(self.analyzer.get_valences(s), ans) def test_get_oxi_state_structure(self): parser = CifParser(os.path.join(test_dir, "LiMn2O4.cif")) s = parser.get_structures()[0] news = self.analyzer.get_oxi_state_decorated_structure(s) self.assertIn(Specie("Mn", 3), news.composition.elements) self.assertIn(Specie("Mn", 4), news.composition.elements)
def _get_valences(self): """ Computes ionic valences of elements for all sites in the structure. """ bv = BVAnalyzer() valences = bv.get_valences(self._structure) el = [site.species_string for site in self.structure.sites] valence_dict = dict(zip(el, valences)) return valence_dict
def _get_valences(self): """ Computes ionic valences of elements for all sites in the structure. """ bv = BVAnalyzer() self._structure = bv.get_oxi_state_decorated_structure(self._structure) try: valences = bv.get_valences(self._structure) except: try: valences = bv.get_valences(self._structure, symm_tol=0.0) except: raise #print valences #el = [site.specie.symbol for site in self._structure.sites] #el = [site.species_string for site in self._structure.sites] #el = [site.specie for site in self._structure.sites] #valence_dict = dict(zip(el, valences)) #print valence_dict return valences
def setUp(self): mgo_latt = [[4.212, 0, 0], [0, 4.212, 0], [0, 0, 4.212]] mgo_specie = ["Mg", 'O'] * 4 mgo_frac_cord = [[0, 0, 0], [0.5, 0, 0], [0.5, 0.5, 0], [0, 0.5, 0], [0.5, 0, 0.5], [0, 0, 0.5], [0, 0.5, 0.5], [0.5, 0.5, 0.5]] self.mgo_uc = Structure(mgo_latt, mgo_specie, mgo_frac_cord, True, True) bv = BVAnalyzer() val = bv.get_valences(self.mgo_uc) el = [site.species_string for site in self.mgo_uc.sites] self.val_dict = dict(zip(el, val))
def setUp(self): filepath = os.path.join(test_dir, 'POSCAR') p = Poscar.from_file(filepath) self.structure = p.structure bv = BVAnalyzer() valences = bv.get_valences(self.structure) el = [site.species_string for site in self.structure.sites] valence_dict = dict(zip(el, valences)) self.rad_dict = {} for k, v in valence_dict.items(): self.rad_dict[k] = float(Specie(k, v).ionic_radius) assert len(self.rad_dict) == len(self.structure.composition)
def setUp(self): filepath1 = os.path.join(PymatgenTest.TEST_FILES_DIR, "Li2O.cif") p = CifParser(filepath1).get_structures(False)[0] bv = BVAnalyzer() valences = bv.get_valences(p) el = [site.species_string for site in p.sites] val_dict = dict(zip(el, valences)) self._radii = {} for k, v in val_dict.items(): k1 = re.sub(r"[1-9,+,\-]", "", k) self._radii[k1] = float(Species(k1, v).ionic_radius) p.remove(0) self._vac_struct = p
def setUp(self): filepath = os.path.join(test_dir, 'POSCAR') p = Poscar.from_file(filepath) self.structure = p.structure bv = BVAnalyzer() valences = bv.get_valences(self.structure) el = [site.species_string for site in self.structure.sites] valence_dict = dict(zip(el, valences)) self.rad_dict = {} for k, v in valence_dict.items(): self.rad_dict[k] = Specie(k,v).ionic_radius assert len(self.rad_dict) == len(self.structure.composition)
def setUp(self): filepath1 = os.path.join(test_dir, 'Li2O.cif') p = CifParser(filepath1).get_structures(False)[0] bv = BVAnalyzer() valences = bv.get_valences(p) el = [site.species_string for site in p.sites] val_dict = dict(zip(el, valences)) self._radii = {} for k,v in val_dict.items(): k1 = re.sub('[1-9,+,\-]', '', k) self._radii[k] = Specie(k1, v).ionic_radius p.remove(0) self._vac_struct = p
def setUp(self): filepath1 = os.path.join(test_dir, 'Li2O.cif') p = CifParser(filepath1).get_structures(False)[0] bv = BVAnalyzer() valences = bv.get_valences(p) el = [site.species_string for site in p.sites] val_dict = dict(zip(el, valences)) self._radii = {} for k, v in val_dict.items(): k1 = re.sub(r'[1-9,+,\-]', '', k) self._radii[k1] = float(Specie(k1, v).ionic_radius) p.remove(0) self._vac_struct = p
class BVAnalyzerTest(unittest.TestCase): def setUp(self): self.analyzer = BVAnalyzer() def test_get_valence(self): parser = CifParser(os.path.join(test_dir, "LiMn2O4.cif")) s = parser.get_structures()[0] ans = [1, 1, 3, 3, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2] self.assertEqual(self.analyzer.get_valences(s), ans) parser = CifParser(os.path.join(test_dir, "LiFePO4.cif")) s = parser.get_structures()[0] ans = [1, 1, 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, -2, -2, -2, -2, -2, -2, -2, - 2, -2, -2, -2, -2, -2, -2, -2, -2] self.assertEqual(self.analyzer.get_valences(s), ans) parser = CifParser(os.path.join(test_dir, "Li3V2(PO4)3.cif")) s = parser.get_structures()[0] ans = [1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, -2, -2, -2, -2, - 2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - 2, -2, -2, -2] self.assertEqual(self.analyzer.get_valences(s), ans) parser = CifParser(os.path.join(test_dir, "Li4Fe3Mn1(PO4)4.cif")) s = parser.get_structures()[0] ans = [1, 1, 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, -2, -2, -2, -2, -2, -2, -2, - 2, -2, -2, -2, -2, -2, -2, -2, -2] self.assertEqual(self.analyzer.get_valences(s), ans) parser = CifParser(os.path.join(test_dir, "NaFePO4.cif")) s = parser.get_structures()[0] ans = [1, 1, 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, -2, -2, -2, -2, -2, -2, -2, - 2, -2, -2, -2, -2, -2, -2, -2, -2] self.assertEqual(self.analyzer.get_valences(s), ans) def test_get_oxi_state_structure(self): parser = CifParser(os.path.join(test_dir, "LiMn2O4.cif")) s = parser.get_structures()[0] news = self.analyzer.get_oxi_state_decorated_structure(s) self.assertIn(Specie("Mn", 3), news.composition.elements) self.assertIn(Specie("Mn", 4), news.composition.elements)
def setUp(self): filepath = os.path.join(test_dir, 'POSCAR') p = Poscar.from_file(filepath) self.structure = p.structure bv = BVAnalyzer() self.structure = bv.get_oxi_state_decorated_structure(self.structure) valences = bv.get_valences(self.structure) radii = [] for i in range(len(valences)): el = self.structure.sites[i].specie.symbol radius = Specie(el, valences[i]).ionic_radius radii.append(radius) el = [site.species_string for site in self.structure.sites] self.rad_dict = dict(zip(el, radii)) for el in self.rad_dict.keys(): print((el, self.rad_dict[el].real))
def _get_valences(self): """ Computes ionic valences of elements for all sites in the structure. """ bv = BVAnalyzer() try: valences = bv.get_valences(self._structure) except: err_str = "BVAnalyzer failed. The defect effective charge, and" err_str += " volume and surface area may not work" print err_str raise LookupError() el = [site.species_string for site in self.structure.sites] valence_dict = dict(zip(el, valences)) return valence_dict
def setUp(self): with open("mp-7000.json", "r") as f: dict_lse = json.load(f) lse = LightStructureEnvironments.from_dict(dict_lse) struct = lse.structure bva = BVAnalyzer() valences = bva.get_valences(structure=struct) lgf = LocalGeometryFinder() lgf.setup_structure(structure=struct) se = lgf.compute_structure_environments(maximum_distance_factor=1.41, only_cations=False, valences=valences) strategy = MultiWeightsChemenvStrategy.stats_article_weights_parameters() self.lse = LightStructureEnvironments.from_structure_environments(strategy=strategy, structure_environments=se) with open("mp-5634.json", "r") as f: dict_lse2 = json.load(f) self.lse2 = LightStructureEnvironments.from_dict(dict_lse2)
def __init__(self, structure, include_bv_charge=False): """ Initializes a Vacancy Generator Args: structure(Structure): pymatgen structure object """ self.structure = structure self.include_bv_charge = include_bv_charge # Find equivalent site list sga = SpacegroupAnalyzer(self.structure) self.symm_structure = sga.get_symmetrized_structure() self.equiv_site_seq = list(self.symm_structure.equivalent_sites) self.struct_valences = None if self.include_bv_charge: bv = BVAnalyzer() self.struct_valences = bv.get_valences(self.structure)
def get_atomic_radii(self): if not self.pymatgen_radii: return None try: bv = BVAnalyzer() valences = bv.get_valences(self._structure) elements = [site.species_string for site in self._structure.sites] valence_dict = dict(zip(elements, valences)) radii = {} for k, v in valence_dict.items(): radii[k] = float(Specie(k, v).ionic_radius) except (ValueError, TypeError) as e: radii = None return radii
def get_valences(self): """ Uses Pymatgen to obtain likely valence states of every element in structure Returns vals: dictionary of average valence state for every element in composition """ struct = self.structure bv = BVAnalyzer() try: valences = bv.get_valences(struct) struct = bv.get_oxi_state_decorated_structure(struct) except: return None if isinstance(valences[0], list): valences = [item for sublist in valences for item in sublist] stoich = defaultdict(int) for site in struct.as_dict()['sites']: elem = site['species'][0]['element'] stoich[elem] += 1 vals = {} for spec in stoich.keys(): vals[spec] = 0.0 for atom in range(len(struct)): try: vals[struct.as_dict()['sites'][atom]['species'][0] ['element']] += valences[atom] except Exception as e: print("Trouble with {}".format(struct.formula)) print('Do you have partial occupancies?') return None for spec in vals: vals[spec] = vals[spec] / stoich[spec] vals[spec] = int(round(vals[spec])) return vals
def get_coordsites_min_max_charge(self, n): """ Minimum and maximum charge of sites surrounding the vacancy site. Args: n: Index of vacancy list """ bv = BVAnalyzer() struct_valences = bv.get_valences(self._structure) coordinated_site_valences = [] def _get_index(site): for i in range(len(self._structure.sites)): if site.is_periodic_image(self._structure.sites[i]): return i raise ValueError("Site not found") for site in self._defect_coord_sites[n]: ind = _get_index(site) coordinated_site_valences.append(struct_valences[ind]) coordinated_site_valences.sort() return coordinated_site_valences[0], coordinated_site_valences[-1]
def process_item(self, item): s = Structure.from_dict(item['structure']) try: bva = BVAnalyzer() valences = bva.get_valences(s) possible_species = { str(Specie(s[idx].specie, oxidation_state=valence)) for idx, valence in enumerate(valences) } method = "BVAnalyzer" except ValueError: try: first_oxi_state_guess = s.composition.oxi_state_guesses()[0] valences = [ first_oxi_state_guess[site.species_string] for site in s ] possible_species = { str(Specie(el, oxidation_state=valence)) for el, valence in first_oxi_state_guess.items() } method = "oxi_state_guesses" except: return { "task_id": item['task_id'], "pymatgen_version": pymatgen_version, "successful": False } return { "task_id": item['task_id'], "possible_species": list(possible_species), "possible_valences": valences, "method": method, "pymatgen_version": pymatgen_version, "successful": True }
def Calc_Ewald(pmg_struct, formal_val=[]): """ input: pmg_struct: pymatgen structure formal_val: list - list of valence for each atom TBD: use input formal valence list to decorate the structure """ # GET valence list if len(formal_val)==0: bv_analyzer=BVAnalyzer(max_radius=4) #max_radius default is 4 formal_val=bv_analyzer.get_valences(pmg_struct) # default for cutoff is set to None real_cutoff = None rec_cutoff = None # Oxidation states are decorated automatically. decorated_struct = bv_analyzer.get_oxi_state_decorated_structure(pmg_struct) NUM_sites=pmg_struct.num_sites # Per atom ewald_per_atom=1/NUM_sites*EwaldSummation(decorated_struct, real_space_cut=real_cutoff, recip_space_cut=rec_cutoff).total_energy return ewald_per_atom
def buckingham_potential(structure, val_dict=None): """ Generate species, buckingham, and spring options for an oxide structure using the parameters in default libraries. Ref: 1. G.V. Lewis and C.R.A. Catlow, J. Phys. C: Solid State Phys., 18, 1149-1161 (1985) 2. T.S.Bush, J.D.Gale, C.R.A.Catlow and P.D. Battle, J. Mater Chem., 4, 831-837 (1994) Args: structure: pymatgen.core.structure.Structure val_dict (Needed if structure is not charge neutral): {El:valence} dict, where El is element. """ if not val_dict: try: # If structure is oxidation state decorated, use that first. el = [site.specie.symbol for site in structure] valences = [site.specie.oxi_state for site in structure] val_dict = dict(zip(el, valences)) except AttributeError: bv = BVAnalyzer() el = [site.specie.symbol for site in structure] valences = bv.get_valences(structure) val_dict = dict(zip(el, valences)) # Try bush library first bpb = BuckinghamPotential("bush") bpl = BuckinghamPotential("lewis") gin = "" for key in val_dict.keys(): use_bush = True el = re.sub(r"[1-9,+,\-]", "", key) if el not in bpb.species_dict.keys(): use_bush = False elif val_dict[key] != bpb.species_dict[el]["oxi"]: use_bush = False if use_bush: gin += "species \n" gin += bpb.species_dict[el]["inp_str"] gin += "buckingham \n" gin += bpb.pot_dict[el] gin += "spring \n" gin += bpb.spring_dict[el] continue # Try lewis library next if element is not in bush # use_lewis = True if el != "O": # For metals the key is "Metal_OxiState+" k = el + "_" + str(int(val_dict[key])) + "+" if k not in bpl.species_dict.keys(): # use_lewis = False raise GulpError(f"Element {k} not in library") gin += "species\n" gin += bpl.species_dict[k] gin += "buckingham\n" gin += bpl.pot_dict[k] else: gin += "species\n" k = "O_core" gin += bpl.species_dict[k] k = "O_shel" gin += bpl.species_dict[k] gin += "buckingham\n" gin += bpl.pot_dict[key] gin += "spring\n" gin += bpl.spring_dict[key] return gin
def run(self, sn): """ Args: sn (SiteNetwork) Returns: ``sn``, with type information. """ # -- Determine local environments # Get an ASE structure with a single mobile site that we'll move around if sn.n_sites == 0: logger.warning("Site network had no sites.") return sn site_struct, site_species = sn[0:1].get_structure_with_sites() pymat_struct = AseAtomsAdaptor.get_structure(site_struct) lgf = cgf.LocalGeometryFinder() site_atom_index = len(site_struct) - 1 coord_envs = [] vertices = [] valences = 'undefined' if self._guess_ionic_bonds: sim_struct = AseAtomsAdaptor.get_structure(sn.structure) valences = np.zeros(shape = len(site_struct), dtype = np.int) bv = BVAnalyzer() try: struct_valences = np.asarray(bv.get_valences(sim_struct)) except ValueError as ve: logger.warning("Failed to compute bond valences: %s" % ve) else: valences = np.zeros(shape = len(site_struct), dtype = np.int) valences[:site_atom_index] = struct_valences[sn.static_mask] mob_val = struct_valences[sn.mobile_mask] if np.any(mob_val != mob_val[0]): logger.warning("Mobile atom estimated valences (%s) not uniform; arbitrarily taking first." % mob_val) valences[site_atom_index] = mob_val[0] finally: valences = list(valences) logger.info("Running site coordination environment analysis...") # Do this once. # __init__ here defaults to disabling structure refinement, so all this # method is doing is making a copy of the structure and setting some # variables to None. lgf.setup_structure(structure = pymat_struct) for site in tqdm(range(sn.n_sites), desc = 'Site'): # Update the position of the site lgf.structure[site_atom_index].coords = sn.centers[site] # Compute structure environments for the site struct_envs = lgf.compute_structure_environments( only_indices = [site_atom_index], valences = valences, additional_conditions = [AdditionalConditions.ONLY_ANION_CATION_BONDS], **self._kwargs ) struct_envs = LightStructureEnvironments.from_structure_environments( strategy = cgf.LocalGeometryFinder.DEFAULT_STRATEGY, structure_environments = struct_envs ) # Store the results # We take the first environment for each site since it's the most likely this_site_envs = struct_envs.coordination_environments[site_atom_index] if len(this_site_envs) > 0: coord_envs.append(this_site_envs[0]) vertices.append( [n['index'] for n in struct_envs.neighbors_sets[site_atom_index][0].neighb_indices_and_images] ) else: coord_envs.append({'ce_symbol' : 'BAD:0', 'ce_fraction' : 0.}) vertices.append([]) del lgf del struct_envs # -- Postprocess # TODO: allow user to ask for full fractional breakdown str_coord_environments = [env['ce_symbol'] for env in coord_envs] # The closer to 1 this is, the better site_type_confidences = np.array([env['ce_fraction'] for env in coord_envs]) coordination_numbers = np.array([int(env['ce_symbol'].split(':')[1]) for env in coord_envs]) assert np.all(coordination_numbers == [len(v) for v in vertices]) typearr = str_coord_environments if self._full_chemenv_site_types else coordination_numbers unique_envs = list(set(typearr)) site_types = np.array([unique_envs.index(t) for t in typearr]) n_types = len(unique_envs) logger.info(("Type " + "{:<8}" * n_types).format(*unique_envs)) logger.info(("# of sites " + "{:<8}" * n_types).format(*np.bincount(site_types))) sn.site_types = site_types sn.vertices = vertices sn.add_site_attribute("coordination_environments", str_coord_environments) sn.add_site_attribute("site_type_confidences", site_type_confidences) sn.add_site_attribute("coordination_numbers", coordination_numbers) return sn
def buckingham_potential(self, structure, val_dict=None): """ Generate species, buckingham, and spring options for an oxide structure using the parameters in default libraries. Ref: 1. G.V. Lewis and C.R.A. Catlow, J. Phys. C: Solid State Phys., 18, 1149-1161 (1985) 2. T.S.Bush, J.D.Gale, C.R.A.Catlow and P.D. Battle, J. Mater Chem., 4, 831-837 (1994) Args: structure: pymatgen.core.structure.Structure val_dict (Needed if structure is not charge neutral) El:valence dictionary, where El is element. """ if not val_dict: bv = BVAnalyzer() el = [site.species_string for site in structure.sites] valences = bv.get_valences(structure) val_dict = dict(zip(el, valences)) #Try bush library first bpb = BuckinghamPotBush() bpl = BuckinghamPotLewis() gin = "" for key in val_dict.keys(): use_bush = True el = re.sub('[1-9,+,\-]', '', key) if el not in bpb.species_dict.keys(): use_bush = False elif val_dict[key] != bpb.species_dict[el]['oxi']: use_bush = False if use_bush: gin += "species \n" gin += bpb.species_dict[el]['inp_str'] gin += "buckingham \n" gin += bpb.pot_dict[el] gin += "spring \n" gin += bpb.spring_dict[el] continue #Try lewis library next if element is not in bush #use_lewis = True if el != "O": # For metals the key is "Metal_OxiState+" k = el + '_' + str(int(val_dict[key])) + '+' if k not in bpl.species_dict.keys(): #use_lewis = False raise GulpError("Element {} not in library".format(k)) gin += "species\n" gin += bpl.species_dict[k] gin += "buckingham\n" gin += bpl.pot_dict[k] else: gin += "species\n" k = "O_core" gin += bpl.species_dict[k] k = "O_shel" gin += bpl.species_dict[k] gin += "buckingham\n" gin += bpl.pot_dict[key] gin += 'spring\n' gin += bpl.spring_dict[key] return gin
def run_task(self, fw_spec): logging.basicConfig( filename='chemenv_structure_environments.log', format='%(levelname)s:%(module)s:%(funcName)s:%(message)s', level=logging.DEBUG) lgf = LocalGeometryFinder() lgf.setup_parameters( centering_type='centroid', include_central_site_in_centroid=True, structure_refinement=lgf.STRUCTURE_REFINEMENT_NONE) if 'chemenv_parameters' in fw_spec: for param, value in fw_spec['chemenv_parameters'].items(): lgf.setup_parameter(param, value) identifier = fw_spec['identifier'] if 'structure' in fw_spec: structure = fw_spec['structure'] else: if identifier[ 'source'] == 'MaterialsProject' and 'material_id' in identifier: if not 'mapi_key' in fw_spec: raise ValueError( 'The mapi_key should be provided to get the structure from the Materials Project' ) a = MPRester(fw_spec['mapi_key']) structure = a.get_structure_by_material_id( identifier['material_id']) else: raise ValueError( 'Either structure or identifier with source = MaterialsProject and material_id ' 'should be provided') info = {} # Compute the structure environments lgf.setup_structure(structure) if 'valences' in fw_spec: valences = fw_spec['valences'] else: try: bva = BVAnalyzer() valences = bva.get_valences(structure=structure) info['valences'] = {'origin': 'BVAnalyzer'} except: valences = 'undefined' info['valences'] = {'origin': 'None'} excluded_atoms = None if 'excluded_atoms' in fw_spec: excluded_atoms = fw_spec['excluded_atoms'] se = lgf.compute_structure_environments(only_cations=False, valences=valences, excluded_atoms=excluded_atoms) # Write to json file if 'json_file' in fw_spec: json_file = fw_spec['json_file'] else: json_file = 'structure_environments.json' f = open(json_file, 'w') json.dump(se.as_dict(), f) f.close() # Save to database if 'mongo_database' in fw_spec: database = fw_spec['mongo_database'] entry = { 'identifier': identifier, 'elements': [elmt.symbol for elmt in structure.composition.elements], 'nelements': len(structure.composition.elements), 'pretty_formula': structure.composition.reduced_formula, 'nsites': len(structure) } saving_option = fw_spec['saving_option'] if saving_option == 'gridfs': gridfs_msonables = { 'structure': structure, 'structure_environments': se } elif saving_option == 'storefile': gridfs_msonables = None if 'se_prefix' in fw_spec: se_prefix = fw_spec['se_prefix'] if not se_prefix.isalpha(): raise ValueError( 'Prefix for structure_environments file is "{}" ' 'while it should be alphabetic'.format(se_prefix)) else: se_prefix = '' if se_prefix: se_rfilename = '{}_{}.json'.format( se_prefix, fw_spec['storefile_basename']) else: se_rfilename = '{}.json'.format( fw_spec['storefile_basename']) se_rfilepath = '{}/{}'.format(fw_spec['storefile_dirpath'], se_rfilename) storage_server = fw_spec['storage_server'] storage_server.put(localpath=json_file, remotepath=se_rfilepath, overwrite=True, makedirs=False) entry['structure_environments_file'] = se_rfilepath else: raise ValueError( 'Saving option is "{}" while it should be ' '"gridfs" or "storefile"'.format(saving_option)) criteria = {'identifier': identifier} if database.collection.find(criteria).count() == 1: database.update_entry(query=criteria, entry_update=entry, gridfs_msonables=gridfs_msonables) else: database.insert_entry(entry=entry, gridfs_msonables=gridfs_msonables)
def run_task(self, fw_spec): logging.basicConfig(filename='chemenv_structure_environments.log', format='%(levelname)s:%(module)s:%(funcName)s:%(message)s', level=logging.DEBUG) lgf = LocalGeometryFinder() lgf.setup_parameters(centering_type='centroid', include_central_site_in_centroid=True, structure_refinement=lgf.STRUCTURE_REFINEMENT_NONE) if 'chemenv_parameters' in fw_spec: for param, value in fw_spec['chemenv_parameters'].items(): lgf.setup_parameter(param, value) identifier = fw_spec['identifier'] if 'structure' in fw_spec: structure = fw_spec['structure'] else: if identifier['source'] == 'MaterialsProject' and 'material_id' in identifier: if not 'mapi_key' in fw_spec: raise ValueError('The mapi_key should be provided to get the structure from the Materials Project') # FIXME: Use MPRester from pymatgen from pymatgen.matproj.rest import MPRester a = MPRester(fw_spec['mapi_key']) structure = a.get_structure_by_material_id(identifier['material_id']) else: raise ValueError('Either structure or identifier with source = MaterialsProject and material_id ' 'should be provided') info = {} # Compute the structure environments lgf.setup_structure(structure) if 'valences' in fw_spec: valences = fw_spec['valences'] else: try: bva = BVAnalyzer() valences = bva.get_valences(structure=structure) info['valences'] = {'origin': 'BVAnalyzer'} except: valences = 'undefined' info['valences'] = {'origin': 'None'} excluded_atoms = None if 'excluded_atoms' in fw_spec: excluded_atoms = fw_spec['excluded_atoms'] se = lgf.compute_structure_environments(only_cations=False, valences=valences, excluded_atoms=excluded_atoms) # Write to json file if 'json_file' in fw_spec: json_file = fw_spec['json_file'] else: json_file = 'structure_environments.json' f = open(json_file, 'w') json.dump(se.as_dict(), f) f.close() # Save to database if 'mongo_database' in fw_spec: database = fw_spec['mongo_database'] entry = {'identifier': identifier, 'elements': [elmt.symbol for elmt in structure.composition.elements], 'nelements': len(structure.composition.elements), 'pretty_formula': structure.composition.reduced_formula, 'nsites': len(structure) } saving_option = fw_spec['saving_option'] if saving_option == 'gridfs': gridfs_msonables = {'structure': structure, 'structure_environments': se} elif saving_option == 'storefile': gridfs_msonables = None if 'se_prefix' in fw_spec: se_prefix = fw_spec['se_prefix'] if not se_prefix.isalpha(): raise ValueError('Prefix for structure_environments file is "{}" ' 'while it should be alphabetic'.format(se_prefix)) else: se_prefix = '' if se_prefix: se_rfilename = '{}_{}.json'.format(se_prefix, fw_spec['storefile_basename']) else: se_rfilename = '{}.json'.format(fw_spec['storefile_basename']) se_rfilepath = '{}/{}'.format(fw_spec['storefile_dirpath'], se_rfilename) storage_server = fw_spec['storage_server'] storage_server.put(localpath=json_file, remotepath=se_rfilepath, overwrite=True, makedirs=False) entry['structure_environments_file'] = se_rfilepath else: raise ValueError('Saving option is "{}" while it should be ' '"gridfs" or "storefile"'.format(saving_option)) criteria = {'identifier': identifier} if database.collection.find(criteria).count() == 1: database.update_entry(query=criteria, entry_update=entry, gridfs_msonables=gridfs_msonables) else: database.insert_entry(entry=entry, gridfs_msonables=gridfs_msonables)
def __init__( self, are_coops=False, filename_ICOHP=None, valences=None, limits=None, structure=None, additional_condition=0, only_bonds_to=None, perc_strength_ICOHP=0.15, valences_from_charges=False, filename_CHARGE=None, ): """ Args: are_coops: (Bool) if True, the file is a ICOOPLIST.lobster and not a ICOHPLIST.lobster; only tested for ICOHPLIST.lobster so far filename_ICOHP: (str) Path to ICOOPLIST.lobster valences: (list of integers/floats) gives valence/charge for each element limits: limit to decide which ICOHPs should be considered structure: (Structure Object) typically constructed by: Structure.from_file("POSCAR") (Structure object from pymatgen.core.structure) additional_condition: Additional condition that decides which kind of bonds will be considered NO_ADDITIONAL_CONDITION = 0 ONLY_ANION_CATION_BONDS = 1 NO_ELEMENT_TO_SAME_ELEMENT_BONDS = 2 ONLY_ANION_CATION_BONDS_AND_NO_ELEMENT_TO_SAME_ELEMENT_BONDS = 3 ONLY_ELEMENT_TO_OXYGEN_BONDS = 4 DO_NOT_CONSIDER_ANION_CATION_BONDS=5 ONLY_CATION_CATION_BONDS=6 only_bonds_to: (list of str) will only consider bonds to certain elements (e.g. ["O"] for oxygen) perc_strength_ICOHP: if no limits are given, this will decide which icohps will still be considered ( relative to the strongest ICOHP) valences_from_charges: if True and path to CHARGE.lobster is provided, will use Lobster charges ( Mulliken) instead of valences filename_CHARGE: (str) Path to Charge.lobster """ self.ICOHP = Icohplist(are_coops=are_coops, filename=filename_ICOHP) self.Icohpcollection = self.ICOHP.icohpcollection self.structure = structure self.limits = limits self.only_bonds_to = only_bonds_to self.are_coops = are_coops if are_coops: raise ValueError("Algorithm only works correctly for ICOHPLIST.lobster") # will check if the additional condition is correctly delivered if additional_condition in range(0, 7): self.additional_condition = additional_condition else: raise ValueError("No correct additional condition") # will read in valences, will prefer manual setting of valences if valences is None: if valences_from_charges and filename_CHARGE is not None: chg = Charge(filename=filename_CHARGE) self.valences = chg.Mulliken else: bv_analyzer = BVAnalyzer() try: self.valences = bv_analyzer.get_valences(structure=self.structure) except ValueError: self.valences = None if additional_condition in [1, 3, 5, 6]: print("Valences cannot be assigned, additional_conditions 1 and 3 and 5 and 6 will not work") else: self.valences = valences if limits is None: self.lowerlimit = None self.upperlimit = None else: self.lowerlimit = limits[0] self.upperlimit = limits[1] # will evaluate coordination environments self._evaluate_ce( lowerlimit=self.lowerlimit, upperlimit=self.upperlimit, only_bonds_to=only_bonds_to, additional_condition=self.additional_condition, perc_strength_ICOHP=perc_strength_ICOHP, )