コード例 #1
0
ファイル: profile.py プロジェクト: mdavezac/LaDa
def linear_rprofile(extract, direction=(0,0,1), nbpoints=20, sigma=0.2, indices=None):
  """ Computes profile for given direction of each wavefunction.
  
      :Parameters:
        extract 
          Extraction object from an escan calculation. 
        nbpoints 
          Number of points to consider along the direction.
        sigma 
          Smearing to use when creating profile. 
        indices 
          Indices of the wavefunctions for which to compute the profile.
          Can also be None, in which case the profile of each wavefunction is
          computed.
      
      Mostly for debugging purposes. Makes much uglier plots than linear_profile.
  """
  from numpy import dot, max, arange, array, exp, multiply, sum
  from numpy.linalg import inv, norm
  from quantities import angstrom
  from pylada.physics import a0

  direction = array(direction, dtype="float64")
  assert norm(direction) > 1e-12, ValueError("Direction cannot be null.")
  assert hasattr(extract, "gwfns"), ValueError("extract does not seem to be an escan extraction object.")
  if indices is None: indices = range(extract.gwfns)
  assert nbpoints > 1, ValueError("The number of points should be strictly larger than 1.")

  # first computes intersection of direction and cell.  At the end, we should
  # end up in a0, knowing the structure cell should be in angstrom.
  direction = array(direction)
  udir = direction / norm(direction)
  dir = dot(inv(extract.structure.cell), direction)
  direction = direction/max(abs(dir)) * extract.structure.scale * angstrom.rescale(a0)

  # then creates array of points along direction.
  zpoints = arange(nbpoints+1, dtype="float64").reshape(nbpoints+1, 1) / float(nbpoints)
  zpoints = multiply(zpoints, direction) 
  x =  sum(zpoints*udir, axis=1).rescale(angstrom) 
  if sigma is None: sigma = 0.75 * norm(zpoints[1] - zpoints[0]) * zpoints.units
  if not hasattr(sigma, "units"): sigma *= angstrom.rescale(a0)

  zpoints = sum(zpoints*udir, axis=1) 
  gaussians = array([exp(-(sum(extract.rvectors*udir, axis=1)-z)**2/sigma**2) for z in zpoints])
  results = []
  for i, wfn in enumerate(extract.rwfns):
    if i not in indices: continue
    results.append( dot(gaussians, wfn.density) )
    results[-1][-1] = results[-1][0]
  return x, array(results)
コード例 #2
0
def read_structure(filename):
    """ Reads crystal structure from Espresso input """
    from numpy import dot, array
    from quantities import bohr_radius as a0, angstrom, Ry
    from ..crystal import Structure
    from .. import error
    from . import Namelist
    from .card import read_cards
    namelists = Namelist()
    namelists.read(filename)
    cards = read_cards(filename)
    if 'atomic_positions' not in set([u.name for u in cards]):
        raise error.RuntimeError("Card ATOMIC_POSITIONS is missing from input")
    positions = [u for u in cards if u.name == 'atomic_positions'][0]
    if 'cell_parameters' not in set([u.name for u in cards]):
        cell_parameters = None
        if namelists.system.ibrav == 0:
            raise error.RuntimeError("Card CELL_PARAMETERS is missing")
    else:
        cell_parameters = [u for u in cards if u.name == 'cell_parameters'][0]

    cell, scale = read_cell_and_scale(namelists.system, cell_parameters)
    result = Structure()

    result.cell = cell
    result.scale = scale
    for line in positions.value.rstrip().lstrip().split('\n'):
        line = line.split()
        result.add_atom(array(line[1:4], dtype='float64'), line[0])

    if positions.subtitle == 'bohr':
        factor = float(a0.rescale(result.scale))
        for atom in result:
            atom.pos *= factor
    elif positions.subtitle == 'angstrom':
        factor = float(angstrom.rescale(result.scale))
        for atom in result:
            atom.pos *= factor
    elif positions.subtitle == 'crystal':
        for atom in result:
            atom.pos = dot(result.cell, atom.pos)
    elif positions.subtitle == 'crystal_sg':
        raise error.RuntimeError(
            "Reading symmetric atomic positions is not implemented")

    if 'atomic_forces' in set([u.name for u in cards]):
        atomic_forces = [u for u in cards if u.name == 'atomic_forces'][0]
        if atomic_forces.value is None:
            raise error.RuntimeError("Atomic forces card is empty")
        lines = atomic_forces.value.rstrip().lstrip().split('\n')
        if len(lines) != len(result):
            raise error.RuntimeError(
                "Number forces and number of atoms do not match")
        for atom, force in zip(result, lines):
            atom.force = array(force.rstrip().lstrip().split()[1:4],
                               dtype='float64') * Ry / a0

    return result
