Esempio n. 1
0
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
Esempio n. 2
0
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
Esempio n. 3
0
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
Esempio n. 4
0
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