Statistiques
| Révision :

root / ase / transport / calculators.py @ 3

Historique | Voir | Annoter | Télécharger (15,15 ko)

1 1 tkerber
import numpy as np
2 1 tkerber
3 1 tkerber
from numpy import linalg
4 1 tkerber
from ase.transport.selfenergy import LeadSelfEnergy, BoxProbe
5 1 tkerber
from ase.transport.greenfunction import GreenFunction
6 1 tkerber
from ase.transport.tools import subdiagonalize, cutcoupling, tri2full, dagger,\
7 1 tkerber
    rotate_matrix
8 1 tkerber
9 1 tkerber
10 1 tkerber
class TransportCalculator:
11 1 tkerber
    """Determine transport properties of a device sandwiched between
12 1 tkerber
    two semi-infinite leads using a Green function method.
13 1 tkerber
    """
14 1 tkerber
15 1 tkerber
    def __init__(self, **kwargs):
16 1 tkerber
        """Create the transport calculator.
17 1 tkerber

18 1 tkerber
        Parameters
19 1 tkerber
        ==========
20 1 tkerber
        h : (N, N) ndarray
21 1 tkerber
            Hamiltonian matrix for the central region.
22 1 tkerber
        s : {None, (N, N) ndarray}, optional
23 1 tkerber
            Overlap matrix for the central region.
24 1 tkerber
            Use None for an orthonormal basis.
25 1 tkerber
        h1 : (N1, N1) ndarray
26 1 tkerber
            Hamiltonian matrix for lead1.
27 1 tkerber
        h2 : {None, (N2, N2) ndarray}, optional
28 1 tkerber
            Hamiltonian matrix for lead2. You may use None if lead1 and lead2
29 1 tkerber
            are identical.
30 1 tkerber
        s1 : {None, (N1, N1) ndarray}, optional
31 1 tkerber
            Overlap matrix for lead1. Use None for an orthonomormal basis.
32 1 tkerber
        hc1 : {None, (N1, N) ndarray}, optional
33 1 tkerber
            Hamiltonian coupling matrix between the first principal
34 1 tkerber
            layer in lead1 and the central region.
35 1 tkerber
        hc2 : {None, (N2, N} ndarray), optional
36 1 tkerber
            Hamiltonian coupling matrix between the first principal
37 1 tkerber
            layer in lead2 and the central region.
38 1 tkerber
        sc1 : {None, (N1, N) ndarray}, optional
39 1 tkerber
            Overlap coupling matrix between the first principal
40 1 tkerber
            layer in lead1 and the central region.
41 1 tkerber
        sc2 : {None, (N2, N) ndarray}, optional
42 1 tkerber
            Overlap coupling matrix between the first principal
43 1 tkerber
            layer in lead2 and the central region.
44 1 tkerber
        energies : {None, array_like}, optional
45 1 tkerber
            Energy points for which calculated transport properties are
46 1 tkerber
            evaluated.
47 1 tkerber
        eta : {1.0e-5, float}, optional
48 1 tkerber
            Infinitesimal for the central region Green function.
49 1 tkerber
        eta1/eta2 : {1.0e-5, float}, optional
50 1 tkerber
            Infinitesimal for lead1/lead2 Green function.
51 1 tkerber
        align_bf : {None, int}, optional
52 1 tkerber
            Use align_bf=m to shift the central region
53 1 tkerber
            by a constant potential such that the m'th onsite element
54 1 tkerber
            in the central region is aligned to the m'th onsite element
55 1 tkerber
            in lead1 principal layer.
56 1 tkerber
        logfile : {None, str}, optional
57 1 tkerber
            Write a logfile to file with name `logfile`.
58 1 tkerber
            Use '-' to write to std out.
59 1 tkerber
        eigenchannels: {0, int}, optional
60 1 tkerber
            Number of eigenchannel transmission coefficients to
61 1 tkerber
            calculate.
62 1 tkerber
        pdos : {None, (N,) array_like}, optional
63 1 tkerber
            Specify which basis functions to calculate the
64 1 tkerber
            projected density of states for.
65 1 tkerber
        dos : {False, bool}, optional
66 1 tkerber
            The total density of states of the central region.
67 1 tkerber
        box: XXX
68 1 tkerber
            YYY
69 1 tkerber

70 1 tkerber
        If hc1/hc2 are None, they are assumed to be identical to
71 1 tkerber
        the coupling matrix elements between neareste neighbor
72 1 tkerber
        principal layers in lead1/lead2.
73 1 tkerber

74 1 tkerber
        Examples
75 1 tkerber
        ========
76 1 tkerber
        >>> import numpy as np
77 1 tkerber
        >>> h = np.array((0,)).reshape((1,1))
78 1 tkerber
        >>> h1 = np.array((0, -1, -1, 0)).reshape(2,2)
79 1 tkerber
        >>> energies = np.arange(-3, 3, 0.1)
80 1 tkerber
        >>> calc = TransportCalculator(h=h, h1=h1, energies=energies)
81 1 tkerber
        >>> T = calc.get_transmission()
82 1 tkerber

83 1 tkerber
        """
