Advertisement
phystota

nu_max_recalculation_same_mean_energy_problem

Apr 16th, 2025
335
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 49.21 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 thresh1 19.82 // threshold energy excitation tripet state
  23. #define thresh2 20.61 // threshold energy excitation singlet state
  24.  
  25. // simulation parameters
  26.  
  27. #define n_e 100000
  28. // #define N_He 1000000 // Helium neutrals number
  29. #define T_n 2.0 // Helium neutral temperature in eV
  30. #define T_e 5.0    // electron Maxwell initial distribution
  31. #define Emin 0.0
  32. #define Emax 4000.0
  33. #define Volume 1.0E-12 // Volume to calculate netral density and collision frequency
  34. #define time 3.0E-4 // 500 microsec time to equalibrate the system
  35. #define dopant 1.0E-5 // addition to avoid zero
  36. #define E_reduced 0.1 // constant electrin field along z-axis
  37.  
  38. #define bin_width 0.05 // 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.05 // 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.         // Linear extrapolation using last two points
  160.         const auto& last = CS[CS.size() - 1];
  161.         const auto& prev = CS[CS.size() - 2];
  162.         double slope = (last.sigma - prev.sigma) / (last.energy - prev.energy);
  163.         return std::max(0.0, last.sigma + slope * (energy - last.energy));      
  164.     }
  165.  
  166.     int step = 0;  
  167.         while (step < CS.size() && energy > CS[step].energy) {
  168.             step++;
  169.         }
  170.  
  171.     double k = (CS[step].sigma - CS[step-1].sigma)/(CS[step].energy - CS[step-1].energy);
  172.     double m = CS[step].sigma - k*CS[step].energy;
  173.    
  174.     return k*energy + m;
  175. }
  176.  
  177.  
  178. struct NeutralParticle {
  179.  
  180.     double energy = 0.0;
  181.     double vx = 0.0;
  182.     double vy = 0.0;
  183.     double vz = 0.0;
  184.  
  185.     void initialize(std::mt19937& gen, std::uniform_real_distribution<double>& dis, std::gamma_distribution<double>& maxwell) {
  186.  
  187.         double R = dis(gen);
  188.  
  189.         // velocity angles in spherical coordinates
  190.         double phi = 2*M_PI*dis(gen);
  191.         double cosTheta = 2.0*dis(gen) - 1.0;
  192.         double sinTheta = sqrt(1.0 - cosTheta*cosTheta);
  193.  
  194.            
  195.         energy = maxwell(gen); // neutrals energies sampled as Maxwell distribution in eV
  196.            
  197.         double speed = sqrt(2*energy*q/M_n);
  198.  
  199.         //velocity components of neutrals in m/s
  200.         vx = speed * sinTheta * cos(phi);
  201.         vy = speed * sinTheta * sin(phi);
  202.         vz = speed * cosTheta;
  203.     }
  204.    
  205. };
  206.  
  207. struct Excited_neutral {
  208.  
  209.     double energy;
  210.     double vx;
  211.     double vy;
  212.     double vz;
  213.    
  214. };
  215.  
  216.  
  217.  
  218. int main() {
  219.  
  220.     clock_t start = clock();
  221.  
  222.     int N_He = 10000000;
  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.  
  238.     std::random_device rd;
  239.     std::mt19937 gen(rd());
  240.     std::uniform_real_distribution<double> dis(0.0, 1.0);
  241.     std::gamma_distribution<double> maxwell_neutral(1.5, T_n);
  242.     std::gamma_distribution<double> maxwell_electron(1.5, T_e);
  243.  
  244.     std::ifstream elastic_cs_dat("cross_sections/elastic.dat");
  245.     if (!elastic_cs_dat.is_open()) {
  246.         std::cerr << "Error opening elastic cross-sections file!" << std::endl;
  247.         return 1;
  248.     }    
  249.  
  250.     std::ifstream excitation1_cs_dat("cross_sections/inelastic_triplet.dat");
  251.     if (!excitation1_cs_dat.is_open()) {
  252.         std::cerr << "Error opening inelastic triplet cross-sections file!" << std::endl;
  253.         return 1;
  254.     }
  255.  
  256.     std::ifstream excitation2_cs_dat("cross_sections/inelastic_singlet.dat");
  257.     if (!excitation2_cs_dat.is_open()) {
  258.         std::cerr << "Error opening inelastic singlet cross-sections file!" << std::endl;
  259.         return 1;
  260.     }
  261.  
  262.     // --- starts reading cross section datafiles
  263.  
  264. //-----------------elastic---------------------------//
  265.     std::vector<CrossSection> elastic_CS_temp;
  266.  
  267.     double energy, sigma;
  268.  
  269.     while (elastic_cs_dat >> energy >> sigma) {
  270.         elastic_CS_temp.push_back({energy, sigma});
  271.     }    
  272.     elastic_cs_dat.close();
  273.  
  274.     energy = 0.0;
  275.     sigma = 0.0;
  276. //-----------------triplet excitation---------------------------//
  277.     std::vector<CrossSection> inelastic1_CS_temp;
  278.  
  279.     while (excitation1_cs_dat >> energy >> sigma) {
  280.         inelastic1_CS_temp.push_back({energy, sigma});
  281.     }    
  282.     excitation1_cs_dat.close();    
  283. //-----------------singlet excitation---------------------------//
  284.     energy = 0.0;
  285.     sigma = 0.0;
  286.  
  287.     std::vector<CrossSection> inelastic2_CS_temp;
  288.  
  289.     while (excitation2_cs_dat >> energy >> sigma) {
  290.         inelastic2_CS_temp.push_back({energy, sigma});
  291.     }    
  292.     excitation2_cs_dat.close();    
  293.  
  294.     // --- finish reading cross-section datafiles  
  295.  
  296.     std::ofstream file0("output_files/velocities.dat");    
  297.     std::ofstream file1("output_files/energies.dat");        
  298.     std::ofstream file2("output_files/energies_final.dat");    
  299.     std::ofstream file3("output_files/histo_random.dat");    
  300.     file3 << std::fixed << std::setprecision(10);
  301.    
  302.     std::ofstream file4("output_files/histo_maxwell.dat");
  303.     file4 << std::fixed << std::setprecision(10);          
  304.    
  305.     std::ofstream file5("output_files/neutral_distribution.dat");    
  306.     std::ofstream file6("output_files/E*f(E).dat");    
  307.     std::ofstream file7("output_files/nu_max.dat");
  308.     std::ofstream file8("output_files/electron_mean_energy.dat");
  309.     std::ofstream file9("output_files/nu_elastic_average_initial.dat");
  310.     std::ofstream file10("output_files/nu_inelastic1_average_initial.dat");
  311.     std::ofstream file11("output_files/nu_elastic_average_final.dat");
  312.     std::ofstream file12("output_files/nu_inelastic1_average_final.dat");
  313.     std::ofstream file13("output_files/log_output.dat");  
  314.     std::ofstream file14("output_files/excited_energies.dat");      
  315.     std::ofstream file15("output_files/excited_histo.dat");            
  316.     std::ofstream file_temp("output_files/collision_rates.dat");  
  317.  
  318.     // Initialize all electrons
  319.     for (auto& e : electrons) {
  320.         e.initialize(gen, dis, maxwell_electron);
  321.     }
  322.  
  323.     // precalculate cross-sections for each energy bin
  324.     for (int i = 0; i < N; i++){
  325.         elastic_vec[i] = interpolate(bin_width*(i+0.5), elastic_CS_temp); //elastiuc
  326.         inelastic1_vec[i] = interpolate(bin_width*(i+0.5), inelastic1_CS_temp); //triplet excitation
  327.         inelastic2_vec[i] = interpolate(bin_width*(i+0.5), inelastic2_CS_temp); //singlet excitation
  328.     }
  329.  
  330.     // precalculate superelastic cross-section (triplet -> ground) for each energy bin
  331.     // detailed balance gives: sigma_j_i(E) = (g_i/g_j)*((E+theshold)/E)*sigma_i_j(E+theshold)
  332.     for (int i = 0; i < N; i++){
  333.         double energy = Emin + (i + 0.5) * bin_width;
  334.         int thresh_bin = (int)( (thresh1 - Emin)/bin_width ); // calculating bin for threshold energy
  335.         superelastic1_vec[i] = (1.0/3.0)*((energy + thresh1)/energy)*interpolate(energy + thresh1, inelastic1_CS_temp); // using detailed balance, calculating backward deexcitation cross-section
  336.         superelastic2_vec[i] = (1.0/1.0)*((energy + thresh2)/energy)*interpolate(energy + thresh2, inelastic2_CS_temp);
  337.     }
  338.  
  339.     for (int i = 0; i < n_e; i++){
  340.         file1 << i << " " << electrons.at(i).energy << "\n";
  341.         file0 << i << " " << electrons[i].vx << " " << electrons[i].vy << " " << electrons[i].vz << "\n";
  342.     }
  343.  
  344.     // -----initial electrons energy distribution starts------------////
  345.     for (int i = 0; i < n_e; i++){
  346.         int bin = (int)( (electrons[i].energy - Emin)/bin_width );
  347.         if (bin >=0 && bin < histo_random.size())
  348.             histo_random[bin]++;
  349.     }
  350.  
  351.     for (int i = 0; i < histo_random.size(); i++){
  352.         double bin_center = Emin + (i + 0.5) * bin_width;
  353.         file3 << bin_center << " " <<  static_cast<double>(histo_random[i])/(electrons.size()*bin_width) << "\n"; // this is electron normalized distribution function
  354.     }
  355.     // -----initial electrons energy distribution ends------------////    
  356.  
  357.     //calculating excited specied population
  358.  
  359.     /*From Boltzman distribution y_k = g_k*exp(-E_k/kT)/norm, where g_k - stat weight of k-state,
  360.     E_k - threshold energy for k-state, norm is a total partition function or normaliztion factor     */
  361.  
  362.     double part_ground = 1.0*exp(-0.0/T_n); // partition function for ground state
  363.     double part_triplet = 3.0*exp(-thresh1/T_n); // partition function for triplet excited state
  364.     double part_singlet = 1.0*exp(-thresh2/T_n); // partition function for singlet excited state
  365.     double part_func_total = part_ground + part_triplet + part_singlet; // total partition function
  366.     double N_trpilet = (part_triplet/part_func_total)*N_He; // population of tripet state
  367.     double N_singlet = (part_singlet/part_func_total)*N_He; // population of singlet state
  368.  
  369.     std::vector<Excited_neutral> exc_1(static_cast<int>(N_trpilet));  // vector to track triplet excited atoms of Helium
  370.     std::vector<Excited_neutral> exc_2(static_cast<int>(N_singlet));  // vector to track singlet excited atoms of Helium    
  371.  
  372.     // adjusting neutrals number:
  373.  
  374.     N_He -= (N_trpilet + N_singlet);
  375.  
  376.     // initializing excited species with Maxwellian distribution
  377.  
  378.     for (auto& exc : exc_1) {
  379.     NeutralParticle tmp_neutral;
  380.     tmp_neutral.initialize(gen, dis, maxwell_neutral);
  381.     exc.energy = tmp_neutral.energy;
  382.     exc.vx = tmp_neutral.vx;
  383.     exc.vy = tmp_neutral.vy;
  384.     exc.vz = tmp_neutral.vz;
  385.     }
  386.  
  387.     for (auto& exc : exc_2) {
  388.     NeutralParticle tmp_neutral;
  389.     tmp_neutral.initialize(gen, dis, maxwell_neutral);
  390.     exc.energy = tmp_neutral.energy;
  391.     exc.vx = tmp_neutral.vx;
  392.     exc.vy = tmp_neutral.vy;
  393.     exc.vz = tmp_neutral.vz;
  394.     }
  395.  
  396.     // std::cout << "Triplet population initialized: " << exc_1.size() << "\n";
  397.     // std::cout << "Singlet population initialized: " << exc_2.size() << "\n";    
  398.  
  399.     // calculating excited specied population finished //
  400.  
  401.     int print_interval = 100;
  402.     int el_coll_counter = 0; // track all elastic collisions
  403.     int exc1_coll_counter = 0; // track all triplet excitation collisions
  404.     int exc2_coll_counter = 0; // track all singlet excitation collisions
  405.     int null_coll_counter = 0; // track null-collisions
  406.     int ee_coll_counter = 0; //track e-e Coulomb collisions
  407.     int super1_coll_counter = 0; // track superelastic triplet collisions
  408.     int super2_coll_counter = 0; // track superelastic triplet collisions    
  409.  
  410.  
  411.     double total_time = 3.0E-3; // Your original `time` constant
  412.     double t_elapsed = 0.0;
  413.  
  414.     // -----calculating nu-max for null-collision method starts ------------////
  415.     double nu_max = 0.0;
  416.     double nu_max_temp = 0.0;
  417.     double sigma_total = 0.0;
  418.    
  419.     for (int i = 0; i < N; i++){
  420.         // Get initial densities
  421.         double n_ground = N_He / Volume;
  422.         double n_excited1 = exc_1.size() / Volume;
  423.         double n_excited2 = exc_2.size() / Volume;
  424.  
  425.         double energy = Emin + (i + 0.5) * bin_width;
  426.  
  427.         // Total collision frequency for this energy bin
  428.         double sigma_total =
  429.             elastic_vec[i] * n_ground +
  430.             inelastic1_vec[i] * n_ground +
  431.             inelastic2_vec[i] * n_ground +
  432.             superelastic1_vec[i] * n_excited1 +
  433.             superelastic2_vec[i] * n_excited2;
  434.  
  435.         double v = sqrt(2 * energy * q / m_e);
  436.         double nu_temp = sigma_total * v;
  437.        
  438.         if (nu_temp > nu_max) nu_max = nu_temp;
  439.     }
  440.  
  441.     std::cout << nu_max << "\n";
  442.     // -----calculating nu-max for null-collision method ends ------------////
  443.  
  444.     double dt = 0.1/nu_max;   // minimum should be 0.1/nu_max to get acceptable numerical error range see Vahedi Surrendra 1995
  445.  
  446.     while (t_elapsed < total_time) {
  447.         // Handle edge case for final step
  448.         if (t_elapsed + dt > total_time) {
  449.             dt = total_time - t_elapsed;
  450.         }    
  451.  
  452.         std::cout << "Progress: " << (t_elapsed/total_time)*100 << "%" << " ";
  453.  
  454.         //using  null-collision technique, getting the number of particles colliding each step: P_collision = 1 - exp(-nu_max*dt)
  455.         int Ne_collided = (1.0-exp(-1.0*dt*nu_max))*n_e;
  456.  
  457.         // Generate shuffled list of electron indices
  458.         int reshuffle_interval = 1;
  459.         std::vector<int> electron_indices(n_e);
  460.         std::iota(electron_indices.begin(), electron_indices.end(), 0); // fill with index
  461.         std::shuffle(electron_indices.begin(), electron_indices.end(), gen); // shuffle the indexes    
  462.  
  463.         // Generate shuffled list of triplet excited atoms indices
  464.         std::vector<int> excited1_indices(exc_1.size());
  465.         std::iota(excited1_indices.begin(), excited1_indices.end(), 0); // fill with index
  466.         std::shuffle(excited1_indices.begin(), excited1_indices.end(), gen); // shuffle the indexes    
  467.  
  468.         // Generate shuffled list of singlet excited atoms indices
  469.         std::vector<int> excited2_indices(exc_2.size());
  470.         std::iota(excited2_indices.begin(), excited2_indices.end(), 0); // fill with index
  471.         std::shuffle(excited2_indices.begin(), excited2_indices.end(), gen); // shuffle the indexes    
  472.  
  473.         int exc1_coll_counter_temp = 0;
  474.         int super1_coll_counter_temp = 0;
  475.         int exc2_coll_counter_temp = 0;
  476.         int super2_coll_counter_temp = 0;
  477.         int null_coll_counter_temp = 0;
  478.  
  479.  
  480.         // calculating mean energy
  481.         double total_energy = 0.0;
  482.         for (const auto& e : electrons) total_energy += e.energy;
  483.         double mean_energy = total_energy / n_e;
  484.         file8 << t_elapsed << " " << mean_energy << "\n";            
  485.  
  486.  
  487.         // setting flags to false each timestep
  488.         for (auto& e : electrons) e.collided_en = false;
  489.         for (auto& e : electrons) e.collided_ee = false;        
  490.  
  491.         int collision_counter_en = 0; // electron-neutral collision counter
  492.         int collision_counter_ee = 0; // e-e collisoin counter
  493.  
  494.         /// -- electrin field heating along E-Z axis begin--- ///
  495.         for (int idx : electron_indices) {
  496.  
  497.             // Update velocity component due to electric field
  498.             double a_z = ((-1.0)*q * E_reduced) / m_e; // acceleration in z-direction, m/s^2
  499.             electrons[idx].vz += a_z * dt;
  500.  
  501.             // Recalculate energy from updated velocity
  502.             double vx = electrons[idx].vx;
  503.             double vy = electrons[idx].vy;
  504.             double vz = electrons[idx].vz;
  505.             electrons[idx].energy = 0.5 * m_e * (vx*vx + vy*vy + vz*vz) / q;
  506.         }
  507.         // -------------------------------------------- filed heating ends ------------------------////////////////
  508.  
  509.         for (int idx : electron_indices) {
  510.  
  511.             if (collision_counter_en >= Ne_collided) break; // quit if reached all collisions
  512.  
  513.             Electron& e = electrons[idx];
  514.             if (e.collided_en) continue;  // Skip already collided electrons
  515.  
  516.             double electron_energy = e.energy;
  517.             int bin_energy = static_cast<int>(electron_energy / bin_width);
  518.             double nu_elastic = (N_He/Volume) * elastic_vec[bin_energy] * sqrt(2.0*electron_energy*q/m_e);
  519.             double nu_inelastic1 = (N_He/Volume) * inelastic1_vec[bin_energy] * sqrt(2.0*electron_energy*q/m_e);
  520.             double nu_superelastic1 = (exc_1.size()/Volume) * superelastic1_vec[bin_energy] * sqrt(2.0*electron_energy*q/m_e);
  521.             double nu_inelastic2 = (N_He/Volume) * inelastic2_vec[bin_energy] * sqrt(2.0*electron_energy*q/m_e);
  522.             double nu_superelastic2 = (exc_2.size()/Volume) * superelastic2_vec[bin_energy] * sqrt(2.0*electron_energy*q/m_e);
  523.  
  524.             double r = dis(gen);
  525.  
  526.             double P0 = nu_elastic/nu_max;
  527.             double P1 = (nu_elastic + nu_inelastic1)/nu_max;
  528.             double P2 = (nu_elastic + nu_inelastic1 + nu_superelastic1)/nu_max;
  529.             double P3 = (nu_elastic + nu_inelastic1 + nu_superelastic1 + nu_inelastic2)/nu_max;
  530.             double P4 = (nu_elastic + nu_inelastic1 + nu_superelastic1 + nu_inelastic2 + nu_superelastic2)/nu_max;            
  531.  
  532.             if (r < P0) {
  533.  
  534.                 // elastic collision happens
  535.  
  536.                 // ----   Collision energy redistribution module
  537.  
  538.  
  539.                 // electron particle X Y Z initial velocities and energy
  540.                 double V0_x = e.vx;
  541.                 double V0_y = e.vy;
  542.                 double V0_z = e.vz;
  543.                 double E_0 = e.energy;
  544.                
  545.                 // neutral that collides with electron
  546.  
  547.                 // randomize particles each collision
  548.  
  549.                 NeutralParticle tmp_neutral;
  550.                 tmp_neutral.initialize(gen, dis, maxwell_neutral);
  551.                 double V_x_n = tmp_neutral.vx;
  552.                 double V_y_n = tmp_neutral.vy;
  553.                 double V_z_n = tmp_neutral.vz;
  554.                 double E_n = tmp_neutral.energy;
  555.  
  556.  
  557.                 double V0 = sqrt(V0_x*V0_x + V0_y*V0_y + V0_z*V0_z);
  558.                
  559.                 // generating random variables to calculate random direction of center-of-mass after the collision
  560.  
  561.                 double R1 = dis(gen);
  562.                 double R2 = dis(gen);
  563.  
  564.                 //// calculating spherical angles for center-of-mass random direction
  565.                 // double theta = acos(1.0- 2.0*R1);
  566.                 // double phi = 2*M_PI*R2;
  567.                 double cos_khi = (2.0 + E_0 - 2.0*pow((1+E_0), R1))/E_0;
  568.                 if (cos_khi >= 1)
  569.                     cos_khi = 1.0 - dopant;
  570.                 if (cos_khi <= -1)
  571.                     cos_khi = -1.0 + dopant;
  572.                                      
  573.                 double sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  574.  
  575.                 double phi = 2.0*M_PI*R2;
  576.                 double cos_theta = V0_x/V0;
  577.                 double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
  578.                 // //calculating final relative velocity with random direction
  579.  
  580.                 //calculating final velocity of electron
  581.  
  582.                 double i_scat = (V0_x/V0)*cos_khi + (1.0 - (V0_x/V0)*(V0_x/V0))*(sin_khi*cos(phi)/sin_theta);
  583.                 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;
  584.                 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;
  585.  
  586.                 //updating electron energy and velocities  
  587.  
  588.                 double delta_E = 2.0*(m_e/M_n)*(1.0 - cos_khi)*E_0;
  589.                 if (e.energy < delta_E) {
  590.                     null_coll_counter++;
  591.                     collision_counter_en++;
  592.                     e.collided_en = true;
  593.                     continue;
  594.                 }    
  595.                 else {                          
  596.                     e.energy = E_0 - delta_E;
  597.                 }
  598.                
  599.  
  600.                 double speed = sqrt(2*e.energy*q/m_e);
  601.  
  602.                 e.vx = speed*i_scat;
  603.                 e.vy = speed*j_scat;
  604.                 e.vz = speed*k_scat;              
  605.  
  606.                 collision_counter_en++;
  607.                 el_coll_counter++;
  608.  
  609.                 e.collided_en = true;
  610.             }        
  611.  
  612.             else if (r < P1) {
  613.  
  614.                 //inelastic 1(triplet) collision happens
  615.  
  616.                 // ----   Collision energy redistribution module
  617.  
  618.                 // electron particle X Y Z initial velocities and energy
  619.                 double V0_x = e.vx;
  620.                 double V0_y = e.vy;
  621.                 double V0_z = e.vz;
  622.                 double E_0 = e.energy;
  623.                
  624.                 // neutral that collides with electron
  625.  
  626.                 // randomize particles each collision
  627.  
  628.                 NeutralParticle tmp_neutral;
  629.                 tmp_neutral.initialize(gen, dis, maxwell_neutral);
  630.                 double V_x_n = tmp_neutral.vx;
  631.                 double V_y_n = tmp_neutral.vy;
  632.                 double V_z_n = tmp_neutral.vz;
  633.                 double E_n = tmp_neutral.energy;
  634.  
  635.  
  636.                 double V0 = sqrt(V0_x*V0_x + V0_y*V0_y + V0_z*V0_z);
  637.                
  638.                 // generating random variables to calculate random direction of center-of-mass after the collision
  639.  
  640.                 double R1 = dis(gen);
  641.                 double R2 = dis(gen);
  642.  
  643.                 //// calculating spherical angles for center-of-mass random direction
  644.                 // double theta = acos(1.0- 2.0*R1);
  645.                 // double phi = 2*M_PI*R2;
  646.                 double cos_khi = (2.0 + E_0 - 2.0*pow((1+E_0), R1))/E_0;
  647.                 double sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  648.  
  649.                 double phi = 2.0*M_PI*R2;
  650.                 double cos_theta = V0_x/V0;
  651.                 double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
  652.                 // //calculating final relative velocity with random direction
  653.  
  654.                 //calculating final velocity of electron
  655.  
  656.                 double i_scat = (V0_x/V0)*cos_khi + (1.0 - (V0_x/V0)*(V0_x/V0))*(sin_khi*cos(phi)/sin_theta);
  657.                 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;
  658.                 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;
  659.  
  660.                 //updating electron energy and velocities        
  661.                
  662.                 if (e.energy < thresh1) {
  663.                     null_coll_counter++;
  664.                     collision_counter_en++;
  665.                     e.collided_en = true;
  666.                     continue;
  667.                 }
  668.                 else {
  669.                     e.energy = E_0 - thresh1;
  670.  
  671.                     double speed = sqrt(2*e.energy*q/m_e);
  672.  
  673.                     e.vx = speed*i_scat;
  674.                     e.vy = speed*j_scat;
  675.                     e.vz = speed*k_scat;
  676.  
  677.                     collision_counter_en++;  
  678.                     exc1_coll_counter++;
  679.                     exc1_coll_counter_temp++;
  680.  
  681.                     e.collided_en = true;
  682.  
  683.                     // pushing this neutral to an array of excited species exc_1
  684.                     if (N_He > 0) {
  685.                         exc_1.push_back({E_n, V_x_n, V_y_n, V_z_n});
  686.                         N_He--;
  687.                     }
  688.                 }
  689.             }    
  690.  
  691.             else if (r < P2) {
  692.  
  693.                 //superelastic 1(triplet -> ground state) collision happens
  694.  
  695.                 if (exc_1.empty()) {
  696.                     null_coll_counter++;
  697.                     collision_counter_en++;
  698.                     e.collided_en = true;
  699.                     continue;            
  700.                 }
  701.  
  702.                 // ----   Collision energy redistribution module
  703.  
  704.                 // electron particle X Y Z initial velocities and energy
  705.                 double V0_x = e.vx;
  706.                 double V0_y = e.vy;
  707.                 double V0_z = e.vz;
  708.                 double E_0 = e.energy;
  709.  
  710.                 double V0 = sqrt(V0_x*V0_x + V0_y*V0_y + V0_z*V0_z);
  711.  
  712.                 // neutral that collides with electron
  713.                 // taking particles from dynamic array of excited neutrals
  714.  
  715.                 int index = std::uniform_int_distribution<int>(0, exc_1.size()-1)(gen);
  716.                 Excited_neutral& exc = exc_1[index];
  717.                 double V_x = exc.vx;
  718.                 double V_y = exc.vy;
  719.                 double V_z = exc.vz;
  720.                 double E = exc.energy;
  721.                
  722.                 // generating random variables to calculate random direction of center-of-mass after the collision
  723.  
  724.                 double R1 = dis(gen);
  725.                 double R2 = dis(gen);
  726.  
  727.                 //// calculating spherical angles for center-of-mass random direction
  728.                 // double theta = acos(1.0- 2.0*R1);
  729.                 // double phi = 2*M_PI*R2;
  730.                 double cos_khi = (2.0 + E_0 - 2.0*pow((1+E_0), R1))/E_0;
  731.                 double sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  732.  
  733.                 double phi = 2.0*M_PI*R2;
  734.                 double cos_theta = V0_x/V0;
  735.                 double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
  736.                 // //calculating final relative velocity with random direction
  737.  
  738.                 //calculating final velocity of electron
  739.  
  740.                 double i_scat = (V0_x/V0)*cos_khi + (1.0 - (V0_x/V0)*(V0_x/V0))*(sin_khi*cos(phi)/sin_theta);
  741.                 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;
  742.                 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;
  743.  
  744.                 //updating electron energy and velocities        
  745.                
  746.                 e.energy = E_0 + thresh1;
  747.  
  748.                 double speed = sqrt(2*e.energy*q/m_e);
  749.  
  750.                 e.vx = speed*i_scat;
  751.                 e.vy = speed*j_scat;
  752.                 e.vz = speed*k_scat;
  753.  
  754.                 //counting collisions, working with flags, popping atom out of the vector
  755.                 if (!exc_1.empty()) {
  756.                     std::swap(exc_1[index], exc_1.back());
  757.                     exc_1.pop_back();
  758.                     N_He++;
  759.                 }
  760.                 collision_counter_en++;  
  761.                 super1_coll_counter++;
  762.                 super1_coll_counter_temp++;
  763.  
  764.                 e.collided_en = true;
  765.             }  
  766.  
  767.             else if (r < P3) {
  768.  
  769.                 //inelastic 1(singlet) excitation collision happens
  770.  
  771.                 // ----   Collision energy redistribution module
  772.  
  773.                 // electron particle X Y Z initial velocities and energy
  774.                 double V0_x = e.vx;
  775.                 double V0_y = e.vy;
  776.                 double V0_z = e.vz;
  777.                 double E_0 = e.energy;
  778.                
  779.                 // neutral that collides with electron
  780.  
  781.                 // randomize particles each collision
  782.  
  783.                 NeutralParticle tmp_neutral;
  784.                 tmp_neutral.initialize(gen, dis, maxwell_neutral);
  785.                 double V_x_n = tmp_neutral.vx;
  786.                 double V_y_n = tmp_neutral.vy;
  787.                 double V_z_n = tmp_neutral.vz;
  788.                 double E_n = tmp_neutral.energy;
  789.  
  790.  
  791.                 double V0 = sqrt(V0_x*V0_x + V0_y*V0_y + V0_z*V0_z);
  792.                
  793.                 // generating random variables to calculate random direction of center-of-mass after the collision
  794.  
  795.                 double R1 = dis(gen);
  796.                 double R2 = dis(gen);
  797.  
  798.                 //// calculating spherical angles for center-of-mass random direction
  799.                 // double theta = acos(1.0- 2.0*R1);
  800.                 // double phi = 2*M_PI*R2;
  801.                 double cos_khi = (2.0 + E_0 - 2.0*pow((1+E_0), R1))/E_0;
  802.                 double sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  803.  
  804.                 double phi = 2.0*M_PI*R2;
  805.                 double cos_theta = V0_x/V0;
  806.                 double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
  807.                 // //calculating final relative velocity with random direction
  808.  
  809.                 //calculating final velocity of electron
  810.  
  811.                 double i_scat = (V0_x/V0)*cos_khi + (1.0 - (V0_x/V0)*(V0_x/V0))*(sin_khi*cos(phi)/sin_theta);
  812.                 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;
  813.                 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;
  814.  
  815.                 //updating electron energy and velocities        
  816.                
  817.                 if (e.energy < thresh2) {
  818.                     null_coll_counter++;
  819.                     collision_counter_en++;
  820.                     e.collided_en = true;
  821.                     continue;
  822.                 }
  823.                 else {
  824.                     e.energy = E_0 - thresh2;
  825.  
  826.                     double speed = sqrt(2*e.energy*q/m_e);
  827.  
  828.                     e.vx = speed*i_scat;
  829.                     e.vy = speed*j_scat;
  830.                     e.vz = speed*k_scat;
  831.  
  832.                     collision_counter_en++;  
  833.                     exc2_coll_counter++;
  834.                     exc2_coll_counter_temp++;
  835.  
  836.                     e.collided_en = true;
  837.  
  838.                     // pushing this neutral to an array of excited species exc_2
  839.  
  840.                     if (N_He > 0) {
  841.                         exc_2.push_back({E_n, V_x_n, V_y_n, V_z_n});
  842.                         N_He--;
  843.                     }
  844.                 }
  845.             }
  846.  
  847.             else if (r < P4) {
  848.  
  849.                 //supernelastic 1(singlet -> ground state) collision happens
  850.  
  851.                 if (exc_2.empty()) {
  852.                     null_coll_counter++;
  853.                     collision_counter_en++;
  854.                     e.collided_en = true;
  855.                     continue;            
  856.                 }
  857.  
  858.                 // ----   Collision energy redistribution module
  859.  
  860.                 // electron particle X Y Z initial velocities and energy
  861.                 double V0_x = e.vx;
  862.                 double V0_y = e.vy;
  863.                 double V0_z = e.vz;
  864.                 double E_0 = e.energy;
  865.  
  866.                 double V0 = sqrt(V0_x*V0_x + V0_y*V0_y + V0_z*V0_z);
  867.  
  868.                 // neutral that collides with electron
  869.                 // taking particles from dynamic array of excited neutrals
  870.  
  871.                 int index = std::uniform_int_distribution<int>(0, exc_2.size()-1)(gen);
  872.                 Excited_neutral& exc = exc_2[index];
  873.                 double V_x = exc.vx;
  874.                 double V_y = exc.vy;
  875.                 double V_z = exc.vz;
  876.                 double E = exc.energy;
  877.                
  878.                 // generating random variables to calculate random direction of center-of-mass after the collision
  879.  
  880.                 double R1 = dis(gen);
  881.                 double R2 = dis(gen);
  882.  
  883.                 //// calculating spherical angles for center-of-mass random direction
  884.                 // double theta = acos(1.0- 2.0*R1);
  885.                 // double phi = 2*M_PI*R2;
  886.                 double cos_khi = (2.0 + E_0 - 2.0*pow((1+E_0), R1))/E_0;
  887.                 double sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  888.  
  889.                 double phi = 2.0*M_PI*R2;
  890.                 double cos_theta = V0_x/V0;
  891.                 double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
  892.                 // //calculating final relative velocity with random direction
  893.  
  894.                 //calculating final velocity of electron
  895.  
  896.                 double i_scat = (V0_x/V0)*cos_khi + (1.0 - (V0_x/V0)*(V0_x/V0))*(sin_khi*cos(phi)/sin_theta);
  897.                 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;
  898.                 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;
  899.  
  900.                 //updating electron energy and velocities        
  901.                
  902.                 e.energy = E_0 + thresh2;
  903.  
  904.                 double speed = sqrt(2*e.energy*q/m_e);
  905.  
  906.                 e.vx = speed*i_scat;
  907.                 e.vy = speed*j_scat;
  908.                 e.vz = speed*k_scat;
  909.  
  910.                 //counting collisions, working with flags, popping atom out of the vector
  911.  
  912.                 if (!exc_2.empty()) {
  913.                     std::swap(exc_2[index], exc_2.back());
  914.                     exc_2.pop_back();
  915.                     N_He++;
  916.                 }
  917.  
  918.                 collision_counter_en++;  
  919.                 super2_coll_counter++;
  920.                 super2_coll_counter_temp++;
  921.  
  922.                 e.collided_en = true;
  923.             }              
  924.  
  925.             else {
  926.                 // null-collision
  927.                 collision_counter_en++;
  928.                 null_coll_counter++;
  929.                 e.collided_en = true;
  930.             }
  931.         }
  932.  
  933. //         // ----- -------now begin e-e collisions ------ /////
  934.  
  935. //         // Reshuffle electron indices for random pairing for e-e collisions
  936. //         std::shuffle(electron_indices.begin(), electron_indices.end(), gen);
  937.  
  938. //         int max_pairs = n_e/2; // each electron collides
  939.        
  940. //         for (int i = 0; i < max_pairs; i++){
  941.  
  942. //             int id1 = electron_indices[2 * i];
  943. //             int id2 = electron_indices[2 * i + 1];
  944. //             if (id1 >= n_e || id2 >= n_e) continue; // Handle edge case
  945.  
  946. //             Electron& e1 = electrons[id1];
  947. //             Electron& e2 = electrons[id2];
  948.  
  949. //             if (e1.collided_ee || e2.collided_ee) continue; //handle already collided cases
  950.  
  951. //             double E_initial = e1.energy + e2.energy; // total initial energy of pair to check the energy conservation
  952.  
  953. //             // generating random variables to calculate random direction of center-of-mass after the collision
  954.  
  955. //             double R1 = dis(gen);
  956. //             double R2 = dis(gen);        
  957.  
  958. //             // ----   Collision energy redistribution module
  959.  
  960. //             // first particle X Y Z initial velocities
  961. //             double V0_x_1 = e1.vx;
  962. //             double V0_y_1 = e1.vy;
  963. //             double V0_z_1 = e1.vz;
  964. //             // second particle X Y Z initial velocities
  965. //             double V0_x_2 = e2.vx;
  966. //             double V0_y_2 = e2.vy;
  967. //             double V0_z_2 = e2.vz;
  968.  
  969. //             // file13 << "V0_x_1: " << V0_x_1 << " " << "V0_y_1: " << V0_y_1 << " " << " V0_z_1: " << V0_z_1 << " ";
  970. //             // file13 << "V0_x_2: " << V0_x_2 << " " << "V0_y_2: " << V0_y_2 << " " << " V0_z_2: " << V0_z_2 << " ";
  971.  
  972. //             // initial relative velocity X Y Z (must be equal to final relative velocity in center-of-mass frame)
  973.  
  974. //             double V0_rel_x = (V0_x_1 - V0_x_2);
  975. //             double V0_rel_y = (V0_y_1 - V0_y_2);
  976. //             double V0_rel_z = (V0_z_1 - V0_z_2);
  977.  
  978. //             if(std::isnan(V0_x_1) || std::isinf(V0_x_1) || fabs(V0_x_1) < 1e-12 || std::isnan(V0_y_1) || std::isinf(V0_y_1) || fabs(V0_y_1) < 1e-12 || std::isnan(V0_z_1) || std::isinf(V0_z_1) || fabs(V0_z_1) < 1e-12){
  979. //                 std::cerr << "Invalid V0_rel_x computed: " << V0_rel_x << " at timestep " << t << std::endl;
  980. //                 std::cerr << "Components of velocities: Vx, Vy, Vz forr the first electron: " << V0_x_1 << " " << V0_y_1 << " " << V0_z_1 << "\n";
  981. //             //    continue;
  982. //             }
  983.  
  984. //             if(std::isnan(V0_x_2) || std::isinf(V0_x_2) || fabs(V0_x_2) < 1e-12 || std::isnan(V0_y_2) || std::isinf(V0_y_2) || fabs(V0_y_2) < 1e-12 || std::isnan(V0_z_2) || std::isinf(V0_z_2) || fabs(V0_z_2) < 1e-12){
  985. //                 std::cerr << "Invalid V0_rel_x computed: " << V0_rel_x << " at timestep " << t << std::endl;
  986. //                 std::cerr << "Components of velocities: Vx, Vy, Vz forr the second electron: " << V0_x_2 << " " << V0_y_2 << " " << V0_z_2 << "\n";
  987. //             //    continue;
  988. //             }    
  989.  
  990. //             if(std::isnan(V0_rel_y) || std::isinf(V0_rel_y) || fabs(V0_rel_y) < 1e-12){
  991. //                 std::cerr << "Invalid V0_rel_y computed: " << V0_rel_y << " at timestep " << t << std::endl;
  992. //                 continue;
  993. //             }    
  994. //             if(std::isnan(V0_rel_z) || std::isinf(V0_rel_z) || fabs(V0_rel_z) < 1e-12){
  995. //                 std::cerr << "Invalid V0_rel_z computed: " << V0_rel_z << " at timestep " << t << std::endl;
  996. //                 continue;
  997. //             }              
  998.  
  999.  
  1000. //             double V0_rel = sqrt(V0_rel_x*V0_rel_x + V0_rel_y*V0_rel_y + V0_rel_z*V0_rel_z);
  1001. //             double V0_rel_normal = sqrt(V0_rel_y*V0_rel_y + V0_rel_z*V0_rel_z);
  1002.  
  1003. //             // file13 << "V0_rel: " << V0_rel << " " << "V0_rel_normal: " << V0_rel_normal << " ";
  1004.  
  1005. //             if(std::isnan(V0_rel) || std::isinf(V0_rel) || fabs(V0_rel) < 1e-12){
  1006. //                 std::cerr << "Invalid V0_rel computed: " << V0_rel << " at timestep " << t << std::endl;
  1007. //                 V0_rel = 1.0E-6;
  1008. //                 e1.collided_ee = true;
  1009. //                 e2.collided_ee = true;
  1010. //                 continue;
  1011. //             }
  1012.            
  1013. //             if(std::isnan(V0_rel_normal) || std::isinf(V0_rel_normal) || fabs(V0_rel_normal) < 1e-12){
  1014. //                 std::cerr << "Invalid V0_rel_normal computed: " << V0_rel << " at timestep " << t << std::endl;
  1015. //                 continue;
  1016. //             }                        
  1017.  
  1018. //             // calculating spherical angles for center-of-mass random direction
  1019. //             double theta = acos(1.0- 2.0*R1);
  1020. //             double phi = 2*M_PI*R2;
  1021.  
  1022. //             // calcluating h for equations 20a, 20b (Nanbu1995)
  1023.  
  1024. //             double eps = 2*M_PI*R1;
  1025.  
  1026. //             double h_x = V0_rel_normal*cos(eps);
  1027. //             double h_y = -(V0_rel_y*V0_rel_x*cos(eps) + V0_rel*V0_rel_z*sin(eps))/V0_rel_normal;
  1028. //             double h_z = -(V0_rel_z*V0_rel_x*cos(eps) - V0_rel*V0_rel_y*sin(eps))/V0_rel_normal;    
  1029.  
  1030. //             //  calculating s (Nanbu1995 eq 19)
  1031.  
  1032. //             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;
  1033.  
  1034. //             // file13 << "s: " << s << " ";
  1035.  
  1036. //             if(std::isnan(s) || std::isinf(s) || fabs(s) < 1e-12){
  1037. //                 std::cerr << "Invalid s computed: " << s << " at timestep " << t << std::endl;
  1038. //                 continue;
  1039. //             }
  1040.  
  1041. //             double A = solve_A(s);  
  1042.  
  1043. //             if(std::isnan(A) || std::isinf(A) || fabs(A) < 1e-12){
  1044. // //                std::cerr << "Invalid A computed: " << A << " at timestep " << t << std::endl;
  1045. //                 A = 1.0E-12;
  1046. // //                continue;
  1047. //             }
  1048.  
  1049.  
  1050. //             // calculating cos(khi) (Nanbu1995 eq 17)
  1051. //             double cos_khi = 0.0;
  1052. //             double sin_khi = 0.0;
  1053.            
  1054. //             if (s < 1.0E-2 & R1 != 0.0) {// taking care of small s  
  1055. //                 cos_khi = 1.0 + s*log(R1);    
  1056. //             }
  1057. //             else {
  1058. //                 cos_khi = (1.0/A)*log(exp(-A) + 2.0*R1*sinh(A));
  1059. //             }
  1060.  
  1061. //             if (cos_khi > 1.0)
  1062. //                 cos_khi = 1.0;
  1063.  
  1064. //             sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  1065.  
  1066.  
  1067. //             //calculating final velocity of first particle
  1068.  
  1069. //             double V_x_1 = V0_x_1 - 0.5*(V0_rel_x*(1.0-cos_khi) + h_x*sin_khi);
  1070. //             double V_y_1 = V0_y_1 - 0.5*(V0_rel_y*(1.0-cos_khi) + h_y*sin_khi);
  1071. //             double V_z_1 = V0_z_1 - 0.5*(V0_rel_z*(1.0-cos_khi) + h_z*sin_khi);
  1072.  
  1073. //             double V_1 = sqrt(V_x_1*V_x_1 + V_y_1*V_y_1 + V_z_1*V_z_1);
  1074.  
  1075. //             //calculating final velocity of second particle
  1076.  
  1077. //             double V_x_2 = V0_x_2 + 0.5*(V0_rel_x*(1.0-cos_khi) + h_x*sin_khi);
  1078. //             double V_y_2 = V0_y_2 + 0.5*(V0_rel_y*(1.0-cos_khi) + h_y*sin_khi);
  1079. //             double V_z_2 = V0_z_2 + 0.5*(V0_rel_z*(1.0-cos_khi) + h_z*sin_khi);
  1080.  
  1081. //             double V_2 = sqrt(V_x_2*V_x_2 + V_y_2*V_y_2 + V_z_2*V_z_2);
  1082.  
  1083. //             // updating velocities
  1084.  
  1085. //             e1.vx = V_x_1;
  1086. //             e1.vy = V_y_1;
  1087. //             e1.vz = V_z_1;
  1088.  
  1089. //             e2.vx = V_x_2; // Update velocity components
  1090. //             e2.vy = V_y_2;
  1091. //             e2.vz = V_z_2;
  1092.  
  1093. //             // calculating final energies of first and second colliding particles
  1094.  
  1095. //             e1.energy = V_1*V_1*m_e/(2.0*q);
  1096. //             e2.energy = V_2*V_2*m_e/(2.0*q);          
  1097.  
  1098. //             double E_final = e1.energy + e2.energy;
  1099.  
  1100.  
  1101. //             // if(fabs(E_final - E_initial) > 1e-6) {
  1102. //             //     std::cerr << "Energy conservation violation: " << E_final - E_initial << " eV\n";
  1103. //             // }
  1104.  
  1105. //             // --- collision energy redistrubution module ends  
  1106.  
  1107. //             // collision counters handling
  1108.  
  1109. //             ee_coll_counter++;
  1110. //             e1.collided_ee = true;
  1111. //             e2.collided_ee = true;
  1112.  
  1113. //         }
  1114. //         //////----------------------e-e coulomb collision ends --------------/////////////////
  1115.  
  1116.  
  1117.         t_elapsed += dt; // Advance time
  1118.  
  1119.         // Recalculate nu_max periodically (e.g., every 100 steps)
  1120.         static int recalc_counter = 0;
  1121.         if (++recalc_counter >= 1) {
  1122.            
  1123.             recalc_counter = 0;
  1124.  
  1125.             // Recalculate nu_max with CURRENT densities
  1126.             nu_max = 0.0;
  1127.             for (int i = 0; i < N; i++) {
  1128.                 double energy = Emin + (i + 0.5) * bin_width;
  1129.                
  1130.                 // Get current densities
  1131.                 double n_ground = N_He / Volume;
  1132.                 double n_excited1 = exc_1.size() / Volume;
  1133.                 double n_excited2 = exc_2.size() / Volume;
  1134.                
  1135.                 // Total collision frequency for this energy bin
  1136.                 double sigma_total =
  1137.                     elastic_vec[i] * n_ground +
  1138.                     inelastic1_vec[i] * n_ground +
  1139.                     inelastic2_vec[i] * n_ground +
  1140.                     superelastic1_vec[i] * n_excited1 +
  1141.                     superelastic2_vec[i] * n_excited2;
  1142.  
  1143.                 double v = sqrt(2 * energy * q / m_e);
  1144.                 double nu_temp = sigma_total * v;
  1145.                
  1146.                 if (nu_temp > nu_max) nu_max = nu_temp;
  1147.             }
  1148.  
  1149.             // Update dt based on new nu_max
  1150.             dt = 0.1 / nu_max;        
  1151.         }
  1152.         std::cout << "   nu_max: " << nu_max << "    " << "dt: " << dt << "\n";  
  1153.     }
  1154.  
  1155.     // ----- final electron energies distribution begins
  1156.     for (int i = 0; i < n_e; i++){
  1157.  
  1158.         file2 << i << " " << electrons[i].energy << "\n";
  1159.  
  1160.         int bin = static_cast<int>( (electrons[i].energy - Emin)/bin_width_smooth);
  1161.         if (bin >=0 && bin < histo_maxwell.size())
  1162.             histo_maxwell[bin]++;
  1163.     }
  1164.  
  1165.     int check = 0;
  1166.     for (int i = 0; i < N_smooth; i++){
  1167.         check += histo_maxwell[i];
  1168.         double bin_center = Emin + (i + 0.5) * bin_width_smooth;
  1169.         file4 << bin_center << " " <<  static_cast<double>(histo_maxwell[i])/(electrons.size()*bin_width_smooth) << "\n"; // getting f(E)
  1170.     }
  1171.  
  1172.         std::cout << "Total # of electrons in a final histogram: " << check << "\n";
  1173.  
  1174.     // ----- final electron energies distribution ends
  1175.  
  1176.     // // ------ excited atoms histogram --------/////
  1177.  
  1178.     // for (int i = 0; i < exc_1.size(); i++) {
  1179.  
  1180.     //     file14 << i << " " << exc_1[i].energy << "\n";
  1181.  
  1182.     //     int bin = static_cast<int>( (exc_1[i].energy - Emin)/bin_width);
  1183.     //     if (bin >=0 && bin < histo_excited.size())
  1184.     //         histo_excited[bin]++;        
  1185.     // }
  1186.  
  1187.     // for (int i = 0; i < histo_excited.size(); i++){
  1188.  
  1189.     //     double bin_center = Emin + (i + 0.5) * bin_width;
  1190.     //     file15 << bin_center << " " <<  static_cast<double>(histo_excited[i])/(electrons.size()*bin_width) << "\n"; // getting f(E)
  1191.     // }
  1192.  
  1193.  
  1194.     file0.close();
  1195.     file1.close();
  1196.     file2.close();
  1197.     file3.close();
  1198.     file4.close();
  1199.     file5.close();
  1200.     file6.close();
  1201.     file7.close();
  1202.     file8.close();
  1203.     file9.close();
  1204.     file10.close();
  1205.     file11.close();
  1206.     file12.close();
  1207.     file13.close();
  1208.     file14.close();
  1209.     file15.close();
  1210.     file_temp.close();
  1211.  
  1212.     clock_t end = clock();
  1213.  
  1214.     double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
  1215.  
  1216.     // std::cout << "# of steps: " << steps << "\n";
  1217.     // std::cout << "# of electrons collided each timesteps:" << Ne_collided << "\n";
  1218.    
  1219.     // std::cout << "Average elastic collisions per timestep: " << static_cast<int>(el_coll_counter/steps) << "\n";
  1220.     // std::cout << "Average null collisions per timestep: " << static_cast<int>(null_coll_counter/steps) << "\n";
  1221.     // std::cout << "\n";
  1222.  
  1223.     // std::cout << "triplet:________" << "\n";
  1224.     // std::cout << "Average triplet excitation collisions per timestep: " << static_cast<int>(exc1_coll_counter/steps) << "\n";
  1225.     // std::cout << "\n";
  1226.     // std::cout << "Average superelastic triplet collisions per timestep: " << static_cast<int>(super1_coll_counter/steps) << "\n";
  1227.     // std::cout << "\n";
  1228.  
  1229.     // std::cout << "singlet:________" << "\n";
  1230.     // std::cout << "Average singlet excitation collisions per timestep: " << static_cast<int>(exc2_coll_counter/steps) << "\n";
  1231.     // std::cout << "\n";
  1232.     // std::cout << "Average superelastic singlet collisions per timestep: " << static_cast<int>(super2_coll_counter/steps) << "\n";
  1233.     // std::cout << "\n";    
  1234.  
  1235.     // std::cout << "Average e-e collisions per timestep: " << static_cast<int>(ee_coll_counter/steps) << "\n";
  1236.  
  1237.     std::cout << "Elapsed time: %f seconds " << elapsed << "\n";
  1238.  
  1239.  
  1240.     return 0;
  1241.  
  1242. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement