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; } }
void Insert_DFT_Charge(ref SpinResolved_Data charge_density, SpinResolved_Data dft_dens) { for (int i = 0; i < nz; i++) { int init_index = (int)Math.Round((i * dz - zmin_pot + zmin) / dz_pot); // index for the initial domain (ie. for charge_density and band_offset) // no interpolation (it doesn't work...) charge_density.Spin_Up[init_index] = dft_dens.Spin_Up[i]; charge_density.Spin_Down[init_index] = dft_dens.Spin_Down[i]; } }
/// <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; }
void Interpolate_DFT_Grid(ref SpinResolved_Data dft_dens, ref Band_Data dft_band_offset, SpinResolved_Data charge_density, Band_Data band_offset) { for (int i = 0; i < nz; i++) { int init_index = (int)Math.Round((i * dz - zmin_pot + zmin) / dz_pot); // index for the initial domain (ie. for charge_density and band_offset) // no interpolation (it doesn't work...) dft_dens.Spin_Up[i] = charge_density.Spin_Up[init_index]; dft_dens.Spin_Down[i] = charge_density.Spin_Down[init_index]; dft_band_offset[i] = band_offset[init_index]; } }
public override void Get_ChargeDensity(ILayer[] layers, ref SpinResolved_Data density, Band_Data chem_pot) { SpinResolved_Data tmp_car = new SpinResolved_Data(new Band_Data(new DoubleVector(nz)), new Band_Data(new DoubleVector(nz))); SpinResolved_Data tmp_dop = new SpinResolved_Data(new Band_Data(new DoubleVector(nz)), new Band_Data(new DoubleVector(nz))); Get_ChargeDensity(layers, ref tmp_car, ref tmp_dop, chem_pot); for (int i = 0; i < nz; i++) { density.Spin_Up.vec[i] = tmp_car.Spin_Up.vec[i] + tmp_dop.Spin_Up.vec[i]; density.Spin_Down.vec[i] = tmp_car.Spin_Down.vec[i] + tmp_dop.Spin_Down.vec[i]; } }
protected override void Initialise_DataClasses(Dictionary <string, object> input_dict) { if (initialise_from_restart) { // initialise data classes for the density and chemical potential this.carrier_charge_density = new SpinResolved_Data(nz_dens); this.dopent_charge_density = new SpinResolved_Data(nz_dens); this.chem_pot = new Band_Data(nz_dens, 0.0); Initialise_from_Restart(input_dict); // and instantiate their derivatives carrier_charge_density_deriv = new SpinResolved_Data(nz_dens); dopent_charge_density_deriv = new SpinResolved_Data(nz_dens); chem_pot.Laplacian = pois_solv.Calculate_Phi_Laplacian(chem_pot); return; } else if (hot_start) { Initialise_from_Hot_Start(input_dict); } // try to get and the carrier and dopent density from the dictionary... they probably won't be there and if not... make them if (input_dict.ContainsKey("Carrier_Density")) { this.carrier_charge_density = (SpinResolved_Data)input_dict["Carrier_Density"]; } else { this.carrier_charge_density = new SpinResolved_Data(nz_pot); } if (input_dict.ContainsKey("Dopent_Density")) { this.dopent_charge_density = (SpinResolved_Data)input_dict["Dopent_Density"]; } else { this.dopent_charge_density = new SpinResolved_Data(nz_pot); } // and instantiate their derivatives carrier_charge_density_deriv = new SpinResolved_Data(nz_pot); dopent_charge_density_deriv = new SpinResolved_Data(nz_pot); // and finally, try to get the chemical potential from the dictionary... if (input_dict.ContainsKey("Chemical_Potential")) { this.chem_pot = (Band_Data)input_dict["Chemical_Potential"]; } }
/// <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); }
/// <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; }
bool Check_Integration_Convergence(SpinResolved_Data density_k, double eps) { // checks whether the maximum (negative) density is less than eps double max_dens = 0.0; for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { if (density_k.Spin_Up.mat[i, j] + density_k.Spin_Down.mat[i, j] < max_dens) { max_dens = density_k.Spin_Up.mat[i, j] + density_k.Spin_Down.mat[i, j]; } } } return(eps > -1.0 * max_dens); }
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); DoubleHermitianEigDecomp eig_decomp = Solve_Eigenvector_Problem(dft_pot, ref charge_density); int max_wavefunction = (from val in eig_decomp.EigenValues where val < no_kb_T * Physics_Base.kB * temperature select val).ToArray().Length; double[] dens_x = new double[nx]; // and generate a density for the y-direction for (int i = 0; i < nx; i++) { for (int k = 0; k < max_wavefunction; k++) { dens_x[i] += DoubleComplex.Norm(eig_decomp.EigenVector(k)[i]) * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i]) * Get_OneD_DoS(energies[k], no_kb_T); } } // multiply the z-densities by the y-density for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { // 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; } charge_density.Spin_Up.mat[i, j] *= dens_x[i]; charge_density.Spin_Down.mat[i, j] *= dens_x[i]; } } // and multiply the density by -e to get the charge density (as these are electrons) charge_density = unit_charge * charge_density; }
public void Write_Out_Density(SpinResolved_Data h, string outfile) { Band_Data h_spin_summed = h.Spin_Summed_Data; System.IO.StreamWriter sw = new System.IO.StreamWriter(outfile); for (int i = 0; i < h_spin_summed.mat.Cols; i++) { for (int j = 0; j < h_spin_summed.mat.Rows; j++) { sw.Write(h_spin_summed.mat[j, i].ToString() + '\t'); if (j == h_spin_summed.mat.Rows - 1) { sw.WriteLine(); } } } sw.Close(); }
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); }
public Band_Data Get_KS_KE(ILayer[] layers, 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); // create temporary density for input into... DoubleMatrix dens_up = new DoubleMatrix(nx, ny, 0.0); DoubleMatrix dens_down = new DoubleMatrix(nx, ny, 0.0); SpinResolved_Data dens = new SpinResolved_Data(new Band_Data(dens_up), new Band_Data(dens_down)); // calculate eigenvectors DoubleHermitianEigDecomp eig_decomp = Solve_Eigenvector_Problem(dft_pot, ref dens); int max_wavefunction = (from val in eig_decomp.EigenValues where val < no_kb_T * Physics_Base.kB * temperature select val).ToArray().Length; // generate kinetic energy data Band_Data ke = new Band_Data(nx, ny, 0.0); Band_Data dens_tot = dens.Spin_Summed_Data; double ke_prefactor = -0.5 * Physics_Base.hbar * Physics_Base.hbar / mass; for (int i = 1; i < nx - 1; i++) { for (int j = 1; j < ny - 1; j++) { for (int k = 0; k < max_wavefunction; k++) { double dy2psi = (dens_tot.mat[i, j + 1] + dens_tot.mat[i, j - 1] - 2.0 * dens_tot.mat[i, j]) * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i]); double dx2psi = DoubleComplex.Norm(eig_decomp.EigenVector(k)[i + 1] + eig_decomp.EigenVector(k)[i - 1] - 2.0 * eig_decomp.EigenVector(k)[i]) * dens_tot.mat[i, j]; double psi_div2psi = (dens_tot.mat[i, j] * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i])) * (dx2psi + dy2psi); ke.mat[i, j] += ke_prefactor * psi_div2psi * Get_OneD_DoS(eig_decomp.EigenValue(k), no_kb_T); } } } return(ke); }
public override SpinResolved_Data Get_ChargeDensity_Deriv(ILayer[] layers, SpinResolved_Data carrier_density_deriv, SpinResolved_Data dopent_density_deriv, Band_Data chem_pot) { // artificially deepen the copies of spin up and spin down Band_Data tmp_spinup = new Band_Data(carrier_density_deriv.Spin_Up.vec.Length, 0.0); Band_Data tmp_spindown = new Band_Data(carrier_density_deriv.Spin_Up.vec.Length, 0.0); for (int i = 0; i < carrier_density_deriv.Spin_Up.vec.Length; i++) { tmp_spinup.vec[i] = carrier_density_deriv.Spin_Up.vec[i]; tmp_spindown.vec[i] = carrier_density_deriv.Spin_Down.vec[i]; } SpinResolved_Data new_density = new SpinResolved_Data(tmp_spinup, tmp_spindown); // finally, get the charge density and send it to this new array Get_ChargeDensity_Deriv(layers, ref new_density, chem_pot); return(new_density); }
void Initialise_from_1D(Dictionary <string, object> input_dict) { // get data from dictionary SpinResolved_Data tmp_1d_density = (SpinResolved_Data)input_dict["Carrier_Density"]; SpinResolved_Data tmp_1d_dopdens = (SpinResolved_Data)input_dict["Dopent_Density"]; Band_Data tmp_pot_1d = (Band_Data)input_dict["Chemical_Potential"]; // calculate where the bottom of the 2D data is int offset_min = (int)Math.Round((zmin_dens - zmin_pot) / dz_pot); // input data from dictionary into arrays for (int i = 0; i < ny_dens; i++) { for (int j = 0; j < nz_dens; j++) { chem_pot.mat[i, j] = tmp_pot_1d.vec[offset_min + j]; // do not add anything to the density if on the edge of the domain if (i == 0 || i == ny_dens - 1 || j == 0 || j == nz_dens - 1) { continue; } // linearly interpolate with exponential envelope in the transverse direction int j_init = (int)Math.Floor(j * Dz_Dens / Dz_Pot); double y = (i - 0.5 * ny_dens) * dy_dens; //double z = (j - 0.5 * nz_dens) * dz_dens; // carrier data double envelope = 0.0; //Math.Exp(-100.0 * y * y / ((double)(ny_dens * ny_dens) * dy_dens * dy_dens));// *Math.Exp(-10.0 * z * z / ((double)(nz_dens * nz_dens) * dz_dens * dz_dens)); carrier_charge_density.Spin_Up.mat[i, j] = envelope * tmp_1d_density.Spin_Up.vec[offset_min + j_init]; // +(tmp_1d_density.Spin_Up.vec[offset_min + j_init + 1] - tmp_1d_density.Spin_Up.vec[offset_min + j_init]) * Dz_Dens / Dz_Pot; carrier_charge_density.Spin_Down.mat[i, j] = envelope * tmp_1d_density.Spin_Down.vec[offset_min + j_init]; // +(tmp_1d_density.Spin_Down.vec[offset_min + j_init + 1] - tmp_1d_density.Spin_Down.vec[offset_min + j_init]) * Dz_Dens / Dz_Pot; // dopent data dopent_charge_density.Spin_Up.mat[i, j] = tmp_1d_dopdens.Spin_Up.vec[offset_min + j_init] + (tmp_1d_dopdens.Spin_Up.vec[offset_min + j_init + 1] - tmp_1d_dopdens.Spin_Up.vec[offset_min + j_init]) * Dz_Dens / Dz_Pot; dopent_charge_density.Spin_Down.mat[i, j] = tmp_1d_dopdens.Spin_Down.vec[offset_min + j_init] + (tmp_1d_dopdens.Spin_Down.vec[offset_min + j_init + 1] - tmp_1d_dopdens.Spin_Down.vec[offset_min + j_init]) * Dz_Dens / Dz_Pot; } } boundary_conditions.Add("surface", (double)input_dict["surface_charge"]); }
public void Write_Out_Density(SpinResolved_Data h, string outfile) { Band_Data h_spin_summed = h.Spin_Summed_Data; System.IO.StreamWriter sw = new System.IO.StreamWriter(outfile); for (int k = 0; k < h_spin_summed.vol.Length; k++) { for (int i = 0; i < h_spin_summed.vol[k].Cols; i++) { for (int j = 0; j < h_spin_summed.vol[k].Rows; j++) { sw.Write(h_spin_summed.vol[k][i, j].ToString() + '\t'); if (i == h_spin_summed.vol[k].Cols - 1 || j == h_spin_summed.vol[k].Rows - 1) { sw.WriteLine(); } } } } sw.Close(); }
public override SpinResolved_Data Get_ChargeDensity(ILayer[] layers, SpinResolved_Data carrier_charge_density, SpinResolved_Data dopent_charge_density, Band_Data chem_pot) { // artificially deepen the copies of spin up and spin down Band_Data tmp_spinup = new Band_Data(new DoubleMatrix(carrier_charge_density.Spin_Up.mat.Rows, carrier_charge_density.Spin_Up.mat.Cols)); Band_Data tmp_spindown = new Band_Data(new DoubleMatrix(carrier_charge_density.Spin_Down.mat.Rows, carrier_charge_density.Spin_Down.mat.Cols)); for (int i = 0; i < carrier_charge_density.Spin_Up.mat.Rows; i++) { for (int j = 0; j < carrier_charge_density.Spin_Up.mat.Cols; j++) { tmp_spinup.mat[i, j] = carrier_charge_density.Spin_Up.mat[i, j]; tmp_spindown.mat[i, j] = carrier_charge_density.Spin_Down.mat[i, j]; } } SpinResolved_Data new_density = new SpinResolved_Data(tmp_spinup, tmp_spindown); // finally, get the charge density and send it to this new array Get_ChargeDensity(layers, ref new_density, chem_pot); return(new_density + dopent_charge_density); }
public SpinResolved_Data Get_ChargeDensity_Deriv(ILayer[] layers, SpinResolved_Data carrier_density_deriv, SpinResolved_Data dopent_density_deriv, Band_Data chem_pot) { SpinResolved_Data electron_density_deriv = carrier_density_deriv.DeepenThisCopy(); SpinResolved_Data hole_density_deriv = carrier_density_deriv.DeepenThisCopy(); // remove the electron density from the hole density SpinResolved_Data and vice versa for (int i = 0; i < electron_density_deriv.Spin_Summed_Data.Length; i++) { if (electron_density_deriv.Spin_Summed_Data.vec[i] > 0.0) { electron_density_deriv.Spin_Up[i] = 0.0; electron_density_deriv.Spin_Down[i] = 0.0; } if (hole_density_deriv.Spin_Summed_Data.vec[i] < 0.0) { hole_density_deriv.Spin_Up[i] = 0.0; hole_density_deriv.Spin_Down[i] = 0.0; } } return(electron_dens_calc.Get_ChargeDensity_Deriv(layers, electron_density_deriv, dopent_density_deriv, chem_pot) + hole_dens_calc.Get_ChargeDensity_Deriv(layers, hole_density_deriv, dopent_density_deriv, chem_pot)); }
public override void Get_ChargeDensity(ILayer[] layers, ref SpinResolved_Data carrier_density, ref SpinResolved_Data dopent_density, Band_Data chem_pot) { Band_Data car_dens_spin_summed = carrier_density.Spin_Summed_Data; Band_Data dop_dens_spin_summed = dopent_density.Spin_Summed_Data; for (int i = 0; i < nz; i++) { double z = dz * i + zmin; double local_dopent_density; double local_carrier_density = car_dens_spin_summed.vec[i]; // get the relevant layer and if it's frozen out, don't recalculate the charge ILayer current_Layer = Solver_Bases.Geometry.Geom_Tool.GetLayer(layers, z); // calculate the density at the given point ZeroD_Density charge_calc = new ZeroD_Density(current_Layer, temperature); if (!current_Layer.Dopents_Frozen_Out(temperature)) { local_dopent_density = charge_calc.Get_DopentDensity(chem_pot.vec[i]); local_carrier_density = charge_calc.Get_CarrierDensity(chem_pot.vec[i]); } else { // if the density is frozen out, on the first step, this will add the carrier density to the dopent density // to give a total, frozen-out charge. After that, the local carrier density is set to zero and so this value // should not change local_dopent_density = dop_dens_spin_summed.vec[i]; local_carrier_density = charge_calc.Get_CarrierDensity(chem_pot.vec[i]); } // as there is no spin dependence in this problem yet, just divide the charge into spin-up and spin-down components equally dopent_density.Spin_Down.vec[i] = 0.5 * local_dopent_density; dopent_density.Spin_Up.vec[i] = 0.5 * local_dopent_density; carrier_density.Spin_Down.vec[i] = 0.5 * local_carrier_density; carrier_density.Spin_Up.vec[i] = 0.5 * local_carrier_density; } }
void Initialise_from_1D(Dictionary <string, object> input_dict) { // get data from dictionary SpinResolved_Data tmp_1d_density = (SpinResolved_Data)input_dict["Carrier_Density"]; SpinResolved_Data tmp_1d_dopdens = (SpinResolved_Data)input_dict["Dopent_Density"]; Band_Data tmp_pot_1d = (Band_Data)input_dict["Chemical_Potential"]; // calculate where the bottom of the 3D data is int offset_min = (int)Math.Round((zmin_dens - zmin_pot) / dz_pot); // this is the charge density modulation in the (x, y) plane so, initially, just put it in uniformly for (int k = 0; k < nz_dens; k++) { for (int i = 0; i < nx_dens; i++) { for (int j = 0; j < ny_dens; j++) { chem_pot.vol[k][i, j] = tmp_pot_1d.vec[offset_min + j]; // do not add anything to the density at the top or bottom of the domain if (k == 0 || k == nz_dens - 1) { continue; } // carrier data carrier_charge_density.Spin_Up.vol[k][i, j] = tmp_1d_density.Spin_Up.vec[k + offset_min]; carrier_charge_density.Spin_Down.vol[k][i, j] = tmp_1d_density.Spin_Down.vec[k + offset_min]; // dopent data dopent_charge_density.Spin_Up.vol[k][i, j] = tmp_1d_dopdens.Spin_Up.vec[k + offset_min]; dopent_charge_density.Spin_Down.vol[k][i, j] = tmp_1d_dopdens.Spin_Down.vec[k + offset_min]; } } } boundary_conditions.Add("surface", (double)input_dict["surface_charge"]); }
public override SpinResolved_Data Get_ChargeDensity_Deriv(ILayer[] layers, SpinResolved_Data carrier_density_deriv, SpinResolved_Data dopent_density_deriv, Band_Data chem_pot) { for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { // leave the edges zeroed if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) { continue; } double x = dx * i + xmin; double y = dy * j + ymin; // get the relevant layer and if it's frozen out, don't recalculate the dopent charge ILayer current_Layer = Solver_Bases.Geometry.Geom_Tool.GetLayer(layers, plane, x, y, pos_z); ZeroD_Density charge_calc = new ZeroD_Density(current_Layer, temperature); if (!current_Layer.Dopents_Frozen_Out(temperature)) { double local_dopent_density_deriv = charge_calc.Get_DopentDensityDeriv(chem_pot.mat[i, j]); dopent_density_deriv.Spin_Up.mat[i, j] = 0.5 * local_dopent_density_deriv; dopent_density_deriv.Spin_Down.mat[i, j] = 0.5 * local_dopent_density_deriv; } else { dopent_density_deriv.Spin_Up.mat[i, j] = 0.0; dopent_density_deriv.Spin_Down.mat[i, j] = 0.0; } carrier_density_deriv.Spin_Up.mat[i, j] = 0.5 * charge_calc.Get_CarrierDensityDeriv(chem_pot.mat[i, j]); carrier_density_deriv.Spin_Down.mat[i, j] = 0.5 * charge_calc.Get_CarrierDensityDeriv(chem_pot.mat[i, j]); } } return(carrier_density_deriv + dopent_density_deriv); }
public override SpinResolved_Data Get_ChargeDensity_Deriv(ILayer[] layers, SpinResolved_Data carrier_density_deriv, SpinResolved_Data dopent_density_deriv, Band_Data chem_pot) { for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { for (int k = 0; k < nz; k++) { double x = dx * i + xmin; double y = dy * j + ymin; double z = dz * k + zmin; // get the relevant layer and if it's frozen out, don't recalculate the dopent charge ILayer current_Layer = Solver_Bases.Geometry.Geom_Tool.GetLayer(layers, x, y, z); ZeroD_Density charge_calc = new ZeroD_Density(current_Layer, temperature); if (!current_Layer.Dopents_Frozen_Out(temperature)) { double local_dopent_density_deriv = charge_calc.Get_DopentDensityDeriv(chem_pot.vol[k][i, j]); dopent_density_deriv.Spin_Up.vol[k][i, j] = 0.5 * local_dopent_density_deriv; dopent_density_deriv.Spin_Down.vol[k][i, j] = 0.5 * local_dopent_density_deriv; } else { dopent_density_deriv.Spin_Up.vol[k][i, j] = 0.0; dopent_density_deriv.Spin_Down.vol[k][i, j] = 0.0; } carrier_density_deriv.Spin_Up.vol[k][i, j] = 0.5 * charge_calc.Get_CarrierDensityDeriv(chem_pot.vol[k][i, j]); carrier_density_deriv.Spin_Down.vol[k][i, j] = 0.5 * charge_calc.Get_CarrierDensityDeriv(chem_pot.vol[k][i, j]); } } } return(carrier_density_deriv + dopent_density_deriv); }
public SpinResolved_Data Get_ChargeDensity(ILayer[] layers, SpinResolved_Data carrier_charge_density, SpinResolved_Data dopent_charge_density, Band_Data chem_pot) { SpinResolved_Data electron_density = carrier_charge_density.DeepenThisCopy(); SpinResolved_Data hole_density = carrier_charge_density.DeepenThisCopy(); // remove the electron density from the hole density SpinResolved_Data and vice versa for (int i = 0; i < electron_density.Spin_Summed_Data.Length; i++) { if (electron_density.Spin_Summed_Data.vec[i] > 0.0) { electron_density.Spin_Up[i] = 0.0; electron_density.Spin_Down[i] = 0.0; } if (hole_density.Spin_Summed_Data.vec[i] < 0.0) { hole_density.Spin_Up[i] = 0.0; hole_density.Spin_Down[i] = 0.0; } } // must remove one lot of dopents due to double counting return(electron_dens_calc.Get_ChargeDensity(layers, electron_density, dopent_charge_density, chem_pot) + hole_dens_calc.Get_ChargeDensity(layers, hole_density, dopent_charge_density, chem_pot) - dopent_charge_density); }
public override SpinResolved_Data Get_ChargeDensity_Deriv(ILayer[] layers, SpinResolved_Data carrier_density_deriv, SpinResolved_Data dopent_density_deriv, Band_Data chem_pot) { // artificially deepen the copies of spin up and spin down Band_Data tmp_spinup = new Band_Data(carrier_density_deriv.Spin_Up.vol[0].Rows, carrier_density_deriv.Spin_Up.vol[0].Cols, carrier_density_deriv.Spin_Up.vol.Length, 0.0); Band_Data tmp_spindown = new Band_Data(carrier_density_deriv.Spin_Down.vol[0].Rows, carrier_density_deriv.Spin_Down.vol[0].Cols, carrier_density_deriv.Spin_Down.vol.Length, 0.0); for (int k = 0; k < carrier_density_deriv.Spin_Up.vol.Length; k++) { // fill with data for (int i = 0; i < carrier_density_deriv.Spin_Up.vol[0].Rows; i++) { for (int j = 0; j < carrier_density_deriv.Spin_Up.vol[0].Cols; j++) { tmp_spinup.vol[k][i, j] = carrier_density_deriv.Spin_Up.vol[k][i, j]; tmp_spindown.vol[k][i, j] = carrier_density_deriv.Spin_Down.vol[k][i, j]; } } } SpinResolved_Data new_density = new SpinResolved_Data(tmp_spinup, tmp_spindown); // finally, get the charge density and send it to this new array Get_ChargeDensity_Deriv(layers, ref new_density, chem_pot); return(new_density + dopent_density_deriv); }
public abstract void Get_ChargeDensity_Deriv(ILayer[] layers, ref SpinResolved_Data density, Band_Data chem_pot);
/// <summary> /// sets the edges to be the same as their nearest neighbour. /// traditionally, would expect the edges to have zero charge but this will be different for simulations in the plane of the 2deg /// </summary> /// <param name="carrier_density"></param> void Set_Edges(SpinResolved_Data carrier_density) { Set_Edges(carrier_density.Spin_Up); Set_Edges(carrier_density.Spin_Down); }
protected override bool Run_Iteration_Routine(IDensity_Solve dens_solv, IPoisson_Solve pois_solv, double tol, int max_iterations) { dens_solv.Reset_DFT_Potential(); dens_solv.Update_DFT_Potential(carrier_charge_density); int count = 0; bool converged = false; if (!no_dft) { dens_solv.DFT_Mixing_Parameter = 0.1; } dens_diff_lim = 0.12; while (!converged) { Stopwatch stpwch = new Stopwatch(); stpwch.Start(); // save old density data Band_Data dens_old = carrier_charge_density.Spin_Summed_Data.DeepenThisCopy(); // Get charge rho(phi) (not dopents as these are included as a flexPDE input) dens_solv.Get_ChargeDensity(layers, ref carrier_charge_density, ref dopent_charge_density, chem_pot); Set_Edges(carrier_charge_density); // Generate an approximate charge-dependent part of the Jacobian, g'(phi) = - d(eps * d( )) - rho'(phi) using the Thomas-Fermi semi-classical method SpinResolved_Data rho_prime = dens_solv.Get_ChargeDensity_Deriv(layers, carrier_charge_density_deriv, dopent_charge_density_deriv, chem_pot); Set_Edges(rho_prime); // Solve stepping equation to find raw Newton iteration step, g'(phi) x = - g(phi) gphi = -1.0 * chem_pot.Laplacian / Physics_Base.q_e - carrier_charge_density.Spin_Summed_Data - dopent_charge_density.Spin_Summed_Data; Set_Edges(gphi); x = pois_solv.Calculate_Newton_Step(rho_prime, gphi, carrier_charge_density, dens_solv.DFT_Potential, dens_solv.Get_XC_Potential(carrier_charge_density)); // chem_pot = pois_solv.Chemical_Potential; // Calculate optimal damping parameter, t, (but damped damping....) if (t == 0.0) { t = t_min; } t = t_damp * Calculate_optimal_t(t / t_damp, chem_pot / Physics_Base.q_e, x, carrier_charge_density, dopent_charge_density, pois_solv, dens_solv, t_min); if (t < 0.0) { Console.WriteLine("Iterator has stalled, setting t = 0"); t = 0.0; } // and check convergence of density Band_Data dens_diff = carrier_charge_density.Spin_Summed_Data - dens_old; Band_Data car_dens_spin_summed = carrier_charge_density.Spin_Summed_Data; double carrier_dens_abs_max = Math.Max(Math.Abs(car_dens_spin_summed.Min()), Math.Abs(car_dens_spin_summed.Max())); // using the relative absolute density difference for (int i = 0; i < dens_diff.Length; i++) { // only calculate density difference for densities more than 1% of the maximum value if (Math.Abs(car_dens_spin_summed[i]) > 0.01 * carrier_dens_abs_max) { dens_diff[i] = Math.Abs(dens_diff[i] / car_dens_spin_summed[i]); } else { dens_diff[i] = 0.0; } } // only renew DFT potential when the difference in density has converged and the iterator has done at least 3 iterations if (dens_diff.Max() < dens_diff_lim && t > 10.0 * t_min && count > 3) { // and set the DFT potential if (dens_solv.DFT_Mixing_Parameter != 0.0) { dens_solv.Print_DFT_diff(carrier_charge_density); } dens_solv.Update_DFT_Potential(carrier_charge_density); // also... if the difference in the old and new dft potentials is greater than for the previous V_xc update, reduce the dft mixing parameter double current_vxc_diff = Math.Max(dens_solv.DFT_diff(carrier_charge_density).Max(), (-1.0 * dens_solv.DFT_diff(carrier_charge_density).Min())); if (current_vxc_diff > max_vxc_diff && dens_diff_lim / 2.0 > min_dens_diff || no_dft) { dens_diff_lim /= 2.0; Console.WriteLine("Minimum percentage density difference reduced to " + dens_diff_lim.ToString()); } max_vxc_diff = current_vxc_diff; // solution is converged if the density accuracy is better than half the minimum possible value for changing the dft potential if (dens_diff.Max() < min_dens_diff / 2.0 && current_vxc_diff < min_vxc_diff && Physics_Base.q_e * x.InfinityNorm() < tol && t != t_min) { converged = true; } } // update t for Poisson solver pois_solv.T = t; chem_pot = chem_pot + t * Physics_Base.q_e * x; base.Checkpoint(); if (count == 0) { pot_init = Physics_Base.q_e * x.InfinityNorm(); } stpwch.Stop(); Console.WriteLine("Iter = " + count.ToString() + "\tDens = " + dens_diff.Max().ToString("F4") + "\tPot = " + (Physics_Base.q_e * x.InfinityNorm()).ToString("F6") + "\tt = " + t.ToString("F5") + "\ttime = " + stpwch.Elapsed.TotalMinutes.ToString("F")); count++; // File.Copy("split_gate.pg6", "split_gate_" + count.ToString("000") + ".pg6"); // reset the potential if the added potential t * x is too small if (converged || count > max_iterations) { Console.WriteLine("Maximum potential change at end of iteration was " + (t * Physics_Base.q_e * x.InfinityNorm()).ToString()); break; } } Console.WriteLine("Iteration complete"); return(converged); }
public Band_Data Get_XC_Potential_Deriv(SpinResolved_Data charge_density) { throw new NotImplementedException(); }
public Band_Data DFT_diff(SpinResolved_Data car_dens) { throw new NotImplementedException(); }