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 100000
- #define Emin 0.0
- #define Emax 450.0
- #define E_mean 100 // mean electron energy, initial distribution
- #define bin_width 0.1 // keep energy step ~ this to maintain cross-section clarity (Ramsauer minimum etc)
- #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 T_e 50.0 // electron Maxwell initial distribution
- #define M_n 6.6464731E-27 // 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 1.0E-4 // 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;
- // initializing Maxwell-Boltzmann distribution with T_e
- 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_e);
- //velocity components of neutrals in m/s
- 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<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_vec(N, 0); // precompiled elastic cross-section-energy vector
- std::vector<double> inelastic1_vec(N, 0); // precompiled inelastic(triplet excitation) 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_neutral(1.5, T_n);
- std::gamma_distribution<double> maxwell_electron(1.5, T_e);
- 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_dat("cross_sections/elastic.dat");
- if (!elastic_cs_dat.is_open()) {
- std::cerr << "Error opening elastic cross-sections file!" << std::endl;
- return 1;
- }
- std::ifstream excitation1_cs_dat("cross_sections/inelastic_triplet.dat");
- if (!elastic_cs_dat.is_open()) {
- std::cerr << "Error opening inelastic triplet cross-sections file!" << std::endl;
- return 1;
- }
- // --- starts reading cross section datafiles
- std::vector<CrossSection> elastic_CS_temp;
- double energy, sigma;
- while (elastic_cs_dat >> energy >> sigma) {
- elastic_CS_temp.push_back({energy, sigma});
- }
- elastic_cs_dat.close();
- energy = 0.0;
- sigma = 0.0;
- std::vector<CrossSection> inelastic1_CS_temp;
- while (excitation1_cs_dat >> energy >> sigma) {
- inelastic1_CS_temp.push_back({energy, sigma});
- }
- excitation1_cs_dat.close();
- // --- finish reading cross-section datafiles
- std::ofstream file0("velocities.dat");
- std::ofstream file1("energies.dat");
- std::ofstream file2("energies_final.dat");
- std::ofstream file3("histo_random.dat");
- file3 << std::fixed << std::setprecision(10);
- std::ofstream file4("histo_maxwell.dat");
- file4 << std::fixed << std::setprecision(10);
- std::ofstream file5("neutral_distribution.dat");
- std::ofstream file6("E*f(E).dat");
- std::ofstream file7("nu_max.dat");
- std::ofstream file8("electron_mean_energy.dat");
- std::ofstream file9("nu.dat");
- std::ofstream file10("inelastic_CS_check.dat");
- // Initialize all electrons
- for (auto& e : electrons) {
- e.initialize(gen, dis, maxwell_electron);
- }
- // initialize all nenutrals
- for (auto&n : neutrals) {
- n.initialize(gen, dis, maxwell_neutral);
- }
- // precalculate elastic cross-section for each energy bin
- for (int i = 0; i < N; i++){
- elastic_vec[i] = interpolate(bin_width*(i+0.5), elastic_CS_temp);
- }
- // precalculate inelastic cross-section (triplet) for each energy bin
- for (int i = 0; i < N; i++){
- inelastic1_vec[i] = interpolate(bin_width*(i+0.5), inelastic1_CS_temp);
- }
- 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";
- }
- // -----initial electrons energy distribution starts------------////
- 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_center = Emin + (i + 0.5) * bin_width;
- file3 << bin_center << " " << bin_center*static_cast<double>(histo_random[i])/(electrons.size()*bin_width) << "\n"; // this is electron normalized distribution function
- }
- // -----initial electrons energy distribution ends------------////
- // -----neutrals Maxwell-Boltzmann distribution starts------------////
- 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_center = Emin + (i + 0.5) * bin_width;
- file5 << bin_center << " " << static_cast<double>(histo_neutral[i])/(neutrals.size()*bin_width) << "\n"; // this is real f(E) - normalized distribution
- file6 << bin_center << " " << bin_center*static_cast<double>(histo_neutral[i])/(neutrals.size()*bin_width) << "\n"; // this should be E*f(E)
- }
- // -----neutrals Maxwell-Boltzmann distribution starts------------////
- // -----calculating nu-max for null-collision method starts ------------////
- double nu_max = 0.0;
- double nu_max_temp = 0.0;
- double sigma_total = 0.0;
- for (int i = 0; i < N; i++){
- nu_max_temp = (N_He/Volume)*elastic_vec[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;
- }
- // -----calculating nu-max for null-collision method ends ------------////
- //----- calculating nu-average from our electron distribution starts---------///
- for (int i = 0; i < N; i++){
- double bin_center = Emin + (i + 0.5) * bin_width;
- file9 << bin_center << " " << (N_He/Volume)*elastic_vec[i] * sqrt(2.0*bin_center*q/m_e)*static_cast<double>(histo_random[i])/(electrons.size()*bin_width) << "\n";
- }
- //----- calculating nu-average from our electron distribution ends ---------///
- 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;
- // int Ne_collided = n_e*0.98; // in case I want to check smth
- // 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 = 1;
- int print_interval = 100;
- for (int t = 0; t < steps; t++){
- std::cout << "timestep remains: " << steps - t << "\n";
- //reshuffle the indices
- 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_vec[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;
- // randomize particles each collision
- NeutralParticle tmp_neutral;
- tmp_neutral.initialize(gen, dis, maxwell_neutral);
- double V0_x_2 = tmp_neutral.vx;
- double V0_y_2 = tmp_neutral.vy;
- double V0_z_2 = tmp_neutral.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;
- }
- }
- // if (t%print_interval == 0){
- // // open datafiles to write each time step to see evolution
- // std::ostringstream filename;
- // filename << "data/distribution_" << std::setw(4) << std::setfill('0') << t << ".dat";
- // std::ofstream file(filename.str());
- // if (!file.is_open()){
- // std::cerr << "Error opening file: " << filename.str() << std::endl;
- // return 1;
- // }
- // // end opening datafiles for each timestep
- // // creating histogram each timestep
- // for (int i = 0; i < n_e; i++){
- // int bin = (int)( (electrons[i].energy - Emin)/bin_width );
- // if (bin >=0 && bin < N)
- // histo_maxwell[bin]++;
- // }
- // // writing data each time step
- // for (int i = 0; i < N; i++){
- // double bin_start = Emin + i*bin_width;
- // 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
- // histo_maxwell[i] = 0;
- // }
- // file.close();
- // // end writing data each timestep
- // calculating mean energy
- double total_energy = 0.0;
- for (const auto& e : electrons) total_energy += e.energy;
- double mean_energy = total_energy / n_e;
- file8 << t*dt << " " << mean_energy << "\n";
- }
- // ----- final electron energies distribution begins
- 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_center = Emin + (i + 0.5) * bin_width;
- file4 << bin_center << " " << bin_center*static_cast<double>(histo_maxwell[i])/(electrons.size()*bin_width) << "\n"; // getting f(E)*E
- }
- std::cout << "Total # of electrons in histo: " << check << "\n";
- // ----- final electron energies distribution begins
- file0.close();
- file1.close();
- file2.close();
- file3.close();
- file4.close();
- file5.close();
- file6.close();
- file7.close();
- file8.close();
- file9.close();
- clock_t end = clock();
- double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
- std::cout << "Ne collided each timesteps:" << Ne_collided << "\n";
- std::cout << "Energies written successfuly\n";
- std::cout << "Elapsed time: %f seconds " << elapsed << "\n";
- return 0;
- }
Advertisement
Advertisement