Advertisement
phystota

electron-neutral collisions

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