Beispiel #1
0
def test_selfenergy_interpolation(atoms, ntk, filename, begin, end, base, scale, direction=0):
    from gpaw.transport.sparse_matrix import Banded_Sparse_HSD, CP_Sparse_HSD, Se_Sparse_Matrix
    from gpaw.transport.selfenergy import LeadSelfEnergy
    from gpaw.transport.contour import Contour
    hl_skmm, sl_kmm = get_hs(atoms)
    dl_skmm = get_lcao_density_matrix(atoms.calc)
    fermi = atoms.calc.get_fermi_level()
    wfs = atoms.calc.wfs
    hl_spkmm, sl_pkmm, dl_spkmm,  \
    hl_spkcmm, sl_pkcmm, dl_spkcmm = get_pk_hsd(2, ntk,
                                                wfs.kd.ibzk_qc,
                                                hl_skmm, sl_kmm, dl_skmm,
                                                None, wfs.dtype,
                                                direction=direction)    
    my_npk = len(wfs.kd.ibzk_qc) / ntk
    my_nspins = len(wfs.kpt_u) / ( my_npk * ntk)
    
    lead_hsd = Banded_Sparse_HSD(wfs.dtype, my_nspins, my_npk)
    lead_couple_hsd = CP_Sparse_HSD(wfs.dtype, my_nspins, my_npk)
    for pk in range(my_npk):
        lead_hsd.reset(0, pk, sl_pkmm[pk], 'S', init=True)
        lead_couple_hsd.reset(0, pk, sl_pkcmm[pk], 'S', init=True)
        for s in range(my_nspins):
            lead_hsd.reset(s, pk, hl_spkmm[s, pk], 'H', init=True)     
            lead_hsd.reset(s, pk, dl_spkmm[s, pk], 'D', init=True)
            lead_couple_hsd.reset(s, pk, hl_spkcmm[s, pk], 'H', init=True)     
            lead_couple_hsd.reset(s, pk, dl_spkcmm[s, pk], 'D', init=True)          
    lead_se = LeadSelfEnergy(lead_hsd, lead_couple_hsd)
    begin += fermi
    end += fermi
    
    ee = np.linspace(begin, end, base)
    cmp_ee = np.linspace(begin, end, base * scale)
  
    se = []
    cmp_se = []
    from scipy import interpolate

    for e in ee:
        se.append(lead_se(e).recover())
    se = np.array(se)
    ne, ny, nz= se.shape
    nie = len(cmp_ee)
    data = np.zeros([nie, ny, nz], se.dtype)
    for yy in range(ny):
        for zz in range(nz):
            ydata = se[:, yy, zz]
            f = interpolate.interp1d(ee, ydata)
            data[:, yy, zz] = f(cmp_ee)
    inter_se_linear = data
    
    for e in cmp_ee:
        cmp_se.append(lead_se(e).recover())
    
    fd = file(filename, 'w')
    cPickle.dump((cmp_se, inter_se_linear, ee, cmp_ee), fd, 2)
    fd.close()
    
    for i,e in enumerate(cmp_ee):
        print(e, np.max(abs(cmp_se[i] - inter_se_linear[i])), 'linear', np.max(abs(cmp_se[i])))