コード例 #3
0
def read_structure(filename):
    """ Reads crystal structure from Espresso input """
    from numpy import dot, array
    from quantities import bohr_radius as a0, angstrom, Ry
    from ..crystal import Structure
    from .. import error
    from . import Namelist
    from .card import read_cards
    namelists = Namelist()
    namelists.read(filename)
    cards = read_cards(filename)
    if 'atomic_positions' not in set([u.name for u in cards]):
        raise error.RuntimeError("Card ATOMIC_POSITIONS is missing from input")
    positions = [u for u in cards if u.name == 'atomic_positions'][0]
    if 'cell_parameters' not in set([u.name for u in cards]):
        cell_parameters = None
        if namelists.system.ibrav == 0:
            raise error.RuntimeError("Card CELL_PARAMETERS is missing")
    else:
        cell_parameters = [u for u in cards if u.name == 'cell_parameters'][0]

    cell, scale = read_cell_and_scale(namelists.system, cell_parameters)
    result = Structure()

    result.cell = cell
    result.scale = scale
    for line in positions.value.rstrip().lstrip().split('\n'):
        line = line.split()
        result.add_atom(array(line[1:4], dtype='float64'), line[0])

    if positions.subtitle == 'bohr':
        factor = float(a0.rescale(result.scale))
        for atom in result:
            atom.pos *= factor
    elif positions.subtitle == 'angstrom':
        factor = float(angstrom.rescale(result.scale))
        for atom in result:
            atom.pos *= factor
    elif positions.subtitle == 'crystal':
        for atom in result:
            atom.pos = dot(result.cell, atom.pos)
    elif positions.subtitle == 'crystal_sg':
        raise error.RuntimeError("Reading symmetric atomic positions is not implemented")

    if 'atomic_forces' in set([u.name for u in cards]):
        atomic_forces = [u for u in cards if u.name == 'atomic_forces'][0]
        if atomic_forces.value is None:
            raise error.RuntimeError("Atomic forces card is empty")
        lines = atomic_forces.value.rstrip().lstrip().split('\n')
        if len(lines) != len(result):
            raise error.RuntimeError("Number forces and number of atoms do not match")
        for atom, force in zip(result, lines):
            atom.force = array(force.rstrip().lstrip().split()[1:4], dtype='float64') * Ry / a0

    return result
コード例 #4
0
def thirdO(latt_vec_array, charge, n):
    """ Function returns 3rd order image charge correction, same as LZ fortran script
    Reference: S. Lany and A. Zunger, Phys. Rev. B 78, 235104 (2008)
               S. Lany and A. Zunger, Model. Simul. Mater. Sci. Eng. 17, 0842002 (2009)
               [Eq. 6, 7]

    Parameters
        defect = pylada.vasp.Extract object
        charge = charge of point defect. Default 1e0 elementary charge
        n = precision in integral of Eq. 7 (LZ 2009), larger the better

    Returns
        third image correction in eV
    """

    cell_scale = 1.0  # SKW: In notebook workflow cell parameters are converted to Cartesians and units of Angstroms
    cell = (latt_vec_array * cell_scale) * angstrom.rescale(a0)

    #Anuj_05/22/18:modified to "third_order"
    thirdO = third_order(
        cell, n) * (4e0 * pi / 3e0) * Ry.rescale(eV) * charge * charge

    return thirdO
