root / ase / dft / stm.py @ 18
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 |