84 1 tkerber
85 1 tkerber
        # The default values for all extra keywords
86 1 tkerber
        self.input_parameters = {'energies': None,
87 1 tkerber
                                 'h': None,
88 1 tkerber
                                 'h1': None,
89 1 tkerber
                                 'h2': None,
90 1 tkerber
                                 's': None,
91 1 tkerber
                                 's1': None,
92 1 tkerber
                                 's2': None,
93 1 tkerber
                                 'hc1': None,
94 1 tkerber
                                 'hc2': None,
95 1 tkerber
                                 'sc1': None,
96 1 tkerber
                                 'sc2': None,
97 1 tkerber
                                 'box': None,
98 1 tkerber
                                 'align_bf': None,
99 1 tkerber
                                 'eta1': 1e-5,
100 1 tkerber
                                 'eta2': 1e-5,
101 1 tkerber
                                 'eta': 1e-5,
102 1 tkerber
                                 'logfile': None, # '-',
103 1 tkerber
                                 'eigenchannels': 0,
104 1 tkerber
                                 'dos': False,
105 1 tkerber
                                 'pdos': [],
106 1 tkerber
                                 }
107 1 tkerber
        self.initialized = False # Changed Hamiltonians?
108 1 tkerber
        self.uptodate = False # Changed energy grid?
109 1 tkerber
        self.set(**kwargs)
110 1 tkerber
111 1 tkerber
    def set(self, **kwargs):
112 1 tkerber
        for key in kwargs:
113 1 tkerber
            if key in ['h', 'h1', 'h2', 'hc1', 'hc2',
114 1 tkerber
                       's', 's1', 's2', 'sc1', 'sc2',
115 1 tkerber
                       'eta', 'eta1', 'eta2', 'align_bf', 'box']:
116 1 tkerber
                self.initialized = False
117 1 tkerber
                self.uptodate = False
118 1 tkerber
                break
119 1 tkerber
            elif key in ['energies', 'eigenchannels', 'dos', 'pdos']:
120 1 tkerber
                self.uptodate = False
121 1 tkerber
            elif key not in self.input_parameters:
122 1 tkerber
                raise KeyError, '\'%s\' not a vaild keyword' % key
123 1 tkerber
124 1 tkerber
        self.input_parameters.update(kwargs)
125 1 tkerber
        log = self.input_parameters['logfile']
126 1 tkerber
        if log is None:
127 1 tkerber
            class Trash:
128 1 tkerber
                def write(self, s):
129 1 tkerber
                    pass
130 1 tkerber
                def flush(self):
131 1 tkerber
                    pass
132 1 tkerber
            self.log = Trash()
133 1 tkerber
        elif log == '-':
134 1 tkerber
            from sys import stdout
135 1 tkerber
            self.log = stdout
136 1 tkerber
        elif 'logfile' in kwargs:
137 1 tkerber
            self.log = open(log, 'w')
138 1 tkerber
139 1 tkerber
    def initialize(self):
140 1 tkerber
        if self.initialized:
141 1 tkerber
            return
142 1 tkerber
143 1 tkerber
        print >> self.log, '# Initializing calculator...'
144 1 tkerber
145 1 tkerber
        p = self.input_parameters
146 1 tkerber
        if p['s'] == None:
147 1 tkerber
            p['s'] = np.identity(len(p['h']))
