├── .gitignore ├── README.md ├── test.py ├── hofstadter.py ├── chern_graphen_Time_Periodic.py ├── Fl_dis.py ├── FL_funcs.py ├── chern_2DHall.py ├── cLasstd2D.py └── graphene_chern.py /.gitignore: -------------------------------------------------------------------------------- 1 | # emacs 2 | *~ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chern Number Calculation 2 | 3 | ### List of file: 4 | 5 | * `test.py`: test the class `cLasstd2D` 6 | * `FL_funcs.py 7 | * `Fl_dis.py` 8 | * `README.md` 9 | * `cLasstd2D.py` 10 | * `chern_2DHall.py` 11 | * `chern_graphen_Time_Periodic.py` 12 | * `graphene_chern.py` 13 | * `hofstadter.py` 14 | 15 | Calss `cLasstd2D` basic object is an infinite two-dimensional 16 | lattice in $k$-representation. The object is given in terms of the 17 | lattice Hamiltonian. The lattice can be with different characteristic 18 | parameters such as the hopping amplitude. In this implementation the 19 | effective Floquet Hamiltonian can be calculated over a period of 20 | applied time-dependent field. One simple example is the 21 | time-dependency of hopping amplitude. -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | # added to test 2 | # test file for class: classtd2D, file: 'classtd2D.py' 3 | import numpy as np 4 | import cLasstd2D as lt2D 5 | # 6 | # Define the type of lattice 7 | grlt = lt2D.lattice2D() 8 | 9 | print("Lattice J: %.1f and Nd: %d " %(grlt.J_hop, grlt.Nd)) 10 | 11 | # Parameters 12 | kx_int, kx_fin = -np.pi, np.pi 13 | ky_int, ky_fin = -np.pi, np.pi 14 | N_res = 100 15 | 16 | kx_iBz, kx_fBz = 0, 4*np.pi/3 17 | ky_iBz, ky_fBz = 0, 2*np.pi/np.sqrt(3) 18 | 19 | kx_range = np.array([kx_int,kx_fin]) 20 | ky_range = np.array([ky_int,ky_fin]) 21 | 22 | kxR = np.linspace(kx_int, kx_fin,N_res) 23 | kyR = np.linspace(ky_int, ky_fin,N_res) 24 | ############# 25 | 26 | # to save quasienergies over k-space 27 | E_arr = np.zeros((2,100,100), float) 28 | 29 | # static Hamiltonian bandstructure calculation 30 | E_arr = grlt.band_structure_static(kx_range, ky_range, N_res) 31 | # dynamic quasienergies 32 | # E_arr = grlt.band_structure_dynamic(kx_range, ky_range, N_res) 33 | chernN, E_arr2 = grlt.chernNum() 34 | 35 | ######################################## 36 | ########## Plot ############ 37 | ######################################## 38 | import matplotlib.pyplot as pl 39 | from mpl_toolkits.mplot3d import Axes3D 40 | 41 | # plot1 42 | ######################################## 43 | fig = pl.figure(figsize=(10,5)) 44 | ax = fig.add_subplot(1,2,1) 45 | 46 | ext = [kx_int, kx_fin, ky_int, ky_fin] 47 | im = ax.imshow(E_arr[1,:,:].T, extent=ext, origin='lower', cmap=pl.cm.RdBu) 48 | cset = ax.contour(E_arr[0,:,:].T, np.arange(-3,0,0.3), origin='lower', 49 | extent=ext, linewidths=2,cmap=pl.cm.Set2) 50 | 51 | ax2 = fig.add_subplot(1,2,2) 52 | ext = [kx_iBz, kx_fBz, ky_iBz, ky_fBz] 53 | im = ax2.imshow(E_arr2[1,:,:].T, extent=ext, origin='lower', cmap=pl.cm.RdBu) 54 | cset = ax2.contour(E_arr2[0,:,:].T, np.arange(-3,0,0.3), origin='lower', 55 | extent=ext, linewidths=2,cmap=pl.cm.Set2) 56 | 57 | pl.show() 58 | 59 | -------------------------------------------------------------------------------- /hofstadter.py: -------------------------------------------------------------------------------- 1 | # This program calculate the eigen energies of a qantum Hall 2 | # system (square lattice), and plot the energy as a function 3 | # of flux (applied magnetic filed). The plot of nergy vs. flux 4 | # would generate hofstadter butterfly 5 | 6 | # Author: Amin Ahmadi 7 | # Date: Jan 16, 2018 8 | ############################################################ 9 | # importing numpy and linear algebra modules 10 | import numpy as np 11 | import numpy.linalg as lg 12 | 13 | # Function that calculate Hamiltonian H(k) 14 | def H_k(k_vec, p, q): 15 | """ This function calulate the Hamiltonian of a 16 | 2D electron gas in presence of an applied magnetic 17 | field. The magnetic field is introduced using 18 | Landau's guage. 19 | in: kx, ky, 20 | p and q are the ratio of p/q = phi/phi0 21 | out: H_k, k-representation of H 22 | """ 23 | Hk = np.zeros((q,q), dtype=complex) 24 | t = 1. # hopping amplitude 25 | phi_ratio = (1.*p)/q # flux per plaquette 26 | 27 | kx = k_vec[0] 28 | ky = k_vec[1] 29 | # diagonal elements 30 | for i in range(q): 31 | Hk[i,i] = -t*np.cos( ky - 2.*(i+1)*np.pi*phi_ratio ) 32 | 33 | # off-diagonal elements 34 | for i in range(q-1): 35 | Hk[i,i+1] = -t 36 | 37 | # Magnetic Bloch element 38 | Hk[0,q-1] = -t*np.exp(-q*1.j*kx) 39 | 40 | # Make it hermitian 41 | Hk = Hk + Hk.conj().T 42 | 43 | return Hk 44 | 45 | ############################################################ 46 | ################### Main Program ######################### 47 | ############################################################ 48 | 49 | q = 501 # phi/phi0 = p/q 50 | k_vec = np.array([3.0,0], float) 51 | E_arr = np.zeros((q,q), float) 52 | for p in range(q): 53 | 54 | 55 | E_k= lg.eigvalsh(H_k(k_vec, p, q)) 56 | 57 | # save data for plotting 58 | E_arr[:, p] = E_k[:] 59 | 60 | # save date in file 61 | np.save('./Result/hofs_501_00', E_arr) 62 | 63 | ############################################################ 64 | #################### plotting ######################### 65 | ############################################################ 66 | 67 | import matplotlib.pyplot as pl 68 | from mpl_toolkits.mplot3d import Axes3D 69 | from matplotlib import cm 70 | 71 | pl.rc("font", family='serif') 72 | pl.rc('text', usetex=True) 73 | 74 | fig = pl.figure(figsize=(8,6)) 75 | ax = fig.add_subplot(1,1,1) 76 | 77 | pq = np.linspace(0,1,q) 78 | 79 | for p in range(q): 80 | ax.plot(pq, E_arr[p,:],',k') 81 | 82 | ax.set_xlabel(r'$\frac{\phi}{\phi_0}$', fontsize=16) 83 | ax.set_ylabel(r'$E$', fontsize=16) 84 | 85 | pl.show() 86 | fig.savefig('./Result/butterfly_501.pdf', bbox_inches='tight') 87 | -------------------------------------------------------------------------------- /chern_graphen_Time_Periodic.py: -------------------------------------------------------------------------------- 1 | # Program to calculate the Band Structure of Floquet 2 | # operator and the chern number associated with quasi 3 | # bandstructure in a continus honeycomb lattice with 4 | # stroboscopic driven field 5 | 6 | # Author: Amin Ahmdi 7 | # Date: Dec 18, 2017 8 | # Date: Jan 8, 2018 9 | # Date: Jan 10, 2018 Code is fixed and result are correct 10 | # Date: Jan 30, 2018 Separate general functions into another file 11 | # ################################################################ 12 | import numpy as np 13 | import numpy.linalg as lg 14 | import matplotlib.pyplot as pl 15 | 16 | # Internal package for Chern Num calculation 17 | import FL_funcs as FL 18 | 19 | ############################################################ 20 | ############## Main Program #################### 21 | ############################################################ 22 | 23 | # The k-domain is the brillouin zone 24 | 25 | x_res = 30 # kx resolution 26 | kx_int = 0 # initial point 27 | kx_fin = 4*np.pi/3 # final point 28 | Dx = (kx_fin - kx_int)/x_res 29 | 30 | y_res = 30 31 | ky_int = 0 32 | ky_fin = 2*np.pi/np.sqrt(3) 33 | Dy = (ky_fin - ky_int)/y_res 34 | 35 | Nd = 2 # dimension of the Hamiltonian 36 | Dk = np.array([Dx,Dy], float) 37 | 38 | T = 1. # One period of driven field 39 | 40 | 41 | H_kc = np.zeros((Nd,Nd), dtype=complex) # k-representation H 42 | E_k = np.zeros((Nd), dtype=complex) # eigenenergies 43 | psi_k = np.zeros((Nd,Nd), dtype=complex) # matrix of eigenvectors 44 | 45 | # time-dependent hopping amplitude 46 | delta = float(input("Enter the hopping difference coefficient: ")) 47 | 48 | # array to save data for plotting 49 | E_arr = np.zeros((Nd, x_res, y_res), dtype=float) 50 | LF_arr = np.zeros((Nd, x_res, y_res), dtype=float) 51 | 52 | LF = np.zeros((Nd), dtype=complex) 53 | sumN = np.zeros((Nd), dtype=complex) 54 | E_k = np.zeros((Nd), dtype=complex) 55 | 56 | # Chern number associated with each band 57 | chernN = np.zeros((Nd), dtype=complex) 58 | 59 | 60 | # loop over kx, first BZ 61 | for ix in range(x_res): 62 | kx = kx_int + ix*Dx 63 | 64 | # loop over ky, first BZ 65 | for iy in range(y_res): 66 | ky = ky_int + iy*Dy 67 | 68 | k_vec = np.array([kx,ky], float) 69 | 70 | 71 | LF, E_k = FL.latF(k_vec, Dk, delta, Nd) 72 | 73 | E_arr[:,ix,iy] = E_k/T 74 | 75 | sumN += LF 76 | 77 | # save data for plotting 78 | LF_arr[:,ix,iy] = -LF.imag/(2.*np.pi) 79 | 80 | chernN = sumN.imag/(2.*np.pi) 81 | print(chernN) 82 | print(sum(chernN)) 83 | ############################################################ 84 | # save data 85 | np.save("./Result/Fl_E4", E_arr) 86 | np.save("./Result/Fl_F124", LF_arr) 87 | ######################################## 88 | ########## Plot ############ 89 | ######################################## 90 | import matplotlib.pyplot as pl 91 | from mpl_toolkits.mplot3d import Axes3D 92 | 93 | # plot1 94 | ######################################## 95 | fig = pl.figure(figsize=(10,5)) 96 | ax = fig.add_subplot(1,2,1) 97 | 98 | ext = [kx_int, kx_fin, ky_int, ky_fin] 99 | im = ax.imshow(E_arr[1,:,:].T, extent=ext, origin='lower', cmap=pl.cm.RdBu) 100 | cset = ax.contour(E_arr[0,:,:].T, np.arange(-3,0,0.3), origin='lower', 101 | extent=ext, linewidths=2,cmap=pl.cm.Set2) 102 | 103 | # plot2 104 | ######################################## 105 | ax2 = fig.add_subplot(1,2,2, projection='3d') 106 | 107 | 108 | kx = np.linspace(kx_int,kx_fin, x_res) 109 | ky = np.linspace(ky_int,ky_fin, y_res) 110 | 111 | kx, ky = np.meshgrid(kx,ky) 112 | 113 | surf = ax2.plot_wireframe(kx, ky, LF_arr[0,:,:].T, rstride=1, 114 | cstride=1, color='0.4') 115 | # # ax.set_xlim(0,2.*np.pi/3.) 116 | 117 | ax2.set_xlabel(r'$k_x$') 118 | ax2.set_ylabel(r'$k_y$') 119 | 120 | pl.show() 121 | 122 | -------------------------------------------------------------------------------- /Fl_dis.py: -------------------------------------------------------------------------------- 1 | # Program to calculate the Band Structure of Floquet 2 | # operator in a continus honeycomb lattice with stroboscopic 3 | # driven field. When time-dependent field parameter delta=1, 4 | # then one expects the quasienergy spectrum must match the 5 | # bandstructure of graphene layer. 6 | 7 | # Author: Amin Ahmdi 8 | # Date: Dec 18, 2017 9 | # ################################################################ 10 | import numpy as np 11 | import numpy.linalg as lg 12 | ############################################################ 13 | 14 | def H_k(k_vec, it, delta=1): 15 | """construct the k-representation of Hamiltonian of a 16 | (infinite) hexagonal lattice. In every 1/3 interval of 17 | one period the hopping amplitudes are changed 18 | from s to delta*s as cyclic parameter. 19 | """ 20 | 21 | # constants 22 | s = 1.0 # hopping amplitude 23 | 24 | b = np.zeros((3,2), float) # unit vectors 25 | J = np.zeros((3), float) 26 | 27 | Hk = np.zeros((2,2), complex) 28 | 29 | b[0,:] = np.array([-1/2., np.sqrt(3)/2.], dtype=float) 30 | b[1,:] = np.array([-1/2.,-np.sqrt(3)/2.], dtype=float) 31 | b[2,:] = np.array([1./2, 0], dtype=float) 32 | 33 | sigx = np.array([[0,1.], 34 | [1.,0]], dtype=complex) 35 | 36 | sigy = np.array([[0,-1.j], 37 | [1.j,0]], dtype=complex) 38 | 39 | if (it==0): 40 | J[0] = delta*s; J[1] = s; J[2] = s 41 | elif (it==1): 42 | J[0] = s; J[1] = delta*s; J[2] = s 43 | elif (it==2): 44 | J[0] = s; J[1] = s; J[2] = delta*s 45 | 46 | for i in range(3): 47 | 48 | aux1 = np.cos( np.dot(b[i], k_vec) ) 49 | aux2 = np.sin( np.dot(b[i], k_vec) ) 50 | Hk += -J[i] * ( aux1*sigx + aux2*sigy ) 51 | 52 | return Hk 53 | ########################################################### 54 | def H_eff(k_vec, delta): 55 | """Construct the Floquet effective Hamiltonian by evolving 56 | of the Hamiltonian in every 1/3 period interval""" 57 | 58 | M_eff = np.eye((NN), dtype=complex) # aux matrix 59 | for it in range(N_t): 60 | 61 | # Construct Fourier transform of Hamiltonian 62 | # and diagonalization 63 | H_kc = H_k(k_vec, it, delta) 64 | 65 | # return eigenenergies and vectors 66 | E_k, U = lg.eig(H_kc) 67 | 68 | # U^-1 * exp(H_d) U 69 | U_inv = lg.inv(U) 70 | 71 | # construct a digonal matrix out of a vector 72 | #H_M= np.diag(np.exp((-1j/3.)*E_k*T)) 73 | M1 = (np.exp((-1j/3.)*E_k*T) * U_inv.T).T 74 | #MM = np.dot(U_inv,np.dot(H_M, U)) 75 | MM = np.dot(U,M1) 76 | M_eff = np.dot(M_eff,MM) 77 | 78 | return M_eff 79 | ############################################################ 80 | 81 | ############################################################ 82 | ############## Main Program #################### 83 | ############################################################ 84 | N_k = 20 # Num of k-points 85 | N_t = 3 # Num of time intervals 86 | NN = 2 87 | Nd = 2 # dimension 88 | T = 1. # One period of driven field 89 | 90 | # During each iterval T/N_t, the Hamiltonian is time-independent 91 | H_kc = np.zeros((NN,NN), dtype=complex) # k-representation H 92 | E_k = np.zeros((NN), dtype=float) # eigenenergies 93 | psi_k = np.zeros((NN,NN), dtype=complex) # matrix of eigenvectors 94 | 95 | # different hopping amplitude 96 | delta = input("Enter the time-parameter hopping coefficient: ") 97 | J = np.pi/16. # hopping amplitude 98 | data_plot = np.zeros((N_k, N_k,2), dtype=float) 99 | 100 | 101 | # loop over k, first BZ 102 | for ikx in range(N_k): 103 | kx = -np.pi + ikx*(2*np.pi/N_k) 104 | # ka = ik*(np.pi/N_k) 105 | for iky in range(N_k): 106 | ky = -np.pi + iky*(2*np.pi/N_k) 107 | k_vec = np.array([kx,ky], float) 108 | 109 | M_eff = H_eff(k_vec, delta) 110 | 111 | E_Fl, UF = lg.eig(M_eff) 112 | E_k = np.sort(np.log(E_Fl).imag) 113 | data_plot[ikx,iky,:] = E_k/T 114 | 115 | ############################################################ 116 | # save data 117 | np.save("./Result/Fl_ddp", data_plot) 118 | 119 | -------------------------------------------------------------------------------- /FL_funcs.py: -------------------------------------------------------------------------------- 1 | # File: FL_funcs.py 2 | # Functions that commonly used in Hamiltonian construction and 3 | # Chern number calculation are collected here 4 | 5 | # Author: Amin Ahmadi 6 | # Date: Jan 31, 2018 7 | ############################################################ 8 | import numpy as np 9 | import numpy.linalg as lg 10 | ############################################################ 11 | def H_k(k_vec, it, delta=1): 12 | """ construct the k-representation of Hamiltonian of a 13 | hexagonal lattice. In every third interval of one period 14 | the hopping amplitude different from the other two. 15 | 16 | input: 17 | ------ 18 | k_vec: real, 2D (kx,ky) vector 19 | it: integer, the time step for periodic driven field 20 | delta: real, time-dependent hopping amplitude 21 | 22 | return: 23 | ------- 24 | H_k: 2x2 matrix of the effective Hamiltonian 25 | """ 26 | 27 | # constants 28 | s = np.pi/16 # hopping amplitude 29 | m = 0.0 # Dirac mass parameter 30 | 31 | b = np.zeros((3,2), float) # nearest neighbors unit vectors 32 | J = np.zeros((3), float) # nearest neighbors hopping 33 | 34 | Hk = np.zeros((2,2), complex) # Hamiltonian 35 | 36 | b[0,:] = 1./2*np.array([1, np.sqrt(3)], dtype=float) 37 | b[1,:] = 1./2*np.array([1,-np.sqrt(3)], dtype=float) 38 | b[2,:] = -np.array([1., 0], dtype=float) 39 | 40 | # Pauli matrices 41 | 42 | sigx = np.array([[0,1.], 43 | [1.,0]], dtype=complex) 44 | 45 | sigy = np.array([[0,-1.j], 46 | [1.j,0]], dtype=complex) 47 | 48 | sigz = np.array([[1.,0], 49 | [0,-1.]], dtype=complex) 50 | 51 | # Time-interval 52 | if (it==0): 53 | J[0] = delta*s; J[1] = s; J[2] = s 54 | elif (it==1): 55 | J[0] = s; J[1] = delta*s; J[2] = s 56 | elif (it==2): 57 | J[0] = s; J[1] = s; J[2] = delta*s 58 | 59 | for i in range(3): 60 | 61 | aux1 = np.cos( np.dot(b[i], k_vec) ) 62 | aux2 = np.sin( np.dot(b[i], k_vec) ) 63 | Hk += -J[i] * ( aux1*sigx - aux2*sigy ) 64 | 65 | Hk += m*sigz 66 | return Hk 67 | ########################################################### 68 | def H_eff(k_vec, delta): 69 | """Calculate the effective Floquet Hamiltonian using 70 | evolution for three time interval. 71 | 72 | input: 73 | ------ 74 | k_vec: real, 2D (kx,ky) vector 75 | delta: time-dependent hopping parameter 76 | 77 | return: 78 | ------- 79 | Efl_k: real (2,) ndarray, quasienergies of 80 | effective Floquet Hamiltonian 81 | Ufl_k: complex (2,2) ndarray, eigenvectors of 82 | effective Floquet Hamiltonian 83 | """ 84 | 85 | Nd = 2 # dimension of Hamiltonian 86 | N_t = 3 # number of time interval 87 | T = 1 # period of field 88 | 89 | M_eff = np.eye((Nd), dtype=complex) # aux matrix 90 | for it in range(N_t): 91 | 92 | # Construct Fourier transform of Hamiltonian 93 | H_kc = H_k(k_vec, it, delta) 94 | 95 | # return eigenenergies and vectors 96 | E_k, U = lg.eig(H_kc) 97 | 98 | # U^-1 * exp(H_d) U 99 | U_inv = lg.inv(U) 100 | 101 | # construct a digonal matrix out of a vector 102 | M1 = (np.exp(-1.j*E_k*T) * U_inv.T).T 103 | 104 | #MM = np.dot(U_inv,np.dot(H_M, U)) 105 | MM = np.dot(U,M1) 106 | M_eff = np.dot(M_eff,MM) 107 | # end of loop 108 | E, aux = lg.eig( M_eff ) 109 | idx = (np.log(E).imag).argsort() 110 | Efl_k = np.log(E).imag[idx] 111 | Ufl_k = aux[:,idx] 112 | 113 | return Efl_k, Ufl_k 114 | ############################################################ 115 | 116 | def build_U(vec1,vec2): 117 | 118 | """ This function calculate the iner product of two 119 | eigenvectors divided by the norm: 120 | 121 | U = /|| 122 | 123 | input: 124 | ------ 125 | vec1 126 | vec2 127 | 128 | return: 129 | ------- 130 | scalar complex number 131 | """ 132 | 133 | # U = /|| 134 | in_product = np.dot(vec1,vec2.conj()) 135 | 136 | U = in_product / np.abs(in_product) 137 | 138 | return U 139 | ############################################################ 140 | 141 | def latF(k_vec, Dk, delta, dim=2): 142 | """ Calulating lattice field using the definition: 143 | F12 = ln[ U1 * U2(k+1) * U1(k_2)^-1 * U2(k)^-1 ] 144 | so for each k=(kx,ky) point, four U must be calculate. 145 | The lattice field has the same dimension of number of 146 | energy bands. 147 | 148 | in: 149 | k_vec=(kx,ky), 150 | Dk=(Dkx,Dky), 151 | dim: dim of H(k) 152 | 153 | out: 154 | F12:lattice field corresponding to each band as a n 155 | dimensional vec 156 | E: Quasienergies 157 | """ 158 | 159 | k = k_vec 160 | E_sort, psi = H_eff(k, delta) 161 | 162 | k = np.array([k_vec[0]+Dk[0], k_vec[1]], float) 163 | E, psiDx = H_eff(k, delta) 164 | 165 | 166 | k = np.array([k_vec[0], k_vec[1]+Dk[1]], float) 167 | E, psiDy = H_eff(k, delta) 168 | 169 | k = np.array([k_vec[0]+Dk[0], k_vec[1]+Dk[1]], float) 170 | E, psiDxDy = H_eff(k, delta) 171 | 172 | 173 | U1x = np.zeros((dim), dtype=complex) 174 | U2y = np.zeros((dim), dtype=complex) 175 | U1y = np.zeros((dim), dtype=complex) 176 | U2x = np.zeros((dim), dtype=complex) 177 | 178 | for i in range(dim): 179 | U1x[i] = build_U(psi[:,i], psiDx[:,i] ) 180 | U2y[i] = build_U(psi[:,i], psiDy[:,i] ) 181 | U1y[i] = build_U(psiDy[:,i], psiDxDy[:,i] ) 182 | U2x[i] = build_U(psiDx[:,i], psiDxDy[:,i] ) 183 | 184 | F12 = np.zeros((dim), dtype=complex) 185 | 186 | F12 = np.log( U1x * U2x * 1./U1y * 1./U2y) 187 | 188 | return F12, E_sort 189 | ############################################################ 190 | -------------------------------------------------------------------------------- /chern_2DHall.py: -------------------------------------------------------------------------------- 1 | # This program calculate the chern number of a 2D system using 2 | # technique that was introduced in paper by T. Fukui et. al. 3 | 4 | # Author: Amin Ahmadi 5 | # Date(in): Oct 30, 2017 6 | # Date2: Nov 6, 2017 7 | # This version would be more structured 8 | ############################################################ 9 | # importing numpy and linear algebra modules 10 | import numpy as np 11 | import numpy.linalg as lg 12 | 13 | ######################################## 14 | ### Functions ### 15 | ######################################## 16 | def H_k(k_vec, dim=3): 17 | """function to construct the Hamiltonian of a 2DEG in 18 | presence of an applied magnetic field. The magnetic 19 | field is introduced using Landau's guage. 20 | input: 21 | ------ 22 | k_vec: vec(2), float, (kx, ky) 23 | dim: integer, dimension of Hamiltonian, depends on magnetic flux 24 | 25 | return: 26 | ------- 27 | Hk: (2,2) complex, k-representation of H 28 | 29 | """ 30 | Hk = np.zeros((dim,dim), dtype=complex) 31 | t = 1 # hopping amplitude 32 | q = 3 33 | phi = 1./q # flux per plaquette 34 | 35 | kx = k_vec[0] 36 | ky = k_vec[1] 37 | # diagonal elements 38 | for i in range(3): 39 | Hk[i,i] = -2*t*np.cos( ky - 2.*(i+1)*np.pi*phi ) 40 | 41 | # off-diagonal elements 42 | Hk[0,1] = -t 43 | Hk[1,2] = -t 44 | Hk[0,2] = -t*np.exp(-q*1.j*kx) 45 | 46 | # Make it hermitian 47 | Hk = Hk + Hk.conj().T 48 | 49 | return Hk 50 | ############################################################ 51 | 52 | def build_U(vec1,vec2): 53 | """function to calculate the iner product of two 54 | eigenvectors divided by the norm: 55 | 56 | U = /|| 57 | 58 | input: 59 | ------ 60 | vec, vec2: vectors complex. 61 | 62 | return: 63 | ------- 64 | U: scalar complex number 65 | 66 | """ 67 | 68 | # U = /|| 69 | in_product = np.dot(vec1,vec2.conj()) 70 | 71 | U = in_product / np.abs(in_product) 72 | 73 | return U 74 | ############################################################ 75 | 76 | def latF(k_vec, Dk, dim): 77 | """calulate lattice field using the definition: F12 = ln[ 78 | U1 * U2(k+1) * U1(k_2)^-1 * U2(k)^-1 ] for each 79 | k=(kx,ky) point, four U must be calculated. The lattice 80 | field has the same dimension of the number of energy 81 | bands. 82 | 83 | input: 84 | ------ 85 | k_vec:vec(2), float, (kx,ky). 86 | Dk: vec(2), float, (Dkx,Dky), 87 | dim:integer, dim of H(k) 88 | 89 | return: 90 | ------- 91 | F12:vec(dim), complex, lattice field corresponding to each band. 92 | E_sort: vec(dim) float, eigenenergies. 93 | """ 94 | 95 | # Here we calculate the band structure and sort 96 | # them from low to high eigenenergies 97 | 98 | k = k_vec 99 | E, aux = lg.eig( H_k(k) ) 100 | idx = E.argsort() 101 | E_sort = E[idx] 102 | psi = aux[:,idx] 103 | 104 | k = np.array([k_vec[0]+Dk[0], k_vec[1]], float) 105 | E, aux = lg.eig( H_k(k) ) 106 | idx = E.argsort() 107 | psiDx = aux[:,idx] 108 | 109 | k = np.array([k_vec[0], k_vec[1]+Dk[1]], float) 110 | E, aux = lg.eig( H_k(k) ) 111 | idx = E.argsort() 112 | psiDy = aux[:,idx] 113 | 114 | k = np.array([k_vec[0]+Dk[0], k_vec[1]+Dk[1]], float) 115 | E, aux = lg.eig( H_k(k) ) 116 | idx = E.argsort() 117 | psiDxDy = aux[:,idx] 118 | 119 | U1x = np.zeros((dim), dtype=complex) 120 | U2y = np.zeros((dim), dtype=complex) 121 | U1y = np.zeros((dim), dtype=complex) 122 | U2x = np.zeros((dim), dtype=complex) 123 | 124 | for i in range(dim): 125 | U1x[i] = build_U(psi[:,i], psiDx[:,i] ) 126 | U2y[i] = build_U(psi[:,i], psiDy[:,i] ) 127 | U1y[i] = build_U(psiDy[:,i], psiDxDy[:,i] ) 128 | U2x[i] = build_U(psiDx[:,i], psiDxDy[:,i] ) 129 | 130 | F12 = np.zeros((dim), dtype=complex) 131 | 132 | F12 = np.log( U1x * U2x * 1./U1y * 1./U2y) 133 | 134 | return F12, E_sort 135 | 136 | ################################################## 137 | ### Main program ### 138 | ################################################## 139 | 140 | x_res = 50 141 | y_res = 50 142 | q = 3 143 | Nd = q # dimension of the Hamiltonian 144 | 145 | Dx = (2.*np.pi/3.)/x_res 146 | Dy = (2.*np.pi)/y_res 147 | Dk = np.array([Dx,Dy], float) 148 | 149 | LF = np.zeros((Nd), dtype=complex) 150 | LF_arr = np.zeros((Nd,x_res, y_res), dtype=float) # plotting array 151 | sumN = np.zeros((Nd), dtype=complex) 152 | E_k = np.zeros((Nd), dtype=complex) 153 | chernN = np.zeros((Nd), dtype=complex) 154 | 155 | for ix in range(x_res): 156 | 157 | kx = ix*Dx 158 | for iy in range(y_res): 159 | 160 | ky = iy*Dy 161 | 162 | k_vec = np.array([kx,ky], float) 163 | LF, E_k = latF(k_vec, Dk, Nd) 164 | 165 | sumN += LF 166 | 167 | # save data for plotting 168 | LF_arr[:,ix,iy] = -LF.imag/(2.*np.pi) 169 | 170 | chernN = sumN.imag/(2.*np.pi) 171 | print("Chern number associated with each band: ", chernN) 172 | 173 | ################################################## 174 | ### Main program ### 175 | ################################################## 176 | 177 | import matplotlib.pyplot as pl 178 | from mpl_toolkits.mplot3d import Axes3D 179 | from matplotlib import cm 180 | 181 | fig = pl.figure() 182 | ax = fig.gca(projection='3d') 183 | 184 | kx = np.linspace(0,2.*np.pi/3., x_res) 185 | ky = np.linspace(0,2.*np.pi, y_res) 186 | 187 | kx, ky = np.meshgrid(ky,kx) 188 | 189 | surf = ax.plot_wireframe(ky, kx, LF_arr[1,:,:], rstride=1, cstride=1, color='0.4') 190 | # ax.set_xlim(0,2.*np.pi/3.) 191 | 192 | # Set viewpoint. 193 | ax.azim = -60 194 | ax.elev = 30 195 | 196 | # Label axes. 197 | ax.set_xlabel(r'$k_x$', fontsize=18) 198 | ax.set_xticks([0.0, np.pi/3, 2*np.pi/3]) 199 | ax.set_xticklabels([r'$0$', r'$\pi/3$', r'$2\pi/3$'], fontsize=16) 200 | ax.set_xlim(0,2*np.pi/3) 201 | 202 | ax.set_ylabel(r'$k_y$', fontsize=18) 203 | ax.yaxis._axinfo['label']['space_factor'] = 2.5 204 | ax.set_yticks([0.0, np.pi, 2*np.pi]) 205 | ax.set_yticklabels([r'$0$', r'$\pi$', r'$2\pi$'], fontsize=16) 206 | ax.set_ylim(0,2*np.pi) 207 | 208 | 209 | ax.set_zlabel(r'$i\tilde{F}_{12}$', fontsize=18) 210 | ax.zaxis._axinfo['label']['space_factor'] = 2.5 211 | 212 | # ax.set_zticks([""]) 213 | 214 | # ax.set_zticklabels([""]) 215 | 216 | 217 | # surf = ax.plot_surface(ky, kx, LF_arr[1,:,:], rstride=1, cstride=1, color='g', norm=0.1, shade=True, 218 | # facecolor='b', linewidth=0, antialiased=False) #cmap=cm.jet 219 | 220 | # To rescale the plot 221 | ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([0.5, 1.5, 1, 1])) 222 | 223 | # ax.auto_scale_xyz([0, 500], [0, 500], [0, 0.15]) 224 | # ax.pbaspect = [.6, 2.6, 0.25] 225 | # fig.colorbar(surf, shrink=1., aspect=5) 226 | 227 | pl.show() 228 | # fig.savefig("chr3.pdf", bbox_inches='tight') 229 | -------------------------------------------------------------------------------- /cLasstd2D.py: -------------------------------------------------------------------------------- 1 | """ Class of infinite two-dimensional lattices. 2 | 3 | The module contains hexagonal and square lattices. 4 | """ 5 | import numpy as np 6 | import numpy.linalg as lg 7 | import FL_funcs as fl 8 | ######################################## 9 | 10 | class lattice2D(): 11 | """ class of 2D infinite hexagonal lattice in k-representation. 12 | """ 13 | def __init__(self, Nd=2, J_hop=1., delta = 1., it = 0, 14 | k_vec = np.zeros((2), float), **kwargs): 15 | """ 16 | the basic structure of a hexagonal lattice is persumed, 17 | if no other parameter are not given 18 | """ 19 | self.J_hop = J_hop # the hopping coefficient 20 | self.Nd = Nd # dimension of Hamiltonian, 21 | self.delta = delta # time-dep hopping amplitude 22 | self.it = it # time-interval that system is in 23 | self.k_vec = k_vec # the k-vector 24 | 25 | # initialize the Hamilnonian matrix 26 | self.H_kc = fl.H_k(k_vec, it, delta) 27 | #################### 28 | def updateH(self,k_vec,it): 29 | """ 30 | update the Hamiltonian matrix by new k-vector 31 | and time-interval it 32 | input: 33 | ------ 34 | k_vec: real (2,), 2D (kx,ky) vector 35 | """ 36 | self.k_vec = k_vec 37 | self.it = it 38 | self.H_kc = fl.H_k(k_vec, self.it, self.delta) 39 | #################### 40 | def evolve(self, k_vec, Nt,**kwargs): 41 | """ evolve the time-dependent parameter of a lattice 42 | and generate effective Floquet Hamiltonian. 43 | 44 | input: 45 | ------ 46 | Nt: int, number of intervals in one period 47 | kwargs: depending on the form of lattice the 48 | time-dependent variables can be different. 49 | 50 | return: 51 | ------- 52 | Efl_k: real (Nd, ) ndarray, sorted quasienergies of 53 | effective Floquet Hamiltonian. 54 | Ufl_k: complex (Nd,Nd) ndarray, sorted eigenvectors of 55 | effective Floquet Hamiltonian 56 | """ 57 | 58 | M_eff = np.eye((self.Nd), dtype=complex) # aux matrix 59 | T = 1. 60 | for it in range(Nt): 61 | 62 | # update the Hamiltonian for time-inteval 63 | self.updateH(k_vec, it) 64 | 65 | # return eigenenergies and vectors 66 | E_k, U = lg.eig(self.H_kc) 67 | 68 | # U^-1 * exp(H_d) U 69 | U_inv = lg.inv(U) 70 | 71 | # construct a digonal matrix out of a vector 72 | M1 = (np.exp(-1.j*E_k*T) * U_inv.T).T 73 | 74 | #MM = np.dot(U_inv,np.dot(H_M, U)) 75 | MM = np.dot(U,M1) 76 | M_eff = np.dot(M_eff,MM) 77 | # end of loop 78 | Ek, Uk = lg.eig( M_eff ) 79 | idx = (np.log(Ek).imag).argsort() 80 | Efl_k = np.log(Ek).imag[idx] 81 | Ufl_k = Uk[idx] 82 | return Efl_k, Ufl_k 83 | #################### 84 | def band_structure_static(self, kx_range, ky_range, N_res): 85 | """ 86 | a Hamiltonian in (kx,ky) is given, evaluate the bandstructire of 87 | a static Hamiltonian E(kx,ky) 88 | input: 89 | ------ 90 | kx_range: real (2,), the domain of kx 91 | ky_range: real (2,), the domain of ky 92 | N_res: int, resolution in kx and kx direction 93 | 94 | output: 95 | ------- 96 | E_arr: real (Nd, N_res, N_res), eigenenergies 97 | """ 98 | kxR = np.linspace(kx_range[0], kx_range[1], N_res) 99 | kyR = np.linspace(ky_range[0], ky_range[1], N_res) 100 | 101 | E_arr = np.zeros((2,N_res,N_res), float) 102 | 103 | # mesh over area in k-space 104 | # Kx, Ky = np.meshgrid(kx,ky) 105 | 106 | for ix, kx in enumerate(kxR): 107 | for iy, ky in enumerate(kyR): 108 | k_vec = np.array([kx,ky], float) 109 | # Construct k-representation of Hamiltonian 110 | self.updateH(k_vec, self.it) 111 | 112 | E_arr[:,ix,iy] = np.sort( np.linalg.eigvalsh(self.H_kc).real ) 113 | 114 | #end-loop ky 115 | #end-loop kx 116 | 117 | return E_arr 118 | #################### 119 | def band_structure_dynamic(self, kx_range, ky_range, N_res): 120 | """ 121 | a Hamiltonian in (kx,ky,t) is given, evaluate the quasienergy bandstructire 122 | of a dynamic Hamiltonian E(kx,ky) 123 | input: 124 | ------ 125 | kx_range: real (2,), the domain of kx 126 | ky_range: real (2,), the domain of ky 127 | N_res: int, resolution in kx and kx direction 128 | 129 | output: 130 | ------- 131 | E_arr: real (Nd, N_res, N_res), eigenenergies 132 | """ 133 | kxR = np.linspace(kx_range[0], kx_range[1], N_res) 134 | kyR = np.linspace(ky_range[0], ky_range[1], N_res) 135 | 136 | Nt = 3 137 | E_arr = np.zeros((2,N_res,N_res), float) 138 | 139 | # mesh over area in k-space 140 | # Kx, Ky = np.meshgrid(kx,ky) 141 | 142 | for ix, kx in enumerate(kxR): 143 | for iy, ky in enumerate(kyR): 144 | k_vec = np.array([kx,ky], float) 145 | 146 | # Floquet eigenvalues and eigenenergies 147 | E_arr[:,ix,iy] , Uaux = self.evolve(k_vec, Nt) 148 | 149 | #end-loop ky 150 | #end-loop kx 151 | 152 | return E_arr 153 | 154 | #################### 155 | def latF(self, k_vec, Dk, delta): 156 | """ Calulating lattice field using the definition: 157 | F12 = ln[ U1 * U2(k+1) * U1(k_2)^-1 * U2(k)^-1 ] 158 | so for each k=(kx,ky) point, four U must be calculate. 159 | The lattice field has the same dimension of number of 160 | energy bands. 161 | 162 | input: 163 | ------ 164 | k_vec=(kx,ky), 165 | Dk=(Dkx,Dky), 166 | 167 | output: 168 | ------- 169 | F12:lattice field corresponding to each band as a n 170 | dimensional vec 171 | E: Quasienergies 172 | """ 173 | 174 | k = k_vec 175 | E_sort, psi = lg.eig( fl.H_k(k, self.it, self.delta) ) 176 | E_sort = np.sort(E_sort) 177 | 178 | k = np.array([k_vec[0]+Dk[0], k_vec[1]], float) 179 | E, psiDx = lg.eig( fl.H_k(k, self.it, self.delta) ) 180 | 181 | 182 | k = np.array([k_vec[0], k_vec[1]+Dk[1]], float) 183 | E, psiDy = lg.eig( fl.H_k(k, self.it, self.delta) ) 184 | 185 | k = np.array([k_vec[0]+Dk[0], k_vec[1]+Dk[1]], float) 186 | E, psiDxDy = lg.eig( fl.H_k(k, self.it, self.delta) ) 187 | 188 | 189 | U1x = np.zeros((self.Nd), dtype=complex) 190 | U2y = np.zeros((self.Nd), dtype=complex) 191 | U1y = np.zeros((self.Nd), dtype=complex) 192 | U2x = np.zeros((self.Nd), dtype=complex) 193 | 194 | for i in range(self.Nd): 195 | U1x[i] = fl.build_U(psi[:,i], psiDx[:,i] ) 196 | U2y[i] = fl.build_U(psi[:,i], psiDy[:,i] ) 197 | U1y[i] = fl.build_U(psiDy[:,i], psiDxDy[:,i] ) 198 | U2x[i] = fl.build_U(psiDx[:,i], psiDxDy[:,i] ) 199 | 200 | F12 = np.zeros((self.Nd), dtype=complex) 201 | 202 | F12 = np.log( U1x * U2x * 1./U1y * 1./U2y) 203 | 204 | return F12, E_sort 205 | ######################################## 206 | 207 | def chernNum(self, kx_Bz=np.array([0,4*np.pi/3]), 208 | ky_Bz=np.array([0,2*np.pi/np.sqrt(3)]), 209 | N_res=30): 210 | """ 211 | To calculate the Chern number of the Hamiltonian (or the Floquet Hamiltonian) 212 | over its Brillouin zone (BZ). 213 | input: 214 | ------ 215 | kx_Bz: real (2,), kx BZ 216 | ky_Bz: real (2,), ky BZ 217 | N_res: int, resolution in kx and kx direction 218 | 219 | output: 220 | ------- 221 | Chrn_Num: real (Nd,), the Chern number associated with each band. 222 | """ 223 | x_eps = 0.3 # shift from Dirac point 224 | x_res = 20 225 | kx_int = 0 + x_eps # -np.pi 226 | kx_fin = 4*np.pi/3 + x_eps 227 | Dx = (kx_fin - kx_int)/x_res 228 | 229 | y_res = 20 230 | ky_int = 0 # -np.pi 231 | ky_fin = 2*np.pi/np.sqrt(3) 232 | Dy = (ky_fin - ky_int)/y_res 233 | 234 | Nd = self.Nd # dimension of the Hamiltonian 235 | Dk = np.array([Dx,Dy], float) 236 | 237 | LF = np.zeros((Nd), dtype=complex) 238 | LF_arr = np.zeros((Nd,x_res, y_res), dtype=float) 239 | E_arr = np.zeros((Nd,x_res, y_res), dtype=float) 240 | sumN = np.zeros((Nd), dtype=complex) 241 | E_k = np.zeros((Nd), dtype=complex) 242 | chernN = np.zeros((Nd), dtype=complex) 243 | 244 | # Loop over kx 245 | for ix in range(x_res): 246 | kx = kx_int + ix*Dx 247 | 248 | # Loop over ky 249 | for iy in range(y_res): 250 | ky = ky_int + iy*Dy 251 | 252 | k_vec = np.array([kx,ky], float) 253 | 254 | LF, E_k = self.latF(k_vec, Dk, self.delta) 255 | 256 | sumN += LF 257 | 258 | # # save data for plotting 259 | LF_arr[:,ix,iy] = LF.imag 260 | 261 | E_arr[:,ix,iy] = np.sort(E_k.real) 262 | 263 | # End of ky Loop 264 | # End of kx Loop 265 | 266 | chernN = sumN.imag/(2*np.pi) 267 | print("Chern number bands are (%.3f, %.3f) " 268 | %(chernN[0], chernN[1])) 269 | print("Sum of all bands Chern Number is %.2f " %(sum(chernN))) 270 | return chernN, E_arr 271 | #################### 272 | 273 | 274 | -------------------------------------------------------------------------------- /graphene_chern.py: -------------------------------------------------------------------------------- 1 | # This program calculate the chern number of a 2D graphene 2 | # technique that was introduced in paper by T. Fukui et. al. 3 | 4 | # Author: Amin Ahmadi 5 | # Date: Dec 20, 2017 6 | # Date: Dec 26, 2017 Make sure the Dirac points are at right places 7 | # Date: Jan 9, 2017 TRS breaking term is added and result are corrects. 8 | ############################################################ 9 | # importing numpy and linear algebra modules 10 | import numpy as np 11 | import numpy.linalg as lg 12 | 13 | def Gamma(kx,ky): 14 | """Returns the gamma function associated with a Hamiltonian 15 | of a 2D graphene layer. 16 | 17 | in: kx, ky 18 | out: scalar complex function 19 | """ 20 | gamma = 2*np.exp(1.j*kx/2)*np.cos(np.sqrt(3.)*ky/2) + \ 21 | np.exp(-1.j*kx) 22 | 23 | return gamma 24 | ############################################################ 25 | 26 | def Beta(kx,ky): 27 | """Returns the beta function associated with the next nearest 28 | hopping between the same sites A to A and B to B in graphen. The 29 | hopping amplitude is purely imaginary. 30 | 31 | This term is Haldane model to break the TRS in a graphene layer. 32 | 33 | in: kx, ky 34 | out: scalar complex function 35 | """ 36 | k_vec = np.array([kx,ky], float) 37 | b1 = np.array([0,np.sqrt(3)], float) 38 | b2 = (-3./2)*np.array([1,1./np.sqrt(3)], float) 39 | b3 = (3./2)*np.array([1,-1./np.sqrt(3)], float) 40 | 41 | beta = np.sin(np.dot(k_vec,b1)) + np.sin(np.dot(k_vec,b2)) + \ 42 | np.sin(np.dot(k_vec,b3)) 43 | 44 | return beta 45 | ############################################################ 46 | def H_k(k_vec): 47 | 48 | """This function gives the matrix 2x2 of Hamiltonian of an 49 | infinite graphene layer 50 | input: 51 | ------ 52 | k_vec: 2x1 matrix float, (kx, ky) 53 | 54 | return: 55 | ------- 56 | Hk: 2x2 complex, k-representation Hamiltonian 57 | """ 58 | t1=1. 59 | m=0.5 60 | t2= m/(3*np.sqrt(3)) - 0.2 61 | t_so= 0.001 62 | 63 | kx = k_vec[0] 64 | ky = k_vec[1] 65 | Nd = 4 # including spin-degree of freedom 66 | phi1 = np.sqrt(3) * np.exp(0.5j*kx) * \ 67 | np.sin(np.sqrt(3)*0.5*ky) 68 | phi2 = np.exp(-1.j*kx) - np.exp(.5j*kx) * \ 69 | np.cos(np.sqrt(3)*0.5*ky) 70 | 71 | 72 | 73 | Hk = np.zeros((Nd,Nd), dtype=complex) 74 | 75 | 76 | Hk[0,0] = 0.5* ( m + t2*Beta(kx,ky) ) # To break TRS 77 | Hk[0,1] = t1*Gamma(kx,ky) 78 | Hk[0,3] = t_so * (1.j*phi1 + phi2) 79 | 80 | 81 | Hk[1,1] = - Hk[0,0] 82 | Hk[1,2] = t_so * (-1.j*phi1.conj() - phi2.conj()) 83 | 84 | Hk[2,2] = Hk[0,0] 85 | Hk[2,3] = Hk[0,1] 86 | 87 | Hk[3,3] = - Hk[0,0] 88 | 89 | Hk += Hk.T.conj() 90 | 91 | return Hk 92 | ############################################################ 93 | def H_Rashba(k_vec): 94 | 95 | """This function returns the 4x4 Hamiltonian of a graphene 96 | layer, including the Haldane next nearest neighbor hopping 97 | and Rashba SOI. 98 | 99 | input: 100 | ------ 101 | k_vec: vec(float,float), (kx,ky) 102 | t2: float, hoping coefficient to the next nearest neighbor 103 | ts: float, Rashba SOI strength 104 | 105 | return: 106 | ------- 107 | Hk: 4x4 complex matrix, Hamiltonian 108 | """ 109 | m=2 110 | tso= 0.06# m/(3*np.sqrt(3)) - 1 111 | tR=0.0000001 112 | 113 | kx = k_vec[0] 114 | ky = k_vec[1] 115 | Nd = 4 # including spin-degree of freedom 116 | 117 | gamma = np.exp(1.j*kx) + 2.* np.exp(-0.5j*kx) * \ 118 | np.cos(np.sqrt(3)*0.5*ky) 119 | phi1 = np.exp(1.j*kx) + 2.* np.exp(-0.5j*kx) * \ 120 | np.cos(np.sqrt(3)*0.5*ky + 2.*np.pi/3) 121 | phi2 = np.exp(1.j*kx) + 2.* np.exp(-0.5j*kx) * \ 122 | np.cos(np.sqrt(3)*0.5*ky - 2.*np.pi/3) 123 | 124 | 125 | 126 | Hk = np.zeros((Nd,Nd), dtype=complex) 127 | 128 | 129 | Hk[0,0] = 0.5* ( m + 2*tso*Beta(kx,ky) ) # To break TRS 130 | Hk[0,1] = gamma 131 | Hk[0,3] = tR * (1.j*phi1) 132 | 133 | 134 | Hk[1,1] = Hk[0,0] 135 | Hk[1,2] = tR * (-1.j*phi2.conj()) 136 | 137 | Hk[2,2] = - Hk[0,0] 138 | Hk[2,3] = Hk[0,1] 139 | 140 | Hk[3,3] = - Hk[0,0] 141 | 142 | Hk += Hk.T.conj() 143 | 144 | return Hk 145 | ############################################################ 146 | def build_U(vec1,vec2): 147 | 148 | """ This function calculate the iner product of two 149 | eigenvectors divided by the norm: 150 | 151 | U = /|| 152 | 153 | in: two vectors vec1, and vec2 154 | out: scalar complex number 155 | """ 156 | 157 | # U = /|| 158 | in_product = np.dot(vec1,vec2.conj()) 159 | 160 | U = in_product / np.abs(in_product) 161 | 162 | return U 163 | ############################################################ 164 | 165 | def latF(k_vec, Dk, dim): 166 | """ Calulating lattice field using the definition: 167 | F12 = ln[ U1 * U2(k+1) * U1(k_2)^-1 * U2(k)^-1 ] 168 | so for each k=(kx,ky) point, four U must be calculate. 169 | The lattice field has the same dimension of number of 170 | energy bands. 171 | 172 | in: k-point k_vec=(kx,ky), Dk=(Dkx,Dky), dim: dim of H(k) 173 | out: lattice field corresponding to each band as a n 174 | dimensional vec 175 | """ 176 | 177 | # Here we calculate the band structure and sort 178 | # them from low to high eigenenergies 179 | 180 | k = k_vec 181 | E, aux = lg.eig( H_Rashba(k) ) 182 | idx = E.real.argsort() 183 | E_sort = E[idx].real 184 | psi = aux[:,idx] 185 | 186 | k = np.array([k_vec[0]+Dk[0], k_vec[1]], float) 187 | E, aux = lg.eig( H_Rashba(k) ) 188 | idx = E.real.argsort() 189 | psiDx = aux[:,idx] 190 | 191 | k = np.array([k_vec[0], k_vec[1]+Dk[1]], float) 192 | E, aux = lg.eig( H_Rashba(k) ) 193 | idx = E.real.argsort() 194 | psiDy = aux[:,idx] 195 | 196 | k = np.array([k_vec[0]+Dk[0], k_vec[1]+Dk[1]], float) 197 | E, aux = lg.eig( H_Rashba(k) ) 198 | idx = E.real.argsort() 199 | psiDxDy = aux[:,idx] 200 | 201 | U1x = np.zeros((dim), dtype=complex) 202 | U2y = np.zeros((dim), dtype=complex) 203 | U1y = np.zeros((dim), dtype=complex) 204 | U2x = np.zeros((dim), dtype=complex) 205 | 206 | for i in range(dim): 207 | U1x[i] = build_U(psi[:,i], psiDx[:,i] ) 208 | U2y[i] = build_U(psi[:,i], psiDy[:,i] ) 209 | U1y[i] = build_U(psiDy[:,i], psiDxDy[:,i] ) 210 | U2x[i] = build_U(psiDx[:,i], psiDxDy[:,i] ) 211 | 212 | F12 = np.zeros((dim), dtype=complex) 213 | 214 | F12 = np.log( U1x * U2x * 1./U1y * 1./U2y) 215 | 216 | return F12, E_sort 217 | 218 | ######################################## 219 | ########## Main Program ############ 220 | ######################################## 221 | x_eps = 0.3 # shift from Dirac point 222 | x_res = 20 223 | kx_int = 0 + x_eps # -np.pi 224 | kx_fin = 4*np.pi/3 + x_eps 225 | Dx = (kx_fin - kx_int)/x_res 226 | 227 | y_res = 20 228 | ky_int = 0 # -np.pi 229 | ky_fin = 2*np.pi/np.sqrt(3) 230 | Dy = (ky_fin - ky_int)/y_res 231 | 232 | Nd = 4 # dimension of the Hamiltonian 233 | Dk = np.array([Dx,Dy], float) 234 | 235 | LF = np.zeros((Nd), dtype=complex) 236 | LF_arr = np.zeros((Nd,x_res, y_res), dtype=float) 237 | E_arr = np.zeros((Nd,x_res, y_res), dtype=float) 238 | sumN = np.zeros((Nd), dtype=complex) 239 | E_k = np.zeros((Nd), dtype=complex) 240 | chernN = np.zeros((Nd), dtype=complex) 241 | 242 | # Loop over kx 243 | for ix in range(x_res): 244 | kx = kx_int + ix*Dx 245 | 246 | # Loop over ky 247 | for iy in range(y_res): 248 | ky = ky_int + iy*Dy 249 | 250 | k_vec = np.array([kx,ky], float) 251 | 252 | LF, E_k = latF(k_vec, Dk, Nd) 253 | 254 | sumN += LF 255 | 256 | # # save data for plotting 257 | LF_arr[:,ix,iy] = LF.imag 258 | 259 | E_arr[:,ix,iy] = np.sort(E_k.real) 260 | 261 | # End of ky Loop 262 | # End of kx Loop 263 | 264 | chernN = sumN.imag/(2*np.pi) 265 | print("Chern number bands are (%.3f, %.3f, %.3f, %.3f) " 266 | %(chernN[0], chernN[1], chernN[2], chernN[3])) 267 | print("Sum of all bands Chern Number is %.2f " %(sum(chernN))) 268 | 269 | ######################################## 270 | ########## Plot ############ 271 | ######################################## 272 | import matplotlib.pyplot as pl 273 | from mpl_toolkits.mplot3d import Axes3D 274 | # Dirac points are 275 | K1 = 2*np.pi/3*np.array([1., 1./np.sqrt(3)], dtype=float) 276 | K2 = 2*np.pi/3*np.array([1., -1./np.sqrt(3)], dtype=float) 277 | 278 | 279 | 280 | fig = pl.figure(figsize=(10,5)) 281 | ax = fig.add_subplot(1,2,1) 282 | 283 | ext = [kx_int, kx_fin, ky_int, ky_fin] 284 | im = ax.imshow(E_arr[1,:,:].T, extent=ext, 285 | origin='lower', cmap=pl.cm.RdBu) 286 | cset = ax.contour(E_arr[0,:,:].T, np.arange(-3,0,0.3), 287 | origin='lower', extent=ext, 288 | linewidths=2,cmap=pl.cm.Set2) 289 | ax.plot(K1[0], K1[1], '*k', label=r'$K_1$') 290 | ax.set_xlim(kx_int, kx_fin) 291 | ax.set_ylim(ky_int, ky_fin) 292 | 293 | ax.legend() 294 | 295 | # # adding the Contour lines with labels 296 | # clabel(cset,inline=True,fmt='%1.1f',fontsize=10) 297 | # colorbar(im) # adding the colobar on the right 298 | 299 | ax2 = fig.add_subplot(1,2,2, projection='3d') 300 | 301 | 302 | kx = np.linspace(kx_int,kx_fin, x_res) 303 | ky = np.linspace(ky_int,ky_fin, y_res) 304 | 305 | kx, ky = np.meshgrid(kx,ky) 306 | 307 | surf = ax2.plot_wireframe(kx, ky, LF_arr[0,:,:].T, 308 | rstride=1, cstride=1, color='0.4') 309 | # # ax.set_xlim(0,2.*np.pi/3.) 310 | 311 | ax2.set_xlabel(r'$k_x$') 312 | ax2.set_ylabel(r'$k_y$') 313 | 314 | pl.show() 315 | --------------------------------------------------------------------------------