class DopingFermi(BaseFeaturizer): """ The fermi level (w.r.t. selected reference energy) associated with a specified carrier concentration (1/cm3) and temperature. This featurizar requires the total density of states and structure. The Structure as dos.structure (e.g. in CompleteDos) is required by FermiDos class. Args: dopings ([float]): list of doping concentrations 1/cm3. Note that a negative concentration is treated as electron majority carrier (n-type) and positive for holes (p-type) eref (str or int or float): energy alignment reference. Defaults to midgap (equilibrium fermi). A fixed number can also be used. str options: "midgap", "vbm", "cbm", "dos_fermi", "band_center" T (float): absolute temperature in Kelvin return_eref: if True, instead of aligning the fermi levels based on eref, it (eref) will be explicitly returned as a feature Returns (featurize returns [float] and featurize_labels returns [str]): examples: fermi_c-1e+20T300 (float): the fermi level for the electron concentration of 1e20 and the temperature of 300K. fermi_c1e+18T600 (float): the fermi level for the hole concentration of 1e18 and the temperature of 600K. midgap eref (float): if return_eref==True then eref (midgap here) energy is returned. In this case other fermi levels returned are absolute as opposed to relative to eref (i.e. if not return_eref) """ def __init__(self, dopings=None, eref="midgap", T=300, return_eref=False): self.dopings = dopings or [-1e20, 1e20] self.eref = eref self.T = T self.return_eref = return_eref self.BC = BandCenter() def featurize(self, dos, bandgap=None): """ Args: dos (pymatgen Dos, CompleteDos or FermiDos): bandgap (float): for example the experimentally measured band gap or one that is calculated via more accurate methods than the one used to generate dos. dos will be scissored to have the same electronic band gap as bandgap. Returns ([float]): features are fermi levels in eV at the given concentrations and temperature + eref in eV if return_eref """ dos = FermiDos(dos, bandgap=bandgap) feats = [] eref = 0.0 for c in self.dopings: fermi = dos.get_fermi(c=c, T=self.T, nstep=50) if isinstance(self.eref, str): if self.eref == "dos_fermi": eref = dos.efermi elif self.eref in ["midgap", "vbm", "cbm"]: ecbm, evbm = dos.get_cbm_vbm() if self.eref == "midgap": eref = (evbm + ecbm) / 2.0 elif self.eref == "vbm": eref = evbm elif self.eref == "cbm": eref = ecbm elif self.eref == "band center": eref = self.BC.featurize(dos.structure.composition)[0] else: raise ValueError('Unsupported "eref": {}'.format( self.eref)) else: eref = self.eref if not self.return_eref: fermi -= eref feats.append(fermi) if self.return_eref: feats.append(eref) return feats def feature_labels(self): """ Returns ([str]): list of names of the features generated by featurize example: "fermi_c-1e+20T300" that is the fermi level for the electron concentration of 1e20 (c-1e+20) and temperature of 300K. """ labels = [] for c in self.dopings: labels.append("fermi_c{}T{}".format(c, self.T)) if self.return_eref: labels.append("{} eref".format(self.eref)) return labels def implementors(self): return ["Alireza Faghaninia"] def citations(self): return []
class GenericFeaturizer(BaseFeaturizer): """ Featurizer to use generic properties available in matminer featurizers; no features from BCA class utilized """ def __init__(self,normalize_formula=False): self.normalize_formula = normalize_formula # don't need ValenceOrbital - valence counts etc. covered in ElementProperty.from_preset('magpie') # self.ValenceOrbital = ValenceOrbital() self.AtomicOrbitals = AtomicOrbitalsMod() self.CohesiveEnergy = CohesiveEnergy() self.BandCenter = BandCenter() self.ValenceOrbitalEnergy = ValenceOrbitalEnergy() # ElementProperty featurizer with magpie properties plus additional properties self.ElementProperty = ElementProperty.from_preset('magpie') self.ElementProperty.features += ['BoilingT', 'BulkModulus', 'ShearModulus', 'Density','MolarVolume', 'FusionEnthalpy','HeatVaporization', 'Polarizability', 'ThermalConductivity'] # range, min, max are irrelevant inside the ternary # self.ElementProperty.stats = ['mean', 'avg_dev','mode'] # check matminer featurizers self.check_matminer_featurizers() def featurize(self,composition): # use BCA just to get composition and metal_composition bca = BCA(composition,'ionic_radius',self.normalize_formula) ao_features = self.AtomicOrbitals.featurize(bca.metal_composition) # H**O and LUMO character and energy levels for metals from atomic orbitals) ao_features = [ao_features[i] for i in range(len(ao_features)) if i not in (0,1,3,4)] # exclude HOMO_character,HOMO_element, LUMO_character, LUMO_element - categoricals ce_features = self.CohesiveEnergy.featurize(bca.metal_composition,formation_energy_per_atom=1e-10) # avg metal elemental cohesive energy bc_features = self.BandCenter.featurize(bca.metal_composition) + self.BandCenter.featurize(bca.composition) ve_features = self.ValenceOrbitalEnergy.featurize(bca.metal_composition) + self.ValenceOrbitalEnergy.featurize(bca.composition) ep_features = self.ElementProperty.featurize(bca.metal_composition) + self.ElementProperty.featurize(bca.composition) mm_features = ao_features + ce_features + bc_features + ve_features + ep_features return mm_features def feature_labels(self): """ Feature labels for matminer-derived features """ labels = [ #AtomicOrbitals labels #'M_HOMO_character', 'M_HOMO_energy', #'M_LUMO_character', 'M_LUMO_energy', 'M_AO_gap', #CohesiveEnergy labels 'M_cohesive_energy_mean', #BandCenter labels 'M_BandCenter', 'BCA_BandCenter', #ValenceOrbitalEnergy labels 'M_ValenceEnergy_mean', 'BCA_ValenceEnergy_mean' ] labels += [f'M {l}' for l in self.ElementProperty.feature_labels()] labels += [f'BCA {l}' for l in self.ElementProperty.feature_labels()] return labels @property def matminer_units(self): """ Feature units for matminer-derived features """ units = [ #ValenceOrbital units 'none', 'none', 'none', 'none', 'none', 'none', 'none', 'none', 'none', #AtomicOrbitals units #'M_HOMO_character', 'energy', #'M_LUMO_character', 'energy', 'energy', #CohesiveEnergy units 'energy', #BandCenter units 'energy', 'energy', #ValenceOrbitalEnergy units 'energy', 'energy' ] units += self.ElementProperty_units return units def feature_units(self): bca_units = BCA(mg.Composition('BaO')).feature_units() return bca_units + self.matminer_units def check_matminer_featurizers(self): """ Check that features and feature order for matminer featurizers are as expected If features or feature order have changed, featurize() may return unexpected features that do not align with feature_labels() """ #verify that matminer feature labels haven't changed if self.AtomicOrbitals.feature_labels() != ['HOMO_character', 'HOMO_element', 'HOMO_energy', 'LUMO_character', 'LUMO_element', 'LUMO_energy', 'gap_AO']: raise Exception('AtomicOrbitals features or labels have changed') if self.CohesiveEnergy.feature_labels() != ['cohesive energy']: raise Exception('CohesiveEnergy features or labels have changed') if self.BandCenter.feature_labels() != ['band center']: raise Exception('BandCenter features or labels have changed') def citations(self): featurizers = [self.AtomicOrbitals, self.CohesiveEnergy, self.BandCenter, self.ValenceOrbitalEnergy] citations = sum([f.citations() for f in featurizers],[]) # add pymatgen citation citations += [ "@article{Ong2012b," "author = {Ong, Shyue Ping and Richards, William Davidson and Jain, Anubhav and Hautier, Geoffroy and Kocher, Michael and Cholia, Shreyas and Gunter, Dan and Chevrier, Vincent L. and Persson, Kristin A. and Ceder, Gerbrand}," "doi = {10.1016/j.commatsci.2012.10.028}," "file = {:Users/shyue/Mendeley Desktop/Ong et al/Computational Materials Science/2013 - Ong et al. - Python Materials Genomics (pymatgen) A robust, open-source python library for materials analysis.pdf:pdf;:Users/shyue/Mendeley Desktop/Ong et al/Computational Materials Science/2013 - Ong et al. - Python Materials Genomics (pymatgen) A robust, open-source python library for materials analysis(2).pdf:pdf}," "issn = {09270256}," "journal = {Computational Materials Science}," "month = feb," "pages = {314--319}," "title = {{Python Materials Genomics (pymatgen): A robust, open-source python library for materials analysis}}," "url = {http://linkinghub.elsevier.com/retrieve/pii/S0927025612006295}," "volume = {68}," "year = {2013}" "}" ] return list(np.unique(citations))
class DopingFermi(BaseFeaturizer): """ The fermi level (w.r.t. selected reference energy) associated with a specified carrier concentration (1/cm3) and temperature. This featurizar requires the total density of states and structure. The Structure as dos.structure (e.g. in CompleteDos) is required by FermiDos class. Args: dopings ([float]): list of doping concentrations 1/cm3. Note that a negative concentration is treated as electron majority carrier (n-type) and positive for holes (p-type) eref (str or int or float): energy alignment reference. Defaults to midgap (equilibrium fermi). A fixed number can also be used. str options: "midgap", "vbm", "cbm", "dos_fermi", "band_center" T (float): absolute temperature in Kelvin return_eref: if True, instead of aligning the fermi levels based on eref, it (eref) will be explicitly returned as a feature Returns (featurize returns [float] and featurize_labels returns [str]): examples: fermi_c-1e+20T300 (float): the fermi level for the electron concentration of 1e20 and the temperature of 300K. fermi_c1e+18T600 (float): fermi level for the hole concentration of 1e18 and the temperature of 600K. midgap eref (float): if return_eref==True then eref (midgap here) energy is returned. In this case, fermi levels are absolute as opposed to relative to eref (i.e. if not return_eref) """ def __init__(self, dopings=None, eref="midgap", T=300, return_eref=False): self.dopings = dopings or [-1e20, 1e20] self.eref = eref self.T = T self.return_eref = return_eref self.BC = BandCenter() def featurize(self, dos, bandgap=None): """ Args: dos (pymatgen Dos, CompleteDos or FermiDos): bandgap (float): for example the experimentally measured band gap or one that is calculated via more accurate methods than the one used to generate dos. dos will be scissored to have the same electronic band gap as bandgap. Returns ([float]): features are fermi levels in eV at the given concentrations and temperature + eref in eV if return_eref """ dos = FermiDos(dos, bandgap=bandgap) feats = [] eref = 0.0 for c in self.dopings: fermi = dos.get_fermi(c=c, T=self.T, nstep=50) if isinstance(self.eref, str): if self.eref == "dos_fermi": eref = dos.efermi elif self.eref in ["midgap", "vbm", "cbm"]: ecbm, evbm = dos.get_cbm_vbm() if self.eref == "midgap": eref = (evbm + ecbm) / 2.0 elif self.eref == "vbm": eref = evbm elif self.eref == "cbm": eref = ecbm elif self.eref == "band center": eref = self.BC.featurize(dos.structure.composition)[0] else: raise ValueError('Unsupported "eref": {}'.format(self.eref)) else: eref = self.eref if not self.return_eref: fermi -= eref feats.append(fermi) if self.return_eref: feats.append(eref) return feats def feature_labels(self): """ Returns ([str]): list of names of the features generated by featurize example: "fermi_c-1e+20T300" that is the fermi level for the electron concentration of 1e20 (c-1e+20) and temperature of 300K. """ labels = [] for c in self.dopings: labels.append("fermi_c{}T{}".format(c, self.T)) if self.return_eref: labels.append("{} eref".format(self.eref)) return labels def implementors(self): return ["Alireza Faghaninia"] def citations(self): return []
class BCA_Featurizer(BaseFeaturizer): def __init__(self,radius_type='ionic_radius',normalize_formula=False): self.radius_type = radius_type self.normalize_formula = normalize_formula self.ValenceOrbital = ValenceOrbital() self.AtomicOrbitals = AtomicOrbitalsMod() self.CohesiveEnergy = CohesiveEnergy() self.BandCenter = BandCenter() self.ValenceOrbitalEnergy = ValenceOrbitalEnergy() #custom ElementProperty featurizer elemental_properties = ['BoilingT', 'MeltingT', 'BulkModulus', 'ShearModulus', 'Row', 'Column', 'Number', 'MendeleevNumber', 'SpaceGroupNumber', 'Density','MolarVolume', 'FusionEnthalpy','HeatVaporization', 'Polarizability', 'ThermalConductivity'] self.ElementProperty = ElementProperty(data_source='magpie',features=elemental_properties, stats=["mean", "std_dev"]) #check matminer featurizers self.check_matminer_featurizers() def featurize(self,composition): bca = BCA(composition,self.radius_type,self.normalize_formula) bca_features = bca.featurize() vo_features = self.ValenceOrbital.featurize(bca.metal_composition) #avg and frac s, p , d, f electrons for metals vo_features += [sum(vo_features[0:3])] #avg total valence electrons for metals ao_features = self.AtomicOrbitals.featurize(bca.metal_composition) #H**O and LUMO character and energy levels for metals from atomic orbitals) ao_features = [ao_features[i] for i in range(len(ao_features)) if i not in (0,1,3,4)]#exclude HOMO_character,HOMO_element, LUMO_character, LUMO_element - categoricals ce_features = self.CohesiveEnergy.featurize(bca.metal_composition,formation_energy_per_atom=1e-10) #avg metal elemental cohesive energy bc_features = self.BandCenter.featurize(bca.metal_composition) + self.BandCenter.featurize(bca.composition) ve_features = self.ValenceOrbitalEnergy.featurize(bca.metal_composition) + self.ValenceOrbitalEnergy.featurize(bca.composition) ep_features = self.ElementProperty.featurize(bca.metal_composition) + self.ElementProperty.featurize(bca.composition) mm_features = vo_features + ao_features + ce_features + bc_features + ve_features + ep_features return list(bca_features.values()) + mm_features @property def ElementProperty_custom_labels(self): """ Generate custom labels for ElementProperty featurizer that follow same naming convention as Perovskite class """ elemental_property_label_map = {'BoilingT':'boil_temp','MeltingT':'melt_temp', 'BulkModulus':'bulk_mod','ShearModulus':'shear_mod', 'Row':'row','Column':'column','Number':'number','MendeleevNumber':'mendeleev','SpaceGroupNumber':'space_group', 'Density':'density','MolarVolume':'molar_vol', 'FusionEnthalpy':'H_fus','HeatVaporization':'H_vap', 'Polarizability':'polarizability', 'ThermalConductivity':'sigma_therm'} element_property_labels = list(map(elemental_property_label_map.get,self.ElementProperty.features)) labels = [] for attr in element_property_labels: for stat in self.ElementProperty.stats: if stat=='std_dev': stat = 'std' labels.append(f'M_{attr}_{stat}') for attr in element_property_labels: for stat in self.ElementProperty.stats: if stat=='std_dev': stat = 'std' labels.append(f'BCA_{attr}_{stat}') return labels @property def ElementProperty_units(self): """ Generate units for ElementProperty featurizer that follow same naming convention as Perovskite class """ elemental_property_unit_map = {'BoilingT':'temperature','MeltingT':'temperature', 'BulkModulus':'pressure','ShearModulus':'pressure', 'Row':'none','Column':'none','Number':'none','MendeleevNumber':'none','SpaceGroupNumber':'none', 'Density':'density','MolarVolume':'volume', 'FusionEnthalpy':'energy','HeatVaporization':'energy', 'Polarizability':'polarizability', 'ThermalConductivity':'therm'} element_property_units = list(map(elemental_property_unit_map.get,self.ElementProperty.features)) units = [] for ep_unit in element_property_units: for stat in self.ElementProperty.stats: units.append(ep_unit) return units*2 def ElementProperty_label_check(self): """ Check that ElementProperty feature labels are as expected If not, features may not align with feature labels """ #ElementProperty.feature_labels() code as of 1/24/20 labels = [] for attr in self.ElementProperty.features: src = self.ElementProperty.data_source.__class__.__name__ for stat in self.ElementProperty.stats: labels.append("{} {} {}".format(src, stat, attr)) if labels!=self.ElementProperty.feature_labels(): raise Exception('ElementProperty features or labels have changed') @property def matminer_labels(self): """ Feature labels for matminer-derived features """ labels = [ #ValenceOrbital labels 'M_ValenceElec_s_mean', 'M_ValenceElec_p_mean', 'M_ValenceElec_d_mean', 'M_ValenceElec_f_mean', 'M_ValenceElec_s_frac', 'M_ValenceElec_p_frac', 'M_ValenceElec_d_frac', 'M_ValenceElec_f_frac', 'M_ValenceElec_tot_mean', #AtomicOrbitals labels #'M_HOMO_character', 'M_HOMO_energy', #'M_LUMO_character', 'M_LUMO_energy', 'M_AO_gap', #CohesiveEnergy labels 'M_cohesive_energy_mean', #BandCenter labels 'M_BandCenter', 'BCA_BandCenter', #ValenceOrbitalEnergy labels 'M_ValenceEnergy_mean', 'BCA_ValenceEnergy_mean' ] labels += self.ElementProperty_custom_labels return labels @property def matminer_units(self): """ Feature units for matminer-derived features """ units = [ #ValenceOrbital units 'none', 'none', 'none', 'none', 'none', 'none', 'none', 'none', 'none', #AtomicOrbitals units #'M_HOMO_character', 'energy', #'M_LUMO_character', 'energy', 'energy', #CohesiveEnergy units 'energy', #BandCenter units 'energy', 'energy', #ValenceOrbitalEnergy units 'energy', 'energy' ] units += self.ElementProperty_units return units def feature_labels(self): bca_feature_labels = list(BCA(mg.Composition('BaO'),self.radius_type,self.normalize_formula).featurize().keys()) return bca_feature_labels + self.matminer_labels def feature_units(self): bca_units = BCA(mg.Composition('BaO')).feature_units() return bca_units + self.matminer_units def check_matminer_featurizers(self): """ Check that features and feature order for matminer featurizers are as expected If features or feature order have changed, featurize() may return unexpected features that do not align with feature_labels() """ #verify that matminer feature labels haven't changed if self.ValenceOrbital.feature_labels() != ['avg s valence electrons', 'avg p valence electrons', 'avg d valence electrons', 'avg f valence electrons', 'frac s valence electrons', 'frac p valence electrons', 'frac d valence electrons', 'frac f valence electrons']: raise Exception('ValenceOrbital features or labels have changed') if self.AtomicOrbitals.feature_labels() != ['HOMO_character', 'HOMO_element', 'HOMO_energy', 'LUMO_character', 'LUMO_element', 'LUMO_energy', 'gap_AO']: raise Exception('AtomicOrbitals features or labels have changed') if self.CohesiveEnergy.feature_labels() != ['cohesive energy']: raise Exception('CohesiveEnergy features or labels have changed') if self.BandCenter.feature_labels() != ['band center']: raise Exception('BandCenter features or labels have changed') self.ElementProperty_label_check() def citations(self): featurizers = [self.ValenceOrbital, self.AtomicOrbitals, self.CohesiveEnergy, self.BandCenter, self.ValenceOrbitalEnergy, BCA(mg.Composition('BaO'))] return list(np.unique(sum([f.citations() for f in featurizers],[])))