def explore_defect(defect, host, **kwargs): """ Diagnostic tool to determine defect from defect calculation and host. :Parameters: defect : `pylada.vasp.ExtractDFT` Extraction object for the vasp calculation of the defect structure. The defect structure should be a supercell of the host. Its unit cell must be an exact multiple of the host unit cell. Atoms may have moved around, however. host : `pylada.vasp.ExtractDFT` Extraction object for the vasp calculation of the host structure. kwargs Passed on to `reindex_sites`. :return: Dictionary containing three items: - 'vacancy': a list of atoms from the host *missing* in the defect structure. The position of these atoms correspond to the missing atom in the supercell (not the translational equivalent of the unit-cell). - 'substitution': list of indices referring to atoms in the defect structure with substituted types (w.r.t. the host). - 'intersititial': list of indices referring to atoms in the defect structure with no counterpart in the host structure. :note: The results may be incorrect if the defects incur too much relaxation. """ from copy import deepcopy from pylada.crystal.defects import reindex_sites from pylada.crystal import structure_to_lattice dstr = defect.structure.copy() hstr = host.structure # modified by Haowei: using the p1 structure for reindexing and potential_alignment hlat_p1 = structure_to_lattice(hstr, primitive=False) reindex_sites(dstr, hlat_p1, **kwargs) result = {'interstitial': [], 'substitution': [], 'vacancy': []} # looks for intersitials and substitutionals. for i, atom in enumerate(dstr.atoms): if atom.site == -1: result['interstitial'].append(i) elif atom.type != hstr.atoms[atom.site].type: result['substitution'].append(i) # looks for vacancies. filled = hlat_p1.to_structure(dstr.cell) reindex_sites(filled, dstr, **kwargs) for atom in filled.atoms: if atom.site != -1: continue result['vacancy'].append(deepcopy(atom)) return result
def potential_alignment(defect, host, maxdiff=None, first_shell=False, tolerance=0.25): """ Returns potential alignment correction. :Parameters: defect An output extraction object as returned by the vasp functional when computing the defect of interest. host An output extraction object as returned by the vasp functional when computing the host matrix. maxdiff : float or None, in eV(?) Maximum difference between the electrostatic potential of an atom and the equivalent host electrostatic potential beyond which that atom is considered pertubed by the defect. If None or negative, then differences in electrostatice potentials are not considered. Default 0.5. first_shell : bool If true then removes from potential alignment the first neighbor of defect atoms. Default False. tolerance Passed on to `reindex_sites`. Default 0.25. :return: The potential alignment in eV (without charge factor). Returns average difference of the electrostatic potential of the unperturbed atoms in the defect structure with respect to the host. *Perturbed* atoms are those flagged as defect by `explore_defect`, their first coordination shell if ''first_shell'' is true, and atoms for which the electrostatic potential differ to far from the average electrostatic potential for each lattice site (parameterized by maxdiff). """ from itertools import chain from numpy import abs, array, mean, any from quantities import eV from . import reindex_sites, first_shell as ffirst_shell from pylada.crystal import structure_to_lattice dstr = defect.structure hstr = host.structure # modified by Haowei: uring the p1 structure for potential alignment # this is necessary for two reasons: # 1, the host unit cell may be a *supercell* due to the magnetic structure # 2, in the conventional defect calculations, we run the calculation for host using the same supercell as defect calculations # but this should not be a problem if the calculations are converged very well hlat_p1 = structure_to_lattice(hstr, primitive=False) reindex_sites(dstr, hlat_p1, tolerance=tolerance) defects = explore_defect(defect, host, tolerance=tolerance) acceptable = [True for a in dstr.atoms] # make interstitials and substitutionals unaceptable. for i in chain(defects['interstitial'], defects['substitution']): acceptable[i] = False if first_shell: for n in ffirst_shell(dstr, dstr.atoms[i].pos, tolerance=tolerance): acceptable[n.index] = False # makes vacancies unacceptable. if first_shell: for atom in defects['vacancy']: for n in ffirst_shell(dstr, atom.pos, tolerance=tolerance): acceptable[n.index] = False # make a deepcopy for backup raw_acceptable = list(acceptable) if maxdiff != None and maxdiff > 0.0: # directly compare the atomic site between the host and defect cell/supercell diff_dh = [ (0.0 * eV if not ok else abs(e - host.electropot[a.site]).rescale(eV)) \ for e, a, ok in zip(defect.electropot, dstr.atoms, acceptable) ] for ixx in range(len(acceptable)): if acceptable[ixx] == False: pass elif float(diff_dh[ixx].magnitude) > maxdiff: acceptable[ixx] = False if not any(acceptable): # if some one try to use maxdiff = 0.0000000001, @&#(@&#(#@^@ print "WARNING: maxdiff is too small! Jump to maxdiff=None" # return to the default one, which accept all the atomic sites except the defect sites acceptable = list(raw_acceptable) iterable = zip(defect.electropot, dstr.atoms, acceptable) return mean([ (e - host.electropot[a.site]).rescale(eV).magnitude\ for e, a, ok in iterable if ok ]) * eV