def input_handling(args): comp1 = Composition(args.composition_1) comp2 = Composition(args.composition_2) entry1 = VirtualEntry.from_composition(comp1) entry2 = VirtualEntry.from_composition(comp2) entry1.stabilize() entry2.stabilize() entry1.energy_correction(args.e1) entry2.energy_correction(args.e2) return entry1, entry2
def get_mix_entry(mix_dict): """ Mixing PDEntry or GrandPotEntry for the binary search algorithm. """ entry1, entry2 = mix_dict.keys() x1, x2 = mix_dict[entry1], mix_dict[entry2] if type(entry1) == GrandPotPDEntry: mid_ori_entry = VirtualEntry.from_mixing({ entry1.original_entry: x1, entry2.original_entry: x2 }) return GrandPotPDEntry(mid_ori_entry, entry1.chempots) else: return VirtualEntry.from_mixing({entry1: x1, entry2: x2})
def get_phase_equilibria_from_composition(args): """ Provides the phase equilibria of a phase with given composition """ comp = Composition(args.composition) entry = VirtualEntry.from_composition(comp) print(entry.get_printable_PE_data_in_pd()) return 0
def get_full_evolution_profile(pd, entry1, entry2, x1, x2): """ This function is used to solve the transition points along a path on convex hull. The essence is to use binary search, which is more accurate and faster than brutal force screening This is a recursive function. :param pd: PhaseDiagram of GrandPotentialPhaseDiagram :param entry1 & entry2: mixing entry1/entry2, PDEntry for pd_mixing, GrandPotEntry for gppd_mixing :param x1 & x2: The mixing ratio range for binary search. :return: An uncleaned but complete profile with all transition points. """ evolution_profile = {} entry_left = get_mix_entry({entry1: x1, entry2: 1 - x1}) entry_right = get_mix_entry({entry1: x2, entry2: 1 - x2}) (decomp1, h1) = pd.get_decomp_and_e_above_hull(entry_left) (decomp2, h2) = pd.get_decomp_and_e_above_hull(entry_right) decomp1 = set(decomp1.keys()) decomp2 = set(decomp2.keys()) evolution_profile[x1] = (decomp1, h1) evolution_profile[x2] = (decomp2, h2) if decomp1 == decomp2: return evolution_profile intersect = decomp1 & decomp2 if len(intersect) > 0: # This is try to catch a single transition point try: rxn = ComputedReaction([entry_left, entry_right], list(intersect)) if not {entry_left, entry_right} < set(rxn.all_entries): return evolution_profile c1 = rxn.coeffs[rxn.all_entries.index(entry_left)] c2 = rxn.coeffs[rxn.all_entries.index( entry_right )] # I know this is tedious but this is the only way I found that works.. x = (c1 * x1 + c2 * x2) / (c1 + c2) if c1 * c2 == 0: return evolution_profile entry_mid = VirtualEntry.from_mixing({ entry_left: c1 / (c1 + c2), entry_right: c2 / (c1 + c2) }) h_mid = pd.get_decomp_and_e_above_hull(entry_mid)[1] evolution_profile[x] = (intersect, h_mid) return evolution_profile except ReactionError: pass x_mid = (x1 + x2) / 2.0 entry_mid = get_mix_entry({entry1: 0.5, entry2: 0.5}) (decomp_mid, h_mid) = pd.get_decomp_and_e_above_hull(entry_mid) decomp_mid = set(decomp_mid.keys()) evolution_profile[x_mid] = (decomp_mid, h_mid) part1 = get_full_evolution_profile(pd, entry1, entry2, x1, x_mid) part2 = get_full_evolution_profile(pd, entry1, entry2, x_mid, x2) evolution_profile.update(part1) evolution_profile.update(part2) return evolution_profile
def get_gppd_entries(self, open_el): if open_el in (self.entry1.composition + self.entry2.composition).keys(): gppd_entries = self.PDEntries else: comp = self.entry1.composition + self.entry2.composition + Composition( open_el.symbol) gppd_entries = VirtualEntry.from_composition( comp).get_GPPD_entries(open_el) return gppd_entries
def get_phase_evolution_profile(args): """ Provides the phase equilibria and decomposition energy evolution process of a phase when open to a specific element Chemical potential is referenced to pure phase of open element. """ comp = Composition(args.composition) entry = VirtualEntry.from_composition(comp) oe = args.open_element entry.stabilize() print(entry.get_printable_evolution_profile(oe, allowpmu=args.posmu)) return 0
def __init__(self, entry1, entry2, entries=None, sup_el=None): comp1 = entry1.composition comp2 = entry2.composition norm1 = 1.0 / entry1.composition.num_atoms norm2 = 1.0 / entry2.composition.num_atoms self.entry1 = VirtualEntry.from_composition(entry1.composition * norm1, energy=entry1.energy * norm1, name=comp1.reduced_formula) self.entry2 = VirtualEntry.from_composition(entry2.composition * norm2, energy=entry2.energy * norm2, name=comp2.reduced_formula) if not entries: entry_mix = VirtualEntry.from_composition(comp1 + comp2) entries = entry_mix.get_PD_entries(sup_el=sup_el) entries += [entry1, entry2] self.PDEntries = entries self.PD = PhaseDiagram(entries)
def get_gppd_transition_chempots(self, open_el, gppd_entries=None): """ This is to get all possible transition chemical potentials from PD (rather than GPPD) Still use pure element ref. # May consider supporting negative miu in the future """ if not gppd_entries: gppd_entries = self.get_gppd_entries(open_el) pd = PhaseDiagram(gppd_entries) vaspref_mius = pd.get_transition_chempots(Element(open_el)) el_ref = VirtualEntry.get_mp_entry(open_el) elref_mius = [miu - el_ref.energy_per_atom for miu in vaspref_mius] return elref_mius
def get_phase_equilibria_and_decomposition_energy_under_mu_from_composition( args): """ Provide the phase equilibria and decomposition energy when open to one element with given miu Chemical potential is referenced to pure phase of open element. """ comp = Composition(args.composition) chempot = {args.open_element: args.chemical_potential} entry = VirtualEntry.from_composition(comp) entry.stabilize() print( entry.get_printable_PE_and_decomposition_in_gppd(chempot, entries=None)) return 0
def plot_vc(args): """ Get the plot data of voltage profile. """ comp = Composition(args.composition) entry = VirtualEntry.from_composition(comp) oe = args.open_element entry.stabilize() common_working_ions = dict(Li=1, Na=1, K=1, Mg=2, Ca=2, Al=3) valence = args.valence if args.valence else common_working_ions[oe] oe_list, v_list = entry.get_vc_plot_data(oe, valence=valence, allowpmu=args.posmu) print(entry.get_printable_vc_plot_data(oe, oe_list, v_list)) entry.get_voltage_profile_plot(oe, oe_list, v_list, valence).show()
def gppd_mixing(self, chempots, gppd_entries=None): """ This function give the phase equilibria of a pseudo-binary in a open system (GPPD). It will give a complete evolution profile for mixing ratio x change from 0 to 1. x is the ratio (both entry norm. to 1 atom/fu(w/o open element) ) or each entry """ open_el = list(chempots.keys())[0] el_ref = VirtualEntry.get_mp_entry(open_el) chempots[open_el] = chempots[open_el] + el_ref.energy_per_atom gppd_entry1 = GrandPotPDEntry( self.entry1, {Element[_]: chempots[_] for _ in chempots}) gppd_entry2 = GrandPotPDEntry( self.entry2, {Element[_]: chempots[_] for _ in chempots}) if not gppd_entries: gppd_entries = self.get_gppd_entries(open_el) gppd = GrandPotentialPhaseDiagram(gppd_entries, chempots) profile = get_full_evolution_profile(gppd, gppd_entry1, gppd_entry2, 0, 1) cleaned = clean_profile(profile) return cleaned