148 1 tkerber
149 1 tkerber
        identical_leads = False
150 1 tkerber
        if p['h2'] == None:
151 1 tkerber
            p['h2'] = p['h1'] # Lead2 is idendical to lead1
152 1 tkerber
            identical_leads = True
153 1 tkerber
154 1 tkerber
        if p['s1'] == None:
155 1 tkerber
            p['s1'] = np.identity(len(p['h1']))
156 1 tkerber
157 1 tkerber
        if p['s2'] == None and not identical_leads:
158 1 tkerber
            p['s2'] = np.identity(len(p['h2'])) # Orthonormal basis for lead 2
159 1 tkerber
        else: # Lead2 is idendical to lead1
160 1 tkerber
            p['s2'] = p['s1']
161 1 tkerber
162 1 tkerber
163 1 tkerber
        h_mm = p['h']
164 1 tkerber
        s_mm = p['s']
165 1 tkerber
        pl1 = len(p['h1']) / 2
166 1 tkerber
        pl2 = len(p['h2']) / 2
167 1 tkerber
        h1_ii = p['h1'][:pl1, :pl1]
168 1 tkerber
        h1_ij = p['h1'][:pl1, pl1:2 * pl1]
169 1 tkerber
        s1_ii = p['s1'][:pl1, :pl1]
170 1 tkerber
        s1_ij = p['s1'][:pl1, pl1:2 * pl1]
171 1 tkerber
        h2_ii = p['h2'][:pl2, :pl2]
172 1 tkerber
        h2_ij = p['h2'][pl2: 2 * pl2, :pl2]
173 1 tkerber
        s2_ii = p['s2'][:pl2, :pl2]
174 1 tkerber
        s2_ij = p['s2'][pl2: 2 * pl2, :pl2]
175 1 tkerber
176 1 tkerber
        if p['hc1'] is None:
177 1 tkerber
            nbf = len(h_mm)
178 1 tkerber
            h1_im = np.zeros((pl1, nbf), complex)
179 1 tkerber
            s1_im = np.zeros((pl1, nbf), complex)
180 1 tkerber
            h1_im[:pl1, :pl1] = h1_ij
181 1 tkerber
            s1_im[:pl1, :pl1] = s1_ij
182 1 tkerber
            p['hc1'] = h1_im
183 1 tkerber
            p['sc1'] = s1_im
184 1 tkerber
        else:
185 1 tkerber
            h1_im = p['hc1']
186 1 tkerber
            if p['sc1'] is not None:
187 1 tkerber
                s1_im = p['sc1']
188 1 tkerber
            else:
189 1 tkerber
                s1_im = np.zeros(h1_im.shape, complex)
190 1 tkerber
                p['sc1'] = s1_im
191 1 tkerber
192 1 tkerber
        if p['hc2'] is None:
193 1 tkerber
            h2_im = np.zeros((pl2, nbf), complex)
194 1 tkerber
            s2_im = np.zeros((pl2, nbf), complex)
195 1 tkerber
            h2_im[-pl2:, -pl2:] = h2_ij
196 1 tkerber
            s2_im[-pl2:, -pl2:] = s2_ij
197 1 tkerber
            p['hc2'] = h2_im
198 1 tkerber
            p['sc2'] = s2_im
199 1 tkerber
        else:
200 1 tkerber
            h2_im = p['hc2']
201 1 tkerber
            if p['sc2'] is not None:
202 1 tkerber
                s2_im = p['sc2']
203 1 tkerber
            else:
204 1 tkerber
                s2_im = np.zeros(h2_im.shape, complex)
205 1 tkerber
                p['sc2'] = s2_im
206 1 tkerber
207 1 tkerber
        align_bf = p['align_bf']
208 1 tkerber
        if align_bf != None:
209 1 tkerber
            diff = (h_mm[align_bf, align_bf] - h1_ii[align_bf, align_bf]) \
210 1 tkerber
                   / s_mm[align_bf, align_bf]
211 1 tkerber
            print >> self.log, '# Aligning scat. H to left lead H. diff=', diff
212 1 tkerber
            h_mm -= diff * s_mm
213 1 tkerber
214 1 tkerber
        # Setup lead self-energies
215 1 tkerber
        # All infinitesimals must be > 0
