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) { // 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); }