// Function for duplicating data structure for quadrature. This helps // preserve certain data that needs to be reused between different nodes // at which we need to evaluate the function to be integrated. func (t *IntegStruct) CopyIntegStruct(s *IntegStruct) { t.E_mode = s.E_mode t.E_Fermi = s.E_Fermi t.mu1 = s.mu1 t.mu2 = s.mu2 t.f1 = s.f1 t.f2 = s.f2 t.f1_prime = s.f1_prime t.f2_prime = s.f2_prime t.V_MTJ = s.V_MTJ t.Temperature = s.Temperature t.deltaL = s.deltaL t.deltaR = s.deltaR t.m_fmL = s.m_fmL t.m_ox = s.m_ox t.m_fmR = s.m_fmR t.t_fmL = s.t_fmL t.t_fmR = s.t_fmR t.N_fmL = s.N_fmL t.N_ox = s.N_ox t.N_fmR = s.N_fmR t.BT_Mat_L = s.BT_Mat_L t.BT_Mat_R = s.BT_Mat_R t.Hamiltonian = cmplxSparse.SparseCopy(s.Hamiltonian) t.ECurrFactor = s.ECurrFactor t.SCurrFactor = s.SCurrFactor return }
func testDiagSolver(n int) { fmt.Println("Initializing Sparse matrix structure\n") tmp := cmplxSparse.New() matrixSize := int(2 * n) fmt.Println("Making sparse ", matrixSize, "x", matrixSize, " Hamiltonian tridiagonal matrix\n") cmplxSparse.MakeHamTriDiag(n, tmp) for idx0 := 0; idx0 < matrixSize; idx0++ { tmp.Data[idx0][2] -= complex(0.0, 5.0) } fmt.Println("Accessing matrix elements of Hamiltonian (m,n):") for idx0 := 0; idx0 < matrixSize; idx0++ { for idx1 := 0; idx1 < matrixSize; idx1++ { test := cmplxSparse.AccessMatrix(idx0, idx1, tmp) fmt.Printf("%f ", test) } fmt.Printf("\n") } fmt.Printf("\n") tmp0 := cmplxSparse.SparseCopy(tmp) cmplxSparse.SparseDiagLU(tmp0) fmt.Println("Accessing matrix elements of LU (m,n):") for idx0 := 0; idx0 < matrixSize; idx0++ { for idx1 := 0; idx1 < matrixSize; idx1++ { test := cmplxSparse.AccessMatrix(idx0, idx1, tmp0) fmt.Printf("%f ", test) } fmt.Printf("\n") } fmt.Printf("\n") InvBuffer := make([][]complex128, matrixSize) for idx0 := range InvBuffer { InvBuffer[idx0] = make([]complex128, matrixSize) for idx1 := range InvBuffer[idx0] { InvBuffer[idx0][idx1] = 0.0 + 0.0i } } for idx0 := range InvBuffer { InvBuffer[idx0][idx0] = 1.0 + 0.0i BufferMatrix := cmplxSparse.SparseDiagLinearSolver(tmp0, InvBuffer[idx0]) InvBuffer[idx0] = BufferMatrix } fmt.Println("Accessing matrix elements of inverse (m,n):") for idx0 := range InvBuffer { for idx1 := range InvBuffer[idx0] { fmt.Printf("%.15g ", InvBuffer[idx0][idx1]) } fmt.Printf("\n") } }
func testLU(n int) { fmt.Println("Initializing Sparse matrix structure\n") tmp := cmplxSparse.New() matrixSize := int(2 * n) fmt.Println("Making sparse ", matrixSize, "x", matrixSize, " Hamiltonian tridiagonal matrix\n") cmplxSparse.MakeHamTriDiag(n, tmp) for idx0 := 0; idx0 < matrixSize; idx0++ { tmp.Data[idx0][2] -= complex(0.0, 5.0) } LU_mat := cmplxSparse.SparseCopy(tmp) cmplxSparse.SparseDiagLU(LU_mat) fmt.Println("Accessing matrix elements (m,n):") for idx0 := 0; idx0 < matrixSize; idx0++ { for idx1 := 0; idx1 < matrixSize; idx1++ { test := cmplxSparse.AccessMatrix(idx0, idx1, LU_mat) fmt.Printf("%f ", test) } fmt.Printf("\n") } fmt.Println("\n") }
// 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 }