Advertisement
phystota

Coulomb_Nanbu

Apr 8th, 2025
540
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.94 KB | None | 0 0
  1. #include <iostream>
  2. #include <random>
  3. #include <fstream>
  4.  
  5. #include <math.h>
  6. #include <tgmath.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. #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_average 100.0 // electron sampling energy
  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.6E-19 // elementary charge    
  22. #define N ( (int)((Emax-Emin)/bin_width) ) // add 1 to include E_max if needed?
  23. #define P 0.99
  24. #define time 5.0E-6
  25. #define dt 1.0E-8
  26. #define steps time/dt
  27. #define Coulomb_log 15.0 // Coulomb logarithm calculated based on Nanmbu paper
  28. #define epsilon_0 8.854188E-12 // Vacuum permittivity
  29. #define Volume 1.0E-14 // volume, m-3
  30.  
  31. double solve_A(double s) {
  32.  
  33.     double A0 = 0.01; // initial guess
  34.     double A = A0;  //starting with initial guess
  35.     double error = 1.0E-7; // accuracy
  36.  
  37.     for (int i = 0; i < 1000; i++){
  38.  
  39.         double f = 1.0/tanh(A) - 1.0/A - exp(-s);
  40.  
  41.         if (abs(f) < error) break;
  42.  
  43.         double dfdA = -1.0/(sinh(A)*sinh(A)) + 1.0/(A*A);
  44.  
  45.         A -= f/dfdA;
  46.  
  47.     }
  48.  
  49.     return A;
  50. }
  51.  
  52. struct Electron {
  53.  
  54.     //velocity components
  55.     double vx = 0.0;
  56.     double vy = 0.0;
  57.     double vz = 0.0;
  58.     //energy in eV
  59.     double energy = 0.0;
  60.     //Collision flag
  61.     bool collided = false;
  62.  
  63.     //initialization function // void func(Type0& t) → means the function expects a reference to variable t of type0
  64.     void initialize(std::mt19937& gen, std::uniform_real_distribution<double>& dis) {
  65.         // velocity angles in spherical coordinates
  66.         double phi = 2*M_PI*dis(gen);
  67.         double cosTheta = 2 * dis(gen) - 1.0;  
  68.         double sinTheta = sqrt(1.0 - cosTheta*cosTheta);
  69.        
  70.         energy = E_average*dis(gen);
  71.        
  72.         double speed = sqrt(2*energy*q/m_e);
  73.  
  74.         vx = speed * sinTheta * cos(phi);
  75.         vy = speed * sinTheta * sin(phi);
  76.         vz = speed * cosTheta;
  77.  
  78.     }
  79.  
  80. };
  81.  
  82. int main() {
  83.  
  84.     std::vector<Electron> electrons(n_e); // better to use vector instead of simple array as it's dynamically allocated (beneficial for ionization)
  85.  
  86.     std::vector<int> histo_random(N, 0); // initialize N size zero-vector for random (initial) histogram
  87.     std::vector<int> histo_maxwell(N, 0); // initialize N size zero-vector for maxwellian histogram
  88.  
  89.     clock_t start = clock();
  90.  
  91.     std::random_device rd;
  92.     std::mt19937 gen(rd());
  93.     std::uniform_real_distribution<double> dis(0.0, 1.0);
  94.  
  95.     std::random_device rd1;
  96.     std::mt19937 gen1(rd1());
  97.     std::uniform_int_distribution<int> pair(0, n_e-1);
  98.  
  99.     std::ofstream file0("velocities.dat");    
  100.     if (!file0.is_open()) {
  101.         std::cerr << "Error opening file!" << std::endl;
  102.         return 1;
  103.     }
  104.  
  105.     std::ofstream file1("energies.dat");    
  106.     if (!file1.is_open()) {
  107.         std::cerr << "Error opening file!" << std::endl;
  108.         return 1;
  109.     }
  110.    
  111.     std::ofstream file2("energies_final.dat");    
  112.     if (!file2.is_open()) {
  113.         std::cerr << "Error opening file!" << std::endl;
  114.         return 1;
  115.     }
  116.  
  117.     std::ofstream file3("histo_random.dat");    
  118.     if (!file3.is_open()) {
  119.         std::cerr << "Error opening file!" << std::endl;
  120.         return 1;
  121.     }
  122.     file3 << std::fixed << std::setprecision(10);
  123.    
  124.     std::ofstream file4("histo_maxwell.dat");    
  125.     if (!file4.is_open()) {
  126.         std::cerr << "Error opening file!" << std::endl;
  127.         return 1;
  128.     }
  129.     file4 << std::fixed << std::setprecision(10);  
  130.  
  131.     std::ofstream file5("mean_energy.dat");        
  132.            
  133.  
  134.  
  135.     // Initialize all electrons
  136.     for (auto& e : electrons) {
  137.         e.initialize(gen, dis);
  138.     }
  139.  
  140.     for (int i = 0; i < n_e; i++){
  141.         file1 << i << " " << electrons.at(i).energy << "\n";
  142.         file0 << i << " " << electrons[i].vx << " " << electrons[i].vy << " " << electrons[i].vz << "\n";
  143.     }
  144.  
  145.     // for (int i = 0; i < N; i++){
  146.     //     std::cout << i << " " << histo_random.at(i) << "\n"; // using vector.at allows to access elements with boundary checkings
  147.     // }
  148.  
  149.  
  150.     for (int i = 0; i < n_e; i++){
  151.         int bin = (int)( (electrons[i].energy - Emin)/bin_width );
  152.         if (bin >=0 && bin < histo_random.size())
  153.             histo_random[bin]++;
  154.     }
  155.  
  156.     for (int i = 0; i < histo_random.size(); i++){\
  157.         double bin_start = Emin + i*bin_width;
  158.         file3 << bin_start << " " <<  bin_start*static_cast<double>(histo_random[i])/(electrons.size()*bin_width) << "\n"; // E*f(E)
  159.     }
  160.  
  161.     // for (int i = 0; i < static_cast<int>(4/0.1)+1; i++){
  162.     //     double s = i*0.1;
  163.     //     std::cout << "s = " << s << ", " << "Solution: A = " << solve_A(s) << "\n";
  164.     // }
  165.  
  166.     double energy_sum = 0.0;
  167.  
  168.     for (int t = 0; t < steps; t++){
  169.        
  170.         std::cout << "timestep remains: " << steps - t << " " << "deltaE: " << "\n";
  171.         std::ostringstream filename;
  172.         filename << "Coulomb_data/distribution_" << std::setw(4) << std::setfill('0') << t << ".dat";
  173.  
  174.         std::ofstream file(filename.str());
  175.         if (!file.is_open()){
  176.         std::cerr << "Error opening file: " << filename.str() << std::endl;
  177.         return 1;
  178.         }
  179.  
  180.         // setting flags to false each timestep
  181.         for (int j = 0; j < n_e; j++){
  182.             electrons.at(j).collided = false;
  183.         }
  184.  
  185.         for (int i = 0 ; i < n_e; i++){
  186.             if (dis(gen) < P) {
  187.                 int partner = -1;
  188.                 int attempts = 0;
  189.  
  190.                 while (attempts < n_e){
  191.                     int candidate = pair(gen1);
  192.                     if (candidate != i && !electrons.at(candidate).collided){
  193.                         partner = candidate;
  194.                         break;
  195.                     }
  196.                     attempts++;
  197.                 }
  198.  
  199.                 if (partner != -1) {
  200.                     electrons.at(i).collided = true;
  201.                     electrons.at(partner).collided = true;
  202.                
  203.  
  204.                 // std::cout << partner << "\n";
  205.  
  206.                 // generating random variables to calculate random direction of center-of-mass after the collision
  207.  
  208.                 double R1 = dis(gen);
  209.                 double R2 = dis(gen);                
  210.  
  211.                 // ----   Collision energy redistribution module
  212.  
  213.                 // --- initial energy of partiecles
  214.  
  215.                 double E0_1 = electrons[i].energy;
  216.                 double E0_2 = electrons[partner].energy;
  217.  
  218.                 // first particle X Y Z initial velocities
  219.                 double V0_x_1 = electrons[i].vx;
  220.                 double V0_y_1 = electrons[i].vy;
  221.                 double V0_z_1 = electrons[i].vz;
  222.  
  223.  
  224.                 // second particle X Y Z initial velocities
  225.                 double V0_x_2 = electrons[partner].vx;
  226.                 double V0_y_2 = electrons[partner].vy;
  227.                 double V0_z_2 = electrons[partner].vz;
  228.  
  229.  
  230.                 // initial relative velocity X Y Z (must be equal to final relative velocity in center-of-mass frame)
  231.  
  232.                 double V0_rel_x = (V0_x_1 - V0_x_2);
  233.                 double V0_rel_y = (V0_y_1 - V0_y_2);
  234.                 double V0_rel_z = (V0_z_1 - V0_z_2);
  235.  
  236.                 double V0_rel = sqrt(V0_rel_x*V0_rel_x + V0_rel_y*V0_rel_y + V0_rel_z*V0_rel_z);
  237.                 double V0_rel_normal = sqrt(V0_rel_y*V0_rel_y + V0_rel_z*V0_rel_z);
  238.  
  239.  
  240.                 // calcluating h for equations 20a, 20b (Nanbu1995)
  241.  
  242.                 double eps = 2*M_PI*R1;
  243.  
  244.                 double h_x = V0_rel_normal*cos(eps);
  245.                 double h_y = -(V0_rel_y*V0_rel_x*cos(eps) + V0_rel*V0_rel_z*sin(eps))/V0_rel_normal;
  246.                 double h_z = -(V0_rel_z*V0_rel_x*cos(eps) - V0_rel*V0_rel_y*sin(eps))/V0_rel_normal;
  247.  
  248.  
  249.                 //  calculating s (Nanbu1995 eq 19)
  250.  
  251.                 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;
  252.                 double A = solve_A(s);
  253.  
  254.  
  255.  
  256.  
  257.                 // calculating cos(khi) (Nanbu1995 eq 17)
  258.                 double cos_khi = 0.0;
  259.                 double sin_khi = 0.0;
  260.                
  261.                 if (s < 1.0E-2) {// taking care of small s  
  262.                     cos_khi = 1.0 + s*log(R1);    
  263.                 }
  264.                 else {
  265.                     cos_khi = (1.0/A)*log(exp(-A) + 2.0*R1*sinh(A));
  266.                     // std::cout << cos_khi << "\n";
  267.                 }
  268.  
  269.                 sin_khi = sqrt(1.0 - cos_khi*cos_khi);
  270.  
  271.  
  272.  
  273.                 //calculating final velocity of first particle
  274.  
  275.                 double V_x_1 = V0_x_1 - 0.5*(V0_rel_x*(1.0-cos_khi) + h_x*sin_khi);
  276.                 double V_y_1 = V0_y_1 - 0.5*(V0_rel_y*(1.0-cos_khi) + h_y*sin_khi);
  277.                 double V_z_1 = V0_z_1 - 0.5*(V0_rel_z*(1.0-cos_khi) + h_z*sin_khi);
  278.  
  279.                 double V_1 = sqrt(V_x_1*V_x_1 + V_y_1*V_y_1 + V_z_1*V_z_1);
  280.  
  281.                 //calculating final velocity of second particle
  282.  
  283.                 double V_x_2 = V0_x_2 + 0.5*(V0_rel_x*(1.0-cos_khi) + h_x*sin_khi);
  284.                 double V_y_2 = V0_y_2 + 0.5*(V0_rel_y*(1.0-cos_khi) + h_y*sin_khi);
  285.                 double V_z_2 = V0_z_2 + 0.5*(V0_rel_z*(1.0-cos_khi) + h_z*sin_khi);
  286.  
  287.                 double V_2 = sqrt(V_x_2*V_x_2 + V_y_2*V_y_2 + V_z_2*V_z_2);
  288.  
  289.                 // calculating final energies of first and second colliding particles
  290.  
  291.                 //redistributing energy
  292.                 electrons[i].energy = V_1*V_1*m_e/(2.0*q);
  293.                 electrons[partner].energy = V_2*V_2*m_e/(2.0*q);      
  294.  
  295.                 //redistributing velocity
  296.                 electrons[i].vx = V_x_1;
  297.                 electrons[i].vy = V_y_1;
  298.                 electrons[i].vz = V_z_1;
  299.  
  300.                 electrons[partner].vx = V_x_2;
  301.                 electrons[partner].vy = V_y_2;
  302.                 electrons[partner].vz = V_z_2;
  303.  
  304.                 // --- collision energy redistrubution module ends  
  305.  
  306.                 energy_sum += E0_1 + E0_2 - electrons[i].energy - electrons[partner].energy;
  307.                  
  308.                 }
  309.  
  310.             }
  311.         }
  312.  
  313.         double total_energy = 0.0;
  314.         for (int i = 0; i < n_e; i++){
  315.             total_energy += electrons[i].energy;
  316.             int bin = (int)( (electrons[i].energy - Emin)/bin_width );
  317.             if (bin >=0 && bin < N)
  318.             histo_maxwell[bin]++;
  319.         }
  320.  
  321.         file5 << t*dt << " " << total_energy << "\n";
  322.  
  323.         for (int i = 0; i < N; i++){
  324.             double bin_start = Emin + i*bin_width;
  325.             file << bin_start << " " <<  static_cast<double>(histo_maxwell[i])/(electrons.size()*bin_width) << "\n"; // later need to divide by total partcles number to get normalized distribution
  326.             histo_maxwell[i] = 0;
  327.         }
  328.  
  329.         file.close();
  330.  
  331.     }
  332.     for (int i = 0; i < n_e; i++){
  333.  
  334.         file2 << i << " " << electrons[i].energy << "\n";
  335.         int bin = (int)( (electrons[i].energy - Emin)/bin_width );
  336.         if (bin >=0 && bin < histo_maxwell.size())
  337.             histo_maxwell[bin]++;
  338.     }
  339.  
  340.     for (int i = 0; i < histo_maxwell.size(); i++){
  341.         double bin_start = Emin + i*bin_width;
  342. //        printf("%5.1f - %5.1f\t%d\n", bin_start, bin_start + bin_width, histo_maxwell[i]);
  343.         file4 << bin_start << " " <<  bin_start*static_cast<double>(histo_maxwell[i])/(electrons.size()*bin_width) << "\n"; // E*f(E)
  344.     }
  345.  
  346.  
  347.     file0.close();
  348.     file1.close();
  349.     file2.close();
  350.     file3.close();
  351.     file4.close();
  352.  
  353.     clock_t end = clock();
  354.  
  355.     double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
  356.  
  357.     std::cout << "Energies written successfuly\n";
  358.     std::cout << "Elapsed time: %f seconds " << elapsed << "\n";
  359.     std::cout << "Total energy change: " << energy_sum << "\n";
  360.  
  361.  
  362.     return 0;
  363.  
  364.  
  365. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement