Advertisement
phystota

superelastic+field_heating(v2)

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