Advertisement
phystota

e-n collisions(v3)

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