root / ase / dft / stm.py @ 7
Historique | Voir | Annoter | Télécharger (4,71 ko)
| 1 | 1 | tkerber | from math import exp, sqrt |
|---|---|---|---|
| 2 | 1 | tkerber | |
| 3 | 1 | tkerber | import numpy as np |
| 4 | 1 | tkerber | |
| 5 | 1 | tkerber | from ase.atoms import Atoms |
| 6 | 1 | tkerber | |
| 7 | 1 | tkerber | |
| 8 | 1 | tkerber | class STM: |
| 9 | 1 | tkerber | def __init__(self, atoms, symmetries=None): |
| 10 | 1 | tkerber | if isinstance(atoms, Atoms): |
| 11 | 1 | tkerber | calc = atoms.get_calculator() |
| 12 | 1 | tkerber | else:
|
| 13 | 1 | tkerber | calc = atoms |
| 14 | 1 | tkerber | atoms = calc.get_atoms() |
| 15 | 1 | tkerber | self.nbands = calc.get_number_of_bands()
|
| 16 | 1 | tkerber | self.weights = calc.get_k_point_weights()
|
| 17 | 1 | tkerber | self.nkpts = len(self.weights) |
| 18 | 1 | tkerber | self.nspins = calc.get_number_of_spins()
|
| 19 | 1 | tkerber | self.eigs = np.array([[calc.get_eigenvalues(k, s)
|
| 20 | 1 | tkerber | for k in range(self.nkpts)] |
| 21 | 1 | tkerber | for s in range(self.nspins)]) |
| 22 | 1 | tkerber | self.eigs -= calc.get_fermi_level()
|
| 23 | 1 | tkerber | self.calc = calc
|
| 24 | 1 | tkerber | self.cell = atoms.get_cell()
|
| 25 | 1 | tkerber | assert not self.cell[2, :2].any() and not self.cell[:2, 2].any() |
| 26 | 1 | tkerber | self.ldos = None |
| 27 | 1 | tkerber | self.symmetries = symmetries or [] |
| 28 | 1 | tkerber | |
| 29 | 1 | tkerber | def calculate_ldos(self, width=None): |
| 30 | 1 | tkerber | if self.ldos is not None and width == self.width: |
| 31 | 1 | tkerber | return
|
| 32 | 1 | tkerber | |
| 33 | 1 | tkerber | if width is None: |
| 34 | 1 | tkerber | width = 0.1
|
| 35 | 1 | tkerber | |
| 36 | 1 | tkerber | ldos = None
|
| 37 | 1 | tkerber | for s in range(self.nspins): |
| 38 | 1 | tkerber | for k in range(self.nkpts): |
| 39 | 1 | tkerber | for n in range(self.nbands): |
| 40 | 1 | tkerber | psi = self.calc.get_pseudo_wave_function(n, k, s)
|
| 41 | 1 | tkerber | if ldos is None: |
| 42 | 1 | tkerber | ldos = np.zeros_like(psi) |
| 43 | 1 | tkerber | f = (exp(-(self.eigs[s, k, n] / width)**2) * |
| 44 | 1 | tkerber | self.weights[k])
|
| 45 | 1 | tkerber | ldos += f * (psi * np.conj(psi)).real |
| 46 | 1 | tkerber | |
| 47 | 1 | tkerber | if 0 in self.symmetries: |
| 48 | 1 | tkerber | # (x,y) -> (-x,y)
|
| 49 | 1 | tkerber | ldos[1:] += ldos[:0:-1].copy() |
| 50 | 1 | tkerber | ldos[1:] *= 0.5 |
| 51 | 1 | tkerber | |
| 52 | 1 | tkerber | if 1 in self.symmetries: |
| 53 | 1 | tkerber | # (x,y) -> (x,-y)
|
| 54 | 1 | tkerber | ldos[:, 1:] += ldos[:, :0:-1].copy() |
| 55 | 1 | tkerber | ldos[:, 1:] *= 0.5 |
| 56 | 1 | tkerber | |
| 57 | 1 | tkerber | if 2 in self.symmetries: |
| 58 | 1 | tkerber | # (x,y) -> (y,x)
|
| 59 | 1 | tkerber | ldos += ldos.transpose((1, 0, 2)).copy() |
| 60 | 1 | tkerber | ldos *= 0.5
|
| 61 | 1 | tkerber | |
| 62 | 1 | tkerber | self.ldos = ldos
|
| 63 | 1 | tkerber | self.width = width
|
| 64 | 1 | tkerber | |
| 65 | 1 | tkerber | #def save_ldos(self, filename='ldos.pckl'):
|
| 66 | 1 | tkerber | |
| 67 | 1 | tkerber | |
| 68 | 1 | tkerber | def get_averaged_current(self, z, width=None): |
| 69 | 1 | tkerber | self.calculate_ldos(width)
|
| 70 | 1 | tkerber | nz = self.ldos.shape[2] |
| 71 | 1 | tkerber | |
| 72 | 1 | tkerber | # Find grid point:
|
| 73 | 1 | tkerber | n = z / self.cell[2, 2] * nz |
| 74 | 1 | tkerber | dn = n - np.floor(n) |
| 75 | 1 | tkerber | n = int(n) % nz
|
| 76 | 1 | tkerber | print n,dn
|
| 77 | 1 | tkerber | |
| 78 | 1 | tkerber | # Average and do linear interpolation:
|
| 79 | 1 | tkerber | return ((1 - dn) * self.ldos[:, :, n].mean() + |
| 80 | 1 | tkerber | dn * self.ldos[:, :, (n + 1) % nz].mean()) |
| 81 | 1 | tkerber | |
| 82 | 1 | tkerber | def scan(self, current, z=None, width=None): |
| 83 | 1 | tkerber | self.calculate_ldos(width)
|
| 84 | 1 | tkerber | |
| 85 | 1 | tkerber | L = self.cell[2, 2] |
| 86 | 1 | tkerber | if z is None: |
| 87 | 1 | tkerber | z = L / 2
|
| 88 | 1 | tkerber | |
| 89 | 1 | tkerber | nz = self.ldos.shape[2] |
| 90 | 1 | tkerber | n = int(round(z / L * nz)) % nz |
| 91 | 1 | tkerber | h = L / nz |
| 92 | 1 | tkerber | |
| 93 | 1 | tkerber | ldos = self.ldos.reshape((-1, nz)) |
| 94 | 1 | tkerber | |
| 95 | 1 | tkerber | heights = np.empty(ldos.shape[0])
|
| 96 | 1 | tkerber | for i, a in enumerate(ldos): |
| 97 | 1 | tkerber | heights[i], z, n = find_height(a, current, z, n, nz, h) |
| 98 | 1 | tkerber | |
| 99 | 1 | tkerber | heights.shape = self.ldos.shape[:2] |
| 100 | 1 | tkerber | return heights
|
| 101 | 1 | tkerber | |
| 102 | 1 | tkerber | def linescan(self, current, p1, p2, npoints=None, z=None, width=None): |
| 103 | 1 | tkerber | self.calculate_ldos(width)
|
| 104 | 1 | tkerber | |
| 105 | 1 | tkerber | L = self.cell[2, 2] |
| 106 | 1 | tkerber | if z is None: |
| 107 | 1 | tkerber | z = L / 2
|
| 108 | 1 | tkerber | |
| 109 | 1 | tkerber | nz = self.ldos.shape[2] |
| 110 | 1 | tkerber | n = int(round(z / L * nz)) % nz |
| 111 | 1 | tkerber | h = L / nz |
| 112 | 1 | tkerber | ldos = self.ldos.reshape((-1, nz)) |
| 113 | 1 | tkerber | |
| 114 | 1 | tkerber | p1 = np.asarray(p1) |
| 115 | 1 | tkerber | p2 = np.asarray(p2) |
| 116 | 1 | tkerber | d = p2 - p1 |
| 117 | 1 | tkerber | s = sqrt(np.dot(d, d)) |
| 118 | 1 | tkerber | |
| 119 | 1 | tkerber | if npints == None: |
| 120 | 1 | tkerber | npoints = int(3 * s / h + 2) |
| 121 | 1 | tkerber | |
| 122 | 1 | tkerber | cell = self.cell[:2, :2] |
| 123 | 1 | tkerber | shape = np.array(self.ldos.shape[:2], float) |
| 124 | 1 | tkerber | M = cell.I |
| 125 | 1 | tkerber | heights = np.empty(npoints) |
| 126 | 1 | tkerber | for i in range(npoints): |
| 127 | 1 | tkerber | p = p1 + i * d / (npoints - 1)
|
| 128 | 1 | tkerber | q = np.dot(M, p) * shape |
| 129 | 1 | tkerber | qi = q.astype(int)
|
| 130 | 1 | tkerber | n0, n1 = qi |
| 131 | 1 | tkerber | f = q - qi |
| 132 | 1 | tkerber | g = 1 - f
|
| 133 | 1 | tkerber | a = (g[0] * g[0] * ldos[n0, n1 ] + |
| 134 | 1 | tkerber | f[0] * g[0] * ldos[n0 + 1, n1 ] + |
| 135 | 1 | tkerber | g[0] * f[0] * ldos[n0, n1 + 1] + |
| 136 | 1 | tkerber | f[0] * f[0] * ldos[n0 + 1, n1 + 1]) |
| 137 | 1 | tkerber | heights[i], z, n = find_height(a, current, z, n, nz, h) |
| 138 | 1 | tkerber | return np.linspace(0, s, npoints), heights |
| 139 | 1 | tkerber | |
| 140 | 1 | tkerber | def cube(self, filename, atoms=None): |
| 141 | 1 | tkerber | pass
|
| 142 | 1 | tkerber | |
| 143 | 1 | tkerber | |
| 144 | 1 | tkerber | def find_height(array, current, z, n, nz, h): |
| 145 | 1 | tkerber | c1 = array[n] |
| 146 | 1 | tkerber | sign = cmp(c1, current)
|
| 147 | 1 | tkerber | m = 0
|
| 148 | 1 | tkerber | while m < nz:
|
| 149 | 1 | tkerber | n = (n + sign) % nz |
| 150 | 1 | tkerber | z += sign * h |
| 151 | 1 | tkerber | c2 = array[n] |
| 152 | 1 | tkerber | if cmp(c2, current) != sign: |
| 153 | 1 | tkerber | break
|
| 154 | 1 | tkerber | c1 = c2 |
| 155 | 1 | tkerber | m += 1
|
| 156 | 1 | tkerber | |
| 157 | 1 | tkerber | if m == nz:
|
| 158 | 1 | tkerber | print z, n, nz, h, current, array
|
| 159 | 1 | tkerber | raise RuntimeError('Tip crash!') |
| 160 | 1 | tkerber | |
| 161 | 1 | tkerber | return z - sign * h * (current - c2) / (c1 - c2), z, n
|
| 162 | 1 | tkerber | |
| 163 | 1 | tkerber | |
| 164 | 1 | tkerber | |
| 165 | 1 | tkerber | |
| 166 | 1 | tkerber |