コード例 #5
0
ファイル: __init__.py プロジェクト: mdavezac/LaDa
def ewald(structure, charges=None, cutoff=None, **kwargs):
  """ Ewald summation. 

      Run-of-the-mill Ewald summation. Nothing fancy, so not very fast for
      large structures.

      :param structure:
         The structure to optimize. The charge of each atom can be given as a
         ``charge`` attribute. Otherwise, they should be in the ``charges``
         map.
      :type structure: py:attr:`~pylada.crystal.Structure`
      :param dict charges:
        Map from atomic-types to charges. If not signed by a unit, then should
        be in units of elementary electronic charge. If an atom has a
        ``charge`` attribute, the attribute takes priority ove items in this
        map.
      :param float cutoff:
        Cutoff energy when computing reciprocal space part. Defaults to
        :py:math:`15 Ry`.
  """
  from numpy import array, dot, zeros
  from numpy.linalg import inv
  from quantities import elementary_charge as em, Ry, a0, angstrom
  from .cppwrappers import ewald

  invcell = inv(structure.cell)
  positions = array([dot(invcell, a.pos) for a in structure], dtype='float64')
  cell = structure.cell.copy(order='F') * structure.scale                      \
         * float(angstrom.rescale(a0))
  if cutoff is None: cutoff = 15
  elif hasattr(cutoff, 'rescale'): cutoff = float(cutoff.rescale(Ry))
  else: cutoff = float(cutoff)
  c = []
  for atom in structure:
    if hasattr(atom, 'charge'): 
      if hasattr(atom.charge, 'rescale'): 
        c.append(float(atom.charge.rescale(em)))
        continue
      try: dummy = float(atom.charge)
      except: raise ValueError('charge attribute is not a floating point.')
      else:
        c.append(dummy)
        continue
    if charges is None: 
      raise ValueError( 'Atom has no charge attribute '                        \
                        'and charge dictionary is empty.' )
    if atom.type not in charges:
      raise ValueError( 'Atom has no charge attribute '                        \
                        'and atomic type {0} not in dictionary'                \
                        .format(atom.type) )
    dummy = c[atom.type]
    if hasattr(dummy, 'rescale'): c.append(float(dummy.rescale(em)))
    else:
      try: dummy = float(dummy)
      except: raise ValueError('charge attribute is not a floating point.')
      else: c.append(dummy)

  c = array(c, order='F', dtype='float64')
  energy, force, cforces, stress = ewald(cell, positions, c, cutoff)

  result = structure.copy()
  for atom, force in zip(result, cforces):
    atom.force = force * Ry / a0
  result.energy = energy * Ry
  result.stress = zeros((3, 3), dtype='float64') * Ry
  result.stress[0, 0] = stress[0] * Ry
  result.stress[1, 1] = stress[1] * Ry
  result.stress[2, 2] = stress[2] * Ry
  result.stress[0, 1] = stress[3] * Ry
  result.stress[1, 0] = stress[3] * Ry
  result.stress[1, 2] = stress[4] * Ry
  result.stress[2, 1] = stress[4] * Ry
  result.stress[0, 2] = stress[5] * Ry
  result.stress[2, 0] = stress[5] * Ry
  return result
コード例 #6
0
ファイル: profile.py プロジェクト: mdavezac/LaDa
def linear_profile(extract, direction=(0,0,1), nbpoints=20, sigma=None, indices=None):
  """ Computes profile for given direction of each wavefunction.
  
      :Parameters:
        extract 
          Extraction object from an escan calculation. 
        nbpoints 
          Number of points to consider along the direction.
        sigma 
          Smearing to use when creating profile. If None, defaults to 3/4 of
          distance between two points along the profile.
        indices 
          Indices of the wavefunctions for which to compute the profile.
          Can also be None, in which case the profile of each wavefunction is
          computed.

      To get nice plots, we compute the convolution of the real-space density
      and a gaussian in fourier space. Comes down to an interpolation in
      real-space.
  """
  from numpy import dot, max, arange, array, tensordot, exp, multiply, sum
  from numpy.linalg import inv, norm
  from quantities import angstrom
  from pylada.physics import a0

  direction = array(direction, dtype="float64")
  assert norm(direction) > 1e-12, ValueError("Direction cannot be null.")
  assert hasattr(extract, "gwfns"), ValueError("extract does not seem to be an escan extraction object.")
  if indices is None: indices = range(extract.gwfns)
  assert nbpoints > 1, ValueError("The number of points should be strictly larger than 1.")

  # first computes intersection of direction and cell.  At the end, we should
  # end up in a0, knowing the structure cell should be in angstrom.
  udir = direction / norm(direction)
  dir = dot(inv(extract.structure.cell), direction)
  direction = direction/max(abs(dir)) * extract.structure.scale * angstrom.rescale(a0)

  # then creates array of points along direction.
  zpoints = arange(nbpoints+1, dtype="float64").reshape(nbpoints+1, 1) / float(nbpoints)
  zpoints = multiply(zpoints, direction.magnitude) * direction.units
  x =  sum(zpoints*udir, axis=1).rescale(angstrom) 

  # computes sigma in g-space.
  if sigma is None: sigma = 0.75 * norm(zpoints[1] - zpoints[0]) * zpoints.units
  if not hasattr(sigma, "units"): sigma *= angstrom.rescale(a0)
  gsigma = 1e0/sigma

  # finds gvectors with zero components in directions perpendicular to direction.
  # dot does not work well with units.
  gvectors = extract.gvectors
  nonzero = (gvectors - dot(gvectors.magnitude, udir).reshape(gvectors.shape[0], 1) * udir * gvectors.units)**2
  nonzero = sum(nonzero, axis=1) < 1e-12
  gvectors = extract.gvectors[nonzero, :]

  # computes all exponentials exp(-i r.g), with r in first dim, and g in second.
  # tensordot does not conserve units. Must do it by hand.
  units = (zpoints.units * gvectors.units).simplified
  translations = exp(1j * tensordot(zpoints, gvectors, ((1),(1))) * units)

  # creates gaussian x translations matrix.
  gpoints = (dot(gvectors.magnitude, udir) * gvectors.units / gsigma).simplified
  gaussians = multiply(exp(-gpoints**2), translations)

  # fourrier operator to get auto-correlation function in g-space.
  units = (extract.rvectors.units * gvectors.units).simplified
  fourier = exp(-1j * tensordot(extract.rvectors, gvectors, ((1),(1))) * units)

  # finally computes profile in fourier space.
  results = []
  for i, wfn in enumerate(extract.rwfns):
    if i not in indices: continue
    # compute fourier transform of real-space density.
    autocorr = dot(fourier.T, wfn.density)
    # now performs summation with gaussian in fourier space.
    results.append(dot(gaussians, autocorr))

  return x, array(results)
