def __init__(self, elems=ATOMS, valence=False, dense=False): """ elems: a list of strings with the names of the elements to be assigned an orbital vector """ self.vectors = np.zeros(shape=(len(elems), 118 if not dense else 59)) self.dictionary = {} for i, elem in enumerate(elems): self.dictionary[elem] = i if not valence: electrons = pymatgen.Element(elem).full_electronic_structure for level, orb, occup in electrons: self._populate( i, level, orb, occup) if not dense else self._populate_dense( i, level, orb, occup) else: tokens = pymatgen.Element(elem).electronic_structure.split(".") for token in tokens: found = RE.findall(token) if found: level, orb, occup = found[0] self._populate( i, int(level), orb, int(occup)) if not dense else self._populate_dense( i, int(level), orb, int(occup))
def rawdataprocessing(elementdict, featperelem, datavariables, feattotal, file): # Read and prepare Data dataraw = gzip.open('data.pickle.gz') data = pickle.load(dataraw, encoding='latin1') dataraw.close() print("Datensatzlaenge: ", len(data), "Anzahl der Features: ", datavariables) newdata = np.zeros( (len(data), datavariables)) # Features(Energy + 3*Elementfeatures) # Generate prepared Data count = 0 for i in range(len(data)): newdata[i, 0] = data[i, 0] print("Ehull", newdata[i, 0]) for j in range(1, 3 + 1): # elementcount for k in range(1, featperelem + 1): count += 1 print("Count", count, "| i j k ", i, j, k) print("data: ", data[i, j]) print("featcount:", k + ((j - 1) * featperelem)) elemstr = data[i, j].decode('UTF-8') # convert b'Elem' --> Elem print(elementdict[mg.Element(elemstr).number]) print("k, feat.", k, elementdict[mg.Element(elemstr).number][k]) print("-----------") newdata[i, (k + ((j - 1) * featperelem) )] = elementdict[mg.Element(elemstr).number][k] print(newdata[i, :]) np.save(open(file, 'wb'), newdata) # dataraw = gzip.open('dataperpared.pickle.gz', 'wb') # pickle.dump(x) return newdata
def readStructures(sfile, central, ligand, radius=3.): """ Read structure from file sfile and look for environments of all central atoms in the structure. Only neighbors of type ligand are taken into account. Returns a list of mg.Molecule in which the first atom is the central atom and the following atoms are the neighbors of type ligand of this central atom. """ struct = mg.Structure.from_file(sfile) central_sites = [ site for site in struct if site.specie == mg.Element(central) ] envs = list() print("Identified sites:") for site in central_sites: mol = mg.Molecule([site.specie], [site.coords]) neighbors = struct.get_neighbors(site, radius) for neighbor, d in neighbors: if neighbor.specie == mg.Element(ligand): mol.append(neighbor.specie, neighbor.coords) envs.append(mol) print("%2s[%2d] : #ligand = %d (%s)" % (site.specie, struct.index(site), len(mol[1:]), " ".join( [at.specie.symbol for at in mol[1:]]))) return envs
def find_next_site_to_reduce_pop(self,list_Fe_ions): """ Identify the next item to be reduced, with the rules: - reduce Fe-C before Fe-N - reduce Fe nearest Na first """ N = pymatgen.Element('N') C = pymatgen.Element('C') # Are there Fe-C ions? sub_list_Fe_ions = [] for ion in list_Fe_ions: if ion.neighbor == C: sub_list_Fe_ions.append(ion) if len(sub_list_Fe_ions) == 0: # there are no Fe-C ion. They must all be Fe-N sub_list_Fe_ions = deepcopy(list_Fe_ions) # Find the site with the smallest Na distance sub_list_Na_d = [] for ion in sub_list_Fe_ions: sub_list_Na_d.append(ion.distance_to_Na) next_ion = sub_list_Fe_ions[ np.argmin(sub_list_Na_d) ] # identify index of the identified ion, and # pop it out of the list for i, ion in enumerate(list_Fe_ions): if ion == next_ion: list_Fe_ions.pop(i) break return next_ion
def getAngles(struct, sigma, npts, amin, amax, cutoff, centralAtom, ligandAtom): """ compute all angles """ # info print("\nBending angles analyses :") print("-------------------------") print("Central atom : %s" % centralAtom) print("Ligand atom : %s" % ligandAtom) print("cutoff : %f" % cutoff) # central sites central_sites = [ site for site in struct if site.specie == mg.Element(centralAtom) ] # x values for histogram aval = np.linspace(amin, amax, npts) # data data = np.zeros(npts) print("\nCoordinence") for csite in central_sites: neighbors = [(site, d) for site, d in struct.get_neighbors(csite, cutoff) if site.specie == mg.Element(ligandAtom)] iat = struct.index(csite) print("%5s(%2d) : %d" % (csite.specie.symbol, iat, len(neighbors))) #neighbors.append((distance, xij)) # compute bending angles and build histogram for i in range(len(neighbors)): isite, di = neighbors[i] xic = isite.coords - csite.coords for j in range(i + 1, len(neighbors)): jsite, dj = neighbors[j] xjc = jsite.coords - csite.coords scal = np.dot(xic, xjc) / di / dj if np.fabs(scal) > 1.: print("Dot product error : %f" % scal) raise ValueError angle = np.degrees(np.arccos(scal)) # add a gaussian function data += norm.pdf(aval, loc=angle, scale=sigma) # print data line = "# structural analysis\n" line += "# column 1 : angle (degree)\n" line += "# column 2 : histogram of angle %s-%s-%s\n" % ( ligandAtom, centralAtom, ligandAtom) for a, h in zip(aval, data): line += "%12.4f %12.4f\n" % (a, h) print("\n=> Print file anaAngles.dat\n") open("anaAngles.dat", "w").write(line) return aval, data
def ox_states_from_binary_formula(self,formula,anion=None,anion_ox_state=None): """ Determine oxidation states from binary formula. Could also use mg.Composition.oxi_state_guesses(), but the logic used is more complex. Args: formula: chemical formula anion: Element symbol of anion. If None, search for common anion anion_ox_state: oxidation state of anion. If None, assume common oxidation state """ comp = mg.Composition(formula) if len(comp.elements) != 2: raise ValueError('Formula must be binary') # determine anion if anion is None: anion = np.intersect1d([e.name for e in comp.elements],self.common_anions) if len(anion) > 1: raise ValueError('Found multiple possible anions in formula. Please specify anion') elif len(anion)==0: raise ValueError('No common anions found in formula. Please specify anion') else: anion = anion[0] metal = np.setdiff1d(comp.elements,mg.Element(anion))[0].name #get common oxidation state for anion if anion_ox_state is None: anion_ox_state = [ox for ox in mg.Element(anion).common_oxidation_states if ox < 0] if len(anion_ox_state) > 1: raise Exception(f"Multiple common oxidation states for {anion}. Please specify anion_ox_state") else: anion_ox_state = anion_ox_state[0] metal_ox_state = -comp.get(anion)*anion_ox_state/comp.get(metal) return {metal:metal_ox_state,anion:anion_ox_state}
def bond_IC(a, b): """ ionic character of bond between elemenets a and b based on Pauling electronegativities """ a_ = mg.Element(a) b_ = mg.Element(b) return 1 - np.exp(-0.25 * (a_.X - b_.X)**2)
def create_pure_structure(self, oxidation_states, basic_atoms, noCells, lattice_system='cubic', lattice_lengths=None, lattice_angles=None, supercell=False): """Creates pure structure given input parameters. """ oxidationA = oxidation_states[0] oxidationB = oxidation_states[1] oxidationC = oxidation_states[2] dopantAtoms = [] noDopants = [] noVacancies = [] noInterstitials = [] vacancyAtoms = [] interstitialAtoms = [] siteA = basic_atoms[0] siteB = basic_atoms[1] siteC = basic_atoms[2] atomA = mg.Element(siteA) atomB = mg.Element(siteB) atomC = mg.Element(siteC) if not lattice_lengths: ionic_radiiA = atomA.ionic_radii[oxidationA] ionic_radiiB = atomB.ionic_radii[oxidationB] ionic_radiiC = atomC.ionic_radii[oxidationC] latticeVal1 = 2 * (ionic_radiiA + ionic_radiiB) / math.pow( 3, 1 / 2) latticeVal2 = 2 * (ionic_radiiA + ionic_radiiC) / math.pow( 2, 1 / 2) latticeConst = max(latticeVal1, latticeVal2) latticeLength = [latticeConst, latticeConst, latticeConst] latticeAngles = [90, 90, 90] # WARNING: HARD-CODE ALERT else: latticeLength = lattice_lengths latticeAngles = lattice_angles lattice = mg.Lattice.from_lengths_and_angles(latticeLength, latticeAngles) structure = mg.Structure(lattice, [siteA, siteB, siteC, siteC, siteC], [[0, 0, 0], [0.5, 0.5, 0.5], [0.5, 0.5, 0], [0.5, 0, 0.5], [0, 0.5, 0.5]]) if supercell: structure.make_supercell(noCells) keyVal = self.structureKey(lattice_system, basic_atoms, dopantAtoms, noDopants, vacancyAtoms, noVacancies, noCells, interstitialAtoms, noInterstitials) return structure, keyVal
def _define_elements_(self, elements): if len(elements) == 1: self.elements = [mg.Element(elements[0])] else: elements_list = [mg.Element(elements[0])] for i in range(1,len(elements)): elements_list += mg.Element(elements[i]) self.elements = elements_list
def _define_elements_(self, elements): if type(elements) == str: elements_list = [mg.Element(elements)] else: elements_list = [mg.Element(elements[0])] for i in range(1, len(elements)): elements_list += mg.Element(elements[i]) return elements_list
def siteavg_mg_prop(self, property_name): ''' general function for averaging properties in mg.Element data dict across A and B sites ''' p_a = np.sum([ self.norm_cat_wts[m] * mg.Element(m).data[property_name] for m in self.A_site ]) p_b = np.sum([ self.norm_cat_wts[m] * mg.Element(m).data[property_name] for m in self.B_site ]) p_tot = (p_a * self.A_sum + p_b * self.B_sum) / (self.A_sum + self.B_sum) return p_a, p_b, p_tot
def elemental_descriptor(A1_ion, A2_ion, B_ion): """Extract elemental descriptors according to the atomic properties of A1_ion, A2_ion and B_ion in the perovskite structure. Args: A1_ion (str): element at A1_ion site, e.g., "La" A2_ion (str): element at A2_ion site, e.g., "Ba" B_ion (str): element at B_ion site, e.g., "Co" Returns: array: compositional descriptors, including common oxidation state of A, common oxidation state of B, Pauling electronegativity of A, Pauling electronegativity B, Tolerance factor, Octahedral factor, ionic ration of A/O, ionic ration of B/O, electronegativity difference of A/O, electronegativity difference of B/O. """ ele_A1 = mg.Element(A1_ion) ele_A2 = mg.Element(A2_ion) ele_B = mg.Element(B_ion) ele_O = mg.Element('O') # A/B ion oxidation state common_oxidation_states_A1 = ele_A1.common_oxidation_states[0] common_oxidation_states_A2 = ele_A2.common_oxidation_states[0] common_oxidation_states_A = np.mean(common_oxidation_states_A1 + common_oxidation_states_A2) common_oxidation_states_B = ele_B.common_oxidation_states[0] # ionic radius property ionic_radius_A1 = float(str(ele_A1.average_ionic_radius)[:-4]) ionic_radius_A2 = float(str(ele_A2.average_ionic_radius)[:-4]) ionic_radius_A = (ionic_radius_A1 + ionic_radius_A2) / 2 ionic_radius_B = float(str(ele_B.average_ionic_radius)[:-4]) ionic_radius_O = float(str(ele_O.average_ionic_radius)[:-4]) # Tolerance factor TF = (ionic_radius_A + ionic_radius_O) / (np.sqrt(2) * (ionic_radius_B + ionic_radius_O)) # Octahedral factor OF = ionic_radius_B / ionic_radius_O # ionic_radius ratios ionic_ration_AO = ionic_radius_A / ionic_radius_O ionic_ration_BO = ionic_radius_B / ionic_radius_O # averaged electronegativity for A and B atoms Pauling_electronegativity_A1 = ele_A1.X Pauling_electronegativity_A2 = ele_A2.X Pauling_electronegativity_A = (Pauling_electronegativity_A1 + Pauling_electronegativity_A2) / 2 Pauling_electronegativity_B = ele_B.X Pauling_electronegativity_O = ele_O.X # Difference in the electronegativity for A-O and B-O Diff_A_O = Pauling_electronegativity_A - Pauling_electronegativity_O Diff_B_O = Pauling_electronegativity_B - Pauling_electronegativity_O return [ common_oxidation_states_A, common_oxidation_states_B, Pauling_electronegativity_A, Pauling_electronegativity_B, TF, OF, ionic_ration_AO, ionic_ration_BO, Diff_A_O, Diff_B_O ]
def AtomTypeCount( unit_cell_formula ): #this does not do a good job of uniquely identifying all elements #in fact, this variable distinction al finalVol = 0 elementTypeArray = np.zeros(8) for elem in unit_cell_formula: el = mg.Element(elem) if (el.is_alkaline): elementTypeArray[7] += 1 # all compounds will have lithium...so no if (el.is_halogen == True): elementTypeArray[0] += 1 if (el.is_transition_metal): elementTypeArray[1] += 1 if (el.is_actinoid): elementTypeArray[2] += 1 if (el.is_chalcogen): elementTypeArray[3] += 1 if (el.is_metalloid): elementTypeArray[4] += 1 if (el.is_rare_earth_metal): elementTypeArray[5] += 1 else: elementTypeArray[6] += 1 return elementTypeArray
def main(self): with open(self.filename, 'a') as fout: fout.write('ATOMIC_SPECIES\n') for ppot in self.ppots: element_symbol = ppot.split('.')[0] mass = mg.Element(element_symbol).atomic_mass fout.write(' {0:>2s} '.format(element_symbol)) fout.write(' {0:8.4f}'.format(mass)) fout.write(' {}\n'.format(ppot)) fout.write('\n') fout.write('ATOMIC_POSITIONS {crystal}\n') for atom in self.atom_info: element = atom['element'] for i in range(len(atom['wyckoff_position'])): for r in atom['positions'][i]: if self.is_rhombohedral: pos = self.hex2trig(r) else: pos = r fout.write(' {:>2s}'.format(element)) for j in range(3): fout.write(' {:9.6f}'.format(float(pos[j]))) fout.write('\n') fout.write('\n') fout.write('K_POINTS {automatic}\n') for i in range(3): fout.write(' {}'.format(self.kpoints[i])) fout.write(' 0 0 0\n')
def Forces( sitesDat ): #input is the sites datastructure from the structures_asdict from structures_query ''' calculates an array of forces given the forces from the relaxation...sort of like a stability measures ''' Fmax = 0 Ftot = 0 for i in range(len(sitesDat)): elem = sitesDat[i] atom = mg.Element(elem['label']) forces = elem['properties']['forces'] F = 0 #we need to calculate magnitude for j in range(3): F += forces[j]**2 F = F**.5 if (F > Fmax): Fmax = F Ftot += F data = { 'Forces': Ftot / len(sitesDat), 'maxForce': Fmax } return data
def ionic_formula_from_ox_state(self,metal,anion,metal_ox_state,anion_ox_state=None,return_mn=False): """ Get binary ionic compound formula with reduced integer units based on oxidation state Parameters: ----------- metal: metal element symbol anion: anion element symbol metal_ox_state: metal oxidation state anion_ox_state: anion oxidation state. If None, will attempt to find the common oxidation state for the anion return_mn: if True, return formula units for metal (m) and anion (n) Returns: chemical formula string MmXn, and m, n if return_mn=True """ #get common oxidation state for anion if anion_ox_state is None: anion_ox_state = [ox for ox in mg.Element(anion).common_oxidation_states if ox < 0] if len(anion_ox_state) > 1: raise Exception(f"Multiple common oxidation states for {anion}. Please specify anion_ox_state") else: anion_ox_state = anion_ox_state[0] #formula MmXn deno = gcd(metal_ox_state,-anion_ox_state) m = -anion_ox_state/deno n = metal_ox_state/deno formula = '{}{}{}{}'.format(metal,m,anion,n) if return_mn==False: return formula else: return formula, m, n
def get_ionization_lookup(df_input): """Convert the input dataframe into a lookup dataframe of ionization energies""" # get the column names columns = df_input.columns # get the first three column names specie_name, ion_charge, ionization_energy = columns[:3] # clean up the dataframe using the two helper functions df_input[specie_name] = df_input[specie_name].apply(clean_specie_name) df_input[ionization_energy] = df_input[ionization_energy].apply( clean_energy_str) # select the first 3 columns and rename them df_input = df_input.iloc[:, :3].rename(columns={ specie_name: "element", ion_charge: "v", ionization_energy: "iv_p1" }) # shift the iv_p1 columns down by one row and create a new column iv from it df_input["iv"] = df_input["iv_p1"].shift() # reorder the columns df_input = df_input[["element", "v", "iv", "iv_p1"]] # get the element element = mg.Element(df_input["element"][0]) # return the rows with 0 < v < max_oxidation_state return df_input[(df_input.v > 0) & (df_input.v <= element.max_oxidation_state)]
def check_bond_length(self): natom = self.config_obj.natom check = True for i in range(natom): atom1 = self.config_obj.atom_list[i] for j in range(i + 1, natom): atom2 = self.config_obj.atom_list[j] sum_atomic_radius = mg.Element(atom1).atomic_radius +\ mg.Element(atom2).atomic_radius bond_length = self.config_obj.calc_length(i, j, aunit=True) if bond_length < sum_atomic_radius: print('{0}-{1}:'.format(atom1, atom2), end='') print('ideal = {0:8.5f} but real = {1:8.5f}'.format( sum_atomic_radius, bond_length)) check = False return check
def ion_class(self): """ Classifies an element as cation or anion Returns classed: list with anion and cations """ elements = self.elements.copy() max_eneg = 0 for i in range(len(elements)): if mg.Element(elements[i]).X > max_eneg: max_eneg = mg.Element(elements[i]).X max_i = i anion = elements.pop(max_i) cations = elements classed = [anion, cations] return classed
def unitCellMass(unit_cell_formula): Mass = 0 label = 'MassUnitcell' for elem in unit_cell_formula: element = mg.Element(elem) masscontribution = element.data['Atomic mass'] * unit_cell_formula[elem] Mass += masscontribution return Mass
def mol_Oh(central, ligand, scale): """ Return a perfect octahedra as a mg.Molecule object. Args: central: (string) Name of the central atom ligand: (string) Name of ligand atoms scale: (float) length of central-ligand distance Returns mg.Molecule object """ species = [mg.Element(central)] + 6 * [mg.Element(ligand)] template = [[0., 0., 0.], [1., 0., 0.], [-1., 0., 0.], [0., 1., 0.], [0., -1., 0.], [0., 0., 1.], [0., 0., -1.]] coords = [[scale * xi for xi in coord] for coord in template] return mg.Molecule(species, coords)
def get_ABE(self, formula, A_site, B_site, verbose=False): """ Estimate average metal-oxygen bond energy for complex perovskite oxide from simple oxide thermo data Formula from Sammells et al. (1992), Solid State Ionics 52, 111-123. Parameters: ----------- formula: oxide formula A_site: list of A-site elements B_site: list of B-site elements verbose: if True, print info about which simple oxides used in calculation """ #validated on compounds in Sammells 1992 - all but CaTi0.7Al0.3O3 agree #validated on (La,Sr)(Cr,Co,Fe)O3 compounds in https://pubs.acs.org/doi/suppl/10.1021/acs.jpcc.6b10571/suppl_file/jp6b10571_si_001.pdf #works if Co3O4 specified in oxide_dict comp = mg.Composition(formula) cd = comp.get_el_amt_dict() metals = [x for x in cd.keys() if x != 'O'] abe = 0 if verbose == True: print('Oxides used for ABE calculation:') for metal in metals: amt = cd[metal] met_mg = mg.Element(metal) try: #oxide_dict specifies which oxide to use oxide = self.oxide_dict[metal] oxide_mg = mg.Composition(oxide) m = oxide_mg.get(metal) n = oxide_mg.get('O') obe = self.oxide_obe(oxide) except KeyError: #if no oxide indicated in oxide_dict "placeholder - for now, take the lowest common oxidation state with a corresponding stable oxide" i = 0 while i != -1: ox = met_mg.common_oxidation_states[i] oxide, m, n = self.oxide_formula(metal, ox, return_mn=True) try: obe = self.oxide_obe(oxide) #print(obe) i = -1 except LookupError as err: i += 1 #try the next oxidation state if verbose == True: print(oxide) #print('m: {}, n: {}'.format(m,n)) if metal in A_site: abe += amt * obe / (12 * m) elif metal in B_site: abe += amt * obe / (6 * m) else: raise KeyError( '{} is not assigned to A or B site'.format(metal)) #print(abe) return abe
def classify_mx_pairs(elem_pair_lst: Union[list, Iterable]): """ Classify all metal-non_metal pairs into three categories. The following represents how relevant the category is with decreasing relevance 1. transition metal and oxygen 2. transition metal and non_oxygen non-metal, non-transition metal and oxygen 3. non-transition metal and non_oxygen non-metal :param elem_pair_lst: List or iterables, all metal-non_metal element pairs. e.g. ["Ti-O", "Ba-O"] :return: Ordered Dictionary, {"trans_oxy": [list of elem pairs in string format], "one_trans_or_one_oxy": [list of elem pairs in string format], "non_trans_non_oxy": [list of elem pairs in string format]} """ # parse all the element pairs from strings to tuples with two Pymatgen Elements elem_pairs_parsed = [ parse_elem_pair(elem_pair) for elem_pair in elem_pair_lst ] # initialize the empty lists trans_oxy = [] one_trans_or_one_oxy = [] non_trans_non_oxy = [] for elem_pair_str, (elem_1, elem_2) in zip(elem_pair_lst, elem_pairs_parsed): # if elem_1 is non_metal and elem_2 is a metal, swap the elements if (not elem_1.is_metal) and elem_2.is_metal: elem_1, elem_2 = elem_2, elem_1 # find all pairs where the metal is a transition metal and the non-metal is oxygen if elem_1.is_transition_metal and (elem_2 == mg.Element("O")): trans_oxy.append(elem_pair_str) # find all pairs where one element is a transition metal or one element is oxygen elif elem_1.is_transition_metal or (elem_2 == mg.Element("O")): one_trans_or_one_oxy.append(elem_pair_str) # find all pairs where the metal is non-transition metal and the non-metal is not oxygen else: non_trans_non_oxy.append(elem_pair_str) return OrderedDict({ "trans_oxy": trans_oxy, "one_trans_or_one_oxy": one_trans_or_one_oxy, "non_trans_non_oxy": non_trans_non_oxy })
def generateElementdict(): # Dictionary with Elements and their properties # Delete preprocessed data when changing featperEle elementdict = {} for i in range(1, 100): # 100 Elements in Dict commonoxidationstate = peri.Element.from_Z(i).common_oxidation_states orbitals = peri.Element.from_Z(i).full_electronic_structure sandp_count = 0 dandf_count = 0 ionizationenergy = 0 valence = 0 if len(commonoxidationstate) == 0: commonoxidationstate = 0 else: commonoxidationstate = peri.Element.from_Z( i).common_oxidation_states[0] for j in range(len(orbitals)): for k in range(len(orbitals[j])): if orbitals[j][k] == "s" or orbitals[j][k] == "p": sandp_count += orbitals[j][2] # count in third position if orbitals[j][k] == "d" or orbitals[j][k] == "f": dandf_count += orbitals[j][2] if i == 1: ionizationenergy = 13.6 else: ionizationenergy = ((i - 1) ^ 2) * 13.6 """ if i == 4 or i == 12 or i == 20: valence = 2 print("alkaine earth set to 2 valence e-") else: valence = peri.Element.from_Z(i).valence print("Element: ", i, "Valence: ", valence, type(valence)) """ # transition metals not working elementdict[i] = [ peri.Element.from_Z(i), # name peri.Element.from_Z(i).number, # atomic number mg.Element(peri.Element.from_Z(i)).group, # group peri.Element(peri.Element.from_Z(i)).row # row # peri.Element.from_Z(i).X, # Pauling electronegativity (none equals zero) # peri.Element.from_Z(i).number, # atomic number # commonoxidationstate, # common oxidation state if non set to zero # peri.Element.from_Z(i).average_ionic_radius, # average ionic radius # mg.Element(peri.Element.from_Z(i)).atomic_mass, # avarage mass # sandp_count, # count of e- in s and p orbitals # dandf_count, # couunt of e- in d and f orbitals # ionizationenergy, # ionizationenergy in eV ] # peri.Element.from_Z(i).valence] # number of valence electrons # print("Element and feat.:", elementdict) return elementdict
def IonRadVsLattice( picklestruct, ion ): #attempts to account for how rectangular the cell is by taking the smallest #unit cell length, and seeing how much larger or smaller than it is compared to the lithium atom volume initialvol = picklestruct.volume unitcelllengths = picklestruct.lattice.abc #we sould not scal ehere as we are comparing lattice to lithium ion radius, which is fixed minlength = np.min(unitcelllengths) Lirad = mg.Element(ion).average_ionic_radius diff = minlength / Lirad return diff / initialvol**(1 / 3)
def atomicNumber( unit_cell_formula): #remember, mean, mode, std, weighted mean, data = list() for elem in unit_cell_formula: element = mg.Element(elem) atmNumber = element.data['Atomic no'] for i in range(int(unit_cell_formula[elem])): data.append(atmNumber) answer = [np.mean(data), np.std(data)] return answer
def mol_D4h(central, ligand, d1, d2): """ Return a D4h molecule as a mg.Molecule object. Args: central: (string) Name of the central atom ligand: (string) Name of ligand atoms d1: (float) length of central-ligand axial distance d2: (float) length of central-ligand equatorial distance Returns mg.Molecule object """ species = [mg.Element(central)] + 6 * [mg.Element(ligand)] template = [[0., 0., 0.], [1., 0., 0.], [-1., 0., 0.], [0., 1., 0.], [0., -1., 0.], [0., 0., 1.], [0., 0., -1.]] coords = [template[0]] \ + [[d1 * xi for xi in coord] for coord in template[1:5]] \ + [[d2 * xi for xi in coord] for coord in template[5:]] return mg.Molecule(species, coords)
def classify_xx_pairs(elem_pair_lst: Union[list, Iterable]): """ Classify all non_metal-non_metal pairs into three categories. The following represents how relevant the category is with decreasing relevance 1. oxygen and oxygen 2. oxygen and non_oxygen non_metal 3. non_oxygen non_metal and non_oxygen non_metal :param elem_pair_lst: List or iterables, all non_metal-non_metal element pairs. e.g. ["Ti-Ti", "Ba-Ba"] :return: Ordered Dictionary, {"oxy_oxy": [list of elem pairs in string format], "oxy_non_oxy": [list of elem pairs in string format], "non_oxy_non_oxy": [list of elem pairs in string format]} """ # parse all the element pairs from strings to tuples with two Pymatgen Elements elem_pairs_parsed = [ parse_elem_pair(elem_pair) for elem_pair in elem_pair_lst ] # initialize the empty lists oxy_oxy = [] oxy_non_oxy = [] non_oxy_non_oxy = [] for elem_pair_str, (elem_1, elem_2) in zip(elem_pair_lst, elem_pairs_parsed): # find all pairs where both non_metals are oxygen if (elem_1 == mg.Element("O")) and (elem_2 == mg.Element("O")): oxy_oxy.append(elem_pair_str) # find all pairs where one of the two non_metals is an oxygen elif (elem_1 == mg.Element("O")) or (elem_2 == mg.Element("O")): oxy_non_oxy.append(elem_pair_str) # find all pairs where none of the two non_metals is an oxygen else: non_oxy_non_oxy.append(elem_pair_str) return OrderedDict({ "oxy_oxy": oxy_oxy, "oxy_non_oxy": oxy_non_oxy, "non_oxy_non_oxy": non_oxy_non_oxy })
def return_relevant_potentials(structure_oxid: mg.Structure, **kwargs): """ Return the relevant potentials for the metal site and non_metal site with the following relevance order 1. transition metal and oxygen 2. transition metal and non-oxygen 3. non-transition metal and oxygen 4. non-transition metal and non-oxygen :param structure_oxid: Pymatgen Structure, the input structure :return: Tuple, the maximum potentials (unit: V) for the metal site and non-metal site """ # get the maximum potentials for all the elements in the structure max_potentials = calc_elem_max_potential(structure_oxid, **kwargs) # get the classification of each element into metal and non_metals _, elem_group, _ = get_elem_info(structure_oxid, makesupercell=False) # get all the metals all_metals = elem_group["all_metals"] # if there are metals, find the transition metals if all_metals: trans_metals = [ metal for metal in all_metals if metal.is_transition_metal ] else: trans_metals = [] # find all the non_metals non_metals = elem_group["non_metals"] # find the corresponding potentials in each group except oxygen all_metals_max = choose_most_elec_neg_potential(all_metals, max_potentials) trans_metals_max = choose_most_elec_neg_potential(trans_metals, max_potentials) non_metals_max = choose_most_elec_neg_potential(non_metals, max_potentials) # if there is oxygen, find the corresponding potential try: oxygen_max = max_potentials[mg.Element("O")] except KeyError: oxygen_max = None # return potential tuples based on the hierarchy # if transition metal and oxygen both exist, return their potentials if trans_metals_max and oxygen_max: return trans_metals_max, oxygen_max # if only transition metal exists, return the transition metal and non_oxygen non_metal potentials elif trans_metals_max and (not oxygen_max): return trans_metals_max, non_metals_max # if only oxygen exists, return the non_transition metal and oxygen potentials elif (not trans_metals_max) and oxygen_max: return all_metals_max, oxygen_max # if neither transition metal nor oxygen exists, return the non_transition metal and non_oxygen potentials else: return all_metals_max, non_metals_max
def _closest_CN(self, el, ox, target): ''' get closest coordination number (in roman numerals) to target convenience function for choosing appropriate Shannon radius - used in ox_combos ''' cn_rom = mg.Element(el).data['Shannon radii']['{}'.format(ox)].keys() cn_rom = [rn for rn in cn_rom if rn in self._rom_to_num.keys() ] #remove any roman numerals not in dict (eg "IVSQ") cn = [self._rom_to_num[rn] for rn in cn_rom] idx = np.argmin(np.abs(np.array(cn) - target)) rom = self._num_to_rom[cn[idx]] return rom