Source code for defermi.chempots.generator

"""
Generate sets of chemical potentials
"""
import numpy as np
import warnings

from pymatgen.core.periodic_table import Element
from pymatgen.core.composition import Composition
from pymatgen.analysis.phase_diagram import PhaseDiagram

from .core import Chempots
from .oxygen import get_pressure_reservoirs_from_precursors
from .phase_diagram import _get_composition_object, PDHandler
from .reservoirs import Reservoirs


[docs] def generate_chempots_from_condition( composition, condition, phase_diagram=None, API_KEY=None, thermo_type='GGA_GGA+U', **kwargs): """ Generate Chempots dictionary using the data from the Materials Project. Condition must be specified as "<el>-rich" or "<el>-poor". The phase diagram with the elements contained in the target composition is build and used to create the chemical potentials range for the target element. Parameters ---------- composition : str or Composition Composition of the target material. condition : str Condition for the choice of chemical potential. "<el>-poor" or "<el>-rich>". phase_diagram : PhaseDiagram Pymatgen PhaseDiagram object. If not provided it is pulled from the Materials Project database. API_KEY : str API KEY for the Materials Project database. If not provided, `pymatgen` looks in the configuration file. thermo_type : str The thermo type to pass to MP database. kwargs : dict Kwargs to pass to `get_phase_diagram_from_chemsys`. Returns ------- Chempots object """ from ..tools.materials_project import MPDatabase comp = _get_composition_object(composition) if type(phase_diagram) == PhaseDiagram: pd = phase_diagram else: chemsys = '-'.join(el.symbol for el in comp.elements) pd = MPDatabase(API_KEY=API_KEY).get_phase_diagram_from_chemsys( chemsys=chemsys, thermo_type=thermo_type, **kwargs) stable_compositions = [entry.composition.get_reduced_composition_and_factor()[0] for entry in pd.stable_entries] if comp not in stable_compositions: raise ValueError(f'No entry with composition={comp} is stable in phase diagram pulled from Materials Project') element, cond = condition.split('-') chempots_ranges = pd.get_chempot_range_stability_phase(target_comp=comp,open_elt=Element(element)) if cond == 'poor': index = 0 elif cond == 'rich': index = 1 elif cond == 'middle': pass else: raise ValueError('Condition needs to be specified as "<el>-rich/poor"') chempots = {} for element, mus in chempots_ranges.items(): if cond == 'middle': chempots[element.symbol] = float(np.mean(mus)) else: chempots[element.symbol] = float(mus[index]) return Chempots(chempots)
[docs] def generate_chempots_from_mp( composition, element=None, phase_diagram=None, API_KEY=None, thermo_type='GGA_GGA+U', **kwargs): """ Generate Chempots dictionary using the data from the Materials Project. `element` can be a periodic table element, or a condition "<el>-poor/rich" (see docs in `generate_chempots_from_condition`). If not provided, oxygen is chosen as target element if present, otherwise the last element in the composition formula is picked. If `element` is provided as condition ("<el>-poor/rich"), a `Chempots` object is returned, otherwise all conditions are pulled from the database and a `Reservoirs` object is returned (a `Chempots` object for every condition). Parameters ---------- composition : str or Composition Composition of the target material. element : str Periodic table element or condition ("<el>-poor/rich") for the choice of chemical potential. if a condition is provided, a `Chempots` object is returned, otherwise all conditions are pulled from the database and a `Reservoirs` object is returned (a `Chempots` object for every condition). phase_diagram : PhaseDiagram Pymatgen PhaseDiagram object. If not provided it is pulled from the Materials Project database. API_KEY : str API KEY for the Materials Project database. If not provided, `pymatgen` looks in the configuration file. thermo_type : str The thermo type to pass to MP database. kwargs : dict Kwargs to pass to `get_phase_diagram_from_chemsys`. Returns ------- chemical_potentials : Chempots or Reservoirs Chemical potentials for the target composition and condition. If `element` is provided as condition ("<el>-poor/rich"), a `Chempots` object is returned, otherwise all conditions are pulled from the database and a `Reservoirs` object is returned (a `Chempots` object for every condition). """ from ..tools.materials_project import MPDatabase string_conditions = ['poor','middle','rich'] print('Pulling chemical potentials from Materials Project database...') comp = _get_composition_object(composition) if type(phase_diagram) == PhaseDiagram: pd = phase_diagram else: chemsys = '-'.join(el.symbol for el in comp.elements) pd = MPDatabase(API_KEY=API_KEY).get_phase_diagram_from_chemsys( chemsys=chemsys, thermo_type=thermo_type, **kwargs) if element: condition = element else: condition = [] composition = Composition(composition) if 'O' in composition: element = 'O' else: element = composition.elements[-1].symbol if any([cond in condition for cond in string_conditions]): chempots = generate_chempots_from_condition( composition=composition, condition=condition, phase_diagram=pd, API_KEY=API_KEY, thermo_type=thermo_type, **kwargs) chemical_potentials = Chempots(chempots) print(f'Chemical potentials for composition = {composition} and condition = {condition}:') print(chempots) else: chemical_potentials = {} if element in composition: for condition in [element +'-'+ cond for cond in string_conditions]: chempots = generate_chempots_from_condition( composition=composition, condition=condition, phase_diagram=pd, API_KEY=API_KEY, thermo_type=thermo_type, **kwargs) chemical_potentials[condition] = chempots chemical_potentials = Reservoirs(chemical_potentials,phase_diagram=pd) else: raise ValueError('Target element is not in composition') print(f'Chemical potentials:\n {chemical_potentials}') return chemical_potentials
[docs] def generate_elemental_chempots(elements, API_KEY=None, thermo_type='GGA_GGA+U', **kwargs): """ Generate chemical potentials for reference elemental phases using the data from the Materials Project database. Parameters ---------- elements : list List of strings with element symbols. API_KEY : str API KEY for the Materials Project database. If not provided, `pymatgen` looks in the configuration file. thermo_type : str The thermo type to pass to MP database. kwargs : dict Kwargs to pass to `get_phase_diagram_from_chemsys`. Returns ------- Chempots object """ chempots = {} from mp_api.client import MPRester with MPRester(API_KEY) as mpr: for el in elements: # code adapted from .tools.materials_project.get_stable_energy_pfu_from_composition to avoid creating a new MPRester instance every time docs = mpr.materials.thermo.search(formula=el,energy_above_hull=(0,0),thermo_types=[thermo_type],**kwargs) if len(docs) > 1: warnings.warn('Search returned more than one entry with E above hull = 0 eV, check manually. Returning the first entry...') entry = docs[0] chempots[el] = entry.energy_per_atom return Chempots(chempots)
[docs] def generate_pressure_reservoirs_from_precursors( precursors, temperature, oxygen_ref=None, pressure_range=(1e-20,1e10), npoints=50, get_pressures_as_strings=False, thermo_type='GGA_GGA+U', **kwargs): """ Generate reservoirs dependent on oxygen partial pressure (`PressureReservoirs`) starting from precursors compostitions. A dictionary with precursor composition and energy per formula unit in eV is generated with data from the Materials Project database. If not provided, the reference for the oxygen chempot at 0 K is also pulled from the DB. Chemical potentials are found from the energies of the precursors and the oxygen chempot value (uses the np.linalg.lstsq function). If the system is underdetermined the minimum-norm solution is found. Parameters ---------- precursors : str or list Compositions of precursors. oxygen_ref : float Absolute chempot of oxygen at 0K. If not provided it is pulled from the MP database. temperature : float Temperature in K. pressure_range : tuple Range in which to evaluate the partial pressure . The default is from 1e-20 to 1e10. npoints : int Number of data points to interpolate the partial pressure with. The default is 50. get_pressures_as_strings : bool Get pressure values (keys in the Reservoirs dict) as strings. The default is set to floats. Returns ------- pressure_reservoirs : PressureReservoirs PressureReservoirs object. """ from ..tools.materials_project import MPDatabase print('Pulling precursors energies from Materials Project database') if type(precursors) == str: precursors = [precursors] if not oxygen_ref: oxygen_ref = MPDatabase().get_stable_energy_pfu_from_composition( composition='O2', thermo_types=[thermo_type], **kwargs) oxygen_ref /= 2 precursors_dict = {} for prec in precursors: energy_pfu = MPDatabase().get_stable_energy_pfu_from_composition( composition=prec, thermo_types=[thermo_type], **kwargs) precursors_dict[prec] = energy_pfu reservoirs = get_pressure_reservoirs_from_precursors( precursors=precursors_dict, oxygen_ref=oxygen_ref, temperature=temperature, pressure_range=pressure_range, npoints=npoints, get_pressures_as_strings=get_pressures_as_strings) return reservoirs