216 1 tkerber
        assert np.all(np.array((p['eta'], p['eta1'], p['eta2'])) > 0.0)
217 1 tkerber
        self.selfenergies = [LeadSelfEnergy((h1_ii, s1_ii),
218 1 tkerber
                                            (h1_ij, s1_ij),
219 1 tkerber
                                            (h1_im, s1_im),
220 1 tkerber
                                            p['eta1']),
221 1 tkerber
                             LeadSelfEnergy((h2_ii, s2_ii),
222 1 tkerber
                                            (h2_ij, s2_ij),
223 1 tkerber
                                            (h2_im, s2_im),
224 1 tkerber
                                            p['eta2'])]
225 1 tkerber
        box = p['box']
226 1 tkerber
        if box is not None:
227 1 tkerber
            print 'Using box probe!'
228 1 tkerber
            self.selfenergies.append(
229 1 tkerber
                BoxProbe(eta=box[0], a=box[1], b=box[2], energies=box[3],
230 1 tkerber
                         S=s_mm, T=0.3))
231 1 tkerber
232 1 tkerber
        #setup scattering green function
233 1 tkerber
        self.greenfunction = GreenFunction(selfenergies=self.selfenergies,
234 1 tkerber
                                           H=h_mm,
235 1 tkerber
                                           S=s_mm,
236 1 tkerber
                                           eta=p['eta'])
237 1 tkerber
238 1 tkerber
        self.initialized = True
239 1 tkerber
240 1 tkerber
    def update(self):
241 1 tkerber
        if self.uptodate:
242 1 tkerber
            return
243 1 tkerber
244 1 tkerber
        p = self.input_parameters
245 1 tkerber
        self.energies = p['energies']
246 1 tkerber
        nepts = len(self.energies)
247 1 tkerber
        nchan = p['eigenchannels']
248 1 tkerber
        pdos = p['pdos']
249 1 tkerber
        self.T_e = np.empty(nepts)
250 1 tkerber
        if p['dos']:
251 1 tkerber
            self.dos_e = np.empty(nepts)
252 1 tkerber
        if pdos != []:
253 1 tkerber
            self.pdos_ne = np.empty((len(pdos), nepts))
254 1 tkerber
        if nchan > 0:
255 1 tkerber
            self.eigenchannels_ne = np.empty((nchan, nepts))
256 1 tkerber
257 1 tkerber
        for e, energy in enumerate(self.energies):
258 1 tkerber
            Ginv_mm = self.greenfunction.retarded(energy, inverse=True)
259 1 tkerber
            lambda1_mm = self.selfenergies[0].get_lambda(energy)
260 1 tkerber
            lambda2_mm = self.selfenergies[1].get_lambda(energy)
261 1 tkerber
            a_mm = linalg.solve(Ginv_mm, lambda1_mm)
262 1 tkerber
            b_mm = linalg.solve(dagger(Ginv_mm), lambda2_mm)
263 1 tkerber
            T_mm = np.dot(a_mm, b_mm)
264 1 tkerber
            if nchan > 0:
265 1 tkerber
                t_n = linalg.eigvals(T_mm).real
266 1 tkerber
                self.eigenchannels_ne[:, e] = np.sort(t_n)[-nchan:]
267 1 tkerber
                self.T_e[e] = np.sum(t_n)
268 1 tkerber
            else:
269 1 tkerber
                self.T_e[e] = np.trace(T_mm).real
270 1 tkerber
271 1 tkerber
            print >> self.log, energy, self.T_e[e]
272 1 tkerber
            self.log.flush()
273 1 tkerber
274 1 tkerber
            if p['dos']:
275 1 tkerber
                self.dos_e[e] = self.greenfunction.dos(energy)
276 1 tkerber
277 1 tkerber
            if pdos != []:
278 1 tkerber
                self.pdos_ne[:, e] = np.take(self.greenfunction.pdos(energy),
279 1 tkerber
                                             pdos)
280 1 tkerber
281 1 tkerber
        self.uptodate = True
282 1 tkerber
283 1 tkerber
    def print_pl_convergence(self):
284 1 tkerber
        self.initialize()
285 1 tkerber
        pl1 = len(self.input_parameters['h1']) / 2
286 1 tkerber
287 1 tkerber
        h_ii = self.selfenergies[0].h_ii
