func main() { fmt.Println("Testing self-energy calculation in cmplxSparse package...\n\n") fmt.Println("Pi =", utils.Pi) fmt.Println("Planck constant =", utils.Hplanck) fmt.Println("Reduced Planck constant =", utils.Hbar) fmt.Println("Elementary charge =", utils.Echarge) fmt.Println("Permeability of free space =", utils.Mu0) fmt.Println("Bohr magneton =", utils.MuB) fmt.Println("Small imaginary number =", utils.Zplus) fmt.Println("Free electron mass =", utils.M0) // Set material parameters m_ox = float64(0.315) m_fm_L = float64(0.72) m_fm_R = m_fm_L Ub = 0.79 E_split_L = 0.930 E_split_R = 0.930 E_Fermi = 2.25 fmt.Println("----------------------------------------") fmt.Printf("Ub = %.15g, E_Fermi = %.15g, E_split_L = %.15g, E_split_R = %.15g\n", Ub, E_Fermi, E_split_L, E_split_R) // Configuration parameters th_F, ph_F = utils.Pi/2.0, 0.0 th_H, ph_H = 0.0, 0.0 Vmtj, Temperature = 0.20, 300.0 fmt.Println("----------------------------------------") fmt.Println("Left FM:") fmt.Printf("th = %.15g, ph = %.15g\n", th_F, ph_F) fmt.Println("----------------------------------------") fmt.Println("Right FM:") fmt.Printf("th = %.15g, ph = %.15g\n", th_H, ph_H) fmt.Println("----------------------------------------") /* aSpace := float64(1.0e-11); d_ox := float64(1.15e-9); d_fm_L := float64(2.0e-11); d_fm_R := float64(2.0e-11); */ aSpace := float64(0.25e-9) d_ox := float64(1.0e-9) d_fm_L := float64(0.5e-9) d_fm_R := float64(0.5e-9) N_fm_L := int(d_fm_L / aSpace) N_fm_R := int(d_fm_R / aSpace) N_ox := int(d_ox/aSpace) - 1 fmt.Printf("Grid spacing = %.15g, N_ox = %d, N_fm_L = %d, N_fm_R = %d\n", aSpace, N_ox, N_fm_L, N_fm_R) fmt.Println("----------------------------------------") t_base := utils.Hbar * utils.Hbar / 2 / utils.Echarge / utils.M0 / aSpace / aSpace t_fm_L := t_base / m_fm_L t_fm_R := t_base / m_fm_R t_ox := t_base / m_ox fmt.Printf("m_fm_L = %.15g, m_ox = %.15g, m_fm_R = %.15g\n", m_fm_L, m_ox, m_fm_R) fmt.Println("----------------------------------------") fmt.Printf("t_fm_L = %.15g, t_ox = %.15g, t_fm_R = %.15g\n", t_fm_L, t_ox, t_fm_R) fmt.Printf("2t_fm_L = %.15g, 2t_ox = %.15g, 2t_fm_R = %.15g\n", 2*t_fm_L, 2*t_ox, 2*t_fm_R) fmt.Println("----------------------------------------") BT_Ptr := cmplxSparse.BasisTransform(th_F, ph_F) BT_L := *BT_Ptr fmt.Printf("BT Matrix (Left):\n %.15g %.15g\n %.15g %.15g\n\n", BT_L[0][0], BT_L[0][1], BT_L[1][0], BT_L[1][1]) SelfEnergyLeft := cmplxSparse.SelfEnergyEntries(1.8915, 2.1897e-07, 0.0, E_split_L, Vmtj, complex(t_fm_L, 0.0), BT_Ptr) fmt.Printf("Self-energy (Left):\n %.15g %.15g\n %.15g %.15g\n\n", SelfEnergyLeft[0][0], SelfEnergyLeft[0][1], SelfEnergyLeft[1][0], SelfEnergyLeft[1][1]) BT_Ptr = cmplxSparse.BasisTransform(th_H, ph_H) BT_L = *BT_Ptr fmt.Printf("BT Matrix (Left):\n %.15g %.15g\n %.15g %.15g\n\n", BT_L[0][0], BT_L[0][1], BT_L[1][0], BT_L[1][1]) SelfEnergyLeft = cmplxSparse.SelfEnergyEntries(1.8915, 2.1897e-07, 0.0, E_split_R, -1.0*Vmtj, complex(t_fm_R, 0.0), BT_Ptr) fmt.Printf("Self-energy (Right):\n %.15g %.15g\n %.15g %.15g\n\n", SelfEnergyLeft[0][0], SelfEnergyLeft[0][1], SelfEnergyLeft[1][0], SelfEnergyLeft[1][1]) }
// This function is called during quadrature integration over energies. func (s *IntegStruct) NEGF_EnergyIntegFunc(EnergyValue float64) *[]float64 { // Initialize the return value t := make([]float64, 4) // Get t0 on left and right of Hamiltonian matrix MatrixSize := len(s.Hamiltonian.Data) MaxDiagIdx := len(s.Hamiltonian.Data[0]) MainDiagIdx := (MaxDiagIdx - 1) / 2 // Adjust Hamiltonian with sigma1 to obtain the inverse of Green's function matrix InvGMatrix := cmplxSparse.SparseCopy(s.Hamiltonian) // Adjust main diagonal with the energy value of interest for idx0 := 0; idx0 < MatrixSize; idx0++ { InvGMatrix.Data[idx0][MainDiagIdx] += complex(EnergyValue, utils.Zplus) } // Store f1*gam1 and f2*gam2. Note that these matrices are non-zero at the top // left and bottom right, respectively. Hence, it is sufficient to store them // as just 2 x 2 matrices. f1gam1, f2gam2 := new([2][2]complex128), new([2][2]complex128) f1, f2 := cmplxSparse.FermiEnergy(EnergyValue, s.mu1, s.Temperature), cmplxSparse.FermiEnergy(EnergyValue, s.mu2, s.Temperature) // Calculate f1*gam1 first and store in a buffer SelfEnergyBuffer := cmplxSparse.SelfEnergyEntries(EnergyValue, s.E_mode, 0.0, s.deltaL, s.V_MTJ, s.t_fmL, s.BT_Mat_L) f1gam1[0][0] = complex(0.0, f1) * (SelfEnergyBuffer[0][0] - cmplx.Conj(SelfEnergyBuffer[0][0])) f1gam1[0][1] = complex(0.0, f1) * (SelfEnergyBuffer[0][1] - cmplx.Conj(SelfEnergyBuffer[1][0])) f1gam1[1][0] = complex(0.0, f1) * (SelfEnergyBuffer[1][0] - cmplx.Conj(SelfEnergyBuffer[0][1])) f1gam1[1][1] = complex(0.0, f1) * (SelfEnergyBuffer[1][1] - cmplx.Conj(SelfEnergyBuffer[1][1])) InvGMatrix.Data[0][MainDiagIdx] -= SelfEnergyBuffer[0][0] InvGMatrix.Data[0][MainDiagIdx+1] -= SelfEnergyBuffer[0][1] InvGMatrix.Data[1][MainDiagIdx-1] -= SelfEnergyBuffer[1][0] InvGMatrix.Data[1][MainDiagIdx] -= SelfEnergyBuffer[1][1] // Calculate f2*gam2 next and store in a buffer SelfEnergyBuffer = cmplxSparse.SelfEnergyEntries(EnergyValue, s.E_mode, 0.0, s.deltaR, -1.0*s.V_MTJ, s.t_fmR, s.BT_Mat_R) f2gam2[0][0] = complex(0.0, f2) * (SelfEnergyBuffer[0][0] - cmplx.Conj(SelfEnergyBuffer[0][0])) f2gam2[0][1] = complex(0.0, f2) * (SelfEnergyBuffer[0][1] - cmplx.Conj(SelfEnergyBuffer[1][0])) f2gam2[1][0] = complex(0.0, f2) * (SelfEnergyBuffer[1][0] - cmplx.Conj(SelfEnergyBuffer[0][1])) f2gam2[1][1] = complex(0.0, f2) * (SelfEnergyBuffer[1][1] - cmplx.Conj(SelfEnergyBuffer[1][1])) // Adjust Hamiltonian with sigma2 to obtain the inverse of Green's function matrix InvGMatrix.Data[MatrixSize-2][MainDiagIdx] -= SelfEnergyBuffer[0][0] InvGMatrix.Data[MatrixSize-2][MainDiagIdx+1] -= SelfEnergyBuffer[0][1] InvGMatrix.Data[MatrixSize-1][MainDiagIdx-1] -= SelfEnergyBuffer[1][0] InvGMatrix.Data[MatrixSize-1][MainDiagIdx] -= SelfEnergyBuffer[1][1] // Calculate Green's Function matrix GMatrix := cmplxSparse.CalcGreensFunc(InvGMatrix) // Calculate Gn = G * (f1*gam1 + f2*gam2) * (G^+)... // Since f1*gam1 and f2*gam2 are sparse matrices, we do not need to do a full matrix multiplication // Calculate the non-zero portion of (f1*gam1 + f2*gam2) * (G^+) and store in RearMultBuffer: // i.e. First two rows of the product are obtained from f1*gam1*(G^+) and are stored as the first // two rows of RearMultBuffer. The last two rows of the product are obtained from f1*gam2*(G^+) and // are stored as the last two rows of RearMultBuffer. RearMultBuffer := make([][]complex128, 4) RearIdx0, RearIdx1 := MatrixSize-2, MatrixSize-1 // Initialize buffer storage for idx0 := 0; idx0 < 4; idx0++ { RearMultBuffer[idx0] = make([]complex128, MatrixSize) } // Calculate each row of RearMultBuffer for idx0 := 0; idx0 < MatrixSize; idx0++ { RearMultBuffer[0][idx0] = f1gam1[0][0]*cmplx.Conj(GMatrix[idx0][0]) + f1gam1[0][1]*cmplx.Conj(GMatrix[idx0][1]) RearMultBuffer[1][idx0] = f1gam1[1][0]*cmplx.Conj(GMatrix[idx0][0]) + f1gam1[1][1]*cmplx.Conj(GMatrix[idx0][1]) if (idx0 > 1) && (idx0 < RearIdx0) { RearMultBuffer[2][idx0] = f2gam2[0][0]*cmplx.Conj(GMatrix[idx0][2]) + f2gam2[0][1]*cmplx.Conj(GMatrix[idx0][3]) RearMultBuffer[3][idx0] = f2gam2[1][0]*cmplx.Conj(GMatrix[idx0][2]) + f2gam2[1][1]*cmplx.Conj(GMatrix[idx0][3]) } else { RearMultBuffer[2][idx0] = f2gam2[0][0]*cmplx.Conj(GMatrix[idx0][RearIdx0]) + f2gam2[0][1]*cmplx.Conj(GMatrix[idx0][RearIdx1]) RearMultBuffer[3][idx0] = f2gam2[1][0]*cmplx.Conj(GMatrix[idx0][RearIdx0]) + f2gam2[1][1]*cmplx.Conj(GMatrix[idx0][RearIdx1]) } } // Finally, compute the required entries of Gn. GnF, GnR := new([2][2]complex128), new([2][2]complex128) GnF[0][0], GnF[0][1], GnF[1][0], GnF[1][1] = 0.0+0.0i, 0.0+0.0i, 0.0+0.0i, 0.0+0.0i GnR[0][0], GnR[0][1], GnR[1][0], GnR[1][1] = 0.0+0.0i, 0.0+0.0i, 0.0+0.0i, 0.0+0.0i PtIdx0 := 2*s.N_fmL + 1 PtIdx1, PtIdx2, PtIdx3 := PtIdx0-1, PtIdx0+1, PtIdx0+2 // GMatrix is actually the transpose of G. Due to the nature of (f1*gam1 + f2*gam2) // the entries of G that we need are: // 1. The first two rows. // 2. The first two columns. // 3. The last two rows. // 4. The last two columns. // Scanning the first index of GMatrix scans through the columns of G. // The second index of GMatrix selects the row. for idx0 := 0; idx0 < 4; idx0++ { if idx0 < 2 { // When multiplication involves first two rows of RearMultBuffer, // we need to access first two columns of G. GnF[0][0] += GMatrix[idx0][PtIdx1] * RearMultBuffer[idx0][PtIdx2] GnF[0][1] += GMatrix[idx0][PtIdx1] * RearMultBuffer[idx0][PtIdx3] GnF[1][0] += GMatrix[idx0][PtIdx0] * RearMultBuffer[idx0][PtIdx2] GnF[1][1] += GMatrix[idx0][PtIdx0] * RearMultBuffer[idx0][PtIdx3] GnR[0][0] += GMatrix[idx0][PtIdx2] * RearMultBuffer[idx0][PtIdx1] GnR[0][1] += GMatrix[idx0][PtIdx2] * RearMultBuffer[idx0][PtIdx0] GnR[1][0] += GMatrix[idx0][PtIdx3] * RearMultBuffer[idx0][PtIdx1] GnR[1][1] += GMatrix[idx0][PtIdx3] * RearMultBuffer[idx0][PtIdx0] } else { // When multiplication involves last two rows of RearMultBuffer, // we need to access last two columns of G. targIdx := MatrixSize - 4 GnF[0][0] += GMatrix[targIdx+idx0][PtIdx1] * RearMultBuffer[idx0][PtIdx2] GnF[0][1] += GMatrix[targIdx+idx0][PtIdx1] * RearMultBuffer[idx0][PtIdx3] GnF[1][0] += GMatrix[targIdx+idx0][PtIdx0] * RearMultBuffer[idx0][PtIdx2] GnF[1][1] += GMatrix[targIdx+idx0][PtIdx0] * RearMultBuffer[idx0][PtIdx3] GnR[0][0] += GMatrix[targIdx+idx0][PtIdx2] * RearMultBuffer[idx0][PtIdx1] GnR[0][1] += GMatrix[targIdx+idx0][PtIdx2] * RearMultBuffer[idx0][PtIdx0] GnR[1][0] += GMatrix[targIdx+idx0][PtIdx3] * RearMultBuffer[idx0][PtIdx1] GnR[1][1] += GMatrix[targIdx+idx0][PtIdx3] * RearMultBuffer[idx0][PtIdx0] } } // We know the Hamiltonian is stored in diagonal format. Extract the wanted values of Hamiltonian // and multiply accordingly to GnF and GnR. HamF, HamR, MatrixBuffer := new([2][2]complex128), new([2][2]complex128), new([2][2]complex128) HamR[0][1] = -1.0 * s.Hamiltonian.Data[PtIdx2][MainDiagIdx-1] HamR[0][0] = -1.0 * s.Hamiltonian.Data[PtIdx2][MainDiagIdx-2] HamR[1][1] = -1.0 * s.Hamiltonian.Data[PtIdx3][MainDiagIdx-2] HamF[1][0] = -1.0 * s.Hamiltonian.Data[PtIdx0][MainDiagIdx+1] HamF[0][0] = -1.0 * s.Hamiltonian.Data[PtIdx1][MainDiagIdx+2] HamF[1][1] = -1.0 * s.Hamiltonian.Data[PtIdx0][MainDiagIdx+2] if MaxDiagIdx > 6 { HamR[1][0] = -1.0 * s.Hamiltonian.Data[PtIdx3][MainDiagIdx-3] HamF[0][1] = -1.0 * s.Hamiltonian.Data[PtIdx1][MainDiagIdx+3] } else { HamR[1][0] = 0.0 + 0.0i HamF[0][1] = 0.0 + 0.0i } // Calculate term from H*Gn - Gn*H; MatrixBuffer[0][0] = 1.0i * (HamR[0][0]*GnF[0][0] + HamR[0][1]*GnF[1][0] - GnR[0][0]*HamF[0][0] - GnR[0][1]*HamF[1][0]) MatrixBuffer[0][1] = 1.0i * (HamR[0][0]*GnF[0][1] + HamR[0][1]*GnF[1][1] - GnR[0][0]*HamF[0][1] - GnR[0][1]*HamF[1][1]) MatrixBuffer[1][0] = 1.0i * (HamR[1][0]*GnF[0][0] + HamR[1][1]*GnF[1][0] - GnR[1][0]*HamF[0][0] - GnR[1][1]*HamF[1][0]) MatrixBuffer[1][1] = 1.0i * (HamR[1][0]*GnF[0][1] + HamR[1][1]*GnF[1][1] - GnR[1][0]*HamF[0][1] - GnR[1][1]*HamF[1][1]) // Calculate current density, and components of spin current t[0] = -1.0 * real(MatrixBuffer[0][0]+MatrixBuffer[1][1]) // Charge current density t[1] = real(MatrixBuffer[1][0] + MatrixBuffer[0][1]) t[2] = real(1.0i*MatrixBuffer[0][1] - 1.0i*MatrixBuffer[1][0]) t[3] = real(MatrixBuffer[0][0] - MatrixBuffer[1][1]) // z-component of spin current density // Return result return &t }