コード例 #7
0
ファイル: __init__.py プロジェクト: spiningup/pylada-light
def ewald(structure, charges=None, cutoff=None, **kwargs):
    """ Ewald summation. 

      Run-of-the-mill Ewald summation. Nothing fancy, so not very fast for
      large structures.

      :param structure:
         The structure to optimize. The charge of each atom can be given as a
         ``charge`` attribute. Otherwise, they should be in the ``charges``
         map.
      :type structure: py:attr:`~pylada.crystal.Structure`
      :param dict charges:
        Map from atomic-types to charges. If not signed by a unit, then should
        be in units of elementary electronic charge. If an atom has a
        ``charge`` attribute, the attribute takes priority ove items in this
        map.
      :param float cutoff:
        Cutoff energy when computing reciprocal space part. Defaults to
        :py:math:`15 Ry`.
  """
    from numpy import array, dot, zeros
    from numpy.linalg import inv
    from quantities import elementary_charge as em, Ry, a0, angstrom
    from .cppwrappers import ewald

    invcell = inv(structure.cell)
    positions = array([dot(invcell, a.pos) for a in structure],
                      dtype='float64')
    cell = structure.cell.copy(order='F') * structure.scale                      \
           * float(angstrom.rescale(a0))
    if cutoff is None: cutoff = 15
    elif hasattr(cutoff, 'rescale'): cutoff = float(cutoff.rescale(Ry))
    else: cutoff = float(cutoff)
    c = []
    for atom in structure:
        if hasattr(atom, 'charge'):
            if hasattr(atom.charge, 'rescale'):
                c.append(float(atom.charge.rescale(em)))
                continue
            try:
                dummy = float(atom.charge)
            except:
                raise ValueError('charge attribute is not a floating point.')
            else:
                c.append(dummy)
                continue
        if charges is None:
            raise ValueError( 'Atom has no charge attribute '                        \
                              'and charge dictionary is empty.' )
        if atom.type not in charges:
            raise ValueError( 'Atom has no charge attribute '                        \
                              'and atomic type {0} not in dictionary'                \
                              .format(atom.type) )
        dummy = c[atom.type]
        if hasattr(dummy, 'rescale'): c.append(float(dummy.rescale(em)))
        else:
            try:
                dummy = float(dummy)
            except:
                raise ValueError('charge attribute is not a floating point.')
            else:
                c.append(dummy)

    c = array(c, order='F', dtype='float64')
    energy, force, cforces, stress = ewald(cell, positions, c, cutoff)

    result = structure.copy()
    for atom, force in zip(result, cforces):
        atom.force = force * Ry / a0
    result.energy = energy * Ry
    result.stress = zeros((3, 3), dtype='float64') * Ry
    result.stress[0, 0] = stress[0] * Ry
    result.stress[1, 1] = stress[1] * Ry
    result.stress[2, 2] = stress[2] * Ry
    result.stress[0, 1] = stress[3] * Ry
    result.stress[1, 0] = stress[3] * Ry
    result.stress[1, 2] = stress[4] * Ry
    result.stress[2, 1] = stress[4] * Ry
    result.stress[0, 2] = stress[5] * Ry
    result.stress[2, 0] = stress[5] * Ry
    return result