288 1 tkerber
        s_ii = self.selfenergies[0].s_ii
289 1 tkerber
        ha_ii = self.greenfunction.H[:pl1, :pl1]
290 1 tkerber
        sa_ii = self.greenfunction.S[:pl1, :pl1]
291 1 tkerber
        c1 = np.abs(h_ii - ha_ii).max()
292 1 tkerber
        c2 = np.abs(s_ii - sa_ii).max()
293 1 tkerber
        print 'Conv (h,s)=%.2e, %2.e' % (c1, c2)
294 1 tkerber
295 1 tkerber
    def plot_pl_convergence(self):
296 1 tkerber
        self.initialize()
297 1 tkerber
        pl1 = len(self.input_parameters['h1']) / 2
298 1 tkerber
        hlead = self.selfenergies[0].h_ii.real.diagonal()
299 1 tkerber
        hprincipal = self.greenfunction.H.real.diagonal[:pl1]
300 1 tkerber
301 1 tkerber
        import pylab as pl
302 1 tkerber
        pl.plot(hlead, label='lead')
303 1 tkerber
        pl.plot(hprincipal, label='principal layer')
304 1 tkerber
        pl.axis('tight')
305 1 tkerber
        pl.show()
306 1 tkerber
307 1 tkerber
    def get_transmission(self):
308 1 tkerber
        self.initialize()
309 1 tkerber
        self.update()
310 1 tkerber
        return self.T_e
311 1 tkerber
312 1 tkerber
    def get_dos(self):
313 1 tkerber
        self.initialize()
314 1 tkerber
        self.update()
315 1 tkerber
        return self.dos_e
316 1 tkerber
317 1 tkerber
    def get_eigenchannels(self, n=None):
318 1 tkerber
        """Get ``n`` first eigenchannels."""
319 1 tkerber
        self.initialize()
320 1 tkerber
        self.update()
321 1 tkerber
        if n is None:
322 1 tkerber
            n = self.input_parameters['eigenchannels']
323 1 tkerber
        return self.eigenchannels_ne[:n]
324 1 tkerber
325 1 tkerber
    def get_pdos(self):
326 1 tkerber
        self.initialize()
327 1 tkerber
        self.update()
328 1 tkerber
        return self.pdos_ne
329 1 tkerber
330 1 tkerber
    def subdiagonalize_bfs(self, bfs, apply=False):
331 1 tkerber
        self.initialize()
332 1 tkerber
        bfs = np.array(bfs)
333 1 tkerber
        p = self.input_parameters
334 1 tkerber
        h_mm = p['h']
335 1 tkerber
        s_mm = p['s']
336 1 tkerber
        ht_mm, st_mm, c_mm, e_m = subdiagonalize(h_mm, s_mm, bfs)
337 1 tkerber
        if apply:
338 1 tkerber
            self.uptodate = False
339 1 tkerber
            h_mm[:] = ht_mm
340 1 tkerber
            s_mm[:] = st_mm
341 1 tkerber
            # Rotate coupling between lead and central region
342 1 tkerber
            for alpha, sigma in enumerate(self.selfenergies):
343 1 tkerber
                sigma.h_im[:] = np.dot(sigma.h_im, c_mm)
344 1 tkerber
                sigma.s_im[:] = np.dot(sigma.s_im, c_mm)
345 1 tkerber
346 1 tkerber
        c_mm = np.take(c_mm, bfs, axis=0)
347 1 tkerber
        c_mm = np.take(c_mm, bfs, axis=1)
348 1 tkerber
        return ht_mm, st_mm, e_m, c_mm
349 1 tkerber
350 1 tkerber
    def cutcoupling_bfs(self, bfs, apply=False):
351 1 tkerber
        self.initialize()
352 1 tkerber
        bfs = np.array(bfs)
353 1 tkerber
        p = self.input_parameters
354 1 tkerber
        h_pp = p['h'].copy()
355 1 tkerber
        s_pp = p['s'].copy()
356 1 tkerber
        cutcoupling(h_pp, s_pp, bfs)
357 1 tkerber
        if apply:
358 1 tkerber
            self.uptodate = False
359 1 tkerber
            p['h'][:] = h_pp
