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])

}
Exemple #2
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
}