Entries#
The philosophy of defect thermodynamics is to collectively analyse a collection of individual defects. In defermi the individual defect data is stored in a DefectEntry object, while the collection of entries is stored in the DefectsAnalysis object. This tutorial focuses on DefectEntry.
A single entry stores information regarding the type of defect and the result of a total energy calculation. We explore now its properties. In practise, the class is usually initialized automatically when importing data (see Import defect calculations). The input arguments are:
defect:Defectobjectenergy_diff: Energy difference btw defective and pristine cell in eVcorrections: Dictionary with correction terms to add to the formation energydata: Dictionary with additional data to storeformation_energy_function: Custom function for the formation energy calculation (optional, see section on custom functions)defect_concentration_function: Custom function for the defect concentrations calculation (optional, see section on custom functions)
[1]:
from defermi.defects import Vacancy
from defermi.entries import DefectEntry
entry = DefectEntry(
defect=Vacancy(specie='O',charge=2,multiplicity=1,bulk_volume=800),
energy_diff=7, #eV
corrections={'elastic':0.01}
)
entry
[1]:
DefectEntry: Name=Vac_O, Charge=2
Defect object#
Defect and DefectComplex objects collect properties of different defect types. defermi supports:
VacancyInterstitialSubstitution(can be used to describe anti-sites as well)PolaronDefectComplex
The main input of the class is the defect species (specie). In case of a Substitution, we include the bulk species that is replaced (bulk_specie). Moreover, we can store information regarding the defect charge and the multiplicity in the simulation cell.
[2]:
defect = Vacancy(specie='O',charge=2,multiplicity=1)
print(defect)
print(f"Defect name: {defect.name}")
print(f"Defect type: {defect.type}")
print(f"Defect species: {defect.specie}")
print(f"Particle number difference btw defect and pristine material: {defect.delta_atoms}")
print(f"Symbol: {defect.symbol_with_charge}")
print(f"Symbol in Kröger-Vink notation: {defect.symbol_with_charge_kv}")
Defect: type=Vacancy, species=O, charge=2
Defect name: Vac_O
Defect type: Vacancy
Defect species: O
Particle number difference btw defect and pristine material: {'O': -1}
Symbol: $V_{O}$$^{+2}$
Symbol in Kröger-Vink notation: $V_{O}$$^{°°}$
Defect objects can be initialized from a string as well. The naming convention is as follows (element = \(A\)):
Vacancy:'Vac_A'(symbol=\(V_{A}\))Interstitial:'Int_A'(symbol=\(A_{i}\))Substitution:'Sub_B_on_A'(symbol=\(B_{A}\))Polaron:'Pol_A'(symbol=\({A}_{A}\))DefectComplex:'Vac_A;Int_A'(symbol=\(V_A - A_i\))
[ ]:
from defermi.defects import get_defect_from_string
defect = get_defect_from_string('Vac_O')
print(defect)
defect_complex = get_defect_from_string('Vac_O;Int_O')
print(defect_complex)
Defect: type=Vacancy, species=O
DefectComplex: [ Defect: type=Vacancy, species=O, Defect: type=Interstitial, species=O, ]
The object can be intialized with structural data as well, specifically from pymatgen’s Structure and Site objects. Using structures allows to store more information, generate new structures, and compute properties (like multiplicity) automatically. The main inputs in this case are defect_site and bulk_structure.
[4]:
from pymatgen.core.structure import Structure
structure = Structure.from_file('../defermi/tests/test_files/structure_bulk.json')
site = structure[0]
print(site)
defect = Vacancy(defect_site=site,bulk_structure=structure)
defect
[3.87973664 2.74338809 6.71990099] Si
[4]:
Defect: type=Vacancy, species=Si, site= [0.29166667 0.29166667 0.29166667]
[5]:
print(f"Composition: {defect.defect_composition}")
print(f"Bulk volume: {defect.bulk_volume} A°")
print(f"Site index: {defect.defect_site_index}")
print(f"Multiplicity: {defect.get_multiplicity()}")
#defect.generate_defect_structure() # generate structure with defect
Composition: Si53
Bulk volume: 1080.993861120942 A°
Site index: 0
Multiplicity: 54
Import#
DefectEntry objects can also be imported from pymatgen Structure objects or from vasp_directories,as already shown for DefectsAnalysis in Import defect calculations. Defects are automatically determined analysing the defect and bulk structures.
[6]:
# from structures
defect_structure = Structure.from_file('../defermi/tests/test_files/structure_vac.json')
bulk_structure = Structure.from_file('../defermi/tests/test_files/structure_bulk.json')
entry_from_structures = DefectEntry.from_structures(
defect_structure=defect_structure,
bulk_structure=bulk_structure,
energy_diff=5, # made-up number, insert your calculation result
corrections={},
charge=-4)
print("\n",entry_from_structures)
Defect automatically identified for defective structure with composition Si53:
Defect: type=Vacancy, species=Si, site= [0.29166667 0.29166667 0.29166667]
DefectEntry
Defect: Defect: type=Vacancy, species=Si, charge=-4, site= [0.29166667 0.29166667 0.29166667]
Energy: 5.0000
Corrections: 0.0000
Charge: -4.0
Multiplicity: 1
Data: []
Name: Vac_Si
[7]:
# from VASP directories
path_defect = '../defermi/tests/test_files/SiO2-defects/Defects/vacancies/Vac_O/q2/2-PBE-OPT'
path_bulk = '../defermi/tests/test_files/SiO2-defects/Bulk-2x2x2-supercell'
entry_from_vasp_dirs = DefectEntry.from_vasp_directories(
path_defect=path_defect,
path_bulk=path_bulk,
corrections={},
initial_structure=True)
print("\n",entry_from_vasp_dirs)
Defect automatically identified for defective structure with composition Si24 O47:
Defect: type=Vacancy, species=O, site= [0.13461155 0.20669714 0.39244549]
DefectEntry
Defect: Defect: type=Vacancy, species=O, charge=2.0, site= [0.13461155 0.20669714 0.39244549]
Energy: 4.0104
Corrections: 0.0000
Charge: 2.0
Multiplicity: 1
Data: []
Name: Vac_O
Formation energy and defect concentration#
The formation energy and defect concentration are computed with the formation_energy and the defect_concentration methods, respectively. For more details on the expressions refer to Formation energies and Defect concentrations. IMPORTANT: For calculations of defect concentrations in \(\mathrm{cm}^{-3}\), the volume of the pristine cell in \(\AA^3\) must be provided if the Defect object in DefectEntry was
not initialized with structures (either manually or from imported calculations).
[8]:
entry.formation_energy(vbm=0,chemical_potentials={'O':-5},fermi_level=0) # eV
[8]:
2.01
[9]:
conc_in_cm3 = entry.defect_concentration(vbm=0,chemical_potentials={'O':-5},fermi_level=0,temperature=1000)
print(f"Defect concentration in cm^-3: {conc_in_cm3}") # cm^-3
conc_per_unit_cell = entry.defect_concentration(vbm=0,chemical_potentials={'O':-5},fermi_level=0,temperature=1000,per_unit_volume=False)
print(f"Defect concentration per unit cell: {conc_per_unit_cell}") # cm^-3
Defect concentration in cm^-3: 92673562982.4248
Defect concentration per unit cell: 7.413885038593984e-11
These routines can also be modified by assigning custom functions, either when the DefectEntry object is initialized (e.g. formation_energy_function kwarg), or at a later stage using the set_formation_energy_function and set_defect_concentration_function methods. The custom functions must accept the arguments of the original function, with the possibility to add other arguments. If the custom functions are modifications of the default functions, these can be accessed as
_default_formation_energy and _default_defect_concentrations.
In the following example, we will include the effects of temperature (\(T\)) and volume (\(V\)) on the formation energy (which are usually neglected as an approximation). We write the formation energy as:
where in this example:
[10]:
# kwargs of the original function + additional kwargs
def formation_energy_with_temperature(
entry,
vbm=0,
chemical_potentials=None,
fermi_level=0,
temperature=0,
delta_volume=0):
# we can use _default_formation_energy to call the default function
default_eform = entry._default_formation_energy(vbm=vbm,chemical_potentials=chemical_potentials,fermi_level=fermi_level)
return default_eform - 0.001 * temperature - 0.01 * delta_volume
entry.set_formation_energy_function(formation_energy_with_temperature)
entry.formation_energy(
vbm=0,
chemical_potentials={'O':-5},
fermi_level=0,
temperature=1000,
delta_volume=100)
[10]:
0.009999999999999787
The same philosophy applies for the defect_concentration function. More details can be found in the SECTION ON CUSTOM FUNCTIONS