def compute_delta(self, comp): """Compute Yang's delta parameter :math:`\sqrt{\sum^n_{i=1} c_i \left( 1 - \\frac{r_i}{\\bar{r}} \\right)^2 }` where :math:`c_i` and :math:`r_i` are the fraction and radius of element :math:`i`, and :math:`\\bar{r}` is the fraction-weighted average of the radii. We use the radii compiled by .. Miracle et al. `https://www.tandfonline.com/doi/ref/10.1179/095066010X12646898728200?scroll=top`. Args: comp (Composition) - Composition to assess Returns: (float) delta """ elements, fractions = zip(*comp.element_composition.items()) # Get the radii of elements radii = self.elem_data.get_elemental_properties( elements, "MiracleRadius") mean_r = PropertyStats.mean(radii, fractions) # Compute the mean (1 - r/\\bar{r})^2 r_dev = np.power(1.0 - np.divide(radii, mean_r), 2) return np.sqrt(PropertyStats.mean(r_dev, fractions))
def featurize(self, comp): """Weighted fraction of valence electrons in each orbital Args: comp: Pymatgen composition object Returns: valence_attributes (list of floats): Average number and/or fraction of valence electrons in specfied orbitals """ elements, fractions = zip(*comp.element_composition.items()) # Get the mean number of electrons in each shell avg = [ PropertyStats.mean(self.data_source.get_elemental_properties( elements, f"N{orb}Valence"), weights=fractions) for orb in self.orbitals ] # If needed, get fraction of electrons in each shell if "frac" in self.props: # NOTE comprhys: even if needed frac isn't used? avg_total_valence = PropertyStats.mean( self.data_source.get_elemental_properties( elements, "NValence"), weights=fractions) frac = [a / avg_total_valence for a in avg] # Get the desired attributes valence_attributes = [] for prop in self.props: valence_attributes += locals()[prop] return valence_attributes
def compute_simultaneous_packing_efficiency(self, comp): """Compute the packing efficiency of the system when the neighbor shell of each atom has the same composition as the alloy. When this criterion is satisfied, it is possible for every atom in this system to be simultaneously as efficiently-packed as possible. Args: comp (Composition): Composition to be assessed Returns (float) Average APE of all atoms (float) Average deviation of the APE of each atom from ideal (0) """ # Compute the average atomic radius of the system elements, fractions = zip(*comp.element_composition.items()) radii = self._data_source.get_elemental_properties(elements, 'MiracleRadius') mean_radius = PropertyStats.mean(radii, fractions) # Compute the APE for each cluster best_ape = [ self.find_ideal_cluster_size(r / mean_radius)[1] for r in radii ] # Return the averages return PropertyStats.mean(best_ape, fractions), PropertyStats.mean(np.abs(best_ape), fractions)
def featurize(self, comp): """ Args: comp: (Composition) Composition to be featurized Returns: avg_anion_affin (single-element list): average electron affinity*formal charge of anions """ # Check if oxidation states have been computed if not has_oxidation_states(comp): raise ValueError('Composition lacks oxidation states') # Get the species and fractions species, fractions = zip(*comp.items()) # Determine which species are anions anions, fractions = zip(*[(s, f) for s, f in zip(species, fractions) if s.oxi_state < 0]) # Compute the electron_affinity*formal_charge for each anion electron_affin = [ self.data_source.get_elemental_property(s.element, "electron_affin") * s.oxi_state for s in anions ] # Compute the average affinity avg_anion_affin = PropertyStats.mean(electron_affin, fractions) return [avg_anion_affin]
def compute_omega(self, comp): """Compute Yang's mixing thermodynamics descriptor :math:`\\frac{T_m \Delta S_{mix}}{ | \Delta H_{mix} | }` Where :math:`T_m` is average melting temperature, :math:`\Delta S_{mix}` is the ideal mixing entropy, and :math:`\Delta H_{mix}` is the average mixing enthalpies of all pairs of elements in the alloy Args: comp (Composition) - Composition to featurizer Returns: (float) Omega """ # Special case: Elemental compound (entropy == 0 -> Omega == 1) if len(comp) == 1: return 0 # Get the element names and fractions elements, fractions = zip( *comp.element_composition.fractional_composition.items()) # Get the mean melting temperature mean_Tm = PropertyStats.mean( self.elem_data.get_elemental_properties(elements, "MeltingT"), fractions) # Get the mixing entropy entropy = np.dot(fractions, np.log(fractions)) * 8.314 / 1000 # Get the mixing enthalpy enthalpy = 0 for i, (e1, f1) in enumerate(zip(elements, fractions)): for e2, f2 in zip(elements[:i], fractions): enthalpy += f1 * f2 * self.dhf_mix.get_mixing_enthalpy(e1, e2) enthalpy *= 4 # Make sure the enthalpy is nonzero # The limit as dH->0 of omega is +\inf. A very small positive dH will approximate # this limit without causing issues with infinite features enthalpy = max(1e-6, abs(enthalpy)) return abs(mean_Tm * entropy / enthalpy)
def featurize(self, strc): # Compute the Voronoi tessellation of each site voro = VoronoiNN(extra_nn_info=True, weight=self.weight) nns = get_all_nearest_neighbors(voro, strc) # Compute the mean bond length of each atom, and the mean # variation within each cell mean_bond_lengths = np.zeros((len(strc), )) bond_length_var = np.zeros_like(mean_bond_lengths) for i, nn in enumerate(nns): weights = [n['weight'] for n in nn] lengths = [n['poly_info']['face_dist'] * 2 for n in nn] mean_bond_lengths[i] = PropertyStats.mean(lengths, weights) # Compute the mean absolute deviation of the bond lengths bond_length_var[i] = PropertyStats.avg_dev(lengths, weights) / \ mean_bond_lengths[i] # Normalize the bond lengths by the average of the whole structure # This is done to make the attributes length-scale-invariant mean_bond_lengths /= mean_bond_lengths.mean() # Compute statistics related to bond lengths features = [ PropertyStats.avg_dev(mean_bond_lengths), mean_bond_lengths.max(), mean_bond_lengths.min() ] features += [ PropertyStats.calc_stat(bond_length_var, stat) for stat in self.stats ] # Compute the variance in volume cell_volumes = [ sum(x['poly_info']['volume'] for x in nn) for nn in nns ] features.append( PropertyStats.avg_dev(cell_volumes) / np.mean(cell_volumes)) return features
def featurize(self, comp): """ Args: comp: Pymatgen Composition object Returns: en_diff_stats (list of floats): Property stats of electronegativity difference """ # Check if oxidation states have been determined if not has_oxidation_states(comp): raise ValueError('Oxidation states have not yet been determined') if not is_ionic(comp): raise ValueError('Composition is not ionic') # Determine the average anion EN anions, anion_fractions = zip(*[(s, x) for s, x in comp.items() if s.oxi_state < 0]) # If there are no anions, raise an Exception if len(anions) == 0: raise Exception('Features not applicable: Compound contains no anions') anion_en = [s.element.X for s in anions] mean_anion_en = PropertyStats.mean(anion_en, anion_fractions) # Determine the EN difference for each cation cations, cation_fractions = zip(*[(s, x) for s, x in comp.items() if s.oxi_state > 0]) # If there are no cations, raise an Exception # It is possible to construct a non-charge-balanced Composition, # so we have to check for both the presence of anions and cations if len(cations) == 0: raise Exception('Features not applicable: Compound contains no cations') en_difference = [mean_anion_en - s.element.X for s in cations] # Compute the statistics return [ PropertyStats.calc_stat(en_difference, stat, cation_fractions) for stat in self.stats ]