Beispiel #2
0
def generate_selfenergy_database(atoms,
                                 ntk,
                                 filename,
                                 direction=0,
                                 kt=0.1,
                                 bias=[-3, 3],
                                 depth=3,
                                 comm=None):
    from gpaw.transport.sparse_matrix import Banded_Sparse_HSD, CP_Sparse_HSD, Se_Sparse_Matrix
    from gpaw.transport.selfenergy import LeadSelfEnergy
    from gpaw.transport.contour import Contour
    hl_skmm, sl_kmm = get_hs(atoms)
    dl_skmm = get_lcao_density_matrix(atoms.calc)
    fermi = atoms.calc.get_fermi_level()
    wfs = atoms.calc.wfs
    hl_spkmm, sl_pkmm, dl_spkmm,  \
    hl_spkcmm, sl_pkcmm, dl_spkcmm = get_pk_hsd(2, ntk,
                                                wfs.ibzk_qc,
                                                hl_skmm, sl_kmm, dl_skmm,
                                                None, wfs.dtype,
                                                direction=direction)
    my_npk = len(wfs.ibzk_qc) / ntk
    my_nspins = len(wfs.kpt_u) / (my_npk * ntk)

    lead_hsd = Banded_Sparse_HSD(wfs.dtype, my_nspins, my_npk)
    lead_couple_hsd = CP_Sparse_HSD(wfs.dtype, my_nspins, my_npk)
    for pk in range(my_npk):
        lead_hsd.reset(0, pk, sl_pkmm[pk], 'S', init=True)
        lead_couple_hsd.reset(0, pk, sl_pkcmm[pk], 'S', init=True)
        for s in range(my_nspins):
            lead_hsd.reset(s, pk, hl_spkmm[s, pk], 'H', init=True)
            lead_hsd.reset(s, pk, dl_spkmm[s, pk], 'D', init=True)
            lead_couple_hsd.reset(s, pk, hl_spkcmm[s, pk], 'H', init=True)
            lead_couple_hsd.reset(s, pk, dl_spkcmm[s, pk], 'D', init=True)
    lead_se = LeadSelfEnergy(lead_hsd, lead_couple_hsd)
    contour = Contour(kt, [fermi] * 2, bias, depth, comm=comm)
    path = contour.get_plot_path(ex=True)
    for nid, energy in zip(path.my_nids, path.my_energies):
        for kpt in wfs.kpt_u:
            if kpt.q % ntk == 0:
                flag = str(kpt.k // ntk) + '_' + str(nid)
                lead_se.s = kpt.s
                lead_se.pk = kpt.q // ntk
                data = lead_se(energy)
                fd = file(flag, 'w')
                cPickle.dump(data, fd, 2)
                fd.close()
Beispiel #3
0
    def initialize_selfenergy_and_green_function(self, tp):
        self.selfenergies = []
        if tp.use_lead:
            for i in range(tp.lead_num):
                self.selfenergies.append(
                    LeadSelfEnergy(tp.lead_hsd[i], tp.lead_couple_hsd[i]))

                self.selfenergies[i].set_bias(tp.bias[i])
def generate_selfenergy_database(atoms, ntk, filename, direction=0, kt=0.1,
                                 bias=[-3,3], depth=3, comm=None):
    from gpaw.transport.sparse_matrix import Banded_Sparse_HSD, CP_Sparse_HSD, Se_Sparse_Matrix
    from gpaw.transport.selfenergy import LeadSelfEnergy
    from gpaw.transport.contour import Contour
    hl_skmm, sl_kmm = get_hs(atoms)
    dl_skmm = get_lcao_density_matrix(atoms.calc)
    fermi = atoms.calc.get_fermi_level()
    wfs = atoms.calc.wfs
    hl_spkmm, sl_pkmm, dl_spkmm,  \
    hl_spkcmm, sl_pkcmm, dl_spkcmm = get_pk_hsd(2, ntk,
                                                wfs.kd.ibzk_qc,
                                                hl_skmm, sl_kmm, dl_skmm,
                                                None, wfs.dtype,
                                                direction=direction)    
    my_npk = len(wfs.kd.ibzk_qc) / ntk
    my_nspins = len(wfs.kpt_u) / ( my_npk * ntk)
    
    lead_hsd = Banded_Sparse_HSD(wfs.dtype, my_nspins, my_npk)
    lead_couple_hsd = CP_Sparse_HSD(wfs.dtype, my_nspins, my_npk)
    for pk in range(my_npk):
        lead_hsd.reset(0, pk, sl_pkmm[pk], 'S', init=True)
        lead_couple_hsd.reset(0, pk, sl_pkcmm[pk], 'S', init=True)
        for s in range(my_nspins):
            lead_hsd.reset(s, pk, hl_spkmm[s, pk], 'H', init=True)     
            lead_hsd.reset(s, pk, dl_spkmm[s, pk], 'D', init=True)
            lead_couple_hsd.reset(s, pk, hl_spkcmm[s, pk], 'H', init=True)     
            lead_couple_hsd.reset(s, pk, dl_spkcmm[s, pk], 'D', init=True)          
    lead_se = LeadSelfEnergy(lead_hsd, lead_couple_hsd)
    contour = Contour(kt, [fermi] * 2, bias, depth, comm=comm)    
    path = contour.get_plot_path(ex=True)
    for nid, energy in zip(path.my_nids, path.my_energies):
        for kpt in wfs.kpt_u:
                if kpt.q % ntk == 0:
                    flag = str(kpt.k // ntk) + '_' + str(nid)
                    lead_se.s = kpt.s
                    lead_se.pk = kpt.q // ntk
                    data = lead_se(energy)
                    fd = file(flag, 'w')    
                    cPickle.dump(data, fd, 2)
                    fd.close()    
Beispiel #5
0
def path_selfenergy(atoms, ntk, filename, begin, end, num=257, direction=0):
    from gpaw.transport.sparse_matrix import Banded_Sparse_HSD, CP_Sparse_HSD, Se_Sparse_Matrix
    from gpaw.transport.selfenergy import LeadSelfEnergy
    from gpaw.transport.contour import Contour
    hl_skmm, sl_kmm = get_hs(atoms)
    dl_skmm = get_lcao_density_matrix(atoms.calc)
    fermi = atoms.calc.get_fermi_level()
    wfs = atoms.calc.wfs
    hl_spkmm, sl_pkmm, dl_spkmm,  \
    hl_spkcmm, sl_pkcmm, dl_spkcmm = get_pk_hsd(2, ntk,
                                                wfs.ibzk_qc,
                                                hl_skmm, sl_kmm, dl_skmm,
                                                None, wfs.dtype,
                                                direction=direction)
    my_npk = len(wfs.ibzk_qc) / ntk
    my_nspins = len(wfs.kpt_u) / (my_npk * ntk)

    lead_hsd = Banded_Sparse_HSD(wfs.dtype, my_nspins, my_npk)
    lead_couple_hsd = CP_Sparse_HSD(wfs.dtype, my_nspins, my_npk)
    for pk in range(my_npk):
        lead_hsd.reset(0, pk, sl_pkmm[pk], 'S', init=True)
        lead_couple_hsd.reset(0, pk, sl_pkcmm[pk], 'S', init=True)
        for s in range(my_nspins):
            lead_hsd.reset(s, pk, hl_spkmm[s, pk], 'H', init=True)
            lead_hsd.reset(s, pk, dl_spkmm[s, pk], 'D', init=True)
            lead_couple_hsd.reset(s, pk, hl_spkcmm[s, pk], 'H', init=True)
            lead_couple_hsd.reset(s, pk, dl_spkcmm[s, pk], 'D', init=True)
    lead_se = LeadSelfEnergy(lead_hsd, lead_couple_hsd)
    begin += fermi
    end += fermi

    ee = np.linspace(begin, end, num)
    se = []

    for e in ee:
        se.append(lead_se(e).recover())
    se = np.array(se)

    fd = file(filename + '_' + str(world.rank), 'w')
    cPickle.dump((se, ee), fd, 2)
    fd.close()
    def initialize(
        self, srf_atoms, tip_atoms, srf_coupling_atoms, tip_coupling_atoms, srf_pl_atoms, tip_pl_atoms, energies, bias=0
    ):
        """
        srf_atoms: the atom index of onsite surface atoms 
        tip_atoms: the atom index of onsite tip atoms
        energies: list of energies, for which the transmission function should be evaluated
        bias: will be used to precalculate the greenfunctions of surface and tip
        """
        self.bias = bias
        self.energies = energies
        self.tip_atoms = tip_atoms
        self.srf_atoms = srf_atoms
        self.srf_coupling_atoms = srf_coupling_atoms
        self.tip_coupling_atoms = tip_coupling_atoms
        # align potentials for onsite surface, onsite tip and tip principle layers according to surface_pl
        srf_alignv = self.align_v(calc1=self.srf_pl, index1=srf_pl_atoms[0], calc2=self.srf, index2=srf_atoms[0])
        tip_alignv = srf_alignv + self.tip.get_fermi_level() - self.srf.get_fermi_level()  # - bias
        tip_pl_alignv = self.align_v(calc1=self.tip, index1=tip_atoms[-1], calc2=self.tip_pl, index2=tip_pl_atoms[-1])
        tip_pl_alignv += tip_alignv
        self.srf_alignv = srf_alignv
        self.tip_alignv = tip_alignv

        # hamiltoian and overlap matrix of onsite tip and srf
        self.h1, self.s1 = Lead_HS(calc=self.tip, atom_list=tip_atoms, shift=srf_alignv - bias).get_HS()
        print("shape of onsite tip", np.shape(self.h1), np.shape(self.s1))
        self.h2, self.s2 = Lead_HS(calc=self.srf, atom_list=srf_atoms, shift=tip_alignv).get_HS()
        print("shape of onsite srf", np.shape(self.h2), np.shape(self.s2))

        # hamiltoian and overlap matrix of two tip and srf principle layers
        self.h10, self.s10 = Lead_HS(
            calc=self.tip_pl, atom_list=tip_pl_atoms, shift=tip_pl_alignv
        ).get_HS()  # 2 and only 2 principle layers
        print("shape of pl tip", np.shape(self.h10), np.shape(self.s10))
        self.h20, self.s20 = Lead_HS(
            calc=self.srf_pl, atom_list=srf_pl_atoms, shift=-bias
        ).get_HS()  # 2 and only 2 principle layers
        print("shape of pl srf", np.shape(self.h20), np.shape(self.s20))

        nbf1, nbf2 = len(self.h1), len(self.h2)  # No. of basis functions of onsite tip and surface atoms
        pl1, pl2 = len(self.h10) / 2, len(self.h20) / 2  # No. of basis functions per principle layer of tip and srf
        nenergies = len(energies)

        # 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 betwen 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 betwen 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.selfenergy2 = LeadSelfEnergy(hs2_dii, hs2_dij, hs2_dim)
        self.greenfunction1 = GreenFunction(self.h1, self.s1, [self.selfenergy1])
        self.greenfunction2 = GreenFunction(self.h2, self.s2, [self.selfenergy2])
        # print 'shape of greenfunction tip:', np.shape(self.greenfunction1)
        # print 'shape of greenfunction srf:', np.shape(self.greenfunction2)
        # shift the bands due to the bias
        self.selfenergy1.set_bias(0.0)
        self.selfenergy2.set_bias(bias)

        # extend the surface potentials and basis functions
        self.setup_stm()

        # tip and surface greenfunction matrices.
        nbf1_small = self.ni  # XXX Change this for efficiency in the future
        nbf2_small = self.nj  # XXX -||-
        coupling_list1 = range(nbf1 - nbf1_small, nbf1)  # XXX -||-
        coupling_list2 = range(0, 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):
            gft1_mm = self.greenfunction1(energy)[coupling_list1]
            gft1_mm = np.take(gft1_mm, coupling_list1, axis=1)

            gft2_mm = self.greenfunction2(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
class STM:
    def __init__(self, srf, tip, srf_pl, tip_pl, eta1=1e-4, eta2=1e-4):
        self.tip = tip  # tip calcualtor
        self.tip_pl = tip_pl  # tip periodic layers calculator
        self.srf = srf  # surface calculator
        self.srf_pl = srf_pl  # surface periodic layers calculator
        self.eta1 = eta1
        self.eta2 = eta2
        self.tgd = tip.gd
        self.sgd = srf.gd

        # assert tgd.h_cv == sgd.h_cv
        # assert not (tgd.h_c - sgd.h_c).any()

    def initialize(
        self, srf_atoms, tip_atoms, srf_coupling_atoms, tip_coupling_atoms, srf_pl_atoms, tip_pl_atoms, energies, bias=0
    ):
        """
        srf_atoms: the atom index of onsite surface atoms 
        tip_atoms: the atom index of onsite tip atoms
        energies: list of energies, for which the transmission function should be evaluated
        bias: will be used to precalculate the greenfunctions of surface and tip
        """
        self.bias = bias
        self.energies = energies
        self.tip_atoms = tip_atoms
        self.srf_atoms = srf_atoms
        self.srf_coupling_atoms = srf_coupling_atoms
        self.tip_coupling_atoms = tip_coupling_atoms
        # align potentials for onsite surface, onsite tip and tip principle layers according to surface_pl
        srf_alignv = self.align_v(calc1=self.srf_pl, index1=srf_pl_atoms[0], calc2=self.srf, index2=srf_atoms[0])
        tip_alignv = srf_alignv + self.tip.get_fermi_level() - self.srf.get_fermi_level()  # - bias
        tip_pl_alignv = self.align_v(calc1=self.tip, index1=tip_atoms[-1], calc2=self.tip_pl, index2=tip_pl_atoms[-1])
        tip_pl_alignv += tip_alignv
        self.srf_alignv = srf_alignv
        self.tip_alignv = tip_alignv

        # hamiltoian and overlap matrix of onsite tip and srf
        self.h1, self.s1 = Lead_HS(calc=self.tip, atom_list=tip_atoms, shift=srf_alignv - bias).get_HS()
        print("shape of onsite tip", np.shape(self.h1), np.shape(self.s1))
        self.h2, self.s2 = Lead_HS(calc=self.srf, atom_list=srf_atoms, shift=tip_alignv).get_HS()
        print("shape of onsite srf", np.shape(self.h2), np.shape(self.s2))

        # hamiltoian and overlap matrix of two tip and srf principle layers
        self.h10, self.s10 = Lead_HS(
            calc=self.tip_pl, atom_list=tip_pl_atoms, shift=tip_pl_alignv
        ).get_HS()  # 2 and only 2 principle layers
        print("shape of pl tip", np.shape(self.h10), np.shape(self.s10))
        self.h20, self.s20 = Lead_HS(
            calc=self.srf_pl, atom_list=srf_pl_atoms, shift=-bias
        ).get_HS()  # 2 and only 2 principle layers
        print("shape of pl srf", np.shape(self.h20), np.shape(self.s20))

        nbf1, nbf2 = len(self.h1), len(self.h2)  # No. of basis functions of onsite tip and surface atoms
        pl1, pl2 = len(self.h10) / 2, len(self.h20) / 2  # No. of basis functions per principle layer of tip and srf
        nenergies = len(energies)

        # 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 betwen 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 betwen 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.selfenergy2 = LeadSelfEnergy(hs2_dii, hs2_dij, hs2_dim)
        self.greenfunction1 = GreenFunction(self.h1, self.s1, [self.selfenergy1])
        self.greenfunction2 = GreenFunction(self.h2, self.s2, [self.selfenergy2])
        # print 'shape of greenfunction tip:', np.shape(self.greenfunction1)
        # print 'shape of greenfunction srf:', np.shape(self.greenfunction2)
        # shift the bands due to the bias
        self.selfenergy1.set_bias(0.0)
        self.selfenergy2.set_bias(bias)

        # extend the surface potentials and basis functions
        self.setup_stm()

        # tip and surface greenfunction matrices.
        nbf1_small = self.ni  # XXX Change this for efficiency in the future
        nbf2_small = self.nj  # XXX -||-
        coupling_list1 = range(nbf1 - nbf1_small, nbf1)  # XXX -||-
        coupling_list2 = range(0, 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):
            gft1_mm = self.greenfunction1(energy)[coupling_list1]
            gft1_mm = np.take(gft1_mm, coupling_list1, axis=1)

            gft2_mm = self.greenfunction2(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

    def scan(self, height=None, zmin=None, zmax=None):
        if height is not None:
            self.current = np.zeros((self.srf.gd.N_c[0], self.srf.gd.N_c[1]))
            h = 0
            h = np.round(height / Bohr / self.srf.gd.h_c[2]).astype(int)
            for px in range(self.srf.gd.N_c[0]):
                for py in range(self.srf.gd.N_c[1]):
                    pos = np.array([px, py, h])
                    V_12 = self.get_Vts(pos)
                    I = self.get_current(V_12)
                    self.current[px, py] = I
                    print("I = ", I)
            return self.current
        if height is None:
            zmin_c = np.round(zmin / Bohr / self.srf.gd.h_c[2]).astype(int)
            zmax_c = np.round(zmax / Bohr / self.srf.gd.h_c[2]).astype(int)
            nz = zmax_c - zmin_c
            self.current = np.zeros((self.srf.gd.N_c[0], self.srf.gd.N_c[1], nz))
            for px in range(self.srf.gd.N_c[0]):
                for py in range(self.srf.gd.N_c[1]):
                    for pz in range(zmin_c, zmax_c):
                        # get coupleing between tip and surface Vts for a given tip position on original srf grids
                        pos = np.array([px, py, pz])
                        V_12 = self.get_Vts(pos)
                        # get current values
                        I = self.get_current(V_12)
                        self.current[px, py, pz - zmin_c] = I
                        print(I)
            return self.current

    def get_current(self, v_12):
        # get transmission
        T_e = self.get_transmission(v_12)
        I = -np.trapz(x=self.energies, y=T_e)
        return I

    def get_transmission(self, v_12):
        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]
            gft2 = self.gft2_emm[e]
            a1 = gft1 - dagger(gft1)
            a2 = gft2 - dagger(gft2)
            v12_a2 = np.dot(v_12, a2)
            v21_a1 = np.dot(v_21, a1)
            T = -np.trace(np.dot(v12_a2, v21_a1))
            T_e[e] = T
            self.T_e = T_e
        return T_e

    def get_Vts(self, tip_pos):
        # tip_pos is in terms of grid points, showing the tip apex position respect to the surface grid
        print("tip_pos is", tip_pos)
        tip_apex_c = self.tip_apex_c[0]
        srf_pos_av = self.srf.atoms.get_positions() / Bohr
        srf_zmax = srf_pos_av[:, 2].max()
        srf_zmax_c = np.round(srf_zmax / self.srf.gd.h_c[2]).astype(int)
        srf_ox = self.srf_extension[0] + tip_pos[0]
        srf_oy = self.srf_extension[1] + tip_pos[1]
        srf_oz = srf_zmax_c + tip_pos[2]
        shift = np.array([srf_ox, srf_oy, srf_oz])
        shift -= tip_apex_c
        for tbox in self.tip_coupling_functions:
            tbox.corner_c += shift
        for tbox in self.tip_coupling_kinetics:
            tbox.corner_c += shift
        # get the combined effective potential on the extended surface grid
        """
        This part needs to be updated for a separate xc calculation
        """
        tip_ox = self.ox
        tip_oy = self.oy
        tip_oz = self.oz
        srf_sx = srf_ox - self.nmx
        srf_ex = srf_ox + self.npx
        srf_sy = srf_oy - self.nmy
        srf_ey = srf_oy + self.npy
        srf_sz = srf_oz - self.nmz
        srf_ez = srf_oz + self.npz
        tip_sx = tip_ox - self.nmx
        tip_ex = tip_ox + self.npx
        tip_sy = tip_oy - self.nmy
        tip_ey = tip_oy + self.npy
        tip_sz = tip_oz - self.nmz
        tip_ez = tip_oz + self.npz
        V_tip = self.tip.get_effective_potential() - self.tip_alignv
        V_couple = self.extvt_G.copy()
        V_couple[srf_sx:srf_ex, srf_sy:srf_ey, srf_sz:srf_ez] = V_tip[tip_sx:tip_ex, tip_sy:tip_ey, tip_sz:tip_ez]

        # get V_ts
        V_ts = np.zeros((self.ni, self.nj))
        for ns in range(len(self.srf_coupling_functions)):
            sf = self.srf_coupling_functions[ns]
            sk = self.srf_coupling_kinetics[ns]
            j1 = sf[0].index
            j2 = j1 + len(sf[0])
            for t in self.tip_coupling_functions:
                i1 = t.index
                i2 = i1 + len(t)
                test_overlap1 = []
                test_overlap2 = []
                for x in range(9):
                    overlap = t | sf[x]
                    if overlap is not None:
                        test_overlap1.append(overlap.copy())
                        test_overlap2.append((np.abs(overlap)).sum())
                    else:
                        test_overlap1.append(overlap)
                        test_overlap2.append(overlap)
                k = np.argmax(test_overlap2)
                if test_overlap1[k] is not None:
                    V_ts[i1:i2, j1:j2] += t | sk[k]
                    V_ts[i1:i2, j1:j2] += t | V_couple | sf[k]

        # return the tbox back :)
        for tbox in self.tip_coupling_functions:
            tbox.corner_c -= shift
        for tbox in self.tip_coupling_kinetics:
            tbox.corner_c -= shift

        # print 'np.shape(V_ts)',np.shape(V_ts)
        return V_ts

    def setup_stm(self):
        tip_pos_av = self.tip.atoms.get_positions() / Bohr
        srf_pos_av = self.srf.atoms.get_positions() / Bohr

        # get selected tip functions on small grids and apply kinetics
        self.tip_coupling_functions = []
        self.tip_coupling_kinetics = []
        i = 0
        for a in self.tip_coupling_atoms:
            setup = self.tip.wfs.setups[a]
            spos_c = tip_pos_av[a] / self.tip.gd.cell_c  # gives the scaled position of tip atoms in tip cell
            for phit in setup.phit_j:
                f = AtomCenteredFunctions(self.tip.gd, [phit], spos_c, i)
                f_kinetic = f.apply_t()
                self.tip_coupling_functions.append(f)
                self.tip_coupling_kinetics.append(f_kinetic)
                i += len(f.f_iG)
        self.ni = i
        # print self.tip_coupling_functions[1].size_c
        # print self.tip_coupling_functions[1].corner_c
        # print np.shape(self.tip_coupling_functions[1].f_iG)

        # get tip cutoff and surface extension
        tip_corner_c = []
        temp_min = self.tip_coupling_kinetics[0].corner_c
        temp_max = self.tip_coupling_kinetics[0].corner_c
        for f in self.tip_coupling_kinetics:
            start_c = np.minimum(f.corner_c, temp_min)
            end_c = np.maximum(f.corner_c + f.size_c, temp_max)
            temp_min = start_c
            temp_max = end_c
        # print "start_c",start_c         # in terms of grid points
        # print "end_c",end_c             # in terms of grid points
        self.tip_zmin = tip_pos_av[:, 2].min()
        self.tip_apex_index = np.where(tip_pos_av[:, 2] < self.tip_zmin + 0.01)
        self.tip_apex_c = np.round(tip_pos_av[self.tip_apex_index] / self.tgd.h_c).astype(int)
        self.ox, self.oy, self.oz = self.tip_apex_c[0]
        self.npx, self.npy, self.npz = np.round(end_c).astype(int) - self.tip_apex_c[0]
        self.nmx, self.nmy, self.nmz = self.tip_apex_c[0] - np.round(start_c).astype(int)

        # get the extension of srf basis functions and effective potentials
        srf_extension = np.array([self.srf.gd.N_c[0], self.srf.gd.N_c[1], 0])
        self.srf_extension = srf_extension
        print("extension of surface in terms of grids:", srf_extension)

        # get surface fucntions on small grid with periodic conditions with entension
        self.srf_coupling_functions = []
        self.srf_coupling_kinetics = []
        j = 0
        for a in self.srf_coupling_atoms:
            setup = self.srf.wfs.setups[a]
            spos_c = srf_pos_av[a] / self.srf.gd.cell_c
            for phit in setup.phit_j:
                f = AtomCenteredFunctions(self.srf.gd, [phit], spos_c, j)
                f.corner_c += srf_extension
                f_kinetic = f.apply_t()
                self.srf_coupling_functions.append(f.periodic())
                self.srf_coupling_kinetics.append(f_kinetic.periodic())
                j += len(f.f_iG)
        self.nj = j
        # print np.shape(self.srf_coupling_functions[0][0].f_iG), self.srf_coupling_functions[0][0].corner_c
        # print np.shape(self.srf_coupling_functions[0][8].f_iG), self.srf_coupling_functions[0][8].corner_c
        # Extend the surface grid and surface effective potential
        svt_G = self.srf.get_effective_potential() - self.srf_alignv + self.bias
        ex, ey, ez = srf_extension
        newsize_c = 2 * srf_extension + self.sgd.N_c
        extvt_G = np.zeros(newsize_c)
        extvt_G[ex:-ex, ey:-ey, :] = svt_G
        extvt_G[:ex, ey:-ey, :] = svt_G[-ex:, :, :]
        extvt_G[-ex:, ey:-ey, :] = svt_G[:ex, :, :]
        extvt_G[:, :ey, :] = extvt_G[:, -2 * ey : -ey, :]
        extvt_G[:, -ey:, :] = extvt_G[:, ey : 2 * ey, :]
        self.extvt_G = extvt_G

        extsgd = GridDescriptor(N_c=newsize_c, cell_cv=self.sgd.h_c * (newsize_c), pbc_c=False, comm=mpi.serial_comm)

        ## Transfer the coner_c of surface basis-functions and srf_kinetics to the extended surface cell
        # for sbox in self.srf_coupling_functions:
        #    sbox.corner_c += srf_extension
        # for sbox in self.srf_coupling_kinetics:
        #    sbox.corner_c += srf_extension

    def align_v(self, calc1, index1, calc2, index2):
        pos1 = calc1.atoms.positions[index1]
        pos1_c = np.round(pos1 / Bohr / calc1.wfs.gd.h_c).astype(int)
        pos1_v = calc1.get_effective_potential()[pos1_c[0], pos1_c[1], pos1_c[2]]
        pos2 = calc2.atoms.positions[index2]
        pos2_c = np.round(pos2 / Bohr / calc2.wfs.gd.h_c).astype(int)
        pos2_v = calc2.get_effective_potential()[pos2_c[0], pos2_c[1], pos2_c[2]]
        return pos2_v - pos1_v
Beispiel #8
0
    def initialize(self, srf_atoms, tip_atoms, srf_coupling_atoms, tip_coupling_atoms, 
                   srf_pl_atoms, tip_pl_atoms, energies, bias=0):
        """
        srf_atoms: the atom index of onsite surface atoms 
        tip_atoms: the atom index of onsite tip atoms
        energies: list of energies, for which the transmission function should be evaluated
        bias: will be used to precalculate the greenfunctions of surface and tip
        """
        self.bias = bias
        self.energies = energies
        self.tip_atoms = tip_atoms
        self.srf_atoms = srf_atoms
        self.srf_coupling_atoms = srf_coupling_atoms
        self.tip_coupling_atoms = tip_coupling_atoms
        #align potentials for onsite surface, onsite tip and tip principle layers according to surface_pl
        srf_alignv = self.align_v(calc1 = self.srf_pl, index1=srf_pl_atoms[0], calc2=self.srf, index2=srf_atoms[0])         
        tip_alignv = srf_alignv + self.tip.get_fermi_level() - self.srf.get_fermi_level() #- bias
        tip_pl_alignv = self.align_v(calc1=self.tip, index1=tip_atoms[-1], calc2=self.tip_pl, index2=tip_pl_atoms[-1])
        tip_pl_alignv += tip_alignv
        self.srf_alignv = srf_alignv
        self.tip_alignv = tip_alignv

        #hamiltoian and overlap matrix of onsite tip and srf
        self.h1, self.s1 = Lead_HS(calc = self.tip, atom_list = tip_atoms, shift = srf_alignv-bias).get_HS()
        print 'shape of onsite tip', np.shape(self.h1), np.shape(self.s1)
        self.h2, self.s2 = Lead_HS(calc = self.srf, atom_list = srf_atoms, shift = tip_alignv).get_HS()        
        print 'shape of onsite srf', np.shape(self.h2), np.shape(self.s2)

        #hamiltoian and overlap matrix of two tip and srf principle layers
        self.h10, self.s10 = Lead_HS(calc = self.tip_pl, atom_list = tip_pl_atoms, shift = tip_pl_alignv).get_HS() #2 and only 2 principle layers
        print 'shape of pl tip', np.shape(self.h10), np.shape(self.s10) 
        self.h20, self.s20 = Lead_HS(calc = self.srf_pl, atom_list = srf_pl_atoms, shift = -bias).get_HS() #2 and only 2 principle layers
        print 'shape of pl srf', np.shape(self.h20), np.shape(self.s20)

        nbf1, nbf2 = len(self.h1), len(self.h2)       #No. of basis functions of onsite tip and surface atoms
        pl1, pl2 = len(self.h10)/2, len(self.h20)/2   #No. of basis functions per principle layer of tip and srf
        nenergies = len(energies)        

        #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 betwen 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 betwen 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.selfenergy2 = LeadSelfEnergy(hs2_dii, hs2_dij, hs2_dim)
        self.greenfunction1 = GreenFunction(self.h1, self.s1,[self.selfenergy1])
        self.greenfunction2 = GreenFunction(self.h2, self.s2,[self.selfenergy2])
        #print 'shape of greenfunction tip:', np.shape(self.greenfunction1)
        #print 'shape of greenfunction srf:', np.shape(self.greenfunction2)
        #shift the bands due to the bias
        self.selfenergy1.set_bias(0.0)
        self.selfenergy2.set_bias(bias)

        # extend the surface potentials and basis functions
        self.setup_stm()


        #tip and surface greenfunction matrices.
        nbf1_small = self.ni #XXX Change this for efficiency in the future
        nbf2_small = self.nj #XXX -||-
        coupling_list1 = range(nbf1-nbf1_small,nbf1)# XXX -||-
        coupling_list2 = range(0,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):
            gft1_mm = self.greenfunction1(energy)[coupling_list1]
            gft1_mm = np.take(gft1_mm, coupling_list1, axis=1)

            gft2_mm = self.greenfunction2(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
Beispiel #9
0
class STM:
    def __init__(self, srf, tip, srf_pl, tip_pl, eta1=1e-4, eta2=1e-4):
        self.tip = tip                     # tip calcualtor
        self.tip_pl = tip_pl               # tip periodic layers calculator
        self.srf = srf                     # surface calculator
        self.srf_pl = srf_pl               # surface periodic layers calculator
        self.eta1 = eta1
        self.eta2 = eta2 
        self.tgd = tip.gd                  
        self.sgd = srf.gd 
        
        #assert tgd.h_cv == sgd.h_cv
        #assert not (tgd.h_c - sgd.h_c).any()

    def initialize(self, srf_atoms, tip_atoms, srf_coupling_atoms, tip_coupling_atoms, 
                   srf_pl_atoms, tip_pl_atoms, energies, bias=0):
        """
        srf_atoms: the atom index of onsite surface atoms 
        tip_atoms: the atom index of onsite tip atoms
        energies: list of energies, for which the transmission function should be evaluated
        bias: will be used to precalculate the greenfunctions of surface and tip
        """
        self.bias = bias
        self.energies = energies
        self.tip_atoms = tip_atoms
        self.srf_atoms = srf_atoms
        self.srf_coupling_atoms = srf_coupling_atoms
        self.tip_coupling_atoms = tip_coupling_atoms
        #align potentials for onsite surface, onsite tip and tip principle layers according to surface_pl
        srf_alignv = self.align_v(calc1 = self.srf_pl, index1=srf_pl_atoms[0], calc2=self.srf, index2=srf_atoms[0])         
        tip_alignv = srf_alignv + self.tip.get_fermi_level() - self.srf.get_fermi_level() #- bias
        tip_pl_alignv = self.align_v(calc1=self.tip, index1=tip_atoms[-1], calc2=self.tip_pl, index2=tip_pl_atoms[-1])
        tip_pl_alignv += tip_alignv
        self.srf_alignv = srf_alignv
        self.tip_alignv = tip_alignv

        #hamiltoian and overlap matrix of onsite tip and srf
        self.h1, self.s1 = Lead_HS(calc = self.tip, atom_list = tip_atoms, shift = srf_alignv-bias).get_HS()
        print 'shape of onsite tip', np.shape(self.h1), np.shape(self.s1)
        self.h2, self.s2 = Lead_HS(calc = self.srf, atom_list = srf_atoms, shift = tip_alignv).get_HS()        
        print 'shape of onsite srf', np.shape(self.h2), np.shape(self.s2)

        #hamiltoian and overlap matrix of two tip and srf principle layers
        self.h10, self.s10 = Lead_HS(calc = self.tip_pl, atom_list = tip_pl_atoms, shift = tip_pl_alignv).get_HS() #2 and only 2 principle layers
        print 'shape of pl tip', np.shape(self.h10), np.shape(self.s10) 
        self.h20, self.s20 = Lead_HS(calc = self.srf_pl, atom_list = srf_pl_atoms, shift = -bias).get_HS() #2 and only 2 principle layers
        print 'shape of pl srf', np.shape(self.h20), np.shape(self.s20)

        nbf1, nbf2 = len(self.h1), len(self.h2)       #No. of basis functions of onsite tip and surface atoms
        pl1, pl2 = len(self.h10)/2, len(self.h20)/2   #No. of basis functions per principle layer of tip and srf
        nenergies = len(energies)        

        #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 betwen 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 betwen 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.selfenergy2 = LeadSelfEnergy(hs2_dii, hs2_dij, hs2_dim)
        self.greenfunction1 = GreenFunction(self.h1, self.s1,[self.selfenergy1])
        self.greenfunction2 = GreenFunction(self.h2, self.s2,[self.selfenergy2])
        #print 'shape of greenfunction tip:', np.shape(self.greenfunction1)
        #print 'shape of greenfunction srf:', np.shape(self.greenfunction2)
        #shift the bands due to the bias
        self.selfenergy1.set_bias(0.0)
        self.selfenergy2.set_bias(bias)

        # extend the surface potentials and basis functions
        self.setup_stm()


        #tip and surface greenfunction matrices.
        nbf1_small = self.ni #XXX Change this for efficiency in the future
        nbf2_small = self.nj #XXX -||-
        coupling_list1 = range(nbf1-nbf1_small,nbf1)# XXX -||-
        coupling_list2 = range(0,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):
            gft1_mm = self.greenfunction1(energy)[coupling_list1]
            gft1_mm = np.take(gft1_mm, coupling_list1, axis=1)

            gft2_mm = self.greenfunction2(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

    def scan(self,height=None,zmin=None,zmax=None):
        if height is not None:
            self.current = np.zeros((self.srf.gd.N_c[0],self.srf.gd.N_c[1]))
            h = 0
            h = np.round(height/Bohr/self.srf.gd.h_c[2]).astype(int)
            for px in range(self.srf.gd.N_c[0]):
                for py in range(self.srf.gd.N_c[1]):
                    pos = np.array([px,py,h])
                    V_12 = self.get_Vts(pos)
                    I = self.get_current(V_12)
                    self.current[px,py] = I
                    print 'I = ', I
            return self.current
        if height is None:
            zmin_c = np.round(zmin/Bohr/self.srf.gd.h_c[2]).astype(int)
            zmax_c = np.round(zmax/Bohr/self.srf.gd.h_c[2]).astype(int)
            nz = zmax_c - zmin_c
            self.current = np.zeros((self.srf.gd.N_c[0],self.srf.gd.N_c[1],nz))
            for px in range(self.srf.gd.N_c[0]):
                for py in range(self.srf.gd.N_c[1]):
                    for pz in range(zmin_c,zmax_c):
                        # get coupleing between tip and surface Vts for a given tip position on original srf grids 
                        pos = np.array([px,py,pz])
                        V_12 = self.get_Vts(pos)
                        # get current values
                        I = self.get_current(V_12)
                        self.current[px,py,pz-zmin_c] = I
                        print I
            return self.current



    def get_current(self, v_12):
        # get transmission
        T_e = self.get_transmission(v_12)
        I = -np.trapz(x = self.energies, y = T_e)         
        return I



    def get_transmission(self, v_12):
        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]
            gft2 = self.gft2_emm[e]            
            a1 = (gft1 - dagger(gft1))
            a2 = (gft2 - dagger(gft2))
            v12_a2 = np.dot(v_12, a2)
            v21_a1 = np.dot(v_21, a1)
            T = -np.trace(np.dot(v12_a2, v21_a1))
            T_e[e] = T
            self.T_e = T_e
        return T_e



    def get_Vts(self, tip_pos):
        # tip_pos is in terms of grid points, showing the tip apex position respect to the surface grid
        print 'tip_pos is', tip_pos
        tip_apex_c = self.tip_apex_c[0] 
        srf_pos_av = self.srf.atoms.get_positions() / Bohr
        srf_zmax = srf_pos_av[:,2].max()
        srf_zmax_c = np.round(srf_zmax / self.srf.gd.h_c[2]).astype(int)
        srf_ox = self.srf_extension[0] + tip_pos[0]
        srf_oy = self.srf_extension[1] + tip_pos[1]
        srf_oz = srf_zmax_c + tip_pos[2]
        shift = np.array([srf_ox, srf_oy, srf_oz])
        shift -= tip_apex_c
        for tbox in self.tip_coupling_functions:
            tbox.corner_c += shift
        for tbox in self.tip_coupling_kinetics:
            tbox.corner_c += shift
        # get the combined effective potential on the extended surface grid
        """
        This part needs to be updated for a separate xc calculation
        """
        tip_ox = self.ox
        tip_oy = self.oy
        tip_oz = self.oz
        srf_sx = srf_ox - self.nmx
        srf_ex = srf_ox + self.npx
        srf_sy = srf_oy - self.nmy
        srf_ey = srf_oy + self.npy
        srf_sz = srf_oz - self.nmz
        srf_ez = srf_oz + self.npz
        tip_sx = tip_ox - self.nmx
        tip_ex = tip_ox + self.npx
        tip_sy = tip_oy - self.nmy
        tip_ey = tip_oy + self.npy
        tip_sz = tip_oz - self.nmz
        tip_ez = tip_oz + self.npz
        V_tip = self.tip.get_effective_potential() - self.tip_alignv 
        V_couple = self.extvt_G.copy()
        V_couple[srf_sx:srf_ex, srf_sy:srf_ey, srf_sz:srf_ez] = V_tip[tip_sx:tip_ex, tip_sy:tip_ey, tip_sz:tip_ez]

        #get V_ts 
        V_ts = np.zeros((self.ni,self.nj))
        for ns in range(len(self.srf_coupling_functions)):
            sf = self.srf_coupling_functions[ns]
            sk = self.srf_coupling_kinetics[ns]
            j1 = sf[0].index
            j2 = j1 + len(sf[0])
            for t in self.tip_coupling_functions:
                i1 = t.index
                i2 = i1 + len(t)
                test_overlap1 = []
                test_overlap2 = []
                for x in range(9):
                    overlap = (t | sf[x])                    
                    if overlap is not None:
                        test_overlap1.append(overlap.copy())
                        test_overlap2.append((np.abs(overlap)).sum())
                    else:
                        test_overlap1.append(overlap)
                        test_overlap2.append(overlap)
                k = np.argmax(test_overlap2)
                if test_overlap1[k] != None:
                    V_ts[i1:i2, j1:j2] += (t|sk[k])
                    V_ts[i1:i2, j1:j2] += (t|V_couple|sf[k])

        # return the tbox back :) 
        for tbox in self.tip_coupling_functions:
            tbox.corner_c -= shift
        for tbox in self.tip_coupling_kinetics:
            tbox.corner_c -= shift
       
        #print 'np.shape(V_ts)',np.shape(V_ts)
        return V_ts       

        
    def setup_stm(self):
        tip_pos_av = self.tip.atoms.get_positions() / Bohr
        srf_pos_av = self.srf.atoms.get_positions() / Bohr
        
        # get selected tip functions on small grids and apply kinetics
        self.tip_coupling_functions = []
        self.tip_coupling_kinetics = []
        i = 0
        for a in self.tip_coupling_atoms:
            setup = self.tip.wfs.setups[a]
            spos_c = tip_pos_av[a] / self.tip.gd.cell_c            #gives the scaled position of tip atoms in tip cell
            for phit in setup.phit_j:
                f = AtomCenteredFunctions(self.tip.gd, [phit], spos_c, i)
                f_kinetic = f.apply_t()
                self.tip_coupling_functions.append(f)
                self.tip_coupling_kinetics.append(f_kinetic)
                i += len(f.f_iG)
        self.ni = i
        #print self.tip_coupling_functions[1].size_c
        #print self.tip_coupling_functions[1].corner_c
        #print np.shape(self.tip_coupling_functions[1].f_iG)

        # get tip cutoff and surface extension
        tip_corner_c = []
        temp_min = self.tip_coupling_kinetics[0].corner_c
        temp_max = self.tip_coupling_kinetics[0].corner_c
        for f in self.tip_coupling_kinetics:
                start_c = np.minimum(f.corner_c, temp_min)
                end_c = np.maximum(f.corner_c+f.size_c, temp_max)
                temp_min = start_c
                temp_max = end_c
        #print "start_c",start_c         # in terms of grid points
        #print "end_c",end_c             # in terms of grid points
        self.tip_zmin = tip_pos_av[:,2].min()
        self.tip_apex_index = np.where(tip_pos_av[:,2] < self.tip_zmin + 0.01)
        self.tip_apex_c = np.round(tip_pos_av[self.tip_apex_index] / self.tgd.h_c).astype(int)
        self.ox, self.oy, self.oz = self.tip_apex_c[0]
        self.npx, self.npy, self.npz = np.round(end_c).astype(int) - self.tip_apex_c[0]
        self.nmx, self.nmy, self.nmz = self.tip_apex_c[0] - np.round(start_c).astype(int)

        # get the extension of srf basis functions and effective potentials
        srf_extension = np.array([self.srf.gd.N_c[0], self.srf.gd.N_c[1], 0])
        self.srf_extension = srf_extension
        print 'extension of surface in terms of grids:', srf_extension
        
        # get surface fucntions on small grid with periodic conditions with entension 
        self.srf_coupling_functions = []
        self.srf_coupling_kinetics = []
        j = 0
        for a in self.srf_coupling_atoms: 
            setup = self.srf.wfs.setups[a]
            spos_c = srf_pos_av[a] / self.srf.gd.cell_c
            for phit in setup.phit_j:
                f = AtomCenteredFunctions(self.srf.gd, [phit], spos_c, j)
                f.corner_c += srf_extension
                f_kinetic = f.apply_t()
                self.srf_coupling_functions.append(f.periodic())
                self.srf_coupling_kinetics.append(f_kinetic.periodic())
                j += len(f.f_iG)
        self.nj = j
        #print np.shape(self.srf_coupling_functions[0][0].f_iG), self.srf_coupling_functions[0][0].corner_c
        #print np.shape(self.srf_coupling_functions[0][8].f_iG), self.srf_coupling_functions[0][8].corner_c
        # Extend the surface grid and surface effective potential
        svt_G = self.srf.get_effective_potential() - self.srf_alignv + self.bias 
        ex,ey,ez = srf_extension
        newsize_c = 2 * srf_extension + self.sgd.N_c
        extvt_G = np.zeros(newsize_c)
        extvt_G[ex:-ex,ey:-ey,:] = svt_G
        extvt_G[:ex,ey:-ey,:]  = svt_G[-ex:,:,:]
        extvt_G[-ex:,ey:-ey,:] = svt_G[:ex,:,:]
        extvt_G[:,:ey,:] = extvt_G[:,-2*ey:-ey,:]
        extvt_G[:,-ey:,:] = extvt_G[:,ey:2*ey,:]
        self.extvt_G = extvt_G  

        extsgd = GridDescriptor(N_c=newsize_c,
                                cell_cv=self.sgd.h_c*(newsize_c),
                                pbc_c=False,
                                comm=mpi.serial_comm)

        ## Transfer the coner_c of surface basis-functions and srf_kinetics to the extended surface cell
        #for sbox in self.srf_coupling_functions:
        #    sbox.corner_c += srf_extension
        #for sbox in self.srf_coupling_kinetics:
        #    sbox.corner_c += srf_extension



    def align_v(self,calc1,index1,calc2,index2):
        pos1 = calc1.atoms.positions[index1]
        pos1_c = np.round(pos1/Bohr/calc1.wfs.gd.h_c).astype(int)
        pos1_v = calc1.get_effective_potential()[pos1_c[0],pos1_c[1],pos1_c[2]]
        pos2 = calc2.atoms.positions[index2]
        pos2_c = np.round(pos2/Bohr/calc2.wfs.gd.h_c).astype(int)
        pos2_v = calc2.get_effective_potential()[pos2_c[0],pos2_c[1],pos2_c[2]]
        return pos2_v - pos1_v