File: stm.py

package info (click to toggle)
python-ase 3.22.1-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 14,344 kB
  • sloc: python: 126,379; xml: 946; makefile: 111; javascript: 47
file content (212 lines) | stat: -rw-r--r-- 7,535 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# flake8: noqa
import time

import numpy as np

from ase.transport.tools import dagger
from ase.transport.selfenergy import LeadSelfEnergy
from ase.transport.greenfunction import GreenFunction
from ase.parallel import world


class STM:
    def __init__(self, h1, s1, h2, s2 ,h10, s10, h20, s20, eta1, eta2, w=0.5, pdos=[], logfile = None):
        """XXX
        
        1. Tip
        2. Surface
        
        h1: ndarray
            Hamiltonian and overlap matrix for the isolated tip
            calculation.  Note, h1 should contain (at least) one
            principal layer.

        h2: ndarray
            Same as h1 but for the surface.

        h10: ndarray
            periodic part of the tip. must include two and only
            two principal layers.

        h20: ndarray
            same as h10, but for the surface

        The s* are the corresponding overlap matrices.  eta1, and eta
        2 are (finite) infinitesimals.  """
        
        self.pl1 = len(h10) // 2 #principal layer size for the tip
        self.pl2 = len(h20) // 2 #principal layer size for the surface
        self.h1 = h1 
        self.s1 = s1
        self.h2 = h2
        self.s2 = s2
        self.h10 = h10 
        self.s10 = s10 
        self.h20 = h20 
        self.s20 = s20
        self.eta1 = eta1
        self.eta2 = eta2
        self.w = w #asymmetry of the applied bias (0.5=>symmetric)
        self.pdos = []
        self.log = logfile

    def initialize(self, energies, bias=0):
        """
            energies: list of energies 
            for which the transmission function should be evaluated.
            bias.
            Will precalculate the surface greenfunctions of the tip and
            surface.
        """
        self.bias = bias
        self.energies = energies
        nenergies = len(energies)
        pl1, pl2 = self.pl1, self.pl2
        nbf1, nbf2 = len(self.h1), len(self.h2)
      
        #periodic part of the tip
        hs1_dii = self.h10[:pl1, :pl1], self.s10[:pl1, :pl1]
        hs1_dij = self.h10[:pl1, pl1:2*pl1], self.s10[:pl1, pl1:2*pl1]
        #coupling between per. and non. per part of the tip
        h1_im = np.zeros((pl1, nbf1), complex) 
        s1_im = np.zeros((pl1, nbf1), complex)
        h1_im[:pl1, :pl1], s1_im[:pl1, :pl1] = hs1_dij
        hs1_dim = [h1_im, s1_im]

        #periodic part the surface 
        hs2_dii = self.h20[:pl2, :pl2], self.s20[:pl2, :pl2]
        hs2_dij = self.h20[pl2:2*pl2, :pl2], self.s20[pl2:2*pl2, :pl2]
        #coupling between per. and non. per part of the surface
        h2_im = np.zeros((pl2, nbf2), complex)
        s2_im = np.zeros((pl2, nbf2), complex) 
        h2_im[-pl2:, -pl2:], s2_im[-pl2:, -pl2:] = hs2_dij
        hs2_dim = [h2_im, s2_im]

        #tip and surface greenfunction 
        self.selfenergy1 = LeadSelfEnergy(hs1_dii, hs1_dij, hs1_dim, self.eta1)
        self.selfenergy2 = LeadSelfEnergy(hs2_dii, hs2_dij, hs2_dim, self.eta2)
        self.greenfunction1 = GreenFunction(self.h1-self.bias*self.w*self.s1, self.s1, 
                                            [self.selfenergy1], self.eta1)
        self.greenfunction2 = GreenFunction(self.h2-self.bias*(self.w-1)*self.s2, self.s2, 
                                            [self.selfenergy2], self.eta2)
        
        #Shift the bands due to the bias.
        bias_shift1 = -bias * self.w
        bias_shift2 = -bias * (self.w - 1)
        self.selfenergy1.set_bias(bias_shift1)
        self.selfenergy2.set_bias(bias_shift2)
        
        #tip and surface greenfunction matrices.
        nbf1_small = nbf1 #XXX Change this for efficiency in the future
        nbf2_small = nbf2 #XXX -||-
        coupling_list1 = list(range(nbf1_small))# XXX -||-
        coupling_list2 = list(range(nbf2_small))# XXX -||-
        self.gft1_emm = np.zeros((nenergies, nbf1_small, nbf1_small), complex) 
        self.gft2_emm = np.zeros((nenergies, nbf2_small, nbf2_small), complex)
 
        for e, energy in enumerate(self.energies):
            if self.log != None: # and world.rank == 0:
                    T = time.localtime()
                    self.log.write(' %d:%02d:%02d, ' % (T[3], T[4], T[5]) +
                                   '%d, %d, %02f\n' % (world.rank, e, energy))
            gft1_mm = self.greenfunction1.retarded(energy)[coupling_list1]
            gft1_mm = np.take(gft1_mm, coupling_list1, axis=1)

            gft2_mm = self.greenfunction2.retarded(energy)[coupling_list2]
            gft2_mm = np.take(gft2_mm, coupling_list2, axis=1)
 
            self.gft1_emm[e] = gft1_mm
            self.gft2_emm[e] = gft2_mm

            if self.log != None and world.rank == 0:
                self.log.flush()

    def get_transmission(self, v_12, v_11_2=None, v_22_1=None):
        """XXX

        v_12:
            coupling between tip and surface 
        v_11_2:
            correction to "on-site" tip elements due to the 
            surface (eq.16). Is only included to first order.
        v_22_1:
            corretion to "on-site" surface elements due to he
            tip (eq.17). Is only included to first order.
        """

        dim0 = v_12.shape[0]
        dim1 = v_12.shape[1]

        nenergies = len(self.energies)
        T_e = np.empty(nenergies,float)
        v_21 = dagger(v_12)
        for e, energy in enumerate(self.energies):
            gft1 = self.gft1_emm[e]
            if v_11_2!=None:
                gf1 = np.dot(v_11_2, np.dot(gft1, v_11_2)) 
                gf1 += gft1 #eq. 16
            else:
                gf1 = gft1
            
            gft2 = self.gft2_emm[e]
            if v_22_1!=None:
                gf2 = np.dot(v_22_1,np.dot(gft2, v_22_1))
                gf2 += gft2 #eq. 17
            else:
                gf2 = gft2
            
            a1 = (gf1 - dagger(gf1))
            a2 = (gf2 - dagger(gf2))
            self.v_12 = v_12
            self.a2 = a2
            self.v_21 = v_21
            self.a1 = a1
            v12_a2 = np.dot(v_12, a2[:dim1])
            v21_a1 = np.dot(v_21, a1[-dim0:])
            self.v12_a2 = v12_a2
            self.v21_a1 = v21_a1
            T = -np.trace(np.dot(v12_a2[:,:dim1], v21_a1[:,-dim0:])) #eq. 11
            assert abs(T.imag).max() < 1e-14
            T_e[e] = T.real
            self.T_e = T_e
        return T_e


    def get_current(self, bias, v_12, v_11_2=None, v_22_1=None):
        """Very simple function to calculate the current.
        
        Asummes zero temperature.

        bias: type? XXX
            bias voltage (V)
            
        v_12: XXX
            coupling between tip and surface.
            
        v_11_2:
            correction to onsite elements of the tip
            due to the potential of the surface.
        v_22_1:
            correction to onsite elements of the surface
            due to the potential of the tip.
        """
        energies = self.energies
        T_e = self.get_transmission(v_12, v_11_2, v_22_1)
        bias_window = sorted(-np.array([bias * self.w, bias * (self.w - 1)]))
        self.bias_window = bias_window
        #print 'bias window', np.around(bias_window,3)
        #print 'Shift of tip lead do to the bias:', self.selfenergy1.bias
        #print 'Shift of surface lead do to the bias:', self.selfenergy2.bias
        i1 = sum(energies < bias_window[0]) 
        i2 = sum(energies < bias_window[1])
        step = 1 
        if i2 < i1:
            step = -1
        
        return np.sign(bias)*np.trapz(x=energies[i1:i2:step], y=T_e[i1:i2:step])