phystota

v3.2 - ionization added.

Apr 24th, 2025
52
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 53.20 KB | None | 0 0
  1. #include <iostream>
  2. #include <random>
  3. #include <fstream>
  4. #include <assert.h>
  5.  
  6. #include <math.h>
  7. #include <time.h>
  8. #include <iomanip>  // For std::fixed and std::setprecision
  9.  
  10. #include <algorithm>  // For std::shuffle
  11. #include <numeric>    // For std::iota
  12.  
  13. //physical constants
  14.  
  15. #define m_e 9.1093837E-31 // electron mass in kg
  16. #define M_n 6.6464731E-27 // Helium atom mass
  17. #define k_b 1.380649E-23 // Boltzmann constant
  18. #define q 1.602176634E-19 // elementary charge    - eV -> J transfer param
  19. #define Coulomb_log 15.87 // Coulomb logarithm
  20. #define epsilon_0 8.854188E-12 // Vacuum permittivity
  21. #define Coulomb_const pow(q,4)/(pow(4.0*M_PI*epsilon_0,2)) // e^4/(4*pi*eps0)^2
  22. #define thresh0 0.0 //elastic threshold
  23. #define thresh1 19.82 // threshold energy excitation tripet state
  24. #define thresh2 20.61 // threshold energy excitation singlet state
  25. #define thresh3 24.587 // threshold energy ionization HeI
  26. #define tau_singlet 0.0195
  27.  
  28. // simulation parameters
  29.  
  30.  
  31. // #define N_He 1000000 // Helium neutrals number
  32. #define T_n 2.0 // Helium neutral temperature in eV
  33. #define T_e 10.0    // electron Maxwell initial distribution
  34. #define Emin 0.0
  35. #define Emax 3000.0
  36. #define Volume 1.0E-12 // Volume to calculate netral density and collision frequency
  37. #define total_time 1.0E-3 // 500 microsec time to equalibrate the system
  38. #define dopant 1.0E-5 // addition to avoid zeros
  39. #define E_reduced 0.6 // constant electrin field along z-axis
  40.  
  41. #define bin_width 0.05 // keep energy step ~ this to maintain cross-section clarity (Ramsauer minimum etc)
  42. #define N ( (int)((Emax-Emin)/bin_width) + 1) // add 1 to include E_max if needed?
  43.  
  44. // handling final energy bin
  45.  
  46. #define bin_width_smooth 0.05 // energy bin for smooth final distribution
  47. #define N_smooth ( (int)((Emax-Emin)/bin_width_smooth) )
  48.  
  49.  
  50.  
  51. double solve_A(double s) { // Netwon method solver
  52.  
  53.     if (s > 3) {
  54.         return 3*exp(-s);
  55.     }
  56.     if (s < 0.01) {
  57.         return 1.0/s;
  58.     }
  59.    
  60.     double A0 = 0.01; // initial guess
  61.     double A = A0;  //starting with initial guess
  62.     double tol = 1.0E-7; // accuracy
  63.  
  64.              
  65.     for (int i = 0; i < 1000; i++){
  66.  
  67.         double tanhA = tanh(A);
  68.         // Avoid division by an extremely small tanh(A)
  69.         if (fabs(tanhA) < 1e-12) {
  70.             std::cerr << "tanh(A) too small, returning fallback at iteration " << i << "\n";
  71.             return 1.0E-7;
  72.         }        
  73.  
  74.         double f = 1.0 / tanhA - 1.0 / A - exp(-s);
  75.         if (fabs(f) < tol)
  76.             break;
  77.  
  78.         double sinhA = sinh(A);
  79.         if (fabs(sinhA) < 1e-12) {
  80.             std::cerr << "sinh(A) too small, returning fallback at iteration " << i << "\n";
  81.             return 1.0E-5;
  82.         }
  83.  
  84.         double dfdA = -1.0/(sinh(A)*sinh(A)) + 1.0/(A*A);
  85.  
  86.         // Check if derivative is too close to zero to avoid huge updates
  87.         if (fabs(dfdA) < 1e-12) {
  88.             std::cerr << "dfdA is too small at iteration " << i << ", returning fallback\n";
  89.             if (s < 0.01) {
  90. //                std::cout << "Small s! Huge A!" << "\n";
  91.                 return 1.0/s;
  92.             }
  93.             if (s > 3) {
  94.                 return 3.0*exp(-s);
  95.             }
  96.         }        
  97.  
  98.         A -= f/dfdA;
  99.  
  100.         // Early check for numerical issues
  101.         if (std::isnan(A) || std::isinf(A)) {
  102.             std::cerr << "Numerical error detected, invalid A at iteration " << i << "\n";
  103.             return (A > 0) ? 1.0E-5 : -1.0E-5;  // Fallback value based on sign
  104.         }        
  105.  
  106.  
  107.     }
  108.  
  109.     return A;
  110. }
  111.  
  112. struct Electron {
  113.  
  114.     //velocity components
  115.     double vx = 0.0;
  116.     double vy = 0.0;
  117.     double vz = 0.0;
  118.     //energy in eV
  119.     double energy = 0.0;
  120.     //Collision flag
  121.     bool collided_en = false;
  122.     bool collided_ee = false;
  123.  
  124.     // initializing Maxwell-Boltzmann distribution with T_e
  125.     void initialize(std::mt19937& gen, std::uniform_real_distribution<double>& dis, std::gamma_distribution<double>& maxwell) {
  126.  
  127.         double R = dis(gen);
  128.  
  129.         // velocity angles in spherical coordinates
  130.         double phi = 2*M_PI*dis(gen);
  131.         double cosTheta = 2.0*dis(gen) - 1.0;
  132.         double sinTheta = sqrt(1.0 - cosTheta*cosTheta);
  133.  
  134.            
  135.         energy = maxwell(gen); // neutrals energies sampled as Maxwell distribution in eV
  136.            
  137.         double speed = sqrt(2*energy*q/m_e);
  138.  
  139.         //velocity components of neutrals in m/s
  140.         vx = speed * sinTheta * cos(phi);
  141.         vy = speed * sinTheta * sin(phi);
  142.         vz = speed * cosTheta;
  143.     }
  144.  
  145.  
  146. };
  147.  
  148. struct CrossSection {
  149.     double energy;
  150.     double sigma;
  151. };
  152.  
  153. double interpolate (double energy, const std::vector<CrossSection>& CS) {
  154.  
  155.  
  156.     if (energy < CS.front().energy) {
  157. //        std::cout << " required energy value lower than range of cross-section data at energy: " << energy << "\n";
  158.         return 0.0;
  159.     }
  160.     if (energy > CS.back().energy) {
  161. //        std::cout << " required energy value higher than range of cross-section data" << "\n";
  162.         return 0.0;        
  163.     }
  164.  
  165.     int step = 0;  
  166.         while (step < CS.size() && energy > CS[step].energy) {
  167.             step++;
  168.         }
  169.  
  170.     double k = (CS[step].sigma - CS[step-1].sigma)/(CS[step].energy - CS[step-1].energy);
  171.     double m = CS[step].sigma - k*CS[step].energy;
  172.    
  173.     return k*energy + m;
  174. }
  175.  
  176.  
  177. struct NeutralParticle {
  178.  
  179.     double energy = 0.0;
  180.     double vx = 0.0;
  181.     double vy = 0.0;
  182.     double vz = 0.0;
  183.  
  184.     void initialize(std::mt19937& gen, std::uniform_real_distribution<double>& dis, std::gamma_distribution<double>& maxwell) {
  185.  
  186.         double R = dis(gen);
  187.  
  188.         // velocity angles in spherical coordinates
  189.         double phi = 2*M_PI*dis(gen);
  190.         double cosTheta = 2.0*dis(gen) - 1.0;
  191.         double sinTheta = sqrt(1.0 - cosTheta*cosTheta);
  192.  
  193.            
  194.         energy = maxwell(gen); // neutrals energies sampled as Maxwell distribution in eV
  195.            
  196.         double speed = sqrt(2*energy*q/M_n);
  197.  
  198.         //velocity components of neutrals in m/s
  199.         vx = speed * sinTheta * cos(phi);
  200.         vy = speed * sinTheta * sin(phi);
  201.         vz = speed * cosTheta;
  202.     }
  203.    
  204. };
  205.  
  206. struct Excited_neutral {
  207.  
  208.     double energy;
  209.     double vx;
  210.     double vy;
  211.     double vz;
  212.    
  213. };
  214.  
  215.  
  216.  
  217. int main() {
  218.  
  219.     clock_t start = clock();
  220.  
  221.     int N_He = 10000000;
  222.     int n_e = 50000;
  223.  
  224.     std::vector<Electron> electrons(n_e); // better to use vector instead of simple array as it's dynamically allocated (beneficial for ionization)
  225. //    std::vector<NeutralParticle> neutrals(N_He); // I don't need a vector of neutrals bcs it's like a backhround in MCC-simulation
  226.  
  227.     std::vector<int> histo_random(N, 0); // initialize N size zero-vector for random (initial) histogram
  228.     std::vector<int> histo_maxwell(N, 0); // initialize N size zero-vector for maxwellian histogram
  229.     std::vector<int> histo_neutral(N, 0); // initialize N size zero-vector for neutral distribution histogram
  230.     std::vector<int> histo_excited(N, 0); // initialize N size zero-vector for excited He distribution histogram
  231.  
  232.     std::vector<double> elastic_vec(N, 0); // precompiled elastic cross-section-energy vector
  233.     std::vector<double> inelastic1_vec(N, 0); // precompiled inelastic(triplet excitation) cross-section-energy vector
  234.     std::vector<double> inelastic2_vec(N, 0); // precompiled inelastic(singlet excitation) cross-section-energy vector    
  235.     std::vector<double> superelastic1_vec(N, 0); // precompiled superelastic(triplet de-excitation) cross-section-energy vector
  236.     std::vector<double> superelastic2_vec(N, 0); // precompiled superelastic(triplet de-excitation) cross-section-energy vector
  237.     std::vector<double> ionization_vec(N, 0); // precompiled ionization cross-section-energy vector
  238.  
  239.     std::random_device rd;
  240.     std::mt19937 gen(rd());
  241.     std::uniform_real_distribution<double> dis(0.0, 1.0);
  242.     std::gamma_distribution<double> maxwell_neutral(1.5, T_n);
  243.     std::gamma_distribution<double> maxwell_electron(1.5, T_e);
  244.  
  245.     std::ifstream elastic_cs_dat("cross_sections/elastic.dat");
  246.     if (!elastic_cs_dat.is_open()) {
  247.         std::cerr << "Error opening elastic cross-sections file!" << std::endl;
  248.         return 1;
  249.     }    
  250.  
  251.     std::ifstream excitation1_cs_dat("cross_sections/inelastic_triplet.dat");
  252.     if (!excitation1_cs_dat.is_open()) {
  253.         std::cerr << "Error opening inelastic triplet cross-sections file!" << std::endl;
  254.         return 1;
  255.     }
  256.  
  257.     std::ifstream excitation2_cs_dat("cross_sections/inelastic_singlet.dat");
  258.     if (!excitation2_cs_dat.is_open()) {
  259.         std::cerr << "Error opening inelastic singlet cross-sections file!" << std::endl;
  260.         return 1;
  261.     }
  262.  
  263.     std::ifstream ionization_cs_dat("cross_sections/ionization.dat");
  264.     if (!ionization_cs_dat.is_open()) {
  265.         std::cerr << "Error opening inelastic singlet cross-sections file!" << std::endl;
  266.         return 1;
  267.     }
  268.  
  269.     // --- starts reading cross section datafiles
  270.  
  271. //-----------------elastic---------------------------//
  272.     std::vector<CrossSection> elastic_CS_temp;
  273.  
  274.     double energy, sigma;
  275.  
  276.     while (elastic_cs_dat >> energy >> sigma) {
  277.         elastic_CS_temp.push_back({energy, sigma});
  278.     }    
  279.     elastic_cs_dat.close();
  280.  
  281.     energy = 0.0;
  282.     sigma = 0.0;
  283. //-----------------triplet excitation---------------------------//
  284.     std::vector<CrossSection> inelastic1_CS_temp;
  285.  
  286.     while (excitation1_cs_dat >> energy >> sigma) {
  287.         inelastic1_CS_temp.push_back({energy, sigma});
  288.     }    
  289.     excitation1_cs_dat.close();    
  290. //-----------------singlet excitation---------------------------//
  291.     energy = 0.0;
  292.     sigma = 0.0;
  293.  
  294.     std::vector<CrossSection> inelastic2_CS_temp;
  295.  
  296.     while (excitation2_cs_dat >> energy >> sigma) {
  297.         inelastic2_CS_temp.push_back({energy, sigma});
  298.     }    
  299.     excitation2_cs_dat.close();    
  300. //-----------------Ionization---------------------------//
  301.     energy = 0.0;
  302.     sigma = 0.0;
  303.  
  304.     std::vector<CrossSection> ionization_CS_temp;
  305.  
  306.     while (ionization_cs_dat >> energy >> sigma) {
  307.         ionization_CS_temp.push_back({energy, sigma});
  308.     }    
  309.     ionization_cs_dat.close();    
  310.     // --- finish reading cross-section datafiles  
  311.  
  312.     std::ofstream file0("output_files/velocities.dat");    
  313.     std::ofstream file1("output_files/energies.dat");        
  314.     std::ofstream file2("output_files/energies_final.dat");    
  315.     std::ofstream file3("output_files/histo_random.dat");    
  316.     file3 << std::fixed << std::setprecision(10);
  317.    
  318.     std::ofstream file4("output_files/histo_maxwell.dat");
  319.     file4 << std::fixed << std::setprecision(10);          
  320.    
  321.     std::ofstream file5("output_files/neutral_distribution.dat");    
  322.     std::ofstream file6("output_files/E*f(E).dat");    
  323.     std::ofstream file7("output_files/nu_max.dat");
  324.     std::ofstream file8("output_files/electron_mean_energy.dat");
  325.     std::ofstream file9("output_files/nu_elastic_average_initial.dat");
  326.     std::ofstream file10("output_files/nu_inelastic1_average_initial.dat");
  327.     std::ofstream file11("output_files/nu_elastic_average_final.dat");
  328.     std::ofstream file12("output_files/nu_inelastic1_average_final.dat");
  329.     std::ofstream file13("output_files/log_output.dat");  
  330.     std::ofstream file14("output_files/excited_energies.dat");      
  331.     std::ofstream file15("output_files/excited_histo.dat");            
  332.     std::ofstream file_temp("output_files/collision_rates.dat");
  333.     std::ofstream file16("output_files/energy_gain.dat");  
  334.     std::ofstream file17("output_files/ionization_check.dat");
  335.  
  336.     // Initialize all electrons
  337.     for (auto& e : electrons) {
  338.         e.initialize(gen, dis, maxwell_electron);
  339.     }
  340.  
  341.     // precalculate cross-sections for each energy bin
  342.     for (int i = 0; i < N; i++){
  343.         elastic_vec[i] = interpolate(bin_width*(i+0.5), elastic_CS_temp); //elastiuc
  344.         inelastic1_vec[i] = interpolate(bin_width*(i+0.5), inelastic1_CS_temp); //triplet excitation
  345.         inelastic2_vec[i] = interpolate(bin_width*(i+0.5), inelastic2_CS_temp); //singlet excitation
  346.         ionization_vec[i] = interpolate(bin_width*(i+0.5), ionization_CS_temp); //ionization
  347.     }
  348.  
  349.     // precalculate superelastic cross-section (triplet -> ground) for each energy bin
  350.     // detailed balance gives: sigma_j_i(E) = (g_i/g_j)*((E+theshold)/E)*sigma_i_j(E+theshold)
  351.     for (int i = 0; i < N; i++){
  352.         double energy = Emin + (i + 0.5) * bin_width;
  353.         int thresh_bin = (int)( (thresh1 - Emin)/bin_width ); // calculating bin for threshold energy
  354.         superelastic1_vec[i] = (1.0/3.0)*((energy + thresh1)/energy)*interpolate(energy + thresh1, inelastic1_CS_temp); // using detailed balance, calculating backward deexcitation cross-section
  355.         superelastic2_vec[i] = (1.0/1.0)*((energy + thresh2)/energy)*interpolate(energy + thresh2, inelastic2_CS_temp);
  356.     }
  357.  
  358.     for (int i = 0; i < n_e; i++){
  359.         file1 << i << " " << electrons.at(i).energy << "\n";
  360.         file0 << i << " " << electrons[i].vx << " " << electrons[i].vy << " " << electrons[i].vz << "\n";
  361.     }
  362.  
  363.     // -----initial electrons energy distribution starts------------////
  364.     for (int i = 0; i < n_e; i++){
  365.         int bin = (int)( (electrons[i].energy - Emin)/bin_width );
  366.         if (bin >=0 && bin < histo_random.size())
  367.             histo_random[bin]++;
  368.     }
  369.  
  370.     for (int i = 0; i < histo_random.size(); i++){
  371.         double bin_center = Emin + (i + 0.5) * bin_width;
  372.         file3 << bin_center << " " <<  static_cast<double>(histo_random[i])/(electrons.size()*bin_width) << "\n"; // this is electron normalized distribution function
  373.     }
  374.  
  375.     //calculating excited specied population
  376.  
  377.     /*From Boltzman distribution y_k = g_k*exp(-E_k/kT)/norm, where g_k - stat weight of k-state,
  378.     E_k - threshold energy for k-state, norm is a total partition function or normaliztion factor     */
  379.  
  380.     double part_ground = 1.0*exp(-0.0/T_n); // partition function for ground state
  381.     double part_triplet = 3.0*exp(-thresh1/T_n); // partition function for triplet excited state
  382.     double part_singlet = 1.0*exp(-thresh2/T_n); // partition function for singlet excited state
  383.     double part_func_total = part_ground + part_triplet + part_singlet; // total partition function
  384.     double N_trpilet = (part_triplet/part_func_total)*N_He; // population of tripet state
  385.     double N_singlet = (part_singlet/part_func_total)*N_He; // population of singlet state
  386.  
  387.     std::vector<Excited_neutral> exc_1(static_cast<int>(N_trpilet));  // vector to track triplet excited atoms of Helium
  388.     std::vector<Excited_neutral> exc_2(static_cast<int>(N_singlet));  // vector to track singlet excited atoms of Helium    
  389.  
  390.     // adjusting neutrals number:
  391.  
  392.     N_He -= (N_trpilet + N_singlet);
  393.  
  394.     std::cout << N_He << "\n";
  395.  
  396.     // initializing excited species with Maxwellian distribution
  397.  
  398.     for (auto& exc : exc_1) {
  399.     NeutralParticle tmp_neutral;
  400.     tmp_neutral.initialize(gen, dis, maxwell_neutral);
  401.     exc.energy = tmp_neutral.energy;
  402.     exc.vx = tmp_neutral.vx;
  403.     exc.vy = tmp_neutral.vy;
  404.     exc.vz = tmp_neutral.vz;
  405.     }
  406.  
  407.     for (auto& exc : exc_2) {
  408.     NeutralParticle tmp_neutral;
  409.     tmp_neutral.initialize(gen, dis, maxwell_neutral);
  410.     exc.energy = tmp_neutral.energy;
  411.     exc.vx = tmp_neutral.vx;
  412.     exc.vy = tmp_neutral.vy;
  413.     exc.vz = tmp_neutral.vz;
  414.     }
  415.  
  416.     std::cout << "Triplet population initialized: " << exc_1.size() << "\n";
  417.     std::cout << "Singlet population initialized: " << exc_2.size() << "\n";    
  418.  
  419.     // calculating excited specied population finished //
  420.  
  421.  
  422.     // //----- calculating number to calculate nu-average (both elastic/inelastic )from our electron distribution starts---------///
  423.     // // --- calculating nu(E)*f(E) for later external integration, using initial f(E)
  424.     // for (int i = 0; i < N; i++){
  425.     //     double bin_center = Emin + (i + 0.5) * bin_width;
  426.     //     file9 << bin_center << " " << (N_He/Volume)*elastic_vec[i] * sqrt(2.0*bin_center*q/m_e)*static_cast<double>(histo_random[i])/(electrons.size()*bin_width) << "\n";
  427.     //     file10 << bin_center << " " << (N_He/Volume)*inelastic1_vec[i] * sqrt(2.0*bin_center*q/m_e)*static_cast<double>(histo_random[i])/(electrons.size()*bin_width) << "\n";
  428.     // }
  429.     // //----- calculating nu-average from our electron distribution ends ---------///    
  430.  
  431.     // double dt = 0.1/nu_max;   // minimum should be 0.1/nu_max to get acceptable numerical error range see Vahedi Surrendra 1995
  432.     // double steps = static_cast<int>(time/dt);
  433.  
  434.     // std::cout << steps << "\n";
  435.     // std::cout << dt << "\n";
  436.  
  437.     // //using  null-collision technique, getting the number of particles colliding each step: P_collision = 1 - exp(-nu_max*dt)
  438.     // int Ne_collided = (1.0-exp(-1.0*dt*nu_max))*n_e;
  439.  
  440.     // std::cout << "Ne_collided:" << Ne_collided << "\n";
  441.  
  442.     int print_interval = 10;
  443.     int el_coll_counter = 0; // track all elastic collisions
  444.     int exc1_coll_counter = 0; // track all triplet excitation collisions
  445.     int exc2_coll_counter = 0; // track all singlet excitation collisions
  446.     int null_coll_counter = 0; // track null-collisions
  447.     int ee_coll_counter = 0; //track e-e Coulomb collisions
  448.     int super1_coll_counter = 0; // track superelastic triplet collisions
  449.     int super2_coll_counter = 0; // track superelastic triplet collisions    
  450.     int ionization_counter = 0; //track ionization collisions
  451.  
  452.  
  453.     double a_z = ((-1.0)*q * E_reduced) / m_e;
  454.     double mass_ratio = 2.0*(m_e/M_n);
  455.     double charge_mass_ratio = 0.5*m_e/q;
  456.     double sqrt_charge_mass = sqrt(2*q/m_e);
  457.     double C1 = -1.0*q*E_reduced;
  458.     double C2 = 0.5*C1*C1/m_e;
  459.  
  460.     double t_elapsed = 0.0;
  461.  
  462.     std::cout << C1 << "    " << C2 << "\n";
  463.  
  464.  
  465.     // -----calculating nu-max for null-collision method starts ------------////
  466.     double nu_max = 0.0;
  467.     double nu_max_temp = 0.0;
  468.     double sigma_total = 0.0;
  469.    
  470.     for (int i = 0; i < N; i++){
  471.         // Get initial densities
  472.         double n_ground = N_He / Volume;
  473.         double n_excited1 = exc_1.size() / Volume;
  474.         double n_excited2 = exc_2.size() / Volume;
  475.  
  476.         double energy = Emin + (i + 0.5) * bin_width;
  477.  
  478.         // Total collision frequency for this energy bin
  479.         double sigma_total =
  480.             elastic_vec[i] * n_ground +
  481.             inelastic1_vec[i] * n_ground +
  482.             inelastic2_vec[i] * n_ground +
  483.             superelastic1_vec[i] * n_excited1 +
  484.             superelastic2_vec[i] * n_excited2 +
  485.             ionization_vec[i] * n_ground;
  486.  
  487.         double v = sqrt(2 * energy * q / m_e);
  488.         double nu_temp = sigma_total * v;
  489.        
  490.         if (nu_temp > nu_max) nu_max = nu_temp;
  491.     }
  492.  
  493.     std::cout << "initial nu_max: " <<nu_max << "\n";
  494.     // -----calculating nu-max for null-collision method ends ------------////    
  495.  
  496.     double dt = 0.1/nu_max;   // minimum should be 0.1/nu_max to get acceptable numerical error range see Vahedi Surrendra 1995
  497.  
  498.  
  499.     while (t_elapsed < total_time) {
  500.         // Handle edge case for final step
  501.         if (t_elapsed + dt > total_time) {
  502.             dt = total_time - t_elapsed;
  503.         }    
  504.  
  505.  
  506.         //using  null-collision technique, getting the number of particles colliding each step: P_collision = 1 - exp(-nu_max*dt)
  507.         int Ne_collided = (1.0-exp(-1.0*dt*nu_max))*n_e;  
  508.  
  509.         // Generate shuffled list of electron indices
  510.         int reshuffle_interval = 1;
  511.         std::vector<int> electron_indices(n_e);
  512.         std::iota(electron_indices.begin(), electron_indices.end(), 0); // fill with index
  513.  
  514.  
  515.         for (int i = 0; i < Ne_collided; ++i) {
  516.             int j = i + std::uniform_int_distribution<int>(0, n_e - i - 1)(gen);
  517.             std::swap(electron_indices[i], electron_indices[j]);
  518.         }
  519.  
  520.         int exc1_coll_counter_temp = 0;
  521.         int super1_coll_counter_temp = 0;
  522.         int exc2_coll_counter_temp = 0;
  523.         int super2_coll_counter_temp = 0;
  524.         int null_coll_counter_temp = 0;
  525.         int ionization_counter_temp = 0;
  526.  
  527.         double energy_exc = 0.0; // calculating excitation losses each timestep
  528.         double energy_sup = 0.0; // calculating superelastic gains each timestep
  529.         double energy_Efield = 0.0; // calculating field gains/losses each timestep
  530.  
  531.  
  532.         // std::cout << "Progress: " << (t_elapsed/total_time)*100 << "\n";
  533.  
  534.         // setting flags to false each timestep
  535.         for (auto& e : electrons) e.collided_en = false;
  536.         for (auto& e : electrons) e.collided_ee = false;        
  537.  
  538.         int collision_counter_en = 0; // electron-neutral collision counter
  539.         int collision_counter_ee = 0; // e-e collisoin counter
  540.  
  541.         /// -- electrin field heating along E-Z axis begin--- /// -- first half!!!
  542.         for (int idx : electron_indices) {
  543.             double half_dt = dt/2.0;
  544.             energy_Efield += ( C1*electrons[idx].vz*half_dt + C2*half_dt*half_dt )/q; // dividing by q to get eV            
  545.             // Update velocity component due to electric field
  546.             // double a_z = ((-1.0)*q * E_reduced) / m_e; // acceleration in z-direction, m/s^2
  547.             electrons[idx].vz += a_z * (dt*0.5); // only half timestep
  548.  
  549.             // Recalculate energy from updated velocity
  550.             double vx = electrons[idx].vx;
  551.             double vy = electrons[idx].vy;
  552.             double vz = electrons[idx].vz;
  553.             electrons[idx].energy = (vx*vx + vy*vy + vz*vz)*charge_mass_ratio;
  554.         }
  555.         // -------------------------------------------- filed heating ends ------------------------//  
  556.  
  557.  
  558.         for (int idx : electron_indices) {
  559.  
  560.             if (collision_counter_en >= Ne_collided) break; // quit if reached all collisions
  561.  
  562.             Electron& e = electrons[idx];
  563.             if (e.collided_en) continue;  // Skip already collided electrons
  564.  
  565.             double dens_neutrals = (N_He/Volume);
  566.             double dens_exc_1 = (exc_1.size()/Volume);
  567.             double dens_exc_2 = (exc_2.size()/Volume);
  568.             double speed = sqrt_charge_mass*sqrt(e.energy);
  569.  
  570.             int bin_energy = static_cast<int>(e.energy / bin_width);
  571.             double nu_elastic = dens_neutrals * elastic_vec[bin_energy] * speed;
  572.             double nu_inelastic1 = dens_neutrals * inelastic1_vec[bin_energy] * speed;
  573.             double nu_superelastic1 = dens_exc_1 * superelastic1_vec[bin_energy] * speed;
  574.             double nu_inelastic2 = dens_neutrals * inelastic2_vec[bin_energy] * speed;
  575.             double nu_superelastic2 = dens_exc_2 * superelastic2_vec[bin_energy] * speed;
  576.             double nu_ionization = dens_neutrals * ionization_vec[bin_energy] * speed;
  577.  
  578.             double r = dis(gen);
  579.  
  580.             double P0 = nu_elastic/nu_max;
  581.             double P1 = (nu_elastic + nu_inelastic1)/nu_max;
  582.             double P2 = (nu_elastic + nu_inelastic1 + nu_superelastic1)/nu_max;
  583.             double P3 = (nu_elastic + nu_inelastic1 + nu_superelastic1 + nu_inelastic2)/nu_max;
  584.             double P4 = (nu_elastic + nu_inelastic1 + nu_superelastic1 + nu_inelastic2 + nu_superelastic2)/nu_max;
  585.             double P5 = (nu_elastic + nu_inelastic1 + nu_superelastic1 + nu_inelastic2 + nu_superelastic2 + nu_ionization)/nu_max;
  586.            
  587.             if (r < P0) {
  588.  
  589.                 // elastic collision happens
  590.  
  591.                 // ----   Collision energy redistribution module
  592.  
  593.                 // electron particle X Y Z initial velocities and energy
  594.                 double V0_x = e.vx;
  595.                 double V0_y = e.vy;
  596.                 double V0_z = e.vz;
  597.                 double E_0 = e.energy;
  598.                
  599.                 // neutral that collides with electron
  600.  
  601.                 // randomize particles each collision
  602.  
  603.                 NeutralParticle tmp_neutral;
  604.                 tmp_neutral.initialize(gen, dis, maxwell_neutral);
  605.                 double V_x_n = tmp_neutral.vx;
  606.                 double V_y_n = tmp_neutral.vy;
  607.                 double V_z_n = tmp_neutral.vz;
  608.                 double E_n = tmp_neutral.energy;
  609.  
  610.  
  611.                 double V0 = sqrt(V0_x*V0_x + V0_y*V0_y + V0_z*V0_z);
  612.  
  613.                 // initial relative velocity
  614.                 double V_rel_x = V0_x - V_x_n;
  615.                 double V_rel_y = V0_y - V_y_n;
  616.                 double V_rel_z = V0_z - V_z_n;
  617.  
  618.                 double V_rel = sqrt(V_rel_x*V_rel_x + V_rel_y*V_rel_y + V_rel_z*V_rel_z);
  619.                 if (std::isnan(V_rel) || std::isinf(V_rel) || fabs(V_rel) < 1e-12) {
  620.                     std::cerr << "Invalid V_rel copmuted: " << V_rel << " at timestep " << t_elapsed << std::endl;
  621.                 }
  622.                 //initial relative velocity unit vectors components
  623.                 double e1 = V_rel_x/V_rel;
  624.                 double e2 = V_rel_y/V_rel;
  625.                 double e3 = V_rel_z/V_rel;
  626.  
  627.                 double C = -V_rel/(1.0+(m_e/M_n));
  628.                 double beta = sqrt(1.0 - thresh0/e.energy);
  629.                 // generating random variables to calculate random direction of center-of-mass after the collision
  630.  
  631.                 double R1 = dis(gen);
  632.                 double R2 = dis(gen);
  633.  
  634.                 //// calculating spherical angles for center-of-mass random direction
  635.                 // double theta = acos(1.0- 2.0*R1);
  636.                 // double phi = 2*M_PI*R2;
  637.                 double cos_khi = 1.0 - 2.0*R1;
  638.                 double sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  639.                 if (std::isnan(sin_khi) || std::isinf(sin_khi)) {
  640.                     std::cerr << "Invalid sin_khi copmuted: " << sin_khi << " at timestep " << t_elapsed << std::endl;
  641.                 }                
  642.  
  643.                 double phi = 2.0*M_PI*R2;
  644.  
  645.                 //calculating velocity change of electron
  646.  
  647.                 double dv_x = C*( (1.0-beta*cos_khi)*e1 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(-e2) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e1*e3)  );
  648.                 double dv_y = C*( (1.0-beta*cos_khi)*e2 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(e1) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e2*e3)  );
  649.                 double dv_z = C*( (1.0-beta*cos_khi)*e3 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(0.0) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(e1*e1 + e2*e2)  );
  650.  
  651.                 //updating electron energy and velocities  
  652.  
  653.                 //updating electron energy and velocities
  654.                
  655.                 e.vx += dv_x;
  656.                 e.vy += dv_y;
  657.                 e.vz += dv_z;
  658.  
  659.                 e.energy = (e.vx*e.vx + e.vy*e.vy + e.vz*e.vz) * charge_mass_ratio;
  660.  
  661.                 collision_counter_en++;
  662.                 el_coll_counter++;
  663.  
  664.                 e.collided_en = true;
  665.             }      
  666.  
  667.             else if (r < P1) {
  668.  
  669.                 //inelastic 1(triplet) collision happens
  670.  
  671.                 // ----   Collision energy redistribution module
  672.  
  673.                 // electron particle X Y Z initial velocities and energy
  674.                 double V0_x = e.vx;
  675.                 double V0_y = e.vy;
  676.                 double V0_z = e.vz;
  677.                 double E_0 = e.energy;
  678.                
  679.                 // neutral that collides with electron
  680.  
  681.                 // randomize particles each collision
  682.  
  683.                 NeutralParticle tmp_neutral;
  684.                 tmp_neutral.initialize(gen, dis, maxwell_neutral);
  685.                 double V_x_n = tmp_neutral.vx;
  686.                 double V_y_n = tmp_neutral.vy;
  687.                 double V_z_n = tmp_neutral.vz;
  688.                 double E_n = tmp_neutral.energy;
  689.  
  690.  
  691.                 double V0 = sqrt(V0_x*V0_x + V0_y*V0_y + V0_z*V0_z);
  692.  
  693.                 // initial relative velocity
  694.                 double V_rel_x = V0_x - V_x_n;
  695.                 double V_rel_y = V0_y - V_y_n;
  696.                 double V_rel_z = V0_z - V_z_n;
  697.  
  698.                 double V_rel = sqrt(V_rel_x*V_rel_x + V_rel_y*V_rel_y + V_rel_z*V_rel_z);
  699.                
  700.                 if (std::isnan(V_rel) || std::isinf(V_rel) || fabs(V_rel) < 1e-12) {
  701.                     std::cerr << "Invalid V_rel copmuted: " << V_rel << " at timestep " << t_elapsed << std::endl;
  702.                 }
  703.  
  704.                 //initial relative velocity unit vectors components
  705.                 double e1 = V_rel_x/V_rel;
  706.                 double e2 = V_rel_y/V_rel;
  707.                 double e3 = V_rel_z/V_rel;
  708.  
  709.                 double C = -V_rel/(1.0+(m_e/M_n));
  710.                 double beta = sqrt(abs(1.0 - thresh1/e.energy));
  711.                 // generating random variables to calculate random direction of center-of-mass after the collision
  712.  
  713.                 double R1 = dis(gen);
  714.                 double R2 = dis(gen);
  715.  
  716.                 //// calculating spherical angles for center-of-mass random direction
  717.                 // double theta = acos(1.0- 2.0*R1);
  718.                 // double phi = 2*M_PI*R2;
  719.                 double cos_khi = 1.0 - 2.0*R1;
  720.                 double sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  721.                 if (std::isnan(sin_khi) || std::isinf(sin_khi)) {
  722.                     std::cerr << "Invalid sin_khi copmuted: " << sin_khi << " at timestep " << t_elapsed << std::endl;
  723.                 }      
  724.                 double phi = 2.0*M_PI*R2;
  725.  
  726.                 //calculating velocity change of electron
  727.  
  728.                 double dv_x = C*( (1.0-beta*cos_khi)*e1 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(-e2) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e1*e3)  );
  729.                 double dv_y = C*( (1.0-beta*cos_khi)*e2 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(e1) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e2*e3)  );
  730.                 double dv_z = C*( (1.0-beta*cos_khi)*e3 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(0.0) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(e1*e1 + e2*e2)  );
  731.  
  732.                 //updating electron energy and velocities        
  733.                
  734.                 if (e.energy < thresh1) {
  735.                     null_coll_counter++;
  736.                     collision_counter_en++;
  737.                     e.collided_en = true;
  738.                     continue;
  739.                 }
  740.                 else {
  741.                     e.vx += dv_x;
  742.                     e.vy += dv_y;
  743.                     e.vz += dv_z;
  744.  
  745.                     e.energy = (e.vx*e.vx + e.vy*e.vy + e.vz*e.vz) * charge_mass_ratio;
  746.  
  747.                     collision_counter_en++;  
  748.                     exc1_coll_counter++;
  749.                     exc1_coll_counter_temp++;
  750.  
  751.                     e.collided_en = true;
  752.  
  753.                     // pushing this neutral to an array of excited species exc_1
  754.                     // if (N_He > 0) {
  755.                     //     exc_1.push_back({E_n, V_x_n, V_y_n, V_z_n});
  756.                     //     N_He--;
  757.                     // }
  758.                 }
  759.             }    
  760.  
  761.             else if (r < P2) {
  762.  
  763.                 //superelastic 1(triplet -> ground state) collision happens
  764.  
  765.                 if (exc_1.empty()) {
  766.                     null_coll_counter++;
  767.                     collision_counter_en++;
  768.                     e.collided_en = true;
  769.                     continue;            
  770.                 }
  771.  
  772.                 // ----   Collision energy redistribution module
  773.  
  774.                 // electron particle X Y Z initial velocities and energy
  775.                 double V0_x = e.vx;
  776.                 double V0_y = e.vy;
  777.                 double V0_z = e.vz;
  778.                 double E_0 = e.energy;
  779.  
  780.                 // neutral that collides with electron
  781.                 // taking particles from dynamic array of excited neutrals
  782.  
  783.                 int index = std::uniform_int_distribution<int>(0, exc_1.size()-1)(gen);
  784.                 Excited_neutral& exc = exc_1[index];
  785.                 double V_x_n = exc.vx;
  786.                 double V_y_n = exc.vy;
  787.                 double V_z_n = exc.vz;
  788.                 double E_n = exc.energy;
  789.                
  790.                 double V0 = sqrt(V0_x*V0_x + V0_y*V0_y + V0_z*V0_z);
  791.  
  792.                 // initial relative velocity
  793.                 double V_rel_x = V0_x - V_x_n;
  794.                 double V_rel_y = V0_y - V_y_n;
  795.                 double V_rel_z = V0_z - V_z_n;
  796.  
  797.                 double V_rel = sqrt(V_rel_x*V_rel_x + V_rel_y*V_rel_y + V_rel_z*V_rel_z);
  798.  
  799.                 if (std::isnan(V_rel) || std::isinf(V_rel) || fabs(V_rel) < 1e-12) {
  800.                     std::cerr << "Invalid V_rel copmuted: " << V_rel << " at timestep " << t_elapsed << std::endl;
  801.                 }
  802.  
  803.                 //initial relative velocity unit vectors components
  804.                 double e1 = V_rel_x/V_rel;
  805.                 double e2 = V_rel_y/V_rel;
  806.                 double e3 = V_rel_z/V_rel;
  807.  
  808.                 double C = -V_rel/(1.0+(m_e/M_n));
  809.                 double beta = sqrt(1.0 + thresh1/e.energy);
  810.                 // generating random variables to calculate random direction of center-of-mass after the collision
  811.  
  812.                 double R1 = dis(gen);
  813.                 double R2 = dis(gen);
  814.  
  815.                 //// calculating spherical angles for center-of-mass random direction
  816.                 // double theta = acos(1.0- 2.0*R1);
  817.                 // double phi = 2*M_PI*R2;
  818.                 double cos_khi = 1.0 - 2.0*R1;
  819.                 double sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  820.                 if (std::isnan(sin_khi) || std::isinf(sin_khi)) {
  821.                     std::cerr << "Invalid sin_khi copmuted: " << sin_khi << " at timestep " << t_elapsed << std::endl;
  822.                 }      
  823.                 double phi = 2.0*M_PI*R2;
  824.  
  825.                 //calculating velocity change of electron
  826.  
  827.                 double dv_x = C*( (1.0-beta*cos_khi)*e1 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(-e2) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e1*e3)  );
  828.                 double dv_y = C*( (1.0-beta*cos_khi)*e2 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(e1) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e2*e3)  );
  829.                 double dv_z = C*( (1.0-beta*cos_khi)*e3 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(0.0) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(e1*e1 + e2*e2)  );
  830.  
  831.                 //updating electron energy and velocities        
  832.                
  833.                 e.vx += dv_x;
  834.                 e.vy += dv_y;
  835.                 e.vz += dv_z;
  836.  
  837.                 e.energy = (e.vx*e.vx + e.vy*e.vy + e.vz*e.vz) * charge_mass_ratio;
  838.  
  839.                 // //counting collisions, working with flags, popping atom out of the vector
  840.                 // if (!exc_1.empty()) {
  841.                 //     std::swap(exc_1[index], exc_1.back());
  842.                 //     exc_1.pop_back();
  843.                 //     N_He++;
  844.                 // }
  845.                 collision_counter_en++;  
  846.                 super1_coll_counter++;
  847.                 super1_coll_counter_temp++;
  848.                 energy_sup += thresh1;
  849.  
  850.                 e.collided_en = true;
  851.             }  
  852.  
  853.             else if (r < P3) {
  854.  
  855.                 //inelastic 1(singlet) excitation collision happens
  856.  
  857.                 // ----   Collision energy redistribution module
  858.  
  859.                 // electron particle X Y Z initial velocities and energy
  860.                 double V0_x = e.vx;
  861.                 double V0_y = e.vy;
  862.                 double V0_z = e.vz;
  863.                 double E_0 = e.energy;
  864.                
  865.                 // neutral that collides with electron
  866.  
  867.                 // randomize particles each collision
  868.  
  869.                 NeutralParticle tmp_neutral;
  870.                 tmp_neutral.initialize(gen, dis, maxwell_neutral);
  871.                 double V_x_n = tmp_neutral.vx;
  872.                 double V_y_n = tmp_neutral.vy;
  873.                 double V_z_n = tmp_neutral.vz;
  874.                 double E_n = tmp_neutral.energy;
  875.  
  876.  
  877.                 double V0 = sqrt(V0_x*V0_x + V0_y*V0_y + V0_z*V0_z);
  878.  
  879.                 // initial relative velocity
  880.                 double V_rel_x = V0_x - V_x_n;
  881.                 double V_rel_y = V0_y - V_y_n;
  882.                 double V_rel_z = V0_z - V_z_n;
  883.  
  884.                 double V_rel = sqrt(V_rel_x*V_rel_x + V_rel_y*V_rel_y + V_rel_z*V_rel_z);
  885.  
  886.                 if (std::isnan(V_rel) || std::isinf(V_rel) || fabs(V_rel) < 1e-12) {
  887.                     std::cerr << "Invalid V_rel copmuted: " << V_rel << " at timestep " << t_elapsed << std::endl;
  888.                 }
  889.  
  890.                 //initial relative velocity unit vectors components
  891.                 double e1 = V_rel_x/V_rel;
  892.                 double e2 = V_rel_y/V_rel;
  893.                 double e3 = V_rel_z/V_rel;
  894.  
  895.                 double C = -V_rel/(1.0+(m_e/M_n));
  896.                 double beta = sqrt(abs(1.0 - thresh2/e.energy));
  897.                 // generating random variables to calculate random direction of center-of-mass after the collision
  898.  
  899.                 double R1 = dis(gen);
  900.                 double R2 = dis(gen);
  901.  
  902.                 //// calculating spherical angles for center-of-mass random direction
  903.                 // double theta = acos(1.0- 2.0*R1);
  904.                 // double phi = 2*M_PI*R2;
  905.                 double cos_khi = 1.0 - 2.0*R1;
  906.                 double sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  907.                 if (std::isnan(sin_khi) || std::isinf(sin_khi)) {
  908.                     std::cerr << "Invalid sin_khi copmuted: " << sin_khi << " at timestep " << t_elapsed << std::endl;
  909.                 }      
  910.                 double phi = 2.0*M_PI*R2;
  911.  
  912.                 //calculating velocity change of electron
  913.  
  914.                 double dv_x = C*( (1.0-beta*cos_khi)*e1 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(-e2) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e1*e3)  );
  915.                 double dv_y = C*( (1.0-beta*cos_khi)*e2 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(e1) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e2*e3)  );
  916.                 double dv_z = C*( (1.0-beta*cos_khi)*e3 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(0.0) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(e1*e1 + e2*e2)  );
  917.  
  918.                 //updating electron energy and velocities        
  919.                
  920.                 if (e.energy < thresh1) {
  921.                     null_coll_counter++;
  922.                     collision_counter_en++;
  923.                     e.collided_en = true;
  924.                     continue;
  925.                 }
  926.                 else {
  927.                     e.vx += dv_x;
  928.                     e.vy += dv_y;
  929.                     e.vz += dv_z;
  930.  
  931.                     e.energy = (e.vx*e.vx + e.vy*e.vy + e.vz*e.vz) * charge_mass_ratio;
  932.  
  933.                     collision_counter_en++;  
  934.                     exc2_coll_counter++;
  935.                     exc2_coll_counter_temp++;
  936.  
  937.                     e.collided_en = true;
  938.  
  939.                     // pushing this neutral to an array of excited species exc_2
  940.  
  941.                     // if (N_He > 0) {
  942.                     //     exc_2.push_back({E_n, V_x_n, V_y_n, V_z_n});
  943.                     //     N_He--;
  944.                     // }
  945.                 }
  946.             }
  947.  
  948.             else if (r < P4) {
  949.  
  950.                 //supernelastic 1(singlet -> ground state) collision happens
  951.  
  952.                 if (exc_2.empty()) {
  953.                     null_coll_counter++;
  954.                     collision_counter_en++;
  955.                     e.collided_en = true;
  956.                     continue;            
  957.                 }
  958.  
  959.                 // ----   Collision energy redistribution module
  960.  
  961.                 // electron particle X Y Z initial velocities and energy
  962.                 double V0_x = e.vx;
  963.                 double V0_y = e.vy;
  964.                 double V0_z = e.vz;
  965.                 double E_0 = e.energy;
  966.  
  967.                 // neutral that collides with electron
  968.                 // taking particles from dynamic array of excited neutrals
  969.  
  970.                 int index = std::uniform_int_distribution<int>(0, exc_2.size()-1)(gen);
  971.                 Excited_neutral& exc = exc_2[index];
  972.                 double V_x_n = exc.vx;
  973.                 double V_y_n = exc.vy;
  974.                 double V_z_n = exc.vz;
  975.                 double E_n = exc.energy;
  976.                
  977.                 double V0 = sqrt(V0_x*V0_x + V0_y*V0_y + V0_z*V0_z);
  978.  
  979.                 // initial relative velocity
  980.                 double V_rel_x = V0_x - V_x_n;
  981.                 double V_rel_y = V0_y - V_y_n;
  982.                 double V_rel_z = V0_z - V_z_n;
  983.  
  984.                 double V_rel = sqrt(V_rel_x*V_rel_x + V_rel_y*V_rel_y + V_rel_z*V_rel_z);
  985.  
  986.                 if (std::isnan(V_rel) || std::isinf(V_rel) || fabs(V_rel) < 1e-12) {
  987.                     std::cerr << "Invalid V_rel copmuted: " << V_rel << " at timestep " << t_elapsed << std::endl;
  988.                 }
  989.  
  990.                 //initial relative velocity unit vectors components
  991.                 double e1 = V_rel_x/V_rel;
  992.                 double e2 = V_rel_y/V_rel;
  993.                 double e3 = V_rel_z/V_rel;
  994.  
  995.                 double C = -V_rel/(1.0+(m_e/M_n));
  996.                 double beta = sqrt(1.0 + thresh2/e.energy);
  997.                 // generating random variables to calculate random direction of center-of-mass after the collision
  998.  
  999.                 double R1 = dis(gen);
  1000.                 double R2 = dis(gen);
  1001.  
  1002.                 //// calculating spherical angles for center-of-mass random direction
  1003.                 // double theta = acos(1.0- 2.0*R1);
  1004.                 // double phi = 2*M_PI*R2;
  1005.                 double cos_khi = 1.0 - 2.0*R1;
  1006.                 double sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  1007.                 if (std::isnan(sin_khi) || std::isinf(sin_khi)) {
  1008.                     std::cerr << "Invalid sin_khi copmuted: " << sin_khi << " at timestep " << t_elapsed << std::endl;
  1009.                 }      
  1010.                 double phi = 2.0*M_PI*R2;
  1011.  
  1012.                 //calculating velocity change of electron
  1013.  
  1014.                 double dv_x = C*( (1.0-beta*cos_khi)*e1 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(-e2) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e1*e3)  );
  1015.                 double dv_y = C*( (1.0-beta*cos_khi)*e2 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(e1) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e2*e3)  );
  1016.                 double dv_z = C*( (1.0-beta*cos_khi)*e3 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(0.0) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(e1*e1 + e2*e2)  );
  1017.  
  1018.                 //updating electron energy and velocities        
  1019.                
  1020.                 e.vx += dv_x;
  1021.                 e.vy += dv_y;
  1022.                 e.vz += dv_z;
  1023.  
  1024.                 e.energy = (e.vx*e.vx + e.vy*e.vy + e.vz*e.vz) * charge_mass_ratio;
  1025.                 //counting collisions, working with flags, popping atom out of the vector
  1026.  
  1027.                 // if (!exc_2.empty()) {
  1028.                 //     std::swap(exc_2[index], exc_2.back());
  1029.                 //     exc_2.pop_back();
  1030.                 //     N_He++;
  1031.                 // }
  1032.  
  1033.                 collision_counter_en++;  
  1034.                 super2_coll_counter++;
  1035.                 super2_coll_counter_temp++;
  1036.                 energy_sup += thresh2;
  1037.  
  1038.                 e.collided_en = true;
  1039.             }              
  1040.  
  1041.             else if (r < P5) {
  1042.  
  1043.                 //ionization e-n collision happens
  1044.  
  1045.                 // ----   Collision energy redistribution module
  1046.  
  1047.                 // electron particle X Y Z initial velocities and energy
  1048.                 double V0_x = e.vx;
  1049.                 double V0_y = e.vy;
  1050.                 double V0_z = e.vz;
  1051.                 double E_0 = e.energy;
  1052.                
  1053.                 // neutral that collides with electron
  1054.  
  1055.                 // randomize particles each collision
  1056.  
  1057.                 NeutralParticle tmp_neutral;
  1058.                 tmp_neutral.initialize(gen, dis, maxwell_neutral);
  1059.                 double V_x_n = tmp_neutral.vx;
  1060.                 double V_y_n = tmp_neutral.vy;
  1061.                 double V_z_n = tmp_neutral.vz;
  1062.                 double E_n = tmp_neutral.energy;
  1063.  
  1064.  
  1065.                 double V0 = sqrt(V0_x*V0_x + V0_y*V0_y + V0_z*V0_z);
  1066.  
  1067.                 // initial relative velocity
  1068.                 double V_rel_x = V0_x - V_x_n;
  1069.                 double V_rel_y = V0_y - V_y_n;
  1070.                 double V_rel_z = V0_z - V_z_n;
  1071.  
  1072.                 double V_rel = sqrt(V_rel_x*V_rel_x + V_rel_y*V_rel_y + V_rel_z*V_rel_z);
  1073.                
  1074.                 if (std::isnan(V_rel) || std::isinf(V_rel) || fabs(V_rel) < 1e-12) {
  1075.                     std::cerr << "Invalid V_rel copmuted: " << V_rel << " at timestep " << t_elapsed << std::endl;
  1076.                 }
  1077.  
  1078.                 //initial relative velocity unit vectors components
  1079.                 double e1 = V_rel_x/V_rel;
  1080.                 double e2 = V_rel_y/V_rel;
  1081.                 double e3 = V_rel_z/V_rel;
  1082.  
  1083.                 double C = -V_rel/(1.0+(m_e/M_n));
  1084.                 double beta = sqrt(abs(1.0 - thresh3/e.energy));
  1085.                 // generating random variables to calculate random direction of center-of-mass after the collision
  1086.  
  1087.                 double R1 = dis(gen);
  1088.                 double R2 = dis(gen);
  1089.  
  1090.                 //// calculating spherical angles for center-of-mass random direction
  1091.                 // double theta = acos(1.0- 2.0*R1);
  1092.                 // double phi = 2*M_PI*R2;
  1093.                 double cos_khi = 1.0 - 2.0*R1;
  1094.                 double sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  1095.                 if (std::isnan(sin_khi) || std::isinf(sin_khi)) {
  1096.                     std::cerr << "Invalid sin_khi copmuted: " << sin_khi << " at timestep " << t_elapsed << std::endl;
  1097.                 }      
  1098.                 double phi = 2.0*M_PI*R2;
  1099.  
  1100.                 //calculating velocity change of electron
  1101.  
  1102.                 double dv_x = C*( (1.0-beta*cos_khi)*e1 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(-e2) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e1*e3)  );
  1103.                 double dv_y = C*( (1.0-beta*cos_khi)*e2 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(e1) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(-e2*e3)  );
  1104.                 double dv_z = C*( (1.0-beta*cos_khi)*e3 + (beta*sin_khi*sin(phi)/sqrt(e1*e1+e2*e2))*(0.0) + (beta*sin_khi*cos(phi)/sqrt(e1*e1+e2*e2))*(e1*e1 + e2*e2)  );
  1105.  
  1106.                 //updating electron energy and velocities        
  1107.                
  1108.                 if (e.energy < thresh3) {
  1109.                     null_coll_counter++;
  1110.                     collision_counter_en++;
  1111.                     e.collided_en = true;
  1112.                     continue;
  1113.                 }
  1114.                 else {
  1115.  
  1116.                     //new electron emerges, and two electrons shares the energy and obtain an equal directions (no physical background for this assumption,
  1117.                     // should think what to do with it - maybe regenerate velocities)
  1118.                    
  1119.                     //scatterd electron velocity rescaled by a factor of 2
  1120.                     e.vx = (e.vx + dv_x)/2.0;
  1121.                     e.vy = (e.vy + dv_y)/2.0;
  1122.                     e.vz = (e.vz + dv_z)/2.0;
  1123.                     e.energy = (e.vx*e.vx + e.vy*e.vy + e.vz*e.vz) * charge_mass_ratio;
  1124.                     e.collided_en = true;
  1125.  
  1126.                     //ejected electron velocity is the same
  1127.                     Electron ejected_e{e.vx, e.vy, e.vz, e.energy, false, false};
  1128.                     electrons.push_back(ejected_e);
  1129.                     n_e++;  
  1130.  
  1131.                     collision_counter_en++;  
  1132.                     ionization_counter++;
  1133.                     ionization_counter_temp++;
  1134.  
  1135.  
  1136.                 }
  1137.             }
  1138.  
  1139.             else {
  1140.                 // null-collision
  1141.                 collision_counter_en++;
  1142.                 null_coll_counter++;
  1143.                 e.collided_en = true;
  1144.             }
  1145.         }
  1146.  
  1147.  
  1148.         /// -- electrin field heating along E-Z axis begin--- /// -- second half!!!
  1149.         for (int idx : electron_indices) {
  1150.             double half_dt = dt/2.0;
  1151.             energy_Efield += ( C1*electrons[idx].vz*half_dt + C2*half_dt*half_dt )/q; //dividing by q to get eV
  1152.             // Update velocity component due to electric field
  1153.             // double a_z = ((-1.0)*q * E_reduced) / m_e; // acceleration in z-direction, m/s^2
  1154.             electrons[idx].vz += a_z * (dt*0.5); // only half timestep
  1155.  
  1156.             // Recalculate energy from updated velocity
  1157.             double vx = electrons[idx].vx;
  1158.             double vy = electrons[idx].vy;
  1159.             double vz = electrons[idx].vz;
  1160.             electrons[idx].energy = (vx*vx + vy*vy + vz*vz) * charge_mass_ratio;
  1161.         }
  1162.         // -------------------------------------------- filed heating ends ------------------------////////////////
  1163.  
  1164.         int decay_counter = 0;
  1165.  
  1166.         // // Iterate backwards to safely remove elements
  1167.         // for (int i = exc_2.size() - 1; i >= 0; --i) {
  1168.         //     if (dis(gen) < dt / tau_singlet) {
  1169.         //         // Swap with last element and pop (like your superelastic code)
  1170.         //         std::swap(exc_2[i], exc_2.back());
  1171.         //         exc_2.pop_back();
  1172.         //         N_He++;
  1173.         //         decay_counter++;
  1174.         //     }
  1175.         // }
  1176.  
  1177.  
  1178.         t_elapsed += dt; // Advance time
  1179.  
  1180.         // Recalculate nu_max periodically (e.g., every 100 steps)
  1181.         static int recalc_counter = 0;
  1182.         if (++recalc_counter >= 1000) {
  1183.            
  1184.             recalc_counter = 0;
  1185.  
  1186.             // Recalculate nu_max with CURRENT densities
  1187.             nu_max = 0.0;
  1188.             for (int i = 0; i < N; i++) {
  1189.                 double energy = Emin + (i + 0.5) * bin_width;
  1190.                
  1191.                 // Get current densities
  1192.                 double n_ground = N_He / Volume;
  1193.                 double n_excited1 = exc_1.size() / Volume;
  1194.                 double n_excited2 = exc_2.size() / Volume;
  1195.                
  1196.                 // Total collision frequency for this energy bin
  1197.                 double sigma_total =
  1198.                     elastic_vec[i] * n_ground +
  1199.                     inelastic1_vec[i] * n_ground +
  1200.                     inelastic2_vec[i] * n_ground +
  1201.                     superelastic1_vec[i] * n_excited1 +
  1202.                     superelastic2_vec[i] * n_excited2 +
  1203.                     ionization_vec[i] * n_ground;
  1204.  
  1205.                 double speed = sqrt_charge_mass*sqrt(energy);
  1206.                 double nu_temp = sigma_total * speed;
  1207.                
  1208.                 if (nu_temp > nu_max) nu_max = nu_temp;
  1209.             }
  1210.  
  1211.             // Update dt based on new nu_max
  1212.             dt = 0.1 / nu_max;        
  1213.         }  
  1214.  
  1215.         // calculating mean energy
  1216.         if (static_cast<int>(t_elapsed/dt)%print_interval == 0) {
  1217.             double total_energy = 0.0;
  1218.             for (const auto& e : electrons) total_energy += e.energy;
  1219.             double mean_energy = total_energy / n_e;
  1220.             file8 << t_elapsed << " " << mean_energy << "\n";            
  1221.             file_temp << t_elapsed << " " << exc_1.size() << " " << exc_2.size() << "\n";
  1222.             std::cout << "Progress: " << (t_elapsed/total_time)*100 << "%" << " ";
  1223.             std::cout << "   nu_max: " << nu_max << "    " << "dt: " << dt << " " << "decay counter: " << decay_counter <<   "\n";
  1224.             file16 << t_elapsed << " " << energy_Efield/n_e << " " << energy_sup/n_e << "\n";
  1225.             file17 << t_elapsed << " " << n_e << "\n";
  1226.         }        
  1227.  
  1228.     }
  1229.  
  1230.     // ----- final electron energies distribution begins
  1231.     for (int i = 0; i < n_e; i++){
  1232.  
  1233.         file2 << i << " " << electrons[i].energy << "\n";
  1234.  
  1235.         int bin = static_cast<int>( (electrons[i].energy - Emin)/bin_width_smooth);
  1236.         if (bin >=0 && bin < histo_maxwell.size())
  1237.             histo_maxwell[bin]++;
  1238.     }
  1239.  
  1240.     int check = 0;
  1241.     for (int i = 0; i < N_smooth; i++){
  1242.         check += histo_maxwell[i];
  1243.         double bin_center = Emin + (i + 0.5) * bin_width_smooth;
  1244.         file4 << bin_center << " " <<  static_cast<double>(histo_maxwell[i])/(electrons.size()*bin_width_smooth) << "\n"; // getting f(E)
  1245.     }
  1246.  
  1247.         std::cout << "Total # of electrons in a final histogram: " << check << "\n";
  1248.         std::cout << "Final nu max: " << nu_max << "\n";
  1249.  
  1250.     // ----- final electron energies distribution ends
  1251.  
  1252.  
  1253.     file0.close();
  1254.     file1.close();
  1255.     file2.close();
  1256.     file3.close();
  1257.     file4.close();
  1258.     file5.close();
  1259.     file6.close();
  1260.     file7.close();
  1261.     file8.close();
  1262.     file9.close();
  1263.     file10.close();
  1264.     file11.close();
  1265.     file12.close();
  1266.     file13.close();
  1267.     file14.close();
  1268.     file15.close();
  1269.     file_temp.close();
  1270.     file16.close();
  1271.     file17.close();
  1272.  
  1273.     clock_t end = clock();
  1274.  
  1275.     double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
  1276.  
  1277.     // std::cout << "# of steps: " << steps << "\n";
  1278.     // std::cout << "# of electrons collided each timesteps:" << Ne_collided << "\n";
  1279.    
  1280.     // std::cout << "Average elastic collisions per timestep: " << static_cast<int>(el_coll_counter/steps) << "\n";
  1281.     // std::cout << "Average null collisions per timestep: " << static_cast<int>(null_coll_counter/steps) << "\n";
  1282.     // std::cout << "\n";
  1283.  
  1284.     // std::cout << "triplet:________" << "\n";
  1285.     // std::cout << "Average triplet excitation collisions per timestep: " << static_cast<int>(exc1_coll_counter/steps) << "\n";
  1286.     // std::cout << "\n";
  1287.     // std::cout << "Average superelastic triplet collisions per timestep: " << static_cast<int>(super1_coll_counter/steps) << "\n";
  1288.     // std::cout << "\n";
  1289.  
  1290.     // std::cout << "singlet:________" << "\n";
  1291.     // std::cout << "Average singlet excitation collisions per timestep: " << static_cast<int>(exc2_coll_counter/steps) << "\n";
  1292.     // std::cout << "\n";
  1293.     // std::cout << "Average superelastic singlet collisions per timestep: " << static_cast<int>(super2_coll_counter/steps) << "\n";
  1294.     // std::cout << "\n";    
  1295.  
  1296.     // std::cout << "Average e-e collisions per timestep: " << static_cast<int>(ee_coll_counter/steps) << "\n";
  1297.  
  1298.     std::cout << "Elapsed time: %f seconds " << elapsed << "\n";
  1299.  
  1300.  
  1301.     return 0;
  1302.  
  1303. }
Add Comment
Please, Sign In to add comment