public override void Initialise(Dictionary <string, object> input_dict) { Console.WriteLine("Initialising Experiment"); // simulation domain inputs Get_From_Dictionary <double>(input_dict, "dx", ref dx_dens); dx_pot = dx_dens; Get_From_Dictionary <double>(input_dict, "dy", ref dy_dens); dy_pot = dy_dens; Get_From_Dictionary <double>(input_dict, "dz", ref dz_dens); dz_pot = dz_dens; Get_From_Dictionary(input_dict, "nx", ref nx_dens); nx_pot = nx_dens; Get_From_Dictionary(input_dict, "ny", ref ny_dens); ny_pot = ny_dens; Get_From_Dictionary(input_dict, "nz", ref nz_dens); nz_pot = nz_dens; // physics parameters are done by the base method (the base will also try to get specific parameters detailed in the input files) base.Initialise(input_dict); // and initialise the data classes for density, its derivatives and the chemical potential Initialise_DataClasses(input_dict); // initialise the density solver dens_solv = Get_Density_Solver(input_dict); // initialise potential solver if (using_flexPDE) { pois_solv = new ThreeD_PoissonSolver(this, using_flexPDE, input_dict); } else if (using_dealii) { pois_solv = new ThreeD_dealII_Solver(this, using_dealii, input_dict); } else { throw new NotImplementedException("Error - Must use either FlexPDE or deal.II for 2D potential solver!"); } device_dimensions.Add("interface_depth", Layers[1].Zmax); pois_solv.Initiate_Poisson_Solver(device_dimensions, boundary_conditions); Console.WriteLine("Experimental parameters initialised"); }
protected override bool Run_Iteration_Routine(IDensity_Solve dens_solv, IPoisson_Solve pois_solv, double tol, int max_iterations) { // calculate initial potential with the given charge distribution Console.WriteLine("Calculating initial potential grid"); pois_solv.Initiate_Poisson_Solver(device_dimensions, boundary_conditions); if (chem_pot == null) chem_pot = Physics_Base.q_e * pois_solv.Get_Potential(carrier_charge_density.Spin_Summed_Data + dopent_charge_density.Spin_Summed_Data); Console.WriteLine("Initial grid complete"); // dens_solv.Set_DFT_Potential(carrier_charge_density); // dens_solv.Get_ChargeDensity(layers, ref carrier_charge_density, ref dopent_charge_density, chem_pot); // dens_solv.Set_DFT_Potential(carrier_charge_density); dens_solv.Reset_DFT_Potential(); dens_solv.Update_DFT_Potential(carrier_charge_density); int count = 0; t = 1.0; bool converged = false; while (!converged) { Band_Data dens_old = carrier_charge_density.Spin_Summed_Data + dopent_charge_density.Spin_Summed_Data; // Get charge rho(phi) dens_solv.Get_ChargeDensity(layers, ref carrier_charge_density, ref dopent_charge_density, chem_pot); // Generate charge-dependent part of the Jacobian, g'(phi) = -d(eps * d( )) - rho'(phi) SpinResolved_Data rho_prime = dens_solv.Get_ChargeDensity_Deriv(layers, carrier_charge_density_deriv, dopent_charge_density_deriv, chem_pot); // Solve stepping equation to find raw Newton iteration step, g'(phi) x = - g(phi) // Calculate Laplacian operating on the given band energy, d(eps * d(phi)) gphi = -1.0 * chem_pot.Laplacian / Physics_Base.q_e - carrier_charge_density.Spin_Summed_Data - dopent_charge_density.Spin_Summed_Data; gphi[0] = 0.0; gphi[gphi.Length - 1] = 0.0; x = pois_solv.Calculate_Newton_Step(rho_prime, gphi); // Calculate optimal damping parameter, t 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); // Check convergence Band_Data dens_spin_summed = carrier_charge_density.Spin_Summed_Data + dopent_charge_density.Spin_Summed_Data; Band_Data dens_diff = dens_spin_summed - dens_old; double dens_abs_max = Math.Max(Math.Abs(dens_spin_summed.Max()), Math.Abs(dens_spin_summed.Min())); 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(dens_spin_summed[i]) > 0.01 * dens_abs_max) dens_diff[i] = Math.Abs(dens_diff[i] / dens_spin_summed[i]); else dens_diff[i] = 0.0; // Update the DFT potential dens_solv.Update_DFT_Potential(carrier_charge_density); double[] diff = new double[Nz_Pot]; for (int j = 0; j < nz_pot; j++) diff[j] = Math.Abs(gphi.vec[j]); double convergence = diff.Sum(); if (Physics_Base.q_e * x.InfinityNorm() < tol) converged = true; // update band energy phi_new = phi_old + t * x Console.WriteLine(Generate_Output_String(count, x, dens_diff)); chem_pot = chem_pot + t * Physics_Base.q_e * x; count++; base.Checkpoint(); // 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() + "meV"); break; } } Console.WriteLine("Iteration complete"); return converged; }
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); }
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 override void Initialise(Dictionary<string, object> input_dict) { Console.WriteLine("Initialising Experiment"); // simulation domain inputs Get_From_Dictionary<double>(input_dict, "dx", ref dx_dens); dx_pot = dx_dens; Get_From_Dictionary<double>(input_dict, "dy", ref dy_dens); dy_pot = dy_dens; Get_From_Dictionary<double>(input_dict, "dz", ref dz_dens); dz_pot = dz_dens; Get_From_Dictionary(input_dict, "nx", ref nx_dens); nx_pot = nx_dens; Get_From_Dictionary(input_dict, "ny", ref ny_dens); ny_pot = ny_dens; Get_From_Dictionary(input_dict, "nz", ref nz_dens); nz_pot = nz_dens; // physics parameters are done by the base method (the base will also try to get specific parameters detailed in the input files) base.Initialise(input_dict); // and initialise the data classes for density, its derivatives and the chemical potential Initialise_DataClasses(input_dict); // initialise the density solver dens_solv = Get_Density_Solver(input_dict); // initialise potential solver if (using_flexPDE) pois_solv = new ThreeD_PoissonSolver(this, using_flexPDE, input_dict); else if (using_dealii) pois_solv = new ThreeD_dealII_Solver(this, using_dealii, input_dict); else throw new NotImplementedException("Error - Must use either FlexPDE or deal.II for 2D potential solver!"); device_dimensions.Add("interface_depth", Layers[1].Zmax); pois_solv.Initiate_Poisson_Solver(device_dimensions, boundary_conditions); Console.WriteLine("Experimental parameters initialised"); }
void Print_VP(Band_Data band_energy, Band_Data x, SpinResolved_Data car_dens, SpinResolved_Data dop_dens, IPoisson_Solve pois_solv, IDensity_Solve dens_solv) { StreamWriter sw = new StreamWriter("vp"); int count_max = 100; double dt = 0.01; for (int i = 0; i < count_max; i++) { sw.WriteLine(calc_vp(i * dt, band_energy, x, car_dens, dop_dens, pois_solv, dens_solv).ToString()); } sw.Close(); }
protected override bool Run_Iteration_Routine(IDensity_Solve dens_solv, IPoisson_Solve pois_solv, double tol, int max_iterations) { // calculate initial potential with the given charge distribution Console.WriteLine("Calculating initial potential grid"); pois_solv.Initiate_Poisson_Solver(device_dimensions, boundary_conditions); if (chem_pot == null) { chem_pot = Physics_Base.q_e * pois_solv.Get_Potential(carrier_charge_density.Spin_Summed_Data + dopent_charge_density.Spin_Summed_Data); } Console.WriteLine("Initial grid complete"); // dens_solv.Set_DFT_Potential(carrier_charge_density); // dens_solv.Get_ChargeDensity(layers, ref carrier_charge_density, ref dopent_charge_density, chem_pot); // dens_solv.Set_DFT_Potential(carrier_charge_density); dens_solv.Reset_DFT_Potential(); dens_solv.Update_DFT_Potential(carrier_charge_density); int count = 0; t = 1.0; bool converged = false; while (!converged) { Band_Data dens_old = carrier_charge_density.Spin_Summed_Data + dopent_charge_density.Spin_Summed_Data; // Get charge rho(phi) dens_solv.Get_ChargeDensity(layers, ref carrier_charge_density, ref dopent_charge_density, chem_pot); // Generate charge-dependent part of the Jacobian, g'(phi) = -d(eps * d( )) - rho'(phi) SpinResolved_Data rho_prime = dens_solv.Get_ChargeDensity_Deriv(layers, carrier_charge_density_deriv, dopent_charge_density_deriv, chem_pot); // Solve stepping equation to find raw Newton iteration step, g'(phi) x = - g(phi) // Calculate Laplacian operating on the given band energy, d(eps * d(phi)) gphi = -1.0 * chem_pot.Laplacian / Physics_Base.q_e - carrier_charge_density.Spin_Summed_Data - dopent_charge_density.Spin_Summed_Data; gphi[0] = 0.0; gphi[gphi.Length - 1] = 0.0; x = pois_solv.Calculate_Newton_Step(rho_prime, gphi); // Calculate optimal damping parameter, t 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); // Check convergence Band_Data dens_spin_summed = carrier_charge_density.Spin_Summed_Data + dopent_charge_density.Spin_Summed_Data; Band_Data dens_diff = dens_spin_summed - dens_old; double dens_abs_max = Math.Max(Math.Abs(dens_spin_summed.Max()), Math.Abs(dens_spin_summed.Min())); 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(dens_spin_summed[i]) > 0.01 * dens_abs_max) { dens_diff[i] = Math.Abs(dens_diff[i] / dens_spin_summed[i]); } else { dens_diff[i] = 0.0; } } // Update the DFT potential dens_solv.Update_DFT_Potential(carrier_charge_density); double[] diff = new double[Nz_Pot]; for (int j = 0; j < nz_pot; j++) { diff[j] = Math.Abs(gphi.vec[j]); } double convergence = diff.Sum(); if (Physics_Base.q_e * x.InfinityNorm() < tol) { converged = true; } // update band energy phi_new = phi_old + t * x Console.WriteLine(Generate_Output_String(count, x, dens_diff)); chem_pot = chem_pot + t * Physics_Base.q_e * x; count++; base.Checkpoint(); // 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() + "meV"); break; } } Console.WriteLine("Iteration complete"); return(converged); }
/// <summary> /// calculates an optimal t based on bisection /// </summary> /// <param name="t"></param> /// <param name="phi"></param> /// <param name="x"></param> /// <param name="car_dens"></param> /// <param name="dop_dens"></param> /// <param name="pois_solv"></param> /// <param name="dens_solv"></param> /// <returns></returns> protected double Calculate_optimal_t(double t, Band_Data phi, Band_Data x, SpinResolved_Data car_dens, SpinResolved_Data dop_dens, IPoisson_Solve pois_solv, IDensity_Solve dens_solv, double minval) { double maxval = 3.0; SpinResolved_Data car_dens_copy = car_dens.DeepenThisCopy(); SpinResolved_Data dop_dens_copy = dop_dens.DeepenThisCopy(); double vpa = calc_vp(t, phi, x, car_dens_copy, dop_dens_copy, pois_solv, dens_solv); double vpb = calc_vp(div_fact * t, phi, x, car_dens_copy, dop_dens_copy, pois_solv, dens_solv); double t_orig = t; // work out whether this is going in the right direction (assuming vp is monotonic) if (Math.Abs(vpb) < Math.Abs(vpa)) { // if 0.5 * t was going downhill, first, halve t seeing as you've already done this step t = div_fact * t; // then halve the damping parameter and check whether you've found a root yet while (Math.Sign(vpb) == Math.Sign(vpa)) { t = div_fact * t; if (t < minval) { if (Math.Sign(calc_vp(1.0, phi, x, car_dens_copy, dop_dens_copy, pois_solv, dens_solv)) == Math.Sign(vpb)) { return(0.5); } //return 1.0 / div_fact; else { return(Calculate_optimal_t(1.0, phi, x, car_dens_copy, dop_dens_copy, pois_solv, dens_solv, minval)); } } vpa = vpb; vpb = calc_vp(t, phi, x, car_dens_copy, dop_dens_copy, pois_solv, dens_solv); } //return 0.5 * (1.0 + (1.0 / div_fact)) * t; return(t - t * (1.0 - 1.0 / div_fact) * vpb / (vpa - vpb)); } else { // if 0.5 * t was going uphill, then we need to be doubling t and looking for the root while (Math.Sign(vpb) == Math.Sign(vpa)) { t = (1.0 / div_fact) * t; if (t > maxval) { return(maxval); } vpa = vpb; vpb = calc_vp(t, phi, x, car_dens_copy, dop_dens_copy, pois_solv, dens_solv); } //return 0.5 * (1.0 + div_fact) * t; return(t - t * (1.0 - div_fact) * vpb / (vpb - vpa)); } }
protected virtual double calc_vp(double t, Band_Data phi, Band_Data x, SpinResolved_Data car_dens, SpinResolved_Data dop_dens, IPoisson_Solve pois_solv, IDensity_Solve dens_solv) { double vp; SpinResolved_Data tmp_dens = dens_solv.Get_ChargeDensity(layers, car_dens, dop_dens, Physics_Base.q_e * (phi + t * x)); Band_Data V_Prime = -1.0 * (phi.Laplacian + t * x.Laplacian) - tmp_dens.Spin_Summed_Data; vp = 0.0; for (int i = 0; i < x.Length; i++) { vp += V_Prime[i] * x[i]; } // multiply by volume element (this works in all dimensions as default dx, dy, dz are 1.0) return(vp * dx_dens * dy_dens * dz_dens); }
protected abstract bool Run_Iteration_Routine(IDensity_Solve dens_solv, IPoisson_Solve pois_solv, double tol, int max_iterations);
protected bool Run_Iteration_Routine(IDensity_Solve dens_solv, IPoisson_Solve pois_solv, double tol) { return(Run_Iteration_Routine(dens_solv, pois_solv, tol, int.MaxValue)); }
protected override bool Run_Iteration_Routine(IDensity_Solve dens_solv, IPoisson_Solve pois_solv, double tol, int max_iterations) { // calculate initial potential with the given charge distribution // Console.WriteLine("Calculating initial potential grid"); // pois_solv.Initiate_Poisson_Solver(device_dimensions, boundary_conditions); // chem_pot = pois_solv.Get_Chemical_Potential(carrier_density.Spin_Summed_Data); // Console.WriteLine("Initial grid complete"); //dens_solv.Set_DFT_Potential(carrier_charge_density); //if (!no_dft) //{ // dens_solv.DFT_Mixing_Parameter = 0.3; //dens_solv.Get_ChargeDensity(layers, ref carrier_charge_density, ref dopent_charge_density, chem_pot); //} dens_solv.Reset_DFT_Potential(); dens_solv.Update_DFT_Potential(carrier_charge_density); int count = 0; bool converged = false; double dens_diff_lim = 0.1; // the maximum percentage change in the density required for update of V_xc double max_vxc_diff = double.MaxValue; // maximum difference for dft potential... if this increases, the dft mixing parameter is reduced 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); // 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); // 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; 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 (count % 5 == 0 && t == t_damp * t_min) // { // t_min *= 2.0; // Console.WriteLine("Iterator has stalled, doubling t_min to " + t_min.ToString()); // } // and check convergence of density Band_Data car_dens_spin_summed = carrier_charge_density.Spin_Summed_Data; Band_Data dens_diff = car_dens_spin_summed - dens_old; 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; } } //if (Math.Max(t * x.Max(), (-t * x).Max()) < pot_lim && t > 10.0 * t_min) // 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) { // once dft potential is starting to be mixed in, set the maximum count to lots // max_count = 1000; // and set the DFT potential 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_dens_diff > max_diff && dens_solv.DFT_Mixing_Parameter / 3.0 > min_alpha) // { // dens_solv.DFT_Mixing_Parameter /= 3.0; // alpha is only incremented if it will be above the value of min_alpha // dens_diff_lim /= 3.0; // Console.WriteLine("DFT mixing parameter reduced to " + dens_solv.DFT_Mixing_Parameter.ToString()); // } if (current_vxc_diff > max_vxc_diff && !no_dft) { dens_diff_lim /= 2.0; //dens_solv.Print_DFT_diff(carrier_charge_density); Console.WriteLine("Minimum percentage density difference reduced to " + dens_diff_lim.ToString()); } max_vxc_diff = current_vxc_diff; // if (alpha_dft <= 0.1) // { // alpha_dft += 0.01; // Console.WriteLine("Setting DFT mixing parameter to " + alpha_dft.ToString()); // dens_solv.Set_DFT_Mixing_Parameter(alpha_dft); // } // if (Math.Max(dens_solv.DFT_diff(carrier_density).Max(), (-1.0 * dens_solv.DFT_diff(carrier_density).Min())) < pot_lim) // converged = true; // solution is converged if the density accuracy is better than half the minimum possible value for changing the dft potential // also, check that the maximum change in the absolute value of the potential is less than a tolerance (default is 0.1meV) if (dens_solv.DFT_diff(carrier_charge_density).InfinityNorm() < tol && Physics_Base.q_e * x.InfinityNorm() < tol) { converged = true; } } /* * // Recalculate the charge density but for the updated potential rho(phi + t * x) * bool edges_fine = false; * while (!edges_fine) * { * edges_fine = true; * SpinResolved_Data tmp_dens = dens_solv.Get_ChargeDensity(layers, carrier_density, dopent_density, chem_pot + t * x); * for (int i = 1; i < ny_dens - 1; i++) * for (int j = 1; j < nz_dens - 1; j++) * //if (i == 1 || j == 1 || i == ny_dens - 2 || j == nz_dens - 2) * if (i == 1 || i == ny_dens - 2) * if (Math.Abs(tmp_dens.Spin_Summed_Data.mat[i, j]) > edge_min_charge) * { * if (x.mat.Max() > 0) * t = 0.5 * t; * else * t = 2.0 * t; * * if (t > t_min) * { * edges_fine = false; * goto end; * } * else * { * // although the edges are not fine at this point, we don't want the code to decrease t any further so we * // break the loop by setting... * edges_fine = true; * goto end; * } * } * * if (!edges_fine) * throw new Exception("Error - Unable to reduce density to zero at edge of density domain.\nSimulation aborted"); * * end: * //if (t < t_damp * t_min) * //{ * // Console.WriteLine("Unable to reduce density to zero at edge of density domain\nRecalculating potential"); * // pois_solv.Set_Boundary_Conditions(top_V, split_V, split_width, bottom_V, surface_charge); * // chem_pot = pois_solv.Get_Chemical_Potential(carrier_density.Spin_Summed_Data); * // dens_solv.Get_ChargeDensity(layers, ref carrier_density, ref dopent_density, chem_pot); * // Console.WriteLine("Potential recalculated"); * // edges_fine = true; * //} * //else * continue; * }*/ // update band energy phi_new = phi_old + t * x pois_solv.T = t; chem_pot = chem_pot + t * Physics_Base.q_e * x; //// and set the DFT potential //if (count % 10 == 0) // dens_solv.Print_DFT_diff(carrier_density); //dens_solv.Set_DFT_Potential(carrier_density); base.Checkpoint(); if (count == 0) { pot_init = Physics_Base.q_e * x.InfinityNorm(); } stpwch.Stop(); Console.WriteLine(Generate_Output_String(count, x, dens_diff) + "\ttime = " + stpwch.Elapsed.TotalMinutes.ToString("F")); if (dens_solv.DFT_Mixing_Parameter != 0.0 && dens_diff.Max() < dens_diff_lim && count > 3) { dens_solv.Print_DFT_diff(carrier_charge_density); } count++; // 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); }
protected override bool Run_Iteration_Routine(IDensity_Solve dens_solv, IPoisson_Solve pois_solv, double tol, int max_iterations) { throw new NotImplementedException(); }