Ejemplo n.º 1
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
}
Ejemplo n.º 2
0
// Function to allow external write access to electrochemical potential
// variables in IntegStruct data structure.
func (s *IntegStruct) SetMu() {
	s.mu1, s.mu2 = s.E_Fermi+0.5*s.V_MTJ, s.E_Fermi-0.5*s.V_MTJ
	s.f1, s.f2 = cmplxSparse.FermiEnergy(s.E_Fermi, s.mu1, s.Temperature), cmplxSparse.FermiEnergy(s.E_Fermi, s.mu2, s.Temperature)
	s.f1_prime, s.f2_prime = 1.0-s.f1, 1.0-s.f2
}