360 1 tkerber
            p['s'][:] = s_pp
361 1 tkerber
            for alpha, sigma in enumerate(self.selfenergies):
362 1 tkerber
                for m in bfs:
363 1 tkerber
                    sigma.h_im[:, m] = 0.0
364 1 tkerber
                    sigma.s_im[:, m] = 0.0
365 1 tkerber
        return h_pp, s_pp
366 1 tkerber
367 1 tkerber
    def lowdin_rotation(self, apply=False):
368 1 tkerber
        p = self.input_parameters
369 1 tkerber
        h_mm = p['h']
370 1 tkerber
        s_mm = p['s']
371 1 tkerber
        eig, rot_mm = linalg.eigh(s_mm)
372 1 tkerber
        eig = np.abs(eig)
373 1 tkerber
        rot_mm = np.dot(rot_mm / np.sqrt(eig), dagger(rot_mm))
374 1 tkerber
        if apply:
375 1 tkerber
            self.uptodate = False
376 1 tkerber
            h_mm[:] = rotate_matrix(h_mm, rot_mm) # rotate C region
377 1 tkerber
            s_mm[:] = rotate_matrix(s_mm, rot_mm)
378 1 tkerber
            for alpha, sigma in enumerate(self.selfenergies):
379 1 tkerber
                sigma.h_im[:] = np.dot(sigma.h_im, rot_mm) # rotate L-C coupl.
380 1 tkerber
                sigma.s_im[:] = np.dot(sigma.s_im, rot_mm)
381 1 tkerber
382 1 tkerber
        return rot_mm
383 1 tkerber
384 1 tkerber
    def get_left_channels(self, energy, nchan=1):
385 1 tkerber
        self.initialize()
386 1 tkerber
        g_s_ii = self.greenfunction.retarded(energy)
387 1 tkerber
        lambda_l_ii = self.selfenergies[0].get_lambda(energy)
388 1 tkerber
        lambda_r_ii = self.selfenergies[1].get_lambda(energy)
389 1 tkerber
390 1 tkerber
        if self.greenfunction.S is None:
391 1 tkerber
            s_s_qsrt_ii = s_s_isqrt = np.identity(len(g_s_ii))
392 1 tkerber
        else:
393 1 tkerber
            s_mm = self.greenfunction.S
394 1 tkerber
            s_s_i, s_s_ii = linalg.eig(s_mm)
395 1 tkerber
            s_s_i = np.abs(s_s_i)
396 1 tkerber
            s_s_sqrt_i = np.sqrt(s_s_i) # sqrt of eigenvalues
397 1 tkerber
            s_s_sqrt_ii = np.dot(s_s_ii * s_s_sqrt_i, dagger(s_s_ii))
398 1 tkerber
            s_s_isqrt_ii = np.dot(s_s_ii / s_s_sqrt_i, dagger(s_s_ii))
399 1 tkerber
400 1 tkerber
        lambdab_r_ii = np.dot(np.dot(s_s_isqrt_ii, lambda_r_ii),s_s_isqrt_ii)
401 1 tkerber
        a_l_ii = np.dot(np.dot(g_s_ii, lambda_l_ii), dagger(g_s_ii))
402 1 tkerber
        ab_l_ii = np.dot(np.dot(s_s_sqrt_ii, a_l_ii), s_s_sqrt_ii)
403 1 tkerber
        lambda_i, u_ii = linalg.eig(ab_l_ii)
404 1 tkerber
        ut_ii = np.sqrt(lambda_i / (2.0 * np.pi)) * u_ii
405 1 tkerber
        m_ii = 2 * np.pi * np.dot(np.dot(dagger(ut_ii), lambdab_r_ii),ut_ii)
406 1 tkerber
        T_i,c_in = linalg.eig(m_ii)
407 1 tkerber
        T_i = np.abs(T_i)
408 1 tkerber
409 1 tkerber
        channels = np.argsort(-T_i)[:nchan]
410 1 tkerber
        c_in = np.take(c_in, channels, axis=1)
411 1 tkerber
        T_n = np.take(T_i, channels)
412 1 tkerber
        v_in = np.dot(np.dot(s_s_isqrt_ii, ut_ii), c_in)
413 1 tkerber
414 1 tkerber
        return T_n, v_in