public DoubleVector Get_EnergyLevels(ILayer[] layers, ref SpinResolved_Data charge_density, Band_Data chem_pot) { // interpolate the input charge density and chemical potential onto a reduced domain to simplify DFT solve SpinResolved_Data dft_dens = new SpinResolved_Data(new Band_Data(new DoubleVector(nz)), new Band_Data(new DoubleVector(nz))); Band_Data dft_pot = new Band_Data(new DoubleVector(nz)); Interpolate_DFT_Grid(ref dft_dens, ref dft_pot, charge_density, chem_pot); Get_Potential(ref dft_pot, layers); DoubleHermitianMatrix hamiltonian = Create_Hamiltonian(layers, dft_pot); DoubleHermitianEigDecomp eig_decomp = new DoubleHermitianEigDecomp(hamiltonian); System.IO.StreamWriter sw_pot = new System.IO.StreamWriter("tmp_pot.dat"); System.IO.StreamWriter sw_dens = new System.IO.StreamWriter("tmp_dens.dat"); Band_Data dft_dens_spin_summed = dft_dens.Spin_Summed_Data; for (int i = 0; i < dft_pot.Length; i++) { sw_dens.WriteLine(dft_dens_spin_summed[i].ToString()); sw_pot.WriteLine(dft_pot[i].ToString()); } sw_dens.Close(); sw_pot.Close(); return(eig_decomp.EigenValues); }
DoubleHermitianMatrix Create_H_Using_SOIP(Band_Data dft_pot, double k) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(2 * nx * ny); // create the SOIP parts of the matrix result = Create_SOIP_Matrix(Direction.x, Direction.y, Direction.z, k, nx, ny); result += Create_SOIP_Matrix(Direction.y, Direction.x, Direction.z, k, nx, ny); // and add the scalar terms from the k-direction for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { // add momentum and potential terms result[i * ny + j, i *ny + j] += Physics_Base.hbar * Physics_Base.hbar * k * k / (2.0 * mass) + dft_pot.mat[i, j]; result[i * ny + j + nx * ny, i *ny + j + nx * ny] += Physics_Base.hbar * Physics_Base.hbar * k * k / (2.0 * mass) + dft_pot.mat[i, j]; // add spin-orbit terms result[i * ny + j, i *ny + j + nx * ny] += (-1.0 * I * alpha * dV_x.mat[i, j] - alpha * dV_y.mat[i, j]) * k; result[i * ny + j + nx * ny, i *ny + j] += (I * alpha * dV_x.mat[i, j] - alpha * dV_y.mat[i, j]) * k; } } return(result); }
DoubleHermitianMatrix Create_Hamiltonian(Band_Data dft_pot, double k) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(2 * nx * ny); DoubleHermitianMatrix h_upup, h_updn, h_dnup, h_dndn; // create sub-matrices with no spin orbit h_upup = Create_NoSOI_Hamiltonian(dft_pot, k, nx, ny); // takes up to up (ie. H_11) h_dndn = Create_NoSOI_Hamiltonian(dft_pot, k, nx, ny); // takes dn to dn (ie. H_22) h_updn = new DoubleHermitianMatrix(nx * ny); // takes up to dn (ie. H_21) h_dnup = new DoubleHermitianMatrix(nx * ny); // takes dn to up (ie. H_12) // add spin-orbit h_upup -= alpha * Create_SOI_Hamiltonian(Direction.y, Direction.x, k, nx, ny); h_dndn -= -1.0 * alpha * Create_SOI_Hamiltonian(Direction.y, Direction.x, k, nx, ny); h_updn -= alpha * (Create_SOI_Hamiltonian(Direction.z, Direction.y, nx, ny) - Create_SOI_Hamiltonian(Direction.y, Direction.z, nx, ny) + I * Create_SOI_Hamiltonian(Direction.z, Direction.x, k, nx, ny)); h_dnup -= alpha * (Create_SOI_Hamiltonian(Direction.z, Direction.y, nx, ny) - Create_SOI_Hamiltonian(Direction.y, Direction.z, nx, ny) - I * Create_SOI_Hamiltonian(Direction.z, Direction.x, k, nx, ny)); // recombine matrices and output result for (int i = 0; i < nx * ny; i++) { for (int j = 0; j < nx * ny; j++) { result[i, j] = h_upup[i, j]; result[i, j + nx * ny] = h_dnup[i, j]; result[i + nx * ny, j] = h_updn[i, j]; result[i + nx * ny, j + nx * ny] = h_dndn[i, j]; } } return(result); }
DoubleHermitianMatrix Create_Hamiltonian(double[] V, double t, int N) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(N); // set off diagonal elements for (int i = 0; i < N; i++) { // coupling sites in the growth direction if (i != 0) { result[i - 1, i] = t; } if (i != N - 1) { result[i + 1, i] = t; } } // set diagonal elements for (int i = 0; i < N; i++) { result[i, i] = -2.0 * t + V[i]; } return(result); }
DoubleHermitianMatrix Create_2DEG_Hamiltonian(double[,] V, double tx, double ty, int nx, int ny, bool periodic_in_x, bool periodic_in_y) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(nx * ny); // set off diagonal elements for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { // coupling sites in the transport direction if (i != 0) { result[i * ny + j, i *ny + j - ny] = tx; } if (i != nx - 1) { result[i * ny + j, i *ny + j + ny] = tx; } // coupling sites in the transverse direction if (j != 0) { result[i * ny + j, i *ny + j - 1] = ty; } if (j != ny - 1) { result[i * ny + j, i *ny + j + 1] = ty; } } } // set diagonal elements for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { result[i * ny + j, i *ny + j] = -2.0 * tx + -2.0 * ty + V[i, j]; } } // and finally, add periodic boundary conditions where appropriate if (periodic_in_x) { for (int j = 0; j < ny; j++) { result[j, j + (nx - 1) * ny] = tx; result[j + (nx - 1) * ny, j] = tx; } } if (periodic_in_y) { for (int i = 0; i < nx; i++) { result[i * ny + 1, i *ny] = ty; result[i * ny, i *ny + 1] = ty; } } return(result); }
/// <summary> /// Calculates and prints the band structure /// </summary> void Print_Band_Structure(Band_Data dft_pot, ILayer[] layers, int Nk, double dk, string outfile, int max_eigval) { // set temperature high so that all wave functions will be calculated double old_temperature = temperature; temperature = 1000.0; if (dV_x == null) { throw new Exception("Error - Band structure derivatives are null! Have you initiated this type properly by calling Get_SOI_parameters(Band_Data chem_pot)?"); } // calculate the energies up to a given maximum energy of the lowest state double k = 0; double[][] energies = new double[Nk][]; for (int i = 0; i < Nk; i++) { k = i * dk; Console.WriteLine(i.ToString() + ": Calculating for k = " + k.ToString()); // generate the Hamiltonian for this k value DoubleHermitianMatrix hamiltonian = Create_H_Using_SOIP(dft_pot, k); // and diagonalise it int max_wavefunction; DoubleHermitianEigDecomp eig_decomp = Diagonalise_Hamiltonian(hamiltonian, out max_wavefunction); // add the calculated energies up to either the maximum required eigenvalue or // to the maximum calculated wave function (which is 50*kb*T above the chemical potential) double[] tmp_energies = new double[max_eigval]; for (int j = 0; j < max_eigval; j++) { if (j < max_wavefunction) { tmp_energies[j] = eig_decomp.EigenValues[j];// - 0.5 * Physics_Base.hbar * Physics_Base.hbar * k * k / Physics_Base.mass; } else { tmp_energies[j] = eig_decomp.EigenValues[max_wavefunction - 1];// -0.5 * Physics_Base.hbar * Physics_Base.hbar * k * k / Physics_Base.mass; } } energies[i] = tmp_energies; } // output the data to file StreamWriter sw = new StreamWriter(outfile); for (int i = 0; i < Nk; i++) { for (int j = 0; j < max_eigval; j++) { sw.Write(energies[i][j].ToString() + "\t"); } sw.WriteLine(); } sw.Close(); // reset temperature temperature = old_temperature; }
/// <summary> /// returns an eigenvector decomposition for the x-direction after having calculated the density in the /// y-direction and storing it in "charge_density" /// </summary> /// <param name="dft_pot"></param> /// <param name="charge_density"></param> /// <returns></returns> private DoubleHermitianEigDecomp Solve_Eigenvector_Problem(Band_Data dft_pot, ref SpinResolved_Data charge_density) { DoubleHermitianEigDecomp eig_decomp; double[] x_energy = new double[nx]; DoubleMatrix dens_up = new DoubleMatrix(nx, ny, 0.0); DoubleMatrix dens_down = new DoubleMatrix(nx, ny, 0.0); // cycle over x-direction calculating the ground state energy and eigenstate in the y-direction for (int i = 0; i < nx; i++) { // pull out the chemical potential for this slice double[] y_dft_pot = new double[ny]; for (int j = 0; j < ny; j++) { y_dft_pot[j] = dft_pot.mat[i, j]; } // calculate its eigendecomposition DoubleHermitianMatrix h_y = Create_Hamiltonian(y_dft_pot, ty, ny); eig_decomp = new DoubleHermitianEigDecomp(h_y); // insert the eigenstate into density and record the local confinement energy x_energy[i] = eig_decomp.EigenValue(0); for (int j = 0; j < ny; j++) { double dens_tot = DoubleComplex.Norm(eig_decomp.EigenVector(0)[j]) * DoubleComplex.Norm(eig_decomp.EigenVector(0)[j]); dens_up[i, j] = 0.5 * dens_tot; dens_down[i, j] = 0.5 * dens_tot; } } // check whether to enforce symmetry in the transverse direction if (force_symmetry) { for (int i = nx / 2 + 1; i < nx; i++) { x_energy[i] = x_energy[nx - i - 1]; for (int j = 0; j < ny; j++) { dens_up[i, j] = dens_up[nx - i - 1, j]; dens_down[i, j] = dens_down[nx - i - 1, j]; } } } // calculate the eigenstates in the x-direction DoubleHermitianMatrix h_x = Create_Hamiltonian(x_energy, tx, nx); eig_decomp = new DoubleHermitianEigDecomp(h_x); energies = eig_decomp.EigenValues; // put the calculated densities into charge_density charge_density = new SpinResolved_Data(new Band_Data(dens_up), new Band_Data(dens_down)); return(eig_decomp); }
public override void Get_ChargeDensity_Deriv(ILayer[] layers, ref SpinResolved_Data charge_density_deriv, Band_Data chem_pot) { Get_SOI_parameters(chem_pot); // if (dV_x == null) // throw new Exception("Error - Band structure derivatives are null! Have you initiated this type properly by calling Get_SOI_parameters(Band_Data chem_pot)?"); // convert the chemical potential into a quantum mechanical potential Band_Data dft_pot = chem_pot.DeepenThisCopy(); Get_Potential(ref dft_pot, layers); // reset charge density charge_density_deriv = 0.0 * charge_density_deriv; // integrate up density from k = 0 to k = k_F int count = 0; double k = 0; // create initial hamiltonian and eigendecomposition int max_wavefunction_old_p, max_wavefunction_old_m; DoubleHermitianMatrix hamiltonian_p = Create_H_Using_SOIP(dft_pot, k); DoubleHermitianEigDecomp eig_decomp_old_p = Diagonalise_Hamiltonian(hamiltonian_p, out max_wavefunction_old_p); DoubleHermitianEigDecomp eig_decomp_old_m = Diagonalise_Hamiltonian(hamiltonian_p, out max_wavefunction_old_m); while (k < kmax) { // increment the wavevector k += delta_k; count += 1; // create new decompositions for forwards and backwards analysis int max_wavefunction_p, max_wavefunction_m; DoubleHermitianEigDecomp eig_decomp_p = Diagonalise_Hamiltonian(Create_H_Using_SOIP(dft_pot, k), out max_wavefunction_p); DoubleHermitianEigDecomp eig_decomp_m = Diagonalise_Hamiltonian(Create_H_Using_SOIP(dft_pot, -1.0 * k), out max_wavefunction_m); SpinResolved_Data new_charge_deriv = new SpinResolved_Data(nx, ny); // cycle over each of the positive bands for (int i = 0; i < max_wavefunction_p; i++) { new_charge_deriv += Calculate_Density_Derivative(eig_decomp_old_p, eig_decomp_p, i); } // and negative bands for (int i = 0; i < max_wavefunction_m; i++) { new_charge_deriv += Calculate_Density_Derivative(eig_decomp_old_m, eig_decomp_m, i); } // set new eigenvalue decompositions and max_wavefunctions to the old ones eig_decomp_old_m = eig_decomp_m; eig_decomp_old_p = eig_decomp_p; max_wavefunction_old_m = max_wavefunction_m; max_wavefunction_old_p = max_wavefunction_p; // and finally, add the charge density calculated charge_density_deriv += new_charge_deriv; } }
/// <summary> /// Calculate the charge density for this potential /// NOTE!!! that the boundary potential is (essentially) set to infty by ringing the density with a set of zeros. /// this prevents potential solvers from extrapolating any residual density at the edge of the eigenstate /// solution out of the charge density calculation domain /// </summary> /// <param name="layers"></param> /// <param name="charge_density"></param> /// <param name="chem_pot"></param> public override void Get_ChargeDensity(ILayer[] layers, ref SpinResolved_Data charge_density, Band_Data chem_pot) { // convert the chemical potential into a quantum mechanical potential Band_Data dft_pot = chem_pot.DeepenThisCopy(); Get_Potential(ref dft_pot, layers); DoubleHermitianMatrix hamiltonian = Create_Hamiltonian(layers, dft_pot); DoubleHermitianEigDecompServer eig_server = new DoubleHermitianEigDecompServer(); eig_server.ComputeEigenValueRange(dft_pot.mat.Min(), no_kb_T * Physics_Base.kB * temperature); eig_server.ComputeVectors = true; DoubleHermitianEigDecomp eig_decomp = eig_server.Factor(hamiltonian); int max_wavefunction = 0; if (eig_decomp.EigenValues.Length != 0) { double min_eigval = eig_decomp.EigenValues.Min(); max_wavefunction = (from val in eig_decomp.EigenValues where val < no_kb_T * Physics_Base.kB * temperature select val).ToArray().Length; } for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { double dens_val = 0.0; // do not add anything to the density if on the edge of the domain if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) { charge_density.Spin_Up.mat[i, j] = 0.0; charge_density.Spin_Down.mat[i, j] = 0.0; continue; } for (int k = 0; k < max_wavefunction; k++) { // and integrate the density of states at this position for this eigenvector from the minimum energy to // (by default) 50 * k_b * T above mu = 0 dens_val += DoubleComplex.Norm(eig_decomp.EigenVector(k)[i * ny + j]) * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i * ny + j]) * Get_OneD_DoS(eig_decomp.EigenValue(k), no_kb_T); } // just share the densities (there is no spin polarisation) charge_density.Spin_Up.mat[i, j] = 0.5 * dens_val; charge_density.Spin_Down.mat[i, j] = 0.5 * dens_val; } } // and multiply the density by -e to get the charge density (as these are electrons) charge_density = unit_charge * charge_density; }
/// <summary> /// returns the eigen-energies for the given potential and charge density /// </summary> public override DoubleVector Get_EnergyLevels(ILayer[] layers, Band_Data pot) { // convert the chemical potential into a quantum mechanical potential Band_Data band_pot = pot.DeepenThisCopy(); Get_Potential(ref band_pot, layers); DoubleHermitianMatrix hamiltonian = Create_Hamiltonian(layers, band_pot); DoubleHermitianEigDecomp eig_decomp = new DoubleHermitianEigDecomp(hamiltonian); return(eig_decomp.EigenValues); }
/// <summary> /// Calculate the charge density for this potential /// NOTE!!! that the boundary potential is (essentially) set to infty by ringing the density with a set of zeros. /// this prevents potential solvers from extrapolating any residual density at the edge of the eigenstate /// solution out of the charge density calculation domain /// </summary> /// <param name="layers"></param> /// <param name="charge_density_deriv"></param> /// <param name="chem_pot"></param> public void Get_ChargeDensity_Deriv(ILayer[] layers, ref SpinResolved_Data charge_density_deriv, Band_Data chem_pot) { // interpolate the input charge density and chemical potential onto a reduced domain to simplify DFT solve SpinResolved_Data dft_dens_deriv = new SpinResolved_Data(nz); Get_ChargeDensity(layers, ref charge_density_deriv, chem_pot); Band_Data dft_pot = new Band_Data(new DoubleVector(nz)); Interpolate_DFT_Grid(ref dft_dens_deriv, ref dft_pot, charge_density_deriv, chem_pot); Get_Potential(ref dft_pot, layers); // // set dft_dens to zero so that the hamiltonian doesn't include the XC term // dft_dens = 0.0 * dft_dens; DoubleHermitianMatrix hamiltonian = Create_Hamiltonian(layers, dft_pot); DoubleHermitianEigDecomp eig_decomp = new DoubleHermitianEigDecomp(hamiltonian); double min_eigval = eig_decomp.EigenValues.Min(); max_wavefunction = (from val in eig_decomp.EigenValues where val < no_kb_T * Physics_Base.kB * temperature select val).ToArray().Length; DoubleVector dens_up_deriv = new DoubleVector(nz, 0.0); DoubleVector dens_down_deriv = new DoubleVector(nz, 0.0); for (int j = 0; j < nz; j++) { double dens_val = 0.0; for (int i = 0; i < max_wavefunction; i++) { // and integrate the density of states at this position for this eigenvector from the minimum energy to // (by default) 50 * k_b * T above mu = 0 //dens_val += dens_of_states.Integrate(min_eigval, no_kb_T * Physics_Base.kB * temperature); dens_val += DoubleComplex.Norm(eig_decomp.EigenVector(i)[j]) * DoubleComplex.Norm(eig_decomp.EigenVector(i)[j]) * Get_TwoD_DoS_Deriv(eig_decomp.EigenValue(i), no_kb_T);// *mass / (2.0 * Math.PI * Physics_Base.hbar * Physics_Base.hbar); } // just share the densities (there is no spin polarisation) dens_up_deriv[j] = 0.5 * dens_val; dens_down_deriv[j] = 0.5 * dens_val; } // and multiply the density derivative by e to get the charge density and by e to convert it to d/dphi (as increasing phi decreases the charge: dn/dphi*-e^2 ) dft_dens_deriv = -1.0 * unit_charge * unit_charge * new SpinResolved_Data(new Band_Data(dens_up_deriv), new Band_Data(dens_down_deriv)); Insert_DFT_Charge(ref charge_density_deriv, dft_dens_deriv); }
/// <summary> /// returns an eigenvector decomposition for the xy-plane after having calculated the density in the /// z-direction and storing it in "charge_density" /// </summary> /// <param name="dft_pot"></param> /// <param name="charge_density"></param> /// <returns></returns> private double[,] Solve_Eigenvector_Problem(Band_Data dft_pot, ref SpinResolved_Data charge_density) { DoubleHermitianEigDecomp eig_decomp; double[,] xy_energy = new double[nx, ny]; Band_Data dens_up = new Band_Data(nx, ny, nz, 0.0); Band_Data dens_down = new Band_Data(nx, ny, nz, 0.0); // cycle over xy-plane calculating the ground state energy and eigenstate in the growth direction for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { // pull out the chemical potential for this slice double[] z_dft_pot = new double[nz]; for (int k = 0; k < nz; k++) { z_dft_pot[k] = dft_pot.vol[k][i, j]; } // calculate its eigendecomposition DoubleHermitianMatrix h_z = Create_Hamiltonian(z_dft_pot, tz, nz); eig_decomp = new DoubleHermitianEigDecomp(h_z); // insert the eigenstate into density and record the local confinement energy xy_energy[i, j] = eig_decomp.EigenValue(0); for (int k = 0; k < nz; k++) { double dens_tot = DoubleComplex.Norm(eig_decomp.EigenVector(0)[k]) * DoubleComplex.Norm(eig_decomp.EigenVector(0)[k]); dens_up.vol[k][i, j] = 0.5 * dens_tot; dens_down.vol[k][i, j] = 0.5 * dens_tot; } } } // calculate the eigenstates in the xy-plane /*DoubleHermitianMatrix h_xy = Create_2DEG_Hamiltonian(xy_energy, tx, ty, nx, ny, true, false); * eig_decomp = new DoubleHermitianEigDecomp(h_xy); * energies = eig_decomp.EigenValues;*/ // put the calculated densities into charge_density charge_density = new SpinResolved_Data(dens_up, dens_down); return(xy_energy); }
public void Write_Out_Hamiltonian(DoubleHermitianMatrix h) { System.IO.StreamWriter sw = new System.IO.StreamWriter("hamiltonian.dat"); for (int i = 0; i < h.Cols; i++) { for (int j = 0; j < h.Rows; j++) { sw.Write(h[i, j].Real.ToString() + '\t'); if (j == h.Rows - 1) { sw.WriteLine(); } } } sw.Close(); }
/// <summary> /// Calculate the charge density for this potential /// NOTE!!! that the boundary potential is (essentially) set to infty by ringing the density with a set of zeros. /// this prevents potential solvers from extrapolating any residual density at the edge of the eigenstate /// solution out of the charge density calculation domain /// </summary> /// <param name="layers"></param> /// <param name="charge_density_deriv"></param> /// <param name="chem_pot"></param> public override void Get_ChargeDensity_Deriv(ILayer[] layers, ref SpinResolved_Data charge_density_deriv, Band_Data chem_pot) { // convert the chemical potential into a quantum mechanical potential Band_Data dft_pot = chem_pot.DeepenThisCopy(); Get_Potential(ref dft_pot, layers); DoubleHermitianMatrix hamiltonian = Create_Hamiltonian(layers, dft_pot); DoubleHermitianEigDecomp eig_decomp = new DoubleHermitianEigDecomp(hamiltonian); double min_eigval = eig_decomp.EigenValues.Min(); int max_wavefunction = (from val in eig_decomp.EigenValues where val < no_kb_T * Physics_Base.kB * temperature select val).ToArray().Length; for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { double dens_val_deriv = 0.0; // do not add anything to the density if on the edge of the domain if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) { charge_density_deriv.Spin_Up.mat[i, j] = 0.0; charge_density_deriv.Spin_Down.mat[i, j] = 0.0; continue; } for (int k = 0; k < max_wavefunction; k++) { // and integrate the density of states at this position for this eigenvector from the minimum energy to // (by default) 50 * k_b * T above mu = 0 dens_val_deriv += DoubleComplex.Norm(eig_decomp.EigenVector(k)[i * ny + j]) * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i * ny + j]) * Get_OneD_DoS_Deriv(eig_decomp.EigenValue(k), no_kb_T); } // just share the densities (there is no spin polarisation) charge_density_deriv.Spin_Up.mat[i, j] = 0.5 * dens_val_deriv; charge_density_deriv.Spin_Down.mat[i, j] = 0.5 * dens_val_deriv; } } // and multiply the density derivative by e to get the charge density and by e to convert it to d/dphi (as increasing phi decreases the charge: dn/dphi*-e^2 ) charge_density_deriv = unit_charge * unit_charge * charge_density_deriv; }
DoubleHermitianMatrix Create_Hamiltonian(ILayer[] layers, Band_Data pot) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(nz); // set off diagonal elements for (int i = 0; i < nz - 1; i++) { result[i + 1, i] = t; result[i, i + 1] = t; } // set diagonal elements for (int i = 0; i < nz; i++) { result[i, i] = -2.0 * t + pot[i]; } return(result); }
DoubleHermitianMatrix Create_NoSOI_Hamiltonian(Band_Data dft_pot, double k, int nx, int ny) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(nx * ny); // set off diagonal elements for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { // coupling sites in the growth direction if (i != 0) { result[i * ny + j, i *ny + j - ny] = tx; } if (i != nx - 1) { result[i * ny + j, i *ny + j + ny] = tx; } // coupling sites in the transverse direction if (j != 0) { result[i * ny + j, i *ny + j - 1] = ty; } if (j != ny - 1) { result[i * ny + j, i *ny + j + 1] = ty; } } } double[,] potential = new double[nx, ny]; // set diagonal elements for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { potential[i, j] = dft_pot.mat[i, j]; potential[i, j] += Physics_Base.hbar * Physics_Base.hbar * k * k / (2.0 * mass); result[i * ny + j, i *ny + j] = -2.0 * tx + -2.0 * ty + potential[i, j]; } } return(result); }
public override void Get_ChargeDensity(ILayer[] layers, ref SpinResolved_Data charge_density, Band_Data chem_pot) { // interpolate the input charge density and chemical potential onto a reduced domain to simplify DFT solve SpinResolved_Data dft_dens = new SpinResolved_Data(nz); Band_Data dft_pot = new Band_Data(new DoubleVector(nz)); Interpolate_DFT_Grid(ref dft_dens, ref dft_pot, charge_density, chem_pot); Get_Potential(ref dft_pot, layers); DoubleHermitianMatrix hamiltonian = Create_Hamiltonian(layers, dft_pot); DoubleHermitianEigDecomp eig_decomp = new DoubleHermitianEigDecomp(hamiltonian); double min_eigval = eig_decomp.EigenValues.Min(); max_wavefunction = (from val in eig_decomp.EigenValues where val < no_kb_T * Physics_Base.kB * temperature select val).ToArray().Length; DoubleVector dens_up = new DoubleVector(nz, 0.0); DoubleVector dens_down = new DoubleVector(nz, 0.0); for (int j = 0; j < nz; j++) { double dens_val = 0.0; for (int i = 0; i < max_wavefunction; i++) { // and integrate the density of states at this position for this eigenvector from the minimum energy to // (by default) 50 * k_b * T above mu = 0 //dens_val += dens_of_states.Integrate(min_eigval, no_kb_T * Physics_Base.kB * temperature); dens_val += DoubleComplex.Norm(eig_decomp.EigenVector(i)[j]) * DoubleComplex.Norm(eig_decomp.EigenVector(i)[j]) * Get_TwoD_DoS(eig_decomp.EigenValue(i), no_kb_T); } // just share the densities (there is no spin polarisation) dens_up[j] = 0.5 * dens_val; dens_down[j] = 0.5 * dens_val; } // and multiply the density by -e to get the charge density (as these are electrons) dft_dens = unit_charge * new SpinResolved_Data(new Band_Data(dens_up), new Band_Data(dens_down)); Insert_DFT_Charge(ref charge_density, dft_dens); }
DoubleHermitianMatrix Create_Hamiltonian(ILayer[] layers, Band_Data pot) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(nx * ny); // set off diagonal elements for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { // coupling sites in the transverse direction if (i != 0) { result[i * ny + j, i *ny + j - ny] = tx; } if (i != nx - 1) { result[i * ny + j, i *ny + j + ny] = tx; } // coupling sites in the growth direction if (j != 0) { result[i * ny + j, i *ny + j - 1] = ty; } if (j != ny - 1) { result[i * ny + j, i *ny + j + 1] = ty; } } } double[,] potential = new double[nx, ny]; // set diagonal elements for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { result[i * ny + j, i *ny + j] = -2.0 * tx + -2.0 * ty + pot.mat[i, j]; } } return(result); }
DoubleHermitianEigDecomp Diagonalise_Hamiltonian(DoubleHermitianMatrix hamiltonian, out int max_wavefunction) { DoubleHermitianEigDecomp eig_decomp; DoubleHermitianEigDecompServer eig_server = new DoubleHermitianEigDecompServer(); eig_server.ComputeEigenValueRange(E_min, no_kb_T * Physics_Base.kB * temperature); eig_server.ComputeVectors = true; eig_decomp = eig_server.Factor(hamiltonian); max_wavefunction = 0; if (eig_decomp.EigenValues.Length != 0) { double min_eigval = eig_decomp.EigenValues.Min(); max_wavefunction = (from val in eig_decomp.EigenValues where val < no_kb_T * Physics_Base.kB * temperature select val).ToArray().Length; } return(eig_decomp); }
DoubleComplexMatrix Product(DoubleHermitianMatrix A, DoubleComplexMatrix B) { return new DoubleComplexMatrix(NMathFunctions.Product(MatrixFunctions.ToGeneralMatrix(A), B)); }
DoubleHermitianMatrix Create_SOI_Hamiltonian(Direction dV, Direction p, double k, int nx, int ny) { DoubleHermitianMatrix result = 0.0 * new DoubleHermitianMatrix(nx * ny); Band_Data dV_data; if (dV == Direction.x) { return(result); } else if (dV == Direction.y) { dV_data = dV_x; } else if (dV == Direction.z) { dV_data = dV_y; } else { throw new Exception("Error - It's completely impossible to get here"); } if (p == Direction.x) { for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { result[i * ny + j, i *ny + j] = Physics_Base.hbar * k * dV_data.mat[i, j]; } } } else if (p == Direction.y) { for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { if (i != nx - 1) { result[i * ny + j, i *ny + j + ny] = -0.5 * I * Physics_Base.hbar * dV_data.mat[i, j] / dx; } if (i != 0) { result[i * ny + j, i *ny + j - ny] = 0.5 * I * Physics_Base.hbar * dV_data.mat[i, j] / dx; } } } } else if (p == Direction.z) { for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { if (i != nx - 1) { result[i * ny + j, i *ny + j + 1] = -0.5 * I * Physics_Base.hbar * dV_data.mat[i, j] / dy; } if (i != 0) { result[i * ny + j, i *ny + j - 1] = 0.5 * I * Physics_Base.hbar * dV_data.mat[i, j] / dy; } } } } else { throw new Exception("Error - It's completely impossible to get here"); } return(result); }
DoubleComplexMatrix Product(DoubleHermitianMatrix A, DoubleComplexMatrix B) { return(new DoubleComplexMatrix(NMathFunctions.Product(MatrixFunctions.ToGeneralMatrix(A), B))); }
private DoubleHermitianMatrix Create_SOIP_Matrix(Direction p, Direction dV, Direction sigma, double k, int nx, int ny) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(2 * nx * ny); double theta; Band_Data dV_data; if (dV == Direction.x) { dV_data = dV_x; theta = theta_x; } else if (dV == Direction.y) { dV_data = dV_y; theta = theta_y; } else if (dV == Direction.z) { dV_data = new Band_Data(nx, ny, 0.0); theta = 0.0; } else { throw new Exception("Error - It's completely impossible to get here"); } if (p == Direction.x) { if (sigma == Direction.x) { throw new InvalidArgumentException("Error - no spin-orbit interaction between p_x and sigma_x"); } else if (sigma == Direction.y) { throw new NotImplementedException(); } else if (sigma == Direction.z) { for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { // off-diagonal couplings in x backward if (i != 0) { // for up -> up result[i * ny + j, i *ny + j - ny] = NMathFunctions.Exp(-1.0 * I * theta * dV_data.mat[i, j]) * tx; // for down -> down result[i * ny + j + nx * ny, i *ny + j - ny + nx * ny] = NMathFunctions.Exp(I * theta * dV_data.mat[i, j]) * tx; } // off-diagonal couplings in x forward if (i != nx - 1) { // for up -> up result[i * ny + j, i *ny + j + ny] = NMathFunctions.Exp(-1.0 * I * theta * dV_data.mat[i, j]) * tx; // for down -> down result[i * ny + j + nx * ny, i *ny + j + ny + nx * ny] = NMathFunctions.Exp(I * theta * dV_data.mat[i, j]) * tx; } // and diagonal couplings result[i * ny + j, i *ny + j] = -2.0 * Math.Cos(theta * dV_data.mat[i, j]) * tx; result[i * ny + j + nx * ny, i *ny + j + nx * ny] = -2.0 * Math.Cos(theta * dV_data.mat[i, j]) * tx; } } } else { throw new Exception("Error - It's completely impossible to get here"); } } else if (p == Direction.y) { if (sigma == Direction.x) { throw new NotImplementedException(); } else if (sigma == Direction.y) { throw new InvalidArgumentException("Error - no spin-orbit interaction between p_y and sigma_y"); } else if (sigma == Direction.z) { for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { // off-diagonal couplings in y backward if (j != 0) { // for up -> up result[i * ny + j, i *ny + j - 1] = NMathFunctions.Exp(-1.0 * I * theta * dV_data.mat[i, j]) * ty; // for down -> down result[i * ny + j + nx * ny, i *ny + j - 1 + nx * ny] = NMathFunctions.Exp(I * theta * dV_data.mat[i, j]) * ty; } // off-diagonal couplings in y forward if (j != ny - 1) { // for up -> up result[i * ny + j, i *ny + j + 1] = NMathFunctions.Exp(-1.0 * I * theta * dV_data.mat[i, j]) * ty; // for down -> down result[i * ny + j + nx * ny, i *ny + j + 1 + nx * ny] = NMathFunctions.Exp(I * theta * dV_data.mat[i, j]) * ty; } // and diagonal couplings result[i * ny + j, i *ny + j] = -2.0 * Math.Cos(theta * dV_data.mat[i, j]) * ty; result[i * ny + j + nx * ny, i *ny + j + nx * ny] = -2.0 * Math.Cos(theta * dV_data.mat[i, j]) * ty; } } } else { throw new Exception("Error - It's completely impossible to get here"); } } else if (p == Direction.z) { throw new NotImplementedException("Error - at the moment, when momentum is in the direction of the wire, you should use a different method"); } else { throw new Exception("Error - It's completely impossible to get here"); } return(result); }
public void Write_Out_Hamiltonian(DoubleHermitianMatrix h) { System.IO.StreamWriter sw = new System.IO.StreamWriter("hamiltonian.dat"); for (int i = 0; i < h.Cols; i++) for (int j = 0; j < h.Rows; j++) { sw.Write(h[i, j].Real.ToString() + '\t'); if (j == h.Rows - 1) sw.WriteLine(); } sw.Close(); }
DoubleHermitianEigDecomp Diagonalise_Hamiltonian(DoubleHermitianMatrix hamiltonian, out int max_wavefunction) { DoubleHermitianEigDecomp eig_decomp; DoubleHermitianEigDecompServer eig_server = new DoubleHermitianEigDecompServer(); eig_server.ComputeEigenValueRange(E_min, no_kb_T * Physics_Base.kB * temperature); eig_server.ComputeVectors = true; eig_decomp = eig_server.Factor(hamiltonian); max_wavefunction = 0; if (eig_decomp.EigenValues.Length != 0) { double min_eigval = eig_decomp.EigenValues.Min(); max_wavefunction = (from val in eig_decomp.EigenValues where val < no_kb_T * Physics_Base.kB * temperature select val).ToArray().Length; } return eig_decomp; }
private DoubleHermitianMatrix Create_SOIP_Matrix(Direction p, Direction dV, Direction sigma, double k, int nx, int ny) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(2 * nx * ny); double theta; Band_Data dV_data; if (dV == Direction.x) { dV_data = dV_x; theta = theta_x; } else if (dV == Direction.y) { dV_data = dV_y; theta = theta_y; } else if (dV == Direction.z) { dV_data = new Band_Data(nx, ny, 0.0); theta = 0.0; } else throw new Exception("Error - It's completely impossible to get here"); if (p == Direction.x) if (sigma == Direction.x) throw new InvalidArgumentException("Error - no spin-orbit interaction between p_x and sigma_x"); else if (sigma == Direction.y) throw new NotImplementedException(); else if (sigma == Direction.z) { for (int i = 0; i < nx; i++) for(int j = 0; j < ny; j++) { // off-diagonal couplings in x backward if (i != 0) { // for up -> up result[i * ny + j, i * ny + j - ny] = NMathFunctions.Exp(-1.0 * I * theta * dV_data.mat[i, j]) * tx; // for down -> down result[i * ny + j + nx * ny, i * ny + j - ny + nx * ny] = NMathFunctions.Exp(I * theta * dV_data.mat[i, j]) * tx; } // off-diagonal couplings in x forward if (i != nx - 1) { // for up -> up result[i * ny + j, i * ny + j + ny] = NMathFunctions.Exp(-1.0 * I * theta * dV_data.mat[i, j]) * tx; // for down -> down result[i * ny + j + nx * ny, i * ny + j + ny + nx * ny] = NMathFunctions.Exp(I * theta * dV_data.mat[i, j]) * tx; } // and diagonal couplings result[i * ny + j, i * ny + j] = -2.0 * Math.Cos(theta * dV_data.mat[i, j]) * tx; result[i * ny + j + nx * ny, i * ny + j + nx * ny] = -2.0 * Math.Cos(theta * dV_data.mat[i, j]) * tx; } } else throw new Exception("Error - It's completely impossible to get here"); else if (p == Direction.y) if (sigma == Direction.x) throw new NotImplementedException(); else if (sigma == Direction.y) throw new InvalidArgumentException("Error - no spin-orbit interaction between p_y and sigma_y"); else if (sigma == Direction.z) { for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) { // off-diagonal couplings in y backward if (j != 0) { // for up -> up result[i * ny + j, i * ny + j - 1] = NMathFunctions.Exp(-1.0 * I * theta * dV_data.mat[i, j]) * ty; // for down -> down result[i * ny + j + nx * ny, i * ny + j - 1 + nx * ny] = NMathFunctions.Exp(I * theta * dV_data.mat[i, j]) * ty; } // off-diagonal couplings in y forward if (j != ny - 1) { // for up -> up result[i * ny + j, i * ny + j + 1] = NMathFunctions.Exp(-1.0 * I * theta * dV_data.mat[i, j]) * ty; // for down -> down result[i * ny + j + nx * ny, i * ny + j + 1 + nx * ny] = NMathFunctions.Exp(I * theta * dV_data.mat[i, j]) * ty; } // and diagonal couplings result[i * ny + j, i * ny + j] = -2.0 * Math.Cos(theta * dV_data.mat[i, j]) * ty; result[i * ny + j + nx * ny, i * ny + j + nx * ny] = -2.0 * Math.Cos(theta * dV_data.mat[i, j]) * ty; } } else throw new Exception("Error - It's completely impossible to get here"); else if (p == Direction.z) throw new NotImplementedException("Error - at the moment, when momentum is in the direction of the wire, you should use a different method"); else throw new Exception("Error - It's completely impossible to get here"); return result; }
DoubleHermitianMatrix Create_NoSOI_Hamiltonian(Band_Data dft_pot, double k, int nx, int ny) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(nx * ny); // set off diagonal elements for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) { // coupling sites in the growth direction if (i != 0) result[i * ny + j, i * ny + j - ny] = tx; if (i != nx - 1) result[i * ny + j, i * ny + j + ny] = tx; // coupling sites in the transverse direction if (j != 0) result[i * ny + j, i * ny + j - 1] = ty; if (j != ny - 1) result[i * ny + j, i * ny + j + 1] = ty; } double[,] potential = new double[nx, ny]; // set diagonal elements for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) { potential[i, j] = dft_pot.mat[i, j]; potential[i, j] += Physics_Base.hbar * Physics_Base.hbar * k * k / (2.0 * mass); result[i * ny + j, i * ny + j] = -2.0 * tx + -2.0 * ty + potential[i, j]; } return result; }
DoubleHermitianMatrix Create_H_Using_SOIP(Band_Data dft_pot, double k) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(2 * nx * ny); // create the SOIP parts of the matrix result = Create_SOIP_Matrix(Direction.x, Direction.y, Direction.z, k, nx, ny); result += Create_SOIP_Matrix(Direction.y, Direction.x, Direction.z, k, nx, ny); // and add the scalar terms from the k-direction for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) { // add momentum and potential terms result[i * ny + j, i * ny + j] += Physics_Base.hbar * Physics_Base.hbar * k * k / (2.0 * mass) + dft_pot.mat[i, j]; result[i * ny + j + nx * ny, i * ny + j + nx * ny] += Physics_Base.hbar * Physics_Base.hbar * k * k / (2.0 * mass) + dft_pot.mat[i, j]; // add spin-orbit terms result[i * ny + j, i * ny + j + nx * ny] += (-1.0 * I * alpha * dV_x.mat[i, j] - alpha * dV_y.mat[i, j]) * k; result[i * ny + j + nx * ny, i * ny + j] += (I * alpha * dV_x.mat[i, j] - alpha * dV_y.mat[i, j]) * k; } return result; }
DoubleHermitianMatrix Create_Hamiltonian(Band_Data dft_pot, double k) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(2 * nx * ny); DoubleHermitianMatrix h_upup, h_updn, h_dnup, h_dndn; // create sub-matrices with no spin orbit h_upup = Create_NoSOI_Hamiltonian(dft_pot, k, nx, ny); // takes up to up (ie. H_11) h_dndn = Create_NoSOI_Hamiltonian(dft_pot, k, nx, ny); // takes dn to dn (ie. H_22) h_updn = new DoubleHermitianMatrix(nx * ny); // takes up to dn (ie. H_21) h_dnup = new DoubleHermitianMatrix(nx * ny); // takes dn to up (ie. H_12) // add spin-orbit h_upup -= alpha * Create_SOI_Hamiltonian(Direction.y, Direction.x, k, nx, ny); h_dndn -= -1.0 * alpha * Create_SOI_Hamiltonian(Direction.y, Direction.x, k, nx, ny); h_updn -= alpha * (Create_SOI_Hamiltonian(Direction.z, Direction.y, nx, ny) - Create_SOI_Hamiltonian(Direction.y, Direction.z, nx, ny) + I * Create_SOI_Hamiltonian(Direction.z, Direction.x, k, nx, ny)); h_dnup -= alpha * (Create_SOI_Hamiltonian(Direction.z, Direction.y, nx, ny) - Create_SOI_Hamiltonian(Direction.y, Direction.z, nx, ny) - I * Create_SOI_Hamiltonian(Direction.z, Direction.x, k, nx, ny)); // recombine matrices and output result for (int i = 0; i < nx * ny; i++) for (int j = 0; j < nx * ny; j++) { result[i, j] = h_upup[i, j]; result[i, j + nx * ny] = h_dnup[i, j]; result[i + nx * ny, j] = h_updn[i, j]; result[i + nx * ny, j + nx * ny] = h_dndn[i, j]; } return result; }
DoubleHermitianMatrix Create_2DEG_Hamiltonian(double[,] V, double tx, double ty, int nx, int ny, bool periodic_in_x, bool periodic_in_y) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(nx * ny); // set off diagonal elements for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) { // coupling sites in the transport direction if (i != 0) result[i * ny + j, i * ny + j - ny] = tx; if (i != nx - 1) result[i * ny + j, i * ny + j + ny] = tx; // coupling sites in the transverse direction if (j != 0) result[i * ny + j, i * ny + j - 1] = ty; if (j != ny - 1) result[i * ny + j, i * ny + j + 1] = ty; } // set diagonal elements for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) result[i * ny + j, i * ny + j] = -2.0 * tx + -2.0 * ty + V[i, j]; // and finally, add periodic boundary conditions where appropriate if (periodic_in_x) for (int j = 0; j < ny; j++) { result[j, j + (nx - 1) * ny] = tx; result[j + (nx - 1) * ny, j] = tx; } if (periodic_in_y) for (int i = 0; i < nx; i++) { result[i * ny + 1, i * ny] = ty; result[i * ny, i * ny + 1] = ty; } return result; }
DoubleHermitianMatrix Create_Hamiltonian(ILayer[] layers, Band_Data pot) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(nz); // set off diagonal elements for (int i = 0; i < nz - 1; i++) { result[i + 1, i] = t; result[i, i + 1] = t; } // set diagonal elements for (int i = 0; i < nz; i++) { result[i, i] = -2.0 * t + pot[i]; } return result; }
DoubleHermitianMatrix Create_Hamiltonian(ILayer[] layers, Band_Data pot) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(nx * ny); // set off diagonal elements for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) { // coupling sites in the transverse direction if (i != 0) result[i * ny + j, i * ny + j - ny] = tx; if (i != nx - 1) result[i * ny + j, i * ny + j + ny] = tx; // coupling sites in the growth direction if (j != 0) result[i * ny + j, i * ny + j - 1] = ty; if (j != ny - 1) result[i * ny + j, i * ny + j + 1] = ty; } double[,] potential = new double[nx, ny]; // set diagonal elements for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) result[i * ny + j, i * ny + j] = -2.0 * tx + -2.0 * ty + pot.mat[i, j]; return result; }
DoubleHermitianMatrix Create_Hamiltonian(double[] V, double t, int N) { DoubleHermitianMatrix result = new DoubleHermitianMatrix(N); // set off diagonal elements for (int i = 0; i < N; i++) { // coupling sites in the growth direction if (i != 0) result[i - 1, i] = t; if (i != N - 1) result[i + 1, i] = t; } // set diagonal elements for (int i = 0; i < N; i++) result[i, i] = -2.0 * t + V[i]; return result; }