Beispiel #1
0
// 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
}
Beispiel #2
0
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")
	}
}
Beispiel #3
0
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")
}
Beispiel #4
0
// 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
}