Advertisement
phystota

e-n collisions(v4) - electrons shuffled

Mar 28th, 2025
474
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.74 KB | None | 0 0
  1. #include <iostream>
  2. #include <random>
  3. #include <fstream>
  4.  
  5. #include <math.h>
  6. #include <time.h>
  7. #include <iomanip>  // For std::fixed and std::setprecision
  8.  
  9. #include <algorithm>  // For std::shuffle
  10. #include <numeric>    // For std::iota
  11.  
  12.  
  13. #define n_e 10000
  14. #define V_0 30000.0     // max velocity using to generate random distribution ---- doesn't work -> produces skew distribution???
  15. #define Emin 0.0
  16. #define Emax 200.0
  17. #define E_mean 100 // mean electron energy, initial distribution
  18. #define bin_width 0.5
  19. #define m_e 9.1E-31 // electron mass in kg
  20. #define k_b 1.38E-23 // Boltzmann constant
  21. #define q 1.602176634E-19 // elementary charge    - eV -> J transfer param
  22. #define N ( (int)((Emax-Emin)/bin_width) ) // add 1 to include E_max if needed?
  23. #define T_n 10.0 // Helium neutral temperature in eV
  24. #define M_n 6.6464731E-31 // Helium atom mass
  25. #define N_He 1000000 // Helium neutrals number
  26. #define Volume 1.0E-12 // Volume to calculate netral density and collision frequency
  27. #define time 5.0E-3 // 500 microsec time to equalibrate the system
  28.  
  29. struct Electron {
  30.  
  31.     //velocity components
  32.     double vx = 0.0;
  33.     double vy = 0.0;
  34.     double vz = 0.0;
  35.     //energy in eV
  36.     double energy = 0.0;
  37.     //Collision flag
  38.     bool collided = false;
  39.  
  40.     //initialization function // void func(Type0& t) → means the function expects a reference to variable t of type0
  41.     void initialize(std::mt19937& gen, std::uniform_real_distribution<double>& dis) {
  42.         // velocity angles in spherical coordinates
  43.         double phi = 2*M_PI*dis(gen);
  44.         double cosTheta = 2.0*dis(gen) - 1.0;
  45.         double sinTheta = sqrt(1.0 - cosTheta*cosTheta);
  46.        
  47.         energy = E_mean*dis(gen);
  48.        
  49.         double speed = sqrt(2*energy*q/m_e);
  50.  
  51.         vx = speed * sinTheta * cos(phi);
  52.         vy = speed * sinTheta * sin(phi);
  53.         vz = speed * cosTheta;
  54.     }
  55. };
  56.  
  57.  
  58. struct CrossSection {
  59.     double energy;
  60.     double sigma;
  61. };
  62.  
  63. double interpolate (double energy, const std::vector<CrossSection>& elastic_CS) {
  64.  
  65.  
  66.     if (energy < elastic_CS.front().energy) {
  67.         std::cout << " required energy value lower than range of cross-section data" << "\n";
  68.         return 0.0;
  69.     }
  70.     if (energy > elastic_CS.back().energy) {
  71.         std::cout << " required energy value higher than range of cross-section data" << "\n";
  72.         return 0.0;        
  73.     }
  74.  
  75.     int step = 0;  
  76.         while (step < elastic_CS.size() && energy > elastic_CS[step].energy) {
  77.             step++;
  78.         }
  79.  
  80.     double k = (elastic_CS[step].sigma - elastic_CS[step-1].sigma)/(elastic_CS[step].energy - elastic_CS[step-1].energy);
  81.     double m = elastic_CS[step].sigma - k*elastic_CS[step].energy;
  82.    
  83.     return k*energy + m;
  84. }
  85.  
  86.  
  87. struct NeutralParticle {
  88.  
  89.     double energy = 0.0;
  90.     double vx = 0.0;
  91.     double vy = 0.0;
  92.     double vz = 0.0;
  93.  
  94.     void initialize(std::mt19937& gen, std::uniform_real_distribution<double>& dis, std::gamma_distribution<double>& maxwell) {
  95.  
  96.         double R = dis(gen);
  97.  
  98.         // velocity angles in spherical coordinates
  99.         double phi = 2*M_PI*dis(gen);
  100.         double cosTheta = 2.0*dis(gen) - 1.0;
  101.         double sinTheta = sqrt(1.0 - cosTheta*cosTheta);
  102.  
  103.            
  104.         energy = maxwell(gen); // neutrals energies sampled as Maxwell distribution in eV
  105.            
  106.         double speed = sqrt(2*energy*q/M_n);
  107.  
  108.         //velocity components of neutrals in m/s
  109.         vx = speed * sinTheta * cos(phi);
  110.         vy = speed * sinTheta * sin(phi);
  111.         vz = speed * cosTheta;
  112.     }
  113.    
  114. };
  115.  
  116.  
  117.  
  118.  
  119. int main() {
  120.  
  121.     clock_t start = clock();
  122.  
  123.     std::vector<Electron> electrons(n_e); // better to use vector instead of simple array as it's dynamically allocated (beneficial for ionization)
  124. //    std::vector<double> neutrals(N_He); // vector for neutrals
  125.     std::vector<NeutralParticle> neutrals(N_He);
  126.  
  127.  
  128.     std::vector<int> histo_random(N, 0); // initialize N size zero-vector for random (initial) histogram
  129.     std::vector<int> histo_maxwell(N, 0); // initialize N size zero-vector for maxwellian histogram
  130.     std::vector<int> histo_neutral(N, 0); // initialize N size zero-vector for neutral distribution histogram
  131.  
  132.     std::vector<double> elastic(N, 0); // precompiled cross-section energy vector
  133.  
  134.     std::random_device rd;
  135.     std::mt19937 gen(rd());
  136.     std::uniform_real_distribution<double> dis(0.0, 1.0);
  137.     std::gamma_distribution<double> maxwell(1.5, T_n);
  138.  
  139.     std::uniform_int_distribution<int> pair(0, n_e-1);
  140.     std::uniform_int_distribution<int> neutral_pair(0, N_He-1);    
  141.  
  142.  
  143.     std::ifstream elastic_cs("cross_sections/elastic.dat");
  144.     if (!elastic_cs.is_open()) {
  145.         std::cerr << "Error opening file!" << std::endl;
  146.         return 1;
  147.     }    
  148.  
  149.     // reading elastic cross section datafile
  150.  
  151.     std::vector<CrossSection> elastic_CS;
  152.  
  153.     double energy, sigma;
  154.  
  155.     while (elastic_cs >> energy >> sigma) {
  156.         elastic_CS.push_back({energy, sigma});
  157.     }    
  158.  
  159.     elastic_cs.close();
  160.  
  161.  
  162.     std::ofstream file0("velocities.dat");    
  163.     if (!file0.is_open()) {
  164.         std::cerr << "Error opening file!" << std::endl;
  165.         return 1;
  166.     }
  167.  
  168.     std::ofstream file1("energies.dat");    
  169.     if (!file1.is_open()) {
  170.         std::cerr << "Error opening file!" << std::endl;
  171.         return 1;
  172.     }
  173.    
  174.     std::ofstream file2("energies_final.dat");    
  175.     if (!file2.is_open()) {
  176.         std::cerr << "Error opening file!" << std::endl;
  177.         return 1;
  178.     }
  179.  
  180.     std::ofstream file3("histo_random.dat");    
  181.     if (!file3.is_open()) {
  182.         std::cerr << "Error opening file!" << std::endl;
  183.         return 1;
  184.     }
  185.     file3 << std::fixed << std::setprecision(10);
  186.    
  187.     std::ofstream file4("histo_maxwell.dat");    
  188.     if (!file4.is_open()) {
  189.         std::cerr << "Error opening file!" << std::endl;
  190.         return 1;
  191.     }
  192.     file4 << std::fixed << std::setprecision(10);          
  193.    
  194.     std::ofstream file5("neutral_distribution.dat");    
  195.     if (!file5.is_open()) {
  196.         std::cerr << "Error opening file!" << std::endl;
  197.         return 1;
  198.     }
  199.  
  200.     std::ofstream file6("E*f(E).dat");    
  201.     if (!file6.is_open()) {
  202.         std::cerr << "Error opening file!" << std::endl;
  203.         return 1;
  204.     }
  205.  
  206.     std::ofstream file7("nu_max.dat");
  207.     if (!file7.is_open()) {
  208.         std::cerr << "Error opening file7!" << std::endl;
  209.         return 1;
  210.     }
  211.  
  212.     // Initialize all electrons
  213.     for (auto& e : electrons) {
  214.         e.initialize(gen, dis);
  215.     }
  216.     // initialize all nenutrals
  217.     for (auto&n : neutrals) {
  218.         n.initialize(gen, dis, maxwell);
  219.     }
  220.     // precalculate cross-sections for each energy bin
  221.     for (int i = 0; i < N; i++){
  222.         elastic[i] = interpolate(bin_width*(i+0.5), elastic_CS);
  223.     }
  224.  
  225.  
  226.     for (int i = 0; i < n_e; i++){
  227.         file1 << i << " " << electrons.at(i).energy << "\n";
  228.         file0 << i << " " << electrons[i].vx << " " << electrons[i].vy << " " << electrons[i].vz << "\n";
  229.     }
  230.  
  231.  
  232.     for (int i = 0; i < n_e; i++){
  233.         int bin = (int)( (electrons[i].energy - Emin)/bin_width );
  234.         if (bin >=0 && bin < histo_random.size())
  235.             histo_random[bin]++;
  236.     }
  237.  
  238.     for (int i = 0; i < histo_random.size(); i++){\
  239.         double bin_start = Emin + i*bin_width;
  240.         file3 << i*bin_width << " " <<  static_cast<double>(histo_random[i])/(electrons.size()*bin_width) << "\n"; // dividing by n_e to get normalized distribution
  241.     }
  242.  
  243.  
  244.     for (int i = 0; i < N_He; i++){
  245.         int bin = (int)( (neutrals[i].energy - Emin)/bin_width );
  246.         if (bin >=0 && bin < histo_neutral.size())
  247.             histo_neutral[bin]++;
  248.     }    
  249.  
  250.     for (int i = 0; i < histo_neutral.size(); i++){
  251.         double bin_start = Emin + i*bin_width;
  252.         file5 << i*bin_width << " " << static_cast<double>(histo_neutral[i])/(neutrals.size()*bin_width) << "\n"; // this is real f(E) - normalized distribution
  253.         file6 << i*bin_width << " " << (i*bin_width)*static_cast<double>(histo_neutral[i])/(neutrals.size()*bin_width) << "\n"; // this should be E*f(E)
  254.  
  255.     }  
  256.  
  257.     double nu_max = 0.0;
  258.     double nu_max_temp = 0.0;
  259.  
  260.     for (int i = 0; i < N; i++){
  261.         nu_max_temp = (N_He/Volume)*elastic[i] * sqrt(2.0*(i*bin_width + bin_width/2.0)*q/m_e);
  262.         file7 << i << " " << nu_max_temp << "\n";
  263.         if (nu_max_temp > nu_max)
  264.             nu_max = nu_max_temp;
  265.     }
  266.  
  267.     std::cout << nu_max << "\n";
  268.  
  269.     double dt = 0.1/nu_max;   // minimum should be 0.1/nu_max to get acceptable numerical error range see Vahedi Surrendra 1995
  270.     double steps = static_cast<int>(time/dt);
  271.  
  272.     std::cout << steps << "\n";
  273.  
  274.     //using  null-collision technique, getting the number of particles colliding each step: P_collision = 1 - exp(-nu_max*dt)
  275.     int Ne_collided = (1.0-exp(-1.0*dt*nu_max))*n_e;
  276.  
  277.     std::cout << Ne_collided << "\n";
  278.  
  279.     // Generate shuffled list of electron indices
  280.     std::vector<int> electron_indices(n_e);
  281.     std::iota(electron_indices.begin(), electron_indices.end(), 0); // fill with index
  282.     std::shuffle(electron_indices.begin(), electron_indices.end(), gen); // shuffle the indexes    
  283.     int reshuffle_interval = 10;
  284.  
  285.     for (int t = 0; t < steps; t++){
  286.         std::cout << "timestep: " << t << "\n";
  287.  
  288.         //reshuffle the indexes
  289.         if (t % reshuffle_interval == 0) {
  290.             std::shuffle(electron_indices.begin(), electron_indices.end(), gen);
  291.         }
  292.  
  293.         // setting flags to false each timestep
  294.         for (auto& e : electrons) e.collided = false;
  295.  
  296.         int collision_counter = 0;
  297.  
  298.  
  299.  
  300.  
  301.         for (int idx : electron_indices) {
  302.  
  303.             if (collision_counter >= Ne_collided) break; // quit if reached all collisions
  304.  
  305.             Electron& e = electrons[idx];
  306.             if (e.collided) continue;  // Skip already collided electrons
  307.  
  308.             double electron_energy = e.energy;
  309.             int bin_energy = static_cast<int>(electron_energy / bin_width);
  310.             double nu_elastic = (N_He/Volume) * elastic[bin_energy] * sqrt(2.0*electron_energy*q/m_e);
  311.  
  312.             if (dis(gen) < nu_elastic/nu_max) {
  313.  
  314.                 // ----   Collision energy redistribution module
  315.  
  316.                 // electron particle X Y Z initial velocities and energy
  317.                 double V0_x_1 = e.vx;
  318.                 double V0_y_1 = e.vy;
  319.                 double V0_z_1 = e.vz;
  320.  
  321.                 // neutral particle X Y Z initial velocities
  322.  
  323.                 int k = neutral_pair(gen);
  324.  
  325.                 double V0_x_2 = neutrals[k].vx;
  326.                 double V0_y_2 = neutrals[k].vy;
  327.                 double V0_z_2 = neutrals[k].vz;
  328.  
  329.                 // initial relative velocity X Y Z (must be equal to final relative velocity in center-of-mass frame)
  330.  
  331.                 double V0_rel_x = (V0_x_1 - V0_x_2);
  332.                 double V0_rel_y = (V0_y_1 - V0_y_2);
  333.                 double V0_rel_z = (V0_z_1 - V0_z_2);
  334.  
  335.                 double V0_rel = sqrt(V0_rel_x*V0_rel_x + V0_rel_y*V0_rel_y + V0_rel_z*V0_rel_z);
  336.  
  337.                 // center-of-mass frame initial velocity (magnitude of it must be equal to the counterpart in this frame)
  338.  
  339.                 double V_cm_x = (m_e*V0_x_1 + M_n*V0_x_2)/(m_e + M_n);
  340.                 double V_cm_y = (m_e*V0_y_1 + M_n*V0_y_2)/(m_e + M_n);
  341.                 double V_cm_z = (m_e*V0_z_1 + M_n*V0_z_2)/(m_e + M_n);                    
  342.  
  343.                 // generating random variables to calculate random direction of center-of-mass after the collision
  344.  
  345.                 double R1 = dis(gen);
  346.                 double R2 = dis(gen);
  347.  
  348.                 // calculating spherical angles for center-of-mass random direction
  349.                 double theta = acos(1.0- 2.0*R1);
  350.                 double phi = 2*M_PI*R2;
  351.  
  352.                 //calculating final relative velocity with random direction
  353.  
  354.                 double V_rel_x = V0_rel*sin(theta)*cos(phi);
  355.                 double V_rel_y = V0_rel*sin(theta)*sin(phi);
  356.                 double V_rel_z = V0_rel*cos(theta);
  357.  
  358.                 double V_rel = sqrt(V_rel_x*V_rel_x + V_rel_y*V_rel_y + V_rel_z*V_rel_z);
  359.  
  360.                 //calculating final velocity of electron
  361.  
  362.                 double V_x_1 = V_cm_x + V_rel_x * (M_n/(m_e + M_n));
  363.                 double V_y_1 = V_cm_y + V_rel_y * (M_n/(m_e + M_n));
  364.                 double V_z_1 = V_cm_z + V_rel_z * (M_n/(m_e + M_n));
  365.  
  366.                 double V_1 = sqrt(V_x_1*V_x_1 + V_y_1*V_y_1 + V_z_1*V_z_1);
  367.  
  368.                 //updating electron energy and velocities
  369.  
  370.                 e.energy = m_e*V_1*V_1/(2.0*q);
  371.                 e.vx = V_x_1;
  372.                 e.vy = V_y_1;
  373.                 e.vz = V_z_1;
  374.  
  375.                 collision_counter++;
  376.  
  377.                 e.collided = true;
  378.             }                      
  379.         }
  380.     }
  381.  
  382.  
  383.     for (int i = 0; i < n_e; i++){
  384.  
  385.         file2 << i << " " << electrons[i].energy << "\n";
  386.  
  387.         int bin = (int)( (electrons[i].energy - Emin)/bin_width );
  388.         if (bin >=0 && bin < histo_maxwell.size())
  389.             histo_maxwell[bin]++;
  390.     }
  391.  
  392.     int check = 0;
  393.     for (int i = 0; i < histo_maxwell.size(); i++){
  394.         check += histo_maxwell[i];
  395.         double bin_start = Emin + i*bin_width;
  396.         file4 << i*bin_width << " " <<  (i*bin_width)*static_cast<double>(histo_maxwell[i])/(electrons.size()*bin_width) << "\n"; // getting f(E)*E
  397.     }
  398.     std::cout << "Total # of electrons in histo: " << check << "\n";
  399.  
  400.  
  401.     file0.close();
  402.     file1.close();
  403.     file2.close();
  404.     file3.close();
  405.     file4.close();
  406.     file5.close();
  407.     file6.close();
  408.     file7.close();
  409.  
  410.     clock_t end = clock();
  411.  
  412.     double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
  413.  
  414.     std::cout << "Energies written successfuly\n";
  415.     std::cout << "Elapsed time: %f seconds " << elapsed << "\n";
  416.  
  417.  
  418.     return 0;
  419.  
  420. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement