Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <iostream>
- #include <random>
- #include <fstream>
- #include <math.h>
- #include <time.h>
- #include <iomanip> // For std::fixed and std::setprecision
- #include <algorithm> // For std::shuffle
- #include <numeric> // For std::iota
- #define n_e 10000
- #define V_0 30000.0 // max velocity using to generate random distribution ---- doesn't work -> produces skew distribution???
- #define Emin 0.0
- #define Emax 200.0
- #define E_mean 100 // mean electron energy, initial distribution
- #define bin_width 0.5
- #define m_e 9.1E-31 // electron mass in kg
- #define k_b 1.38E-23 // Boltzmann constant
- #define q 1.602176634E-19 // elementary charge - eV -> J transfer param
- #define N ( (int)((Emax-Emin)/bin_width) ) // add 1 to include E_max if needed?
- #define T_n 10.0 // Helium neutral temperature in eV
- #define M_n 6.6464731E-31 // Helium atom mass
- #define N_He 1000000 // Helium neutrals number
- #define Volume 1.0E-12 // Volume to calculate netral density and collision frequency
- #define time 5.0E-3 // 500 microsec time to equalibrate the system
- struct Electron {
- //velocity components
- double vx = 0.0;
- double vy = 0.0;
- double vz = 0.0;
- //energy in eV
- double energy = 0.0;
- //Collision flag
- bool collided = false;
- //initialization function // void func(Type0& t) → means the function expects a reference to variable t of type0
- void initialize(std::mt19937& gen, std::uniform_real_distribution<double>& dis) {
- // velocity angles in spherical coordinates
- double phi = 2*M_PI*dis(gen);
- double cosTheta = 2.0*dis(gen) - 1.0;
- double sinTheta = sqrt(1.0 - cosTheta*cosTheta);
- energy = E_mean*dis(gen);
- double speed = sqrt(2*energy*q/m_e);
- vx = speed * sinTheta * cos(phi);
- vy = speed * sinTheta * sin(phi);
- vz = speed * cosTheta;
- }
- };
- struct CrossSection {
- double energy;
- double sigma;
- };
- double interpolate (double energy, const std::vector<CrossSection>& elastic_CS) {
- if (energy < elastic_CS.front().energy) {
- std::cout << " required energy value lower than range of cross-section data" << "\n";
- return 0.0;
- }
- if (energy > elastic_CS.back().energy) {
- std::cout << " required energy value higher than range of cross-section data" << "\n";
- return 0.0;
- }
- int step = 0;
- while (step < elastic_CS.size() && energy > elastic_CS[step].energy) {
- step++;
- }
- double k = (elastic_CS[step].sigma - elastic_CS[step-1].sigma)/(elastic_CS[step].energy - elastic_CS[step-1].energy);
- double m = elastic_CS[step].sigma - k*elastic_CS[step].energy;
- return k*energy + m;
- }
- struct NeutralParticle {
- double energy = 0.0;
- double vx = 0.0;
- double vy = 0.0;
- double vz = 0.0;
- void initialize(std::mt19937& gen, std::uniform_real_distribution<double>& dis, std::gamma_distribution<double>& maxwell) {
- double R = dis(gen);
- // velocity angles in spherical coordinates
- double phi = 2*M_PI*dis(gen);
- double cosTheta = 2.0*dis(gen) - 1.0;
- double sinTheta = sqrt(1.0 - cosTheta*cosTheta);
- energy = maxwell(gen); // neutrals energies sampled as Maxwell distribution in eV
- double speed = sqrt(2*energy*q/M_n);
- //velocity components of neutrals in m/s
- vx = speed * sinTheta * cos(phi);
- vy = speed * sinTheta * sin(phi);
- vz = speed * cosTheta;
- }
- };
- int main() {
- clock_t start = clock();
- std::vector<Electron> electrons(n_e); // better to use vector instead of simple array as it's dynamically allocated (beneficial for ionization)
- // std::vector<double> neutrals(N_He); // vector for neutrals
- std::vector<NeutralParticle> neutrals(N_He);
- std::vector<int> histo_random(N, 0); // initialize N size zero-vector for random (initial) histogram
- std::vector<int> histo_maxwell(N, 0); // initialize N size zero-vector for maxwellian histogram
- std::vector<int> histo_neutral(N, 0); // initialize N size zero-vector for neutral distribution histogram
- std::vector<double> elastic(N, 0); // precompiled cross-section energy vector
- std::random_device rd;
- std::mt19937 gen(rd());
- std::uniform_real_distribution<double> dis(0.0, 1.0);
- std::gamma_distribution<double> maxwell(1.5, T_n);
- std::uniform_int_distribution<int> pair(0, n_e-1);
- std::uniform_int_distribution<int> neutral_pair(0, N_He-1);
- std::ifstream elastic_cs("cross_sections/elastic.dat");
- if (!elastic_cs.is_open()) {
- std::cerr << "Error opening file!" << std::endl;
- return 1;
- }
- // reading elastic cross section datafile
- std::vector<CrossSection> elastic_CS;
- double energy, sigma;
- while (elastic_cs >> energy >> sigma) {
- elastic_CS.push_back({energy, sigma});
- }
- elastic_cs.close();
- std::ofstream file0("velocities.dat");
- if (!file0.is_open()) {
- std::cerr << "Error opening file!" << std::endl;
- return 1;
- }
- std::ofstream file1("energies.dat");
- if (!file1.is_open()) {
- std::cerr << "Error opening file!" << std::endl;
- return 1;
- }
- std::ofstream file2("energies_final.dat");
- if (!file2.is_open()) {
- std::cerr << "Error opening file!" << std::endl;
- return 1;
- }
- std::ofstream file3("histo_random.dat");
- if (!file3.is_open()) {
- std::cerr << "Error opening file!" << std::endl;
- return 1;
- }
- file3 << std::fixed << std::setprecision(10);
- std::ofstream file4("histo_maxwell.dat");
- if (!file4.is_open()) {
- std::cerr << "Error opening file!" << std::endl;
- return 1;
- }
- file4 << std::fixed << std::setprecision(10);
- std::ofstream file5("neutral_distribution.dat");
- if (!file5.is_open()) {
- std::cerr << "Error opening file!" << std::endl;
- return 1;
- }
- std::ofstream file6("E*f(E).dat");
- if (!file6.is_open()) {
- std::cerr << "Error opening file!" << std::endl;
- return 1;
- }
- std::ofstream file7("nu_max.dat");
- if (!file7.is_open()) {
- std::cerr << "Error opening file7!" << std::endl;
- return 1;
- }
- // Initialize all electrons
- for (auto& e : electrons) {
- e.initialize(gen, dis);
- }
- // initialize all nenutrals
- for (auto&n : neutrals) {
- n.initialize(gen, dis, maxwell);
- }
- // precalculate cross-sections for each energy bin
- for (int i = 0; i < N; i++){
- elastic[i] = interpolate(bin_width*(i+0.5), elastic_CS);
- }
- for (int i = 0; i < n_e; i++){
- file1 << i << " " << electrons.at(i).energy << "\n";
- file0 << i << " " << electrons[i].vx << " " << electrons[i].vy << " " << electrons[i].vz << "\n";
- }
- for (int i = 0; i < n_e; i++){
- int bin = (int)( (electrons[i].energy - Emin)/bin_width );
- if (bin >=0 && bin < histo_random.size())
- histo_random[bin]++;
- }
- for (int i = 0; i < histo_random.size(); i++){\
- double bin_start = Emin + i*bin_width;
- file3 << i*bin_width << " " << static_cast<double>(histo_random[i])/(electrons.size()*bin_width) << "\n"; // dividing by n_e to get normalized distribution
- }
- for (int i = 0; i < N_He; i++){
- int bin = (int)( (neutrals[i].energy - Emin)/bin_width );
- if (bin >=0 && bin < histo_neutral.size())
- histo_neutral[bin]++;
- }
- for (int i = 0; i < histo_neutral.size(); i++){
- double bin_start = Emin + i*bin_width;
- file5 << i*bin_width << " " << static_cast<double>(histo_neutral[i])/(neutrals.size()*bin_width) << "\n"; // this is real f(E) - normalized distribution
- file6 << i*bin_width << " " << (i*bin_width)*static_cast<double>(histo_neutral[i])/(neutrals.size()*bin_width) << "\n"; // this should be E*f(E)
- }
- double nu_max = 0.0;
- double nu_max_temp = 0.0;
- for (int i = 0; i < N; i++){
- nu_max_temp = (N_He/Volume)*elastic[i] * sqrt(2.0*(i*bin_width + bin_width/2.0)*q/m_e);
- file7 << i << " " << nu_max_temp << "\n";
- if (nu_max_temp > nu_max)
- nu_max = nu_max_temp;
- }
- std::cout << nu_max << "\n";
- double dt = 0.1/nu_max; // minimum should be 0.1/nu_max to get acceptable numerical error range see Vahedi Surrendra 1995
- double steps = static_cast<int>(time/dt);
- std::cout << steps << "\n";
- //using null-collision technique, getting the number of particles colliding each step: P_collision = 1 - exp(-nu_max*dt)
- int Ne_collided = (1.0-exp(-1.0*dt*nu_max))*n_e;
- std::cout << Ne_collided << "\n";
- // Generate shuffled list of electron indices
- std::vector<int> electron_indices(n_e);
- std::iota(electron_indices.begin(), electron_indices.end(), 0); // fill with index
- std::shuffle(electron_indices.begin(), electron_indices.end(), gen); // shuffle the indexes
- int reshuffle_interval = 10;
- for (int t = 0; t < steps; t++){
- std::cout << "timestep: " << t << "\n";
- //reshuffle the indexes
- if (t % reshuffle_interval == 0) {
- std::shuffle(electron_indices.begin(), electron_indices.end(), gen);
- }
- // setting flags to false each timestep
- for (auto& e : electrons) e.collided = false;
- int collision_counter = 0;
- for (int idx : electron_indices) {
- if (collision_counter >= Ne_collided) break; // quit if reached all collisions
- Electron& e = electrons[idx];
- if (e.collided) continue; // Skip already collided electrons
- double electron_energy = e.energy;
- int bin_energy = static_cast<int>(electron_energy / bin_width);
- double nu_elastic = (N_He/Volume) * elastic[bin_energy] * sqrt(2.0*electron_energy*q/m_e);
- if (dis(gen) < nu_elastic/nu_max) {
- // ---- Collision energy redistribution module
- // electron particle X Y Z initial velocities and energy
- double V0_x_1 = e.vx;
- double V0_y_1 = e.vy;
- double V0_z_1 = e.vz;
- // neutral particle X Y Z initial velocities
- int k = neutral_pair(gen);
- double V0_x_2 = neutrals[k].vx;
- double V0_y_2 = neutrals[k].vy;
- double V0_z_2 = neutrals[k].vz;
- // initial relative velocity X Y Z (must be equal to final relative velocity in center-of-mass frame)
- double V0_rel_x = (V0_x_1 - V0_x_2);
- double V0_rel_y = (V0_y_1 - V0_y_2);
- double V0_rel_z = (V0_z_1 - V0_z_2);
- double V0_rel = sqrt(V0_rel_x*V0_rel_x + V0_rel_y*V0_rel_y + V0_rel_z*V0_rel_z);
- // center-of-mass frame initial velocity (magnitude of it must be equal to the counterpart in this frame)
- double V_cm_x = (m_e*V0_x_1 + M_n*V0_x_2)/(m_e + M_n);
- double V_cm_y = (m_e*V0_y_1 + M_n*V0_y_2)/(m_e + M_n);
- double V_cm_z = (m_e*V0_z_1 + M_n*V0_z_2)/(m_e + M_n);
- // generating random variables to calculate random direction of center-of-mass after the collision
- double R1 = dis(gen);
- double R2 = dis(gen);
- // calculating spherical angles for center-of-mass random direction
- double theta = acos(1.0- 2.0*R1);
- double phi = 2*M_PI*R2;
- //calculating final relative velocity with random direction
- double V_rel_x = V0_rel*sin(theta)*cos(phi);
- double V_rel_y = V0_rel*sin(theta)*sin(phi);
- double V_rel_z = V0_rel*cos(theta);
- double V_rel = sqrt(V_rel_x*V_rel_x + V_rel_y*V_rel_y + V_rel_z*V_rel_z);
- //calculating final velocity of electron
- double V_x_1 = V_cm_x + V_rel_x * (M_n/(m_e + M_n));
- double V_y_1 = V_cm_y + V_rel_y * (M_n/(m_e + M_n));
- double V_z_1 = V_cm_z + V_rel_z * (M_n/(m_e + M_n));
- double V_1 = sqrt(V_x_1*V_x_1 + V_y_1*V_y_1 + V_z_1*V_z_1);
- //updating electron energy and velocities
- e.energy = m_e*V_1*V_1/(2.0*q);
- e.vx = V_x_1;
- e.vy = V_y_1;
- e.vz = V_z_1;
- collision_counter++;
- e.collided = true;
- }
- }
- }
- for (int i = 0; i < n_e; i++){
- file2 << i << " " << electrons[i].energy << "\n";
- int bin = (int)( (electrons[i].energy - Emin)/bin_width );
- if (bin >=0 && bin < histo_maxwell.size())
- histo_maxwell[bin]++;
- }
- int check = 0;
- for (int i = 0; i < histo_maxwell.size(); i++){
- check += histo_maxwell[i];
- double bin_start = Emin + i*bin_width;
- file4 << i*bin_width << " " << (i*bin_width)*static_cast<double>(histo_maxwell[i])/(electrons.size()*bin_width) << "\n"; // getting f(E)*E
- }
- std::cout << "Total # of electrons in histo: " << check << "\n";
- file0.close();
- file1.close();
- file2.close();
- file3.close();
- file4.close();
- file5.close();
- file6.close();
- file7.close();
- clock_t end = clock();
- double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
- std::cout << "Energies written successfuly\n";
- std::cout << "Elapsed time: %f seconds " << elapsed << "\n";
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement