phystota

v3.2

Jul 8th, 2025
14
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 92.39 KB | None | 0 0
  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cstdbool>
  5. #include <Eigen/Sparse>
  6. #include <cmath>
  7. #include <ctime>
  8. #include <random>
  9. #include <vector>
  10. #include <string>
  11. #include <fstream>
  12. #include <sstream>
  13. #include <algorithm>    
  14. #include <stdexcept>
  15. #include <iostream>
  16. #include <iomanip>
  17. #include <sys/stat.h>
  18. #include <errno.h>
  19. #include <stdio.h>
  20. #include <unordered_map>
  21.  
  22. using namespace::std;
  23. using namespace Eigen;
  24.  
  25. // constants
  26.  
  27. const double     PI             = 3.141592653589793;          // mathematical constant Pi
  28. const double     TWO_PI         = 2.0 * PI;                   // two times Pi
  29. const double     E_CHARGE       = 1.60217662e-19;             // electron charge [C]
  30. const double     EV_TO_J        = E_CHARGE;                   // eV <-> Joule conversion factor
  31. const double     E_MASS         = 9.109e-31;                  // mass of electron [kg]
  32. const double     HE_MASS        = 6.67e-27;                   // mass of He atom [kg]
  33. const double     MU_HEHE        = HE_MASS / 2.0;              // reduced mass of two He atoms [kg]
  34. const double     K_BOLTZMANN    = 1.38064852e-23;             // Boltzmann's constant [J/K]
  35. const double     EPSILON0       = 8.85418781e-12;             // permittivity of free space [F/m]
  36.  
  37.  
  38. // simulation parameters
  39.  
  40. const int        N_G            = 501;                        // number of grid points
  41. const int        N_T            = 2000;                        // time steps within an RF period
  42.  
  43.  
  44. const double     FREQUENCY      = 13.56e6;                    // driving frequency [Hz]
  45. const double     VOLTAGE        = 350.0;                      // voltage amplitude [V]
  46. const double     L              = 0.04;                      // electrode gap [m]
  47. const double     PRESSURE       = 13.3322;              // gas pressure [Pa] // n*k*T to match Turner's case
  48. const double     T_neutral      = 300.0;                      // background gas temperature [K] (also ion temperature)
  49. const double     T_electron     = 30000.0;                    // initial electron temperatutre [K]
  50. const double     WEIGHT         = 41875.0;                  // weight of superparticles
  51. const double     ELECTRODE_AREA = 1.6e-4;                     // (fictive) electrode area [m^2]
  52. const int        N_INIT         = 65536;                      // number of initial electrons and ions
  53.  
  54. // additional (derived) constants
  55.  
  56. const double     PERIOD         = 1.0 / FREQUENCY;                              // RF period length [s]
  57. const double     DT_E           = PERIOD / (double)(N_T);                       // electron time step [s]
  58. const int        N_SUB          = 5;                                            // ions move only in these cycles (subcycling)
  59. const int        N_avg          = 1;                                            // number of cycles to average rates --- Artem
  60.       int        counter        = 0;                                            // cycles since update of rates --- Artem
  61. const double     DT_I           = N_SUB * DT_E;                                 // ion time step [s]
  62. const double     DX             = L / (double)(N_G - 1);                        // spatial grid division [m]
  63. const double     INV_DX         = 1.0 / DX;                                     // inverse of spatial grid size [1/m]
  64. const double     GAS_DENSITY    = PRESSURE / (K_BOLTZMANN * T_neutral);         // background gas density [1/m^3]
  65. const double     OMEGA          = TWO_PI * FREQUENCY;                           // angular frequency [rad/s]
  66.  
  67. const double     Diff_coeff     = 8.992E-6*pow(T_neutral,1.5)/(PRESSURE/133.3); // diffusion coefficient for metastable states
  68. const double     kT_over_m      = K_BOLTZMANN*T_electron/E_MASS;                // KT/m for calculations
  69.  
  70. // electron and ion cross sections
  71.  
  72. const int        N_CS           = 8;                          // total number of processes / cross sections
  73. const int        E_ELA          = 0;                          // process identifier: electron/elastic
  74. const int        E_ION          = 1;                          // process identifier: electron/ionization
  75. const int        I_ISO          = 2;                          // process identifier: ion/elastic/isotropic
  76. const int        I_BACK         = 3;                          // process identifier: ion/elastic/backscattering
  77. const int        E_EXC_1        = 4;                          // process identifier: electron/excitation1
  78. const int        E_EXC_2        = 5;                          // process identifier: electron/excitation1
  79. const int        E_SUPER_1      = 6;                          // triplet superelastic - Artem
  80. const int        E_SUPER_2      = 7;                          // singlet superelastic - Artem
  81.  
  82. const double     E_EXC_TH_1     = 19.82;                      // electron impact excitation threshold [eV]
  83. const double     E_EXC_TH_2     = 20.61;                      // electron impact excitation threshold [eV]
  84. const double     E_ION_TH       = 24.587;                     // electron impact ionization threshold [eV]
  85. const int        CS_RANGES      = 1000000;                    // number of entries in cross section arrays
  86. const double     DE_CS          = 0.001;                      // energy division in cross section arrays [eV]
  87. typedef float    cross_section[CS_RANGES];                    // cross section array
  88. cross_section    sigma[N_CS];                                 // set of cross section arrays
  89. cross_section    sigma_tot_e;                                 // total macroscopic cross section of electrons
  90. cross_section    sigma_tot_i;                                 // total macroscopic cross section of ions
  91.  
  92. double nu_avg;                                                // average nu for electrons thorugh 1 cycle
  93.  
  94. // particle coordinates
  95.  
  96. const int        MAX_N_P = 10000000;                           // maximum number of particles (electrons / ions)
  97. typedef double   particle_vector[MAX_N_P];                    // array for particle properties
  98. int              N_e = 0;                                     // number of electrons
  99. int              N_i = 0;                                     // number of ions
  100. int              N_e1 = 0;                                    // number of singlet excited states
  101. int              N_e2 = 0;                                    // number of triplet excited states
  102. particle_vector  x_e, vx_e, vy_e, vz_e;                       // coordinates of electrons (one spatial, three velocity components)
  103. particle_vector  x_i, vx_i, vy_i, vz_i;                       // coordinates of ions (one spatial, three velocity components)
  104.  
  105.  
  106.  
  107.  
  108. typedef double   xvector[N_G];                                // array for quantities defined at gird points
  109. xvector          efield,pot;                                  // electric field and potential
  110. xvector          e_density,i_density;                         // electron and ion densities
  111. xvector          cumul_e_density,cumul_i_density;             // cumulative densities
  112.  
  113. //excited states handling
  114. xvector e1_density;
  115. xvector e2_density;
  116. xvector sum_electron_density = {0.0}; xvector avg_electron_density = {0.0};
  117.  
  118. xvector sum_rate1f = {0.0}; xvector sum_rate1b = {0.0}; xvector sum_rate2f = {0.0}; xvector sum_rate2b = {0.0};
  119.  
  120. xvector avg_rate1f = {0.0}; xvector avg_rate1b = {0.0}; xvector avg_rate2f = {0.0}; xvector avg_rate2b = {0.0};
  121.  
  122.                                        // array for Triplet excitation rates!!! Artem
  123. xvector S1 = {0.0};
  124. xvector S2 = {0.0};                                           // source terms for DRR module -- Artem
  125.  
  126. typedef unsigned long long int Ullong;                        // compact name for 64 bit unsigned integer
  127. Ullong       N_e_abs_pow  = 0;                                // counter for electrons absorbed at the powered electrode
  128. Ullong       N_e_abs_gnd  = 0;                                // counter for electrons absorbed at the grounded electrode
  129. Ullong       N_i_abs_pow  = 0;                                // counter for ions absorbed at the powered electrode
  130. Ullong       N_i_abs_gnd  = 0;                                // counter for ions absorbed at the grounded electrode
  131.  
  132. // electron energy probability function
  133.  
  134. const int    N_EEPF  = 200000;                                 // number of energy bins in Electron Energy Probability Function (EEPF)
  135. const double DE_EEPF = 0.05;                                 // resolution of EEPF [eV]
  136. typedef double eepf_vector[N_EEPF];                          // array for EEPF
  137. eepf_vector eepf     = {0.0};                                // time integrated EEPF in the center of the plasma
  138.  
  139. // ION VELOCITY and ENERGY PROBABILITY FUNCTION --- Artem
  140.  
  141. //spatial resolution
  142. const int       N_sp   = 100;                           // number of spatial bins of the discharge regions
  143. const double    DX_PF   = L/N_sp;                  // corresponded spatial step for probability functions
  144. //IVDF
  145. const int   N_IVDF  = 50000;
  146. const double DV_IVDF = 10.0;
  147. std::vector<std::vector<double>>
  148.     ivdf_pos(N_sp, std::vector<double>(N_IVDF, 0.0)),  //  positive part of IVDF
  149.     ivdf_neg(N_sp, std::vector<double>(N_IVDF, 0.0));  //  negative part of IVDF
  150.  
  151. //IEPF
  152. const int    N_IEPF  = 200000;                                 // number of energy bins in Ion Energy Probability Function (EEPF)
  153. const double DE_IEPF = 0.001;                                 // resolution of IEPF [eV]
  154. std::vector<std::vector<double>>
  155.     iepf(N_sp, std::vector<double>(N_IEPF, 0.0));               //  IEDF array
  156.  
  157. // ion flux-energy distributions
  158.  
  159. const int    N_IFED   = 200;                                 // number of energy bins in Ion Flux-Energy Distributions (IFEDs)
  160. const double DE_IFED  = 1.0;                                 // resolution of IFEDs [eV]
  161. typedef int  ifed_vector[N_IFED];                            // array for IFEDs
  162. ifed_vector  ifed_pow = {0};                                 // IFED at the powered electrode
  163. ifed_vector  ifed_gnd = {0};                                 // IFED at the grounded electrode
  164. double       mean_i_energy_pow;                              // mean ion energy at the powered electrode
  165. double       mean_i_energy_gnd;                              // mean ion energy at the grounded electrode
  166.  
  167. // spatio-temporal (XT) distributions
  168.  
  169. const int N_BIN                     = 10;                    // number of time steps binned for the XT distributions
  170. const int N_XT                      = N_T / N_BIN;           // number of spatial bins for the XT distributions
  171. typedef double xt_distr[N_G][N_XT];                          // array for XT distributions (decimal numbers)
  172. xt_distr pot_xt                     = {0.0};                 // XT distribution of the potential
  173. xt_distr efield_xt                  = {0.0};                 // XT distribution of the electric field
  174. xt_distr ne_xt                      = {0.0};                 // XT distribution of the electron density
  175. xt_distr ni_xt                      = {0.0};                 // XT distribution of the ion density
  176. xt_distr ue_xt                      = {0.0};                 // XT distribution of the mean electron velocity
  177. xt_distr ui_xt                      = {0.0};                 // XT distribution of the mean ion velocity
  178. xt_distr je_xt                      = {0.0};                 // XT distribution of the electron current density
  179. xt_distr ji_xt                      = {0.0};                 // XT distribution of the ion current density
  180. xt_distr powere_xt                  = {0.0};                 // XT distribution of the electron powering (power absorption) rate
  181. xt_distr poweri_xt                  = {0.0};                 // XT distribution of the ion powering (power absorption) rate
  182. xt_distr meanee_xt                  = {0.0};                 // XT distribution of the mean electron energy
  183. xt_distr meanei_xt                  = {0.0};                 // XT distribution of the mean ion energy
  184. xt_distr counter_e_xt               = {0.0};                 // XT counter for electron properties
  185. xt_distr counter_i_xt               = {0.0};                 // XT counter for ion properties
  186. xt_distr ioniz_rate_xt              = {0.0};                 // XT distribution of the ionisation rate
  187.  
  188. xt_distr e1_xt                      = {0.0};                 // XT distribution of triplet excited states density - Artem
  189. xt_distr e2_xt                      = {0.0};                 // XT distribution of singlet excited states density - Artem
  190.  
  191. double   mean_energy_accu_center    = 0;                     // mean electron energy accumulator in the center of the gap
  192. Ullong   mean_energy_counter_center = 0;                     // mean electron energy counter in the center of the gap
  193. Ullong   N_e_coll                   = 0;                     // counter for electron collisions
  194. Ullong   N_i_coll                   = 0;                     // counter for ion collisions
  195. double   Time;                                               // total simulated time (from the beginning of the simulation)
  196. int      cycle,no_of_cycles,cycles_done;                     // current cycle and total cycles in the run, cycles completed
  197. int      arg1;                                               // used for reading command line arguments
  198. char     st0[80];                                            // used for reading command line arguments
  199. FILE     *datafile;                                          // used for saving data
  200. bool     measurement_mode;                                   // flag that controls measurements and data saving
  201.  
  202. const double gamma_i                = 0.0;                   // yielding coefficient for ions bombarding the surface
  203. const double gamma_m                = 0.3;                   // yielding coefficient for metastables bombarding the surface
  204. const double gamma_p                = -10.0;                   // yielding coefficient for photons bombarding the surface
  205. const double gamma_k                = 0.5;                     // recombination coefficient  
  206. const double r                      = 0.0;                   // probabilty for the electron to reflect at the surface
  207. const double T_SEE                  = 300;                   // initial temperature of SEE electrons [K]
  208.  
  209. const double Ry                     = 13.6057;                // Rydberg constant, eV
  210. const double Bohr                   = 0.8797e-20;             // atomic consant related to Bohr Radius, m^2
  211.  
  212.  
  213. //---------------------------------------------------------------------------//
  214. // C++ Mersenne Twister 19937 generator                                      //
  215. // R01(MTgen) will genarate uniform distribution over [0,1) interval         //
  216. // RMB(MTgen) will generate Maxwell-Boltzmann distribution (of gas atoms)    //
  217. //---------------------------------------------------------------------------//
  218.  
  219. std::random_device rd{};
  220. std::mt19937 MTgen(rd());
  221. std::uniform_real_distribution<> R01(0.0, 1.0);
  222. std::normal_distribution<> RMB_n(0.0,sqrt(K_BOLTZMANN * T_neutral / HE_MASS));
  223. std::normal_distribution<> RMB_e(0.0,sqrt(K_BOLTZMANN * T_electron / E_MASS));
  224. std::normal_distribution<> RMB_SEE(0.0,sqrt(K_BOLTZMANN * T_SEE / E_MASS));
  225.  
  226. enum class ProcKind { ELASTIC, IONIZATION, EXCITATION, SUPERELASTIC };
  227.  
  228. struct Process {
  229.     ProcKind              kind;
  230.     std::string           name;                                                             // e.g. "1^1S→2^3S"
  231.     int                   csSlot;                                                           // index into sigma (filled later)
  232.     int                   init_Lvl;                                                         // index in levels vector
  233.     int                   final_Lvl;                                                        //   "
  234.     double                threshold_eV;                                                     // 0 for elastic
  235.     double                A1 = 0.0, A2 = 0.0, A3 = 0.0, A4 = 0.0, A5 = 0.0, A6 =0.0;        // coefficients for cross-section calculation [Ralchenko2008]
  236.     char                  type;                                                             // type of a process - dipole allowed/forbidden etc..
  237.     std::vector<double>   cum_rate;                                                 // storing cumulative rate of this process, feed to avg_rate() function
  238.     std::vector<double>   avg_rate;                                                 //stoding average rate of this process, feed to DRR
  239. };
  240.  
  241. struct level {
  242.     std::string             name;
  243.     double                  th_energy;
  244.     int                     weight;
  245.     std::vector<double>     density;
  246. };
  247.  
  248. struct transition {
  249.     std::string     lvl_init;
  250.     std::string     lvl_final;
  251.     char            type;
  252.     double          thresh_energy;    
  253.     double          A1, A2, A3, A4, A5, A6;
  254.     int             is_superelastic;
  255. };
  256.  
  257. int find_lvl(int init_lvl, int fin_lvl, const std::vector<Process>& eProc){
  258.     int check = -1;
  259.     string name;
  260.     for (auto const& proc : eProc){
  261.         if ((proc.kind == ProcKind::EXCITATION) && (proc.init_Lvl == fin_lvl) && (proc.final_Lvl == init_lvl)){
  262.             return proc.csSlot;
  263.             check += 1;
  264.         }
  265.     }
  266.     if (check != 0) {
  267.         for (auto const& proc : eProc){
  268.             if ((proc.kind == ProcKind::SUPERELASTIC) && (proc.init_Lvl == init_lvl) && (proc.final_Lvl == fin_lvl)){
  269.                 name = proc.name;
  270.             }
  271.         }
  272.         std::cout << "Can't find an excitation pair for superelastic process: " << name << "\n";
  273.         return -1;
  274.     }  
  275. }
  276.  
  277. class CSInterpolator {
  278. public:
  279.   // load "filename" which must have two whitespace‐separated columns:
  280.   //    energy (eV)    cross_section (in m^2 or cm^2 as you prefer)
  281.   CSInterpolator(const std::string &filename) {
  282.     std::ifstream in(filename);
  283.     if (!in) throw std::runtime_error("CSInterpolator: cannot open " + filename);
  284.     double E, sigma;
  285.     std::string line;
  286.     while (std::getline(in, line)) {
  287.       std::istringstream iss(line);
  288.       if (iss >> E >> sigma) {
  289.         E_pts_.push_back(E);
  290.         sigma_pts_.push_back(sigma);
  291.       }
  292.     }
  293.     if (E_pts_.size()<2)
  294.       throw std::runtime_error("CSInterpolator: need at least two data points in " + filename);
  295.   }
  296.  
  297.   // return σ(E) by simple linear interpolation (clamped to end‐points)
  298.   double operator()(double E) const {
  299.     auto it = std::lower_bound(E_pts_.begin(), E_pts_.end(), E);
  300.     if (it == E_pts_.begin()) {
  301. //        std::cerr << "Warning: E="<<E<<" below data range, clamping to "<< 0.0 <<"\n";
  302.         return 0.0;
  303.     }  
  304.     if (it == E_pts_.end()){
  305. //        std::cerr << "Warning: E="<<E<<" above data range, clamping to "<< sigma_pts_.back() <<"\n";
  306.         return sigma_pts_.back();
  307.     }    
  308.     size_t idx = (it - E_pts_.begin());
  309.     double E1 = E_pts_[idx-1], E2 = E_pts_[idx];
  310.     double s1 = sigma_pts_[idx-1], s2 = sigma_pts_[idx];
  311.     // linear interp
  312.     return s1 + (s2 - s1) * (E - E1)/(E2 - E1);
  313.   }
  314.  
  315. private:
  316.   std::vector<double> E_pts_, sigma_pts_;
  317. };
  318.  
  319. // Cross-Section function for excitation cross-sections.
  320. // here for type 'd' - dipole-forbidden, 's' - spin-forbidden, 'a' - dipole-allowed
  321. // g - stat weight of initial level (g = 1 for ground state). See paper: RalchenkoJanev2008
  322. double forbidden_CS(double A1, double A2, double A3, double A4, double A5, double A6, double E, double E_th, int g, char type){
  323.     double x = E/E_th;
  324.     double strength, S1, S2, CS;
  325.  
  326.     if (type == 'd'){
  327.         S1 = (A1 + A2/x + A3/(x*x) + A4/(x*x*x));
  328.         S2 = (x*x/(x*x + A5));
  329.     }
  330.     if (type == 's'){
  331.         S1 = (A1 + A2/x + A3/(x*x) + A4/(x*x*x));
  332.         S2 = (1.0/(x*x + A5));
  333.     }
  334.     if (type == 'a'){
  335.         S1 = (A1*log(x) + A2 + A3/x + A4/(x*x) + A5/(x*x*x));
  336.         S2 = (x + 1.0)/(x + A6);
  337.     }
  338.     strength = S1*S2;
  339.     CS = Bohr * Ry/(g*E) * strength;
  340.     return CS;
  341. }
  342.  
  343. // N_CS - number of cross-sections
  344. void set_electron_cross_sections_ar(const std::vector<Process>& eProc, const std::vector<level>& levels,  std::vector<std::vector<float>>& sigma_temp){
  345.     double  A1, A2, A3, A4, A5, A6;
  346.     double  threshold;
  347.     char    type;
  348.     int     g;
  349.  
  350.     printf(">> eduPIC: Setting e- / He cross sections\n");
  351.  
  352.     // load your four datafiles (make sure these names match your files!)
  353.     CSInterpolator cs_ela  ("CS/He_electron_elastic.dat");   // two‐col: E σ_ela
  354.     CSInterpolator cs_ion  ("CS/He_electron_ionization.dat");// two‐col: E σ_ion
  355.    
  356.     int P = eProc.size();
  357.     for(int i=0; i<CS_RANGES; i++){
  358.         double en = (i==0 ? DE_CS : DE_CS * i);         // energy grid
  359.         for (int s = 0; s < P; s++){
  360.             switch(eProc[s].kind){
  361.                 case ProcKind::ELASTIC:{sigma_temp[s][i] = cs_ela(en); break;}
  362.                 case ProcKind::IONIZATION: {sigma_temp[s][i] = cs_ion(en); break;}
  363.                 case ProcKind::EXCITATION: {                    
  364.                     double threshold = eProc[s].threshold_eV;
  365.                     if (en < threshold) {          // <–– guard reinstated
  366.                         sigma_temp[s][i] = 0.0f;
  367.                         break;
  368.                     }
  369.                     int g = levels[eProc[s].init_Lvl].weight;  // g_init
  370.                     sigma_temp[s][i] = forbidden_CS(
  371.                         eProc[s].A1, eProc[s].A2, eProc[s].A3,
  372.                         eProc[s].A4, eProc[s].A5, eProc[s].A6,
  373.                         en, threshold, g, eProc[s].type);
  374.  
  375.                     if (sigma_temp[s][i] < 0.0f)        // numerical safety
  376.                         sigma_temp[s][i] = 0.0f;
  377.                     break;
  378.                    
  379.                 } break;
  380.             default: ;    
  381.             }
  382.         }
  383.     }
  384.     for (int i = 0; i<CS_RANGES; i++){
  385.         for (int s = 0; s < P; s++){
  386.             if (eProc[s].kind != ProcKind::SUPERELASTIC) continue;
  387.             double en = (i==0 ? DE_CS : DE_CS * i);  
  388.             threshold = eProc[s].threshold_eV;
  389.             double enPlus = en + threshold;
  390.             int    idx    = enPlus / DE_CS;
  391.             int lvl_init = eProc[s].init_Lvl;
  392.             int lvl_fin = eProc[s].final_Lvl;            
  393.             int excitation_pair = find_lvl(lvl_init, lvl_fin, eProc);
  394.             sigma_temp[s][i]   = (idx<CS_RANGES && en>0)
  395.             ? (static_cast<double>(levels[eProc[s].final_Lvl].weight) / static_cast<double>(levels[eProc[s].init_Lvl].weight)) *
  396.                 (enPlus/en) * sigma_temp[excitation_pair][idx]   : 0.0;
  397.         }
  398.     }        
  399. }
  400.  
  401. //------------------------------------------------------------------------------//
  402. //  ion cross sections: A. V. Phelps, J. Appl. Phys. 76, 747 (1994)             //
  403. //------------------------------------------------------------------------------//
  404.  
  405. void set_ion_cross_sections_ar(void){
  406.     int    i;
  407.     double e_com,e_lab,qmom,qback,qiso;
  408.    
  409.     printf(">> eduPIC: Setting He+ / He cross sections\n");
  410.     for(i=0; i<CS_RANGES; i++){
  411.         if (i == 0) {e_com = DE_CS;} else {e_com = DE_CS * i;}             // ion energy in the center of mass frame of reference
  412.         e_lab = 2.0 * e_com;                                               // ion energy in the laboratory frame of reference
  413.         qiso  = 7.63 *pow(10,-20) * pow(e_com, -0.5);
  414.         qback = 1.0 * pow(10,-19) * pow( (e_com/1000.0), -0.15 ) * pow( (1.0 + e_com/1000.0), -0.25 ) * pow( (1.0 + 5.0/e_com), -0.15 );
  415.         sigma[I_ISO][i]  = qiso;             // cross section for He+ / He isotropic part of elastic scattering
  416.         sigma[I_BACK][i] = qback;            // cross section for He+ / He backward elastic scattering
  417.     }
  418. }
  419.  
  420. //----------------------------------------------------------------------//
  421. //  calculation of total cross sections for electrons and ions          //
  422. //----------------------------------------------------------------------//
  423.  
  424. void calc_total_cross_sections(void){
  425.     int i;
  426.    
  427.     for(i=0; i<CS_RANGES; i++){
  428.         sigma_tot_e[i] = (sigma[E_ELA][i] + sigma[E_EXC_1][i] + sigma[E_EXC_2][i] + sigma[E_ION][i]) * GAS_DENSITY;   // total macroscopic cross section of electrons
  429.         sigma_tot_i[i] = (sigma[I_ISO][i] + sigma[I_BACK][i]) * GAS_DENSITY;                    // total macroscopic cross section of ions
  430.     }
  431. }
  432.  
  433. //----------------------------------------------------------------------//
  434. //  test of cross sections for electrons and ions                       //
  435. //----------------------------------------------------------------------//
  436.  
  437. void test_cross_sections(void){
  438.     FILE  * f;
  439.     int   i,j;
  440.    
  441.     f = fopen("cross_sections.dat","w");        // cross sections saved in data file: cross_sections.dat
  442.     for(i=0; i<CS_RANGES; i++){
  443.         fprintf(f,"%12.4f ",i*DE_CS);
  444.         for(j=0; j<N_CS; j++) fprintf(f,"%14e ",sigma[j][i]);
  445.         fprintf(f,"\n");
  446.     }
  447.     fclose(f);
  448. }
  449.  
  450. //---------------------------------------------------------------------//
  451. // find upper limit of collision frequencies                           //
  452. //---------------------------------------------------------------------//
  453.  
  454. double max_electron_coll_freq (void){
  455.     int i;
  456.     double e,v,nu,nu_max;
  457.     nu_max = 0;
  458.     for(i=0; i<CS_RANGES; i++){
  459.         e  = i * DE_CS;
  460.         v  = sqrt(2.0 * e * EV_TO_J / E_MASS);
  461.         nu = v * sigma_tot_e[i];
  462.         if (nu > nu_max) {nu_max = nu;}
  463.     }
  464.     return nu_max;
  465. }
  466.  
  467. double max_ion_coll_freq (void){
  468.     int i;
  469.     double e,g,nu,nu_max;
  470.     nu_max = 0;
  471.     for(i=0; i<CS_RANGES; i++){
  472.         e  = i * DE_CS;
  473.         g  = sqrt(2.0 * e * EV_TO_J / MU_HEHE);
  474.         nu = g * sigma_tot_i[i];
  475.         if (nu > nu_max) nu_max = nu;
  476.     }
  477.     return nu_max;
  478. }
  479.  
  480. //----------------------------------------------------------------------//
  481. // initialization of the simulation by placing a given number of        //
  482. // electrons and ions at random positions between the electrodes        //
  483. //----------------------------------------------------------------------//
  484.  
  485.  
  486. void print_excitation_densities(std::vector<level>& levels) {
  487.     double total_e1 = 0.0, total_e2 = 0.0;
  488.     const double cell_volume = ELECTRODE_AREA * DX;
  489.    
  490.     // Sum densities across all grid cells
  491.     for (int p = 0; p < N_G; p++) {
  492.         total_e1 += levels[1].density[p];  // triplet state density
  493.         total_e2 += levels[2].density[p];  // singlet state density
  494.     }
  495.     printf("average Triplet SP = %8.2e | average Singlet SP = %8.2e\n", total_e1/N_G, total_e2/N_G);
  496. }
  497.  
  498. void init(int nseed, std::vector<level>& levels){
  499.     int i;
  500.    
  501.     for (i=0; i<nseed; i++){
  502.         x_e[i]  = L * R01(MTgen);                                                   // initial random position of the electron
  503.         vx_e[i] = RMB_e(MTgen); vy_e[i] = RMB_e(MTgen); vz_e[i] = RMB_e(MTgen);     // initial velocity components of the electron
  504.         x_i[i]  = L * R01(MTgen);                                                   // initial random position of the ion
  505.         vx_i[i] = RMB_n(MTgen); vy_i[i] = RMB_n(MTgen); vz_i[i] = RMB_n(MTgen);     // initial velocity components of the ion
  506.     }
  507.     N_e = nseed;    // initial number of electrons
  508.     N_i = nseed;    // initial number of ions
  509.  
  510.     double n0 = 2.7e+9; double a0 = -1.3e+8; double b0 = 5.0e+15;
  511.     for (int p= 0; p < N_G; p++){
  512.         for (auto& lvl:levels){
  513.             if (lvl.name == "1^1S") lvl.density[p] = GAS_DENSITY;
  514.             else if (lvl.name == "2^3S"){
  515.                 lvl.density[p] = abs(n0 * (a0 *(pow(L,4) - pow((p*DX - 0.5*L),2)) +
  516.                                 b0 * (pow(L,8) -  pow((p*DX - 0.5*L),8)) ));
  517.             }
  518.             else if (lvl.name == "2^1S") {
  519.                 lvl.density[p] = 0.5 * abs(  n0 * (a0 *(pow(L,4) - pow((p*DX - 0.5*L),2)) +
  520.                             b0 * (pow(L,8) -  pow((p*DX - 0.5*L),8)) ));
  521.             }
  522.         }
  523.     }    
  524. }
  525.  
  526. //----------------------------------------------------------------------//
  527. // e / He collision  (cold gas approximation)                           //
  528. //----------------------------------------------------------------------//
  529.  
  530. void collision_electron (double xe, double *vxe, double *vye, double *vze, int eindex,
  531.                             const std::vector<level>& levels, const std::vector<Process>& eProc, const std::vector<std::vector<float>>& sigma_temp){
  532.     const double F1 = E_MASS  / (E_MASS + HE_MASS);
  533.     const double F2 = HE_MASS / (E_MASS + HE_MASS);
  534.     double t0,t1,t2,t3,t4,t5;
  535.     double g,g2,gx,gy,gz,wx,wy,wz,theta,phi;
  536.     double chi,eta,chi2,eta2,sc,cc,se,ce,st,ct,sp,cp,energy,threshold,e_sc,e_ej;
  537.  
  538.     double  probability_total = 0.0;
  539.     double  probability_cumulative = 0.0;
  540.     size_t  sel = 0;
  541.     double  rnd = R01(MTgen);
  542.  
  543.     // - Artem
  544.     // Determine cell p where the electron is
  545.     double c0 = xe * INV_DX;
  546.     int pos = std::max(0, std::min(N_G - 1, static_cast<int>(c0)));
  547.  
  548.     for (int i = 0; i < eProc.size(); i++){
  549.         const Process& ch = eProc[i];
  550.         double dens = (ch.kind == ProcKind::SUPERELASTIC) ? levels[ch.init_Lvl].density[pos] : GAS_DENSITY;
  551.         probability_total += sigma_temp[i][eindex] * dens;
  552.     }
  553.  
  554.     for (int i = 0; i < eProc.size(); i++) {
  555.         const Process& ch = eProc[i];
  556.         double dens = (ch.kind == ProcKind::SUPERELASTIC) ? levels[ch.init_Lvl].density[pos] : GAS_DENSITY;
  557.         probability_cumulative += sigma_temp[i][eindex] * dens;
  558.         if (rnd < probability_cumulative/probability_total) {sel = i; break;} // if cumulative probability/total is larger than a random number, stop and save id of a Process
  559.     }
  560.  
  561.     const Process& ch = eProc[sel];         // now we've chosen the process, performing kineitcs:
  562. //------------------------------------------------------------------------------------------------/////
  563.     // calculate relative velocity before collision & velocity of the centre of mass
  564.     gx = (*vxe);
  565.     gy = (*vye);
  566.     gz = (*vze);
  567.     g  = sqrt(gx * gx + gy * gy + gz * gz);
  568.     wx = F1 * (*vxe);
  569.     wy = F1 * (*vye);
  570.     wz = F1 * (*vze);
  571.     // find Euler angles
  572.     if (gx == 0) {theta = 0.5 * PI;}
  573.     else {theta = atan2(sqrt(gy * gy + gz * gz),gx);}
  574.     if (gy == 0) {
  575.         if (gz > 0){phi = 0.5 * PI;} else {phi = - 0.5 * PI;}
  576.     } else {phi = atan2(gz, gy);}
  577.     st  = sin(theta);
  578.     ct  = cos(theta);
  579.     sp  = sin(phi);
  580.     cp  = cos(phi);
  581. //----------------------------------------------------------------------------------------///
  582.     switch (ch.kind) {
  583.         case ProcKind::ELASTIC:{
  584.             chi = acos(1.0 - 2.0 * R01(MTgen));          // isotropic scattering
  585.             eta = TWO_PI * R01(MTgen);                   // azimuthal angle
  586.             break;
  587.         }
  588.         case ProcKind::EXCITATION:{
  589.             energy = 0.5 * E_MASS * g * g;               // electron energy
  590.             threshold = ch.threshold_eV;                 // threshold energy
  591.             energy = fabs(energy - threshold * EV_TO_J);  // subtract energy loss for excitation
  592.             g   = sqrt(2.0 * energy / E_MASS);           // relative velocity after energy loss
  593.             chi = acos(1.0 - 2.0 * R01(MTgen));          // isotropic scattering
  594.             eta = TWO_PI * R01(MTgen);                   // azimuthal angle        
  595.             break;  
  596.         }
  597.         case ProcKind::IONIZATION:{
  598.             energy = 0.5 * E_MASS * g * g;               // electron energy
  599.             threshold = ch.threshold_eV;                 // threshold energy
  600.             energy = fabs(energy - threshold * EV_TO_J);  // subtract energy loss of ionization
  601.             e_ej = 0.5*energy;                          // energy of the ejected electron
  602.             e_sc = fabs(energy - e_ej);                  // energy of scattered electron after the collision        
  603.             g    = sqrt(2.0 * e_sc / E_MASS);            // relative velocity of scattered electron
  604.             g2   = sqrt(2.0 * e_ej / E_MASS);            // relative velocity of ejected electron
  605.             chi = acos(1.0 - 2.0 * R01(MTgen));          // isotropic scattering for scattered electron (as in Turner's case)
  606.             chi2 = acos(1.0 - 2.0 * R01(MTgen));          // isotropic scattering for ejected electrons (as in Turner's case)
  607.             eta  = TWO_PI * R01(MTgen);                  // azimuthal angle for scattered electron
  608.             eta2 = eta + PI;                             // azimuthal angle for ejected electron
  609.             sc  = sin(chi2);
  610.             cc  = cos(chi2);
  611.             se  = sin(eta2);
  612.             ce  = cos(eta2);
  613.             gx  = g2 * (ct * cc - st * sc * ce);
  614.             gy  = g2 * (st * cp * cc + ct * cp * sc * ce - sp * sc * se);
  615.             gz  = g2 * (st * sp * cc + ct * sp * sc * ce + cp * sc * se);
  616.             x_e[N_e]  = xe;                              // add new electron
  617.             vx_e[N_e] = wx + F2 * gx;
  618.             vy_e[N_e] = wy + F2 * gy;
  619.             vz_e[N_e] = wz + F2 * gz;
  620.             N_e++;
  621.             x_i[N_i]  = xe;                              // add new ion
  622.             vx_i[N_i] = RMB_n(MTgen);                      // velocity is sampled from background thermal distribution
  623.             vy_i[N_i] = RMB_n(MTgen);
  624.             vz_i[N_i] = RMB_n(MTgen);
  625.             N_i++;
  626.             break;
  627.         }        
  628.         case ProcKind::SUPERELASTIC:{
  629.             energy = 0.5 * E_MASS * g * g;               // electron energy
  630.             threshold = ch.threshold_eV;                 // threshold energy
  631.             energy = fabs(energy + threshold * EV_TO_J);  // add energy gain after superelastic collision
  632.             g   = sqrt(2.0 * energy / E_MASS);           // relative velocity after energy loss
  633.             chi = acos(1.0 - 2.0 * R01(MTgen));          // isotropic scattering
  634.             eta = TWO_PI * R01(MTgen);                   // azimuthal angle        
  635.             break;              
  636.         }
  637.  
  638.     }  
  639.     // scatter the primary electron  
  640.     sc = sin(chi);
  641.     cc = cos(chi);
  642.     se = sin(eta);
  643.     ce = cos(eta);
  644.     // compute new relative velocity:  
  645.     gx = g * (ct * cc - st * sc * ce);
  646.     gy = g * (st * cp * cc + ct * cp * sc * ce - sp * sc * se);
  647.     gz = g * (st * sp * cc + ct * sp * sc * ce + cp * sc * se);  
  648.     // post-collision velocity of the colliding electron  
  649.     (*vxe) = wx + F2 * gx;
  650.     (*vye) = wy + F2 * gy;
  651.     (*vze) = wz + F2 * gz;
  652. }
  653.  
  654. //----------------------------------------------------------------------//
  655. // He+ / He collision                                                   //
  656. //----------------------------------------------------------------------//
  657.  
  658. void collision_ion (double *vx_1, double *vy_1, double *vz_1,
  659.                     double *vx_2, double *vy_2, double *vz_2, int e_index){
  660.     double   g,gx,gy,gz,wx,wy,wz,rnd;
  661.     double   theta,phi,chi,eta,st,ct,sp,cp,sc,cc,se,ce,t1,t2;
  662.    
  663.     // calculate relative velocity before collision
  664.     // random Maxwellian target atom already selected (vx_2,vy_2,vz_2 velocity components of target atom come with the call)
  665.    
  666.     gx = (*vx_1)-(*vx_2);
  667.     gy = (*vy_1)-(*vy_2);
  668.     gz = (*vz_1)-(*vz_2);
  669.     g  = sqrt(gx * gx + gy * gy + gz * gz);
  670.     wx = 0.5 * ((*vx_1) + (*vx_2));
  671.     wy = 0.5 * ((*vy_1) + (*vy_2));
  672.     wz = 0.5 * ((*vz_1) + (*vz_2));
  673.    
  674.     // find Euler angles
  675.    
  676.     if (gx == 0) {theta = 0.5 * PI;} else {theta = atan2(sqrt(gy * gy + gz * gz),gx);}
  677.     if (gy == 0) {
  678.         if (gz > 0){phi = 0.5 * PI;} else {phi = - 0.5 * PI;}
  679.     } else {phi = atan2(gz, gy);}
  680.    
  681.     // determine the type of collision based on cross sections and generate scattering angle
  682.    
  683.     t1  =      sigma[I_ISO][e_index];
  684.     t2  = t1 + sigma[I_BACK][e_index];
  685.     rnd = R01(MTgen);
  686.     if  (rnd < (t1 /t2)){                        // isotropic scattering
  687.         chi = acos(1.0 - 2.0 * R01(MTgen));      // scattering angle
  688.     } else {                                     // backward scattering
  689.         chi = PI;                                // scattering angle
  690.     }
  691.     eta = TWO_PI * R01(MTgen);                   // azimuthal angle
  692.     sc  = sin(chi);
  693.     cc  = cos(chi);
  694.     se  = sin(eta);
  695.     ce  = cos(eta);
  696.     st  = sin(theta);
  697.     ct  = cos(theta);
  698.     sp  = sin(phi);
  699.     cp  = cos(phi);
  700.    
  701.     // compute new relative velocity
  702.    
  703.     gx = g * (ct * cc - st * sc * ce);
  704.     gy = g * (st * cp * cc + ct * cp * sc * ce - sp * sc * se);
  705.     gz = g * (st * sp * cc + ct * sp * sc * ce + cp * sc * se);
  706.    
  707.     // post-collision velocity of the ion
  708.    
  709.     (*vx_1) = wx + 0.5 * gx;
  710.     (*vy_1) = wy + 0.5 * gy;
  711.     (*vz_1) = wz + 0.5 * gz;
  712. }
  713.  
  714. //-----------------------------------------------------------------//
  715. // solve Poisson equation (Thomas algorithm)                       //
  716. //-----------------------------------------------------------------//
  717.  
  718. void solve_Poisson (xvector rho1, double tt){
  719.     const double A =  1.0;
  720.     const double B = -2.0;
  721.     const double C =  1.0;
  722.     const double S = 1.0 / (2.0 * DX);
  723.     const double ALPHA = -DX * DX / EPSILON0;
  724.     xvector      g, w, f;
  725.     int          i;
  726.    
  727.     // apply potential to the electrodes - boundary conditions
  728.    
  729.     pot[0]     = VOLTAGE * cos(OMEGA * tt);         // potential at the powered electrode
  730.     pot[N_G-1] = 0.0;                               // potential at the grounded electrode
  731.    
  732.     // solve Poisson equation
  733.    
  734.     for(i=1; i<=N_G-2; i++) f[i] = ALPHA * rho1[i];
  735.     f[1] -= pot[0];
  736.     f[N_G-2] -= pot[N_G-1];
  737.     w[1] = C/B;
  738.     g[1] = f[1]/B;
  739.     for(i=2; i<=N_G-2; i++){
  740.         w[i] = C / (B - A * w[i-1]);
  741.         g[i] = (f[i] - A * g[i-1]) / (B - A * w[i-1]);
  742.     }
  743.     pot[N_G-2] = g[N_G-2];
  744.     for (i=N_G-3; i>0; i--) pot[i] = g[i] - w[i] * pot[i+1];            // potential at the grid points between the electrodes
  745.    
  746.     // compute electric field
  747.    
  748.     for(i=1; i<=N_G-2; i++) efield[i] = (pot[i-1] - pot[i+1]) * S;      // electric field at the grid points between the electrodes
  749.     efield[0]     = (pot[0]     - pot[1])     * INV_DX - rho1[0]     * DX / (2.0 * EPSILON0);   // powered electrode
  750.     efield[N_G-1] = (pot[N_G-2] - pot[N_G-1]) * INV_DX + rho1[N_G-1] * DX / (2.0 * EPSILON0);   // grounded electrode
  751. }
  752.  
  753. //---------------------------------------------------------------------//
  754. // simulation of one radiofrequency cycle                              //
  755. //---------------------------------------------------------------------//
  756.  
  757. void accumulate_rates(std::vector<Process>& eProc, const std::vector<std::vector<float>>& sigma_temp) {
  758.     double v_sqr, velocity, energy, c0_temp;
  759.     int energy_index, p_temp;
  760.     const double Volume = (ELECTRODE_AREA * DX);
  761.  
  762.     for (int k=0; k<N_e; k++){                              
  763.         v_sqr = vx_e[k] * vx_e[k] + vy_e[k] * vy_e[k] + vz_e[k] * vz_e[k];
  764.         velocity = sqrt(v_sqr);
  765.         energy   = 0.5 * E_MASS * v_sqr / EV_TO_J;
  766.         energy_index = min( int(energy / DE_CS + 0.5), CS_RANGES-1);
  767.  
  768.         c0_temp = x_e[k] * INV_DX;
  769.         p_temp = std::max(0, std::min(N_G-1, static_cast<int>(c0_temp)));
  770.  
  771.         for (int i = 0; i < eProc.size(); i++){
  772.             Process& ch = eProc[i];
  773.             if (ch.kind == ProcKind::ELASTIC || ch.kind == ProcKind::IONIZATION) continue;            
  774.             ch.cum_rate[p_temp] += sigma_temp[ch.csSlot][energy_index] * velocity * WEIGHT;
  775.         }        
  776.     }    
  777. }
  778.  
  779. // averaging the rates each RF cycle
  780. void average_rates(std::vector<Process>& eProc) {
  781.     const double inv_NT = 1.0 / (N_avg * N_T); // averaging through N_avg RF periods each contains N_T cycles
  782.     const double inv_Volume = 1.0/(ELECTRODE_AREA * DX);
  783.     for (int p = 0; p < N_G; p++) {    
  784.         for (int i = 0; i < eProc.size(); i++){
  785.             Process& ch = eProc[i];
  786.             if (ch.kind == ProcKind::ELASTIC || ch.kind == ProcKind::IONIZATION) continue;
  787.             ch.avg_rate[p] = ch.cum_rate[p] * (inv_NT * inv_Volume);
  788.         }  
  789.     }      
  790. }
  791.  
  792.  
  793. void update_excited_dens(const std::vector<Process>& eProc, std::vector<level>& levels) {
  794.     using SpMat = Eigen::SparseMatrix<double>;
  795.  
  796.     SpMat A(3*N_G,3*N_G); A.reserve(Eigen::VectorXi::Constant(3*N_G, 9));
  797.     SpMat B(3*N_G,3*N_G); B.reserve(Eigen::VectorXi::Constant(3*N_G, 9));
  798.  
  799.     Eigen::VectorXd sol_old(3*N_G), sol_new(3*N_G);                      // solution for 1^1S, 2^3S, 2^1S
  800.     Eigen::VectorXd RHS(3*N_G);                                          // RHS for our new equation
  801.  
  802.  
  803.     double v_mean = sqrt(8.0*K_BOLTZMANN*T_neutral/(M_PI*HE_MASS));
  804.     double alpha = 1.0;
  805.     double DT = alpha*2.0*DX*DX/Diff_coeff;
  806.     double Rk = (DX*gamma_k*v_mean)/((2.0-gamma_k)*Diff_coeff);
  807.  
  808.     // temporal assignment of rates:
  809.  
  810.     for (int i = 0; i < N_G; i++){
  811.         avg_rate1f[i] = eProc[2].avg_rate[i];
  812.         avg_rate2f[i] = eProc[3].avg_rate[i];
  813.         avg_rate1b[i] = eProc[4].avg_rate[i];
  814.         avg_rate2b[i] = eProc[5].avg_rate[i];
  815.     }    
  816.  
  817.     for (int i = 0; i < N_G; i++){
  818.         double Ai = -alpha;
  819.         double Ci = -alpha;
  820.         double B0i = 1.0 + 2.0*alpha + 0.5*DT*(avg_rate1f[i] + avg_rate2f[i]);    //1^1S
  821.         double B1i = 1.0 + 2.0*alpha + 0.5*DT*(avg_rate1b[i]);                    //2^3S
  822.         double B2i = 1.0 + 2.0*alpha + 0.5*DT*(avg_rate2b[i]);                    //2^1S
  823.         double D00i = -0.5*DT*(avg_rate1b[i]);                      //1^1S <- 2^3S
  824.         double D01i = -0.5*DT*(avg_rate2b[i]);                      //1^1S <- 2^1S  
  825.         double D1i = -0.5*DT*(avg_rate1f[i]);                                      //2^3S
  826.         double D2i = -0.5*DT*(avg_rate2f[i]);                                      //2^1S
  827.         if (i==0){
  828.             // A.insert(i,i)                       = B0i - Rk*Ci;  A.insert(i, i+1)                    = Ai + Ci;
  829.             A.insert(i,i)                       = 1.0;  A.insert(i, i+1)                    = 0.0;
  830.             A.insert(i+N_G,i+N_G)               = B1i - Rk*Ci;  A.insert(i+N_G, i+N_G+1)            = Ai + Ci;
  831.             A.insert(i+2*N_G,i+2*N_G)           = B2i - Rk*Ci;  A.insert(i+2*N_G, i+2*N_G+1)        = Ai + Ci;
  832.            
  833.             // A.insert(i,         i+N_G)  =   D00i;   A.insert(i,         i+2*N_G)  =   D01i;
  834.             A.insert(i,         i+N_G)  =   0.0;   A.insert(i,         i+2*N_G)  =   0.0;            
  835.             A.insert(i+N_G,     i)      =   D1i;    A.insert(i+N_G,     i+2*N_G)  =   0.0;
  836.             A.insert(i+2*N_G,   i)      =   D2i;    A.insert(i+2*N_G,   i + N_G)  =   0.0;
  837.         }
  838.         else if (i == N_G-1){
  839.             // A.insert(i,i-1)                 = Ai + Ci;      A.insert(i,i)                   = B0i - Rk*Ai;
  840.             A.insert(i,i-1)                 = 0.0;          A.insert(i,i)                   = 1.0;            
  841.             A.insert(i+N_G,i+N_G-1)         = Ai + Ci;      A.insert(i+N_G,i+N_G)           = B1i - Rk*Ai;
  842.             A.insert(i+2*N_G,i+2*N_G-1)     = Ai + Ci;      A.insert(i+2*N_G,i+2*N_G)       = B2i - Rk*Ai;
  843.        
  844.             A.insert(i,         i+N_G)  =   D00i;   A.insert(i,         i+2*N_G)  =   D01i;
  845.             A.insert(i+N_G,     i)      =   D1i;    A.insert(i+N_G,     i+2*N_G)  =   0.0;
  846.             A.insert(i+2*N_G,   i)      =   D2i;    A.insert(i+2*N_G,   i + N_G)  =   0.0;        
  847.         }
  848.         else {
  849.             // A.insert(i,         i-1)        = Ci;   A.insert(i,         i)          = B0i;  A.insert(i,         i+1)       = Ai;
  850.             A.insert(i,         i-1)        = 0.0;   A.insert(i,         i)          = 1.0;  A.insert(i,         i+1)       = 0.0;            
  851.             A.insert(i+N_G,     i+N_G-1)    = Ci;   A.insert(i+N_G,     i+N_G)      = B1i;  A.insert(i+N_G,     i+N_G+1)   = Ai;
  852.             A.insert(i+2*N_G,   i+2*N_G-1)  = Ci;   A.insert(i+2*N_G,   i+2*N_G)    = B2i;  A.insert(i+2*N_G,   i+2*N_G+1) = Ai;
  853.  
  854.             A.insert(i, i+N_G)              = D00i; A.insert(i, i+2*N_G)            = D01i;
  855.             A.insert(i+N_G, i)              = D1i;  A.insert(i+N_G, i+2*N_G)        = 0.0;
  856.             A.insert(i+2*N_G, i)            = D2i;  A.insert(i+2*N_G, i+N_G)        = 0.0;
  857.         }
  858.     }
  859.     for (int i = 0; i < N_G; i++){
  860.         double Ai = alpha;
  861.         double Ci = alpha;
  862.         double B0i = 1.0 - 2.0*alpha - 0.5*(avg_rate1f[i] + avg_rate2f[i]);    //1^1S
  863.         double B1i = 1.0 - 2.0*alpha - 0.5*(avg_rate1b[i]);                    //2^3S
  864.         double B2i = 1.0 - 2.0*alpha - 0.5*(avg_rate2b[i]);                    //2^1S
  865.         double D00i = 0.5*DT*(avg_rate1b[i]);                      //1^1S <- 2^3S
  866.         double D01i = 0.5*DT*(avg_rate2b[i]);                      //1^1S <- 2^1S  
  867.         double D1i = 0.5*DT*(avg_rate1f[i]);                                      //2^3S
  868.         double D2i = 0.5*DT*(avg_rate2f[i]);                                      //2^1S
  869.         if (i==0){
  870.             B.insert(i,i)                       = B0i - Rk*Ci;  B.insert(i, i+1)                    = Ai + Ci;
  871.             B.insert(i+N_G,i+N_G)               = B1i - Rk*Ci;  B.insert(i+N_G, i+N_G+1)            = Ai + Ci;
  872.             B.insert(i+2*N_G,i+2*N_G)           = B2i - Rk*Ci;  B.insert(i+2*N_G, i+2*N_G+1)        = Ai + Ci;
  873.            
  874.             B.insert(i,         i+N_G)  =   D00i;   B.insert(i,         i+2*N_G)  =   D01i;
  875.             B.insert(i+N_G,     i)      =   D1i;    B.insert(i+N_G,     i+2*N_G)  =   0.0;
  876.             B.insert(i+2*N_G,   i)      =   D2i;    B.insert(i+2*N_G,   i + N_G)  =   0.0;
  877.         }
  878.         else if (i == N_G-1){
  879.             B.insert(i,i-1)                 = Ai + Ci;      B.insert(i,i)                   = B0i - Rk*Ai;
  880.             B.insert(i+N_G,i+N_G-1)         = Ai + Ci;      B.insert(i+N_G,i+N_G)           = B1i - Rk*Ai;
  881.             B.insert(i+2*N_G,i+2*N_G-1)     = Ai + Ci;      B.insert(i+2*N_G,i+2*N_G)       = B2i - Rk*Ai;
  882.        
  883.             B.insert(i,         i+N_G)  =   D00i;   B.insert(i,         i+2*N_G)  =   D01i;
  884.             B.insert(i+N_G,     i)      =   D1i;    B.insert(i+N_G,     i+2*N_G)  =   0.0;
  885.             B.insert(i+2*N_G,   i)      =   D2i;    B.insert(i+2*N_G,   i + N_G)  =   0.0;        
  886.         }
  887.         else {
  888.             B.insert(i,         i-1)        = Ci;   B.insert(i,         i)          = B0i;  B.insert(i,         i+1)       = Ai;
  889.             B.insert(i+N_G,     i+N_G-1)    = Ci;   B.insert(i+N_G,     i+N_G)      = B1i;  B.insert(i+N_G,     i+N_G+1)   = Ai;
  890.             B.insert(i+2*N_G,   i+2*N_G-1)  = Ci;   B.insert(i+2*N_G,   i+2*N_G)    = B2i;  B.insert(i+2*N_G,   i+2*N_G+1) = Ai;
  891.  
  892.             B.insert(i, i+N_G)              = D00i; B.insert(i, i+2*N_G)            = D01i;
  893.             B.insert(i+N_G, i)              = D1i;  B.insert(i+N_G, i+2*N_G)        = 0.0;
  894.             B.insert(i+2*N_G, i)            = D2i;  B.insert(i+2*N_G, i+N_G)        = 0.0;
  895.         }
  896.     }    
  897.    
  898.     A.makeCompressed(); B.makeCompressed();
  899.  
  900.     Eigen::SparseLU<SpMat> solver;
  901.     solver.analyzePattern(A);
  902.     solver.factorize(A);
  903.     if(solver.info() != Eigen::Success)
  904.         throw std::runtime_error("LU factorization failed");    
  905.  
  906.     //initial conditions:
  907.     for (int i = 0; i < N_G; i++){
  908.         sol_old(i) = GAS_DENSITY;
  909.         sol_old(i+N_G) = levels[1].density[i];
  910.         sol_old(i+2*N_G) = levels[2].density[i];
  911.     }
  912.  
  913.     bool        converged       = false;
  914.     double      accuracy        = 1.0E-10;
  915.    
  916.  
  917.     std::cout << "Timestep chosen: " << DT_E << "\n";
  918.  
  919.     //main loop:
  920.     for (int n = 0; n < 1000000; n++){
  921.  
  922.         RHS = B * sol_old;
  923.  
  924.         for (int p = 0; p < N_G; ++p) {
  925.             RHS[p]          = GAS_DENSITY;
  926.         }
  927.  
  928.         sol_new = solver.solve(RHS);
  929.         if(solver.info() != Eigen::Success)
  930.             throw std::runtime_error("Solve failed at step " + std::to_string(n));
  931.  
  932.         //checking convergence        
  933.         double rel = 0.0;
  934.         for (int i = 0; i < 3*N_G; ++i) {
  935.             double abs = fabs(sol_new(i) - sol_old(i));
  936.             rel = std::max(rel, abs/sol_old(i));        
  937.         }
  938.         if ((rel < accuracy)) {
  939.             converged = true;
  940.             std::cout << "Steady-state reached after " << n << " iterations.\n";
  941.             std::cout << "Relative changes are: " << rel <<  "\n";
  942.             break;
  943.         }            
  944.  
  945.         sol_old.swap(sol_new);        
  946.     }
  947.  
  948.     for (int i = 0; i < N_G; i++){
  949.         levels[1].density[i] = sol_new(i+N_G); levels[2].density[i] = sol_new(i+2*N_G);
  950.     }
  951. }
  952.  
  953. void do_one_cycle (std::vector<level>& levels, std::vector<Process>& eProc, const std::vector<std::vector<float>>& sigma_temp){
  954.     const double DV       = ELECTRODE_AREA * DX;
  955.     const double FACTOR_W = WEIGHT / DV;
  956.     const double FACTOR_E = DT_E / E_MASS * E_CHARGE;
  957.     const double FACTOR_I = DT_I / HE_MASS * E_CHARGE;
  958.     const double MIN_X_EEPF    = 0.45 * L;                       // min. position for EEPF collection
  959.     const double MAX_X_EEPF    = 0.55 * L;                       // max. position for EEPF collection
  960.  
  961.     int      k, t, p, energy_index, velocity_index;
  962.     double   g, g_sqr, gx, gy, gz, vx_a, vy_a, vz_a, e_x, energy, nu, p_coll, v_sqr, velocity;
  963.     double   mean_v, c0, c1, c2, rate;
  964.     bool     out;
  965.     xvector  rho;
  966.     int      t_index;
  967.  
  968.     nu_avg = 0.0;
  969.    
  970.     for (t=0; t<N_T; t++){          // the RF period is divided into N_T equal time intervals (time step DT_E)
  971.         Time += DT_E;               // update of the total simulated time
  972.         t_index = t / N_BIN;        // index for XT distributions
  973.        
  974.         // step 1: compute densities at grid points
  975.        
  976.         for(p=0; p<N_G; p++) e_density[p] = 0;                             // electron density - computed in every time step
  977.         for(k=0; k<N_e; k++){
  978.  
  979.             if      (p < 0)        p = 0;
  980.             else if (p > N_G - 2)  p = N_G - 2;
  981.             c0 = x_e[k] * INV_DX;
  982.             p  = int(c0);
  983.             e_density[p]   += (p + 1 - c0) * FACTOR_W;
  984.             e_density[p+1] += (c0 - p) * FACTOR_W;
  985.         }
  986.         e_density[0]     *= 2.0; // double at the edge bcs working with half-domain (no left/right neighbour)
  987.         e_density[N_G-1] *= 2.0;
  988.         for(p=0; p<N_G; p++) cumul_e_density[p] += e_density[p];
  989.        
  990.         if ((t % N_SUB) == 0) {                                            // ion density - computed in every N_SUB-th time steps (subcycling)
  991.             for(p=0; p<N_G; p++) i_density[p] = 0;
  992.             for(k=0; k<N_i; k++){
  993.                 c0 = x_i[k] * INV_DX;
  994.                 p  = int(c0);
  995.                 i_density[p]   += (p + 1 - c0) * FACTOR_W;  
  996.                 i_density[p+1] += (c0 - p) * FACTOR_W;
  997.             }
  998.             i_density[0]     *= 2.0; // double at the edge bcs working with half-domain (no left/right neighbour)
  999.             i_density[N_G-1] *= 2.0;
  1000.         }
  1001.         for(p=0; p<N_G; p++) cumul_i_density[p] += i_density[p];
  1002.        
  1003.         // step 2: solve Poisson equation
  1004.        
  1005.         for(p=0; p<N_G; p++) rho[p] = E_CHARGE * (i_density[p] - e_density[p]);  // get charge density
  1006.         solve_Poisson(rho,Time);                                                 // compute potential and electric field
  1007.        
  1008.         // steps 3 & 4: move particles according to electric field interpolated to particle positions
  1009.        
  1010.         for(k=0; k<N_e; k++){                       // move all electrons in every time step
  1011.             c0  = x_e[k] * INV_DX;
  1012.             p   = int(c0);
  1013.             c1  = p + 1.0 - c0;
  1014.             c2  = c0 - p;
  1015.             e_x = c1 * efield[p] + c2 * efield[p+1];
  1016.            
  1017.             if (measurement_mode) {
  1018.                
  1019.                 // measurements: 'x' and 'v' are needed at the same time, i.e. old 'x' and mean 'v'
  1020.                
  1021.                 mean_v = vx_e[k] - 0.5 * e_x * FACTOR_E;
  1022.                 counter_e_xt[p][t_index]   += c1;
  1023.                 counter_e_xt[p+1][t_index] += c2;
  1024.                 ue_xt[p][t_index]   += c1 * mean_v;
  1025.                 ue_xt[p+1][t_index] += c2 * mean_v;
  1026.                 v_sqr  = mean_v * mean_v + vy_e[k] * vy_e[k] + vz_e[k] * vz_e[k];
  1027.                 energy = 0.5 * E_MASS * v_sqr / EV_TO_J;
  1028.                 meanee_xt[p][t_index]   += c1 * energy;
  1029.                 meanee_xt[p+1][t_index] += c2 * energy;
  1030.                 energy_index = min( int(energy / DE_CS + 0.5), CS_RANGES-1);
  1031.                 velocity = sqrt(v_sqr);
  1032.                 rate = sigma[E_ION][energy_index] * velocity * DT_E * GAS_DENSITY;
  1033.                 ioniz_rate_xt[p][t_index]   += c1 * rate;
  1034.                 ioniz_rate_xt[p+1][t_index] += c2 * rate;
  1035.  
  1036.                 // measure EEPF in the center
  1037.                
  1038.                 if ((MIN_X_EEPF < x_e[k]) && (x_e[k] < MAX_X_EEPF)){
  1039.                     energy_index = (int)(energy / DE_EEPF);
  1040.                     if (energy_index < N_EEPF) {eepf[energy_index] += 1.0;}
  1041.                     mean_energy_accu_center += energy;
  1042.                     mean_energy_counter_center++;
  1043.                 }
  1044.             }
  1045.            
  1046.             // update velocity and position
  1047.  
  1048.             vx_e[k] -= e_x * FACTOR_E;
  1049.             x_e[k]  += vx_e[k] * DT_E;
  1050.         }
  1051.        
  1052.         if ((t % N_SUB) == 0) {                       // move all ions in every N_SUB-th time steps (subcycling)
  1053.             for(k=0; k<N_i; k++){
  1054.                 c0  = x_i[k] * INV_DX;
  1055.                 p   = int(c0);
  1056.                 c1  = p + 1 - c0;
  1057.                 c2  = c0 - p;
  1058.                 e_x = c1 * efield[p] + c2 * efield[p+1];
  1059.                
  1060.                 if (measurement_mode) {
  1061.                    
  1062.                     // measurements: 'x' and 'v' are needed at the same time, i.e. old 'x' and mean 'v'
  1063.  
  1064.                     mean_v = vx_i[k] + 0.5 * e_x * FACTOR_I;
  1065.                     counter_i_xt[p][t_index]   += c1;
  1066.                     counter_i_xt[p+1][t_index] += c2;
  1067.                     ui_xt[p][t_index]   += c1 * mean_v;
  1068.                     ui_xt[p+1][t_index] += c2 * mean_v;
  1069.                     v_sqr  = mean_v * mean_v + vy_i[k] * vy_i[k] + vz_i[k] * vz_i[k];
  1070.                     energy = 0.5 * HE_MASS * v_sqr / EV_TO_J;
  1071.                     meanei_xt[p][t_index]   += c1 * energy;
  1072.                     meanei_xt[p+1][t_index] += c2 * energy;
  1073.                     velocity = sqrt(v_sqr);                             // actually, it's a speed: a velocity includes direction. A
  1074.                    
  1075.                     int b = int(x_i[k]/DX_PF);
  1076.                     int v_idx = int(vx_i[k] / DV_IVDF);
  1077.                     int e_idx = int(energy / DE_IEPF);
  1078.  
  1079.                     if (b < 0 || b >= N_sp) continue;
  1080.                     if (abs(v_idx) > N_IVDF) std::cerr << "Make IVDF larger!" << "\n";
  1081.                     else if (v_idx >= 0 && v_idx < N_IVDF) ivdf_pos[b][v_idx] += 1.0;
  1082.                     else if (v_idx < 0 && -v_idx < N_IVDF)  ivdf_neg[b][-v_idx] += 1.0;
  1083.  
  1084.                     if (e_idx < N_IEPF) iepf[b][e_idx] += 1.0;
  1085.                     else std::cerr << "Make IEDF larger!" << "\n";
  1086.                 }
  1087.                
  1088.                 // update velocity and position and accumulate absorbed energy
  1089.                
  1090.                 vx_i[k] += e_x * FACTOR_I;
  1091.                 x_i[k]  += vx_i[k] * DT_I;
  1092.             }
  1093.         }
  1094.        
  1095.             // step 5: check boundaries -- changed by Artem
  1096.            
  1097.             k = 0;
  1098.             while(k < N_e) {    // check boundaries for all electrons in every time step
  1099.                 out = false;
  1100.                 if ((x_e[k] < 0) || (x_e[k] > L)) {out = true;}                   // the electron is out at the electrodes
  1101.                 if (out) {
  1102.                     if (R01(MTgen)< r) {                                          // reflecting elastically with r probability
  1103.                         x_e[k] < 0 ? x_e[k] *= -1.0 : x_e[k] = 2.0*L - x_e[k];    // if left/right border -> put on a left/right border
  1104.                         vx_e[k] *= -1.0;                                          // elastic reflection                          
  1105.                         }
  1106.                     else {                                                        // remove the electron
  1107.                         x_e[k] < 0 ? N_e_abs_pow++ : N_e_abs_gnd++;               // absorption calculation at the powered/grounded (left/right) electrode
  1108.                         x_e [k] = x_e [N_e-1];                                    // pushing last element on a vacant place
  1109.                         vx_e[k] = vx_e[N_e-1];
  1110.                         vy_e[k] = vy_e[N_e-1];
  1111.                         vz_e[k] = vz_e[N_e-1];
  1112.                         N_e--;
  1113.                     }
  1114.                 } else k++;
  1115.             }
  1116.            
  1117.             if ((t % N_SUB) == 0) {   // check boundaries for all ions in every N_SUB-th time steps (subcycling)
  1118.                 k = 0;
  1119.                 while(k < N_i) {
  1120.                     out = false;
  1121.                     if (x_i[k] < 0) {       // the ion is out at the powered electrode
  1122.                         N_i_abs_pow++;
  1123.                         out    = true;
  1124.                         v_sqr  = vx_i[k] * vx_i[k] + vy_i[k] * vy_i[k] + vz_i[k] * vz_i[k];
  1125.                         energy = 0.5 * HE_MASS *  v_sqr/ EV_TO_J;
  1126.                         energy_index = (int)(energy / DE_IFED);
  1127.                         if (energy_index < N_IFED) {ifed_pow[energy_index]++;}       // save IFED at the powered electrode
  1128.  
  1129.                         if (R01(MTgen)< gamma_i) {
  1130.                             if (N_e < MAX_N_P){                                                     // adding a new electron on the left electrode
  1131.                                 N_e++;
  1132.                                 double u = R01(MTgen);                                              // with energy sampled from Maxwellian distribution
  1133.                                 vx_e[N_e-1] = sqrt(-2.0*kT_over_m*log(u));                          // half-maxwellian in x direction
  1134.                                 vy_e[N_e-1] = RMB_e(MTgen); vz_e[N_e-1] = RMB_e(MTgen);
  1135.                                 x_e[N_e-1] = 0.0;
  1136.                                 if (std::isnan(vx_e[N_e-1])) {
  1137.                                     std::cerr << "Error: SEE electron has Vx NAN!";
  1138.                                     vx_e[N_e-1] = RMB_e(MTgen);
  1139.                                 }
  1140.                             }
  1141.                             else{
  1142.                                 std::cerr << "Impossible to emit a new SE at the powered electrode" <<"\n";
  1143.                             }
  1144.                         }
  1145.                     }
  1146.                     if (x_i[k] > L) {       // the ion is out at the grounded electrode
  1147.                         N_i_abs_gnd++;
  1148.                         out    = true;
  1149.                         v_sqr  = vx_i[k] * vx_i[k] + vy_i[k] * vy_i[k] + vz_i[k] * vz_i[k];
  1150.                         energy = 0.5 * HE_MASS * v_sqr / EV_TO_J;
  1151.                         energy_index = (int)(energy / DE_IFED);
  1152.                         if (energy_index < N_IFED) {ifed_gnd[energy_index]++;}        // save IFED at the grounded electrode
  1153.  
  1154.                         if (R01(MTgen)< gamma_i) {
  1155.                             if (N_e < MAX_N_P){                                                     // adding a new electron on the left electrode
  1156.                                 N_e++;                                                              // with energy sampled from Maxwellian distribution
  1157.                                 double u = R01(MTgen);                                              
  1158.                                 vx_e[N_e-1] = sqrt(-2.0*kT_over_m*log(u));                          // half-maxwellian in x direction
  1159.                                 vy_e[N_e-1] = RMB_e(MTgen); vz_e[N_e-1] = RMB_e(MTgen);
  1160.                                 x_e[N_e-1] = L;
  1161.                             }
  1162.                             else{
  1163.                                 std::cerr << "Impossible to emit a new SE at the grounded electrode" <<"\n";
  1164.                             }
  1165.                         }                  
  1166.                     }
  1167.  
  1168.                     if (out) {  // delete the ion, if out
  1169.                         x_i [k] = x_i [N_i-1];
  1170.                         vx_i[k] = vx_i[N_i-1];
  1171.                         vy_i[k] = vy_i[N_i-1];
  1172.                         vz_i[k] = vz_i[N_i-1];
  1173.                         N_i--;
  1174.                     } else k++;
  1175.                 }
  1176.             }
  1177.        
  1178.         // step 6: collisions
  1179.  
  1180.        
  1181.        
  1182.         for (k=0; k<N_e; k++){                              // checking for occurrence of a collision for all electrons in every time step
  1183.             v_sqr = vx_e[k] * vx_e[k] + vy_e[k] * vy_e[k] + vz_e[k] * vz_e[k];
  1184.             velocity = sqrt(v_sqr);
  1185.             energy   = 0.5 * E_MASS * v_sqr / EV_TO_J;
  1186.             energy_index = min( int(energy / DE_CS + 0.5), CS_RANGES-1);
  1187.             int e_crdnt = std::max(0, std::min(N_G-1, static_cast<int>(x_e[k] * INV_DX)));
  1188.             double frequency_total = 0.0;                                                               //collision frequency sigma*density*velocity for this particular electron
  1189.             double probability_total = 0.0;                                                             //collision probability sigma*density for this particular electron
  1190.  
  1191.             // Artem  - adding superelastic impact on total collisoinal probability//
  1192.  
  1193.             for (int i = 0; i < eProc.size(); i++){
  1194.                 const Process& ch = eProc[i];
  1195.                 double dens = (ch.kind == ProcKind::SUPERELASTIC) ? levels[ch.init_Lvl].density[e_crdnt] : GAS_DENSITY;
  1196.                 probability_total += sigma_temp[i][energy_index] * dens;
  1197.             }
  1198.             frequency_total = probability_total * velocity;
  1199.            
  1200.             p_coll = 1.0 - exp(- frequency_total * DT_E);                  // collision probability for electrons
  1201.             if (R01(MTgen) < p_coll) {                      // electron collision takes place
  1202.                 collision_electron(x_e[k], &vx_e[k], &vy_e[k], &vz_e[k], energy_index, levels, eProc, sigma_temp);
  1203.                 N_e_coll++;
  1204.             }
  1205.         }
  1206.        
  1207.         if ((t % N_SUB) == 0) {                             // checking for occurrence of a collision for all ions in every N_SUB-th time steps (subcycling)
  1208.             for (k=0; k<N_i; k++){
  1209.                 vx_a = RMB_n(MTgen);                          // pick velocity components of a random target gas atom
  1210.                 vy_a = RMB_n(MTgen);
  1211.                 vz_a = RMB_n(MTgen);
  1212.                 gx   = vx_i[k] - vx_a;                       // compute the relative velocity of the collision partners
  1213.                 gy   = vy_i[k] - vy_a;
  1214.                 gz   = vz_i[k] - vz_a;
  1215.                 g_sqr = gx * gx + gy * gy + gz * gz;
  1216.                 g = sqrt(g_sqr);
  1217.                 energy = 0.5 * MU_HEHE * g_sqr / EV_TO_J;
  1218.                 energy_index = min( int(energy / DE_CS + 0.5), CS_RANGES-1);
  1219.                 nu = sigma_tot_i[energy_index] * g;
  1220.                 p_coll = 1 - exp(- nu * DT_I);              // collision probability for ions
  1221.                 if (R01(MTgen)< p_coll) {                   // ion collision takes place
  1222.                     collision_ion (&vx_i[k], &vy_i[k], &vz_i[k], &vx_a, &vy_a, &vz_a, energy_index);
  1223.                     N_i_coll++;
  1224.                 }
  1225.             }
  1226.         }
  1227.  
  1228.         //step 7: accumulate rates
  1229.         accumulate_rates(eProc, sigma_temp);
  1230.  
  1231.         if (measurement_mode) {
  1232.            
  1233.             // collect 'xt' data from the grid
  1234.            
  1235.             for (p=0; p<N_G; p++) {
  1236.                 pot_xt   [p][t_index] += pot[p];
  1237.                 efield_xt[p][t_index] += efield[p];
  1238.                 ne_xt    [p][t_index] += e_density[p];
  1239.                 ni_xt    [p][t_index] += i_density[p];
  1240.                 e1_xt    [p][t_index] += levels[1].density[p];  // Artem
  1241.                 e2_xt    [p][t_index] += levels[2].density[p];  // Artem
  1242.             }
  1243.         }
  1244.        
  1245.         if ((t % 1000) == 0) printf(" c = %8d  t = %8d  #e = %8d  #i = %8d\n", cycle,t,N_e,N_i);
  1246.     }
  1247.  
  1248.     counter++;
  1249.  
  1250.     // updating denisites each N_avg cycles: --- Artem
  1251.     if (counter%N_avg == 0) {
  1252.         // compute average rates over a cycle
  1253.         average_rates(eProc);
  1254.         // updating densities
  1255.         update_excited_dens(eProc, levels);
  1256.         // reset accumulators
  1257.         for (int i = 0; i < eProc.size(); i++){
  1258.             Process& ch = eProc[i];
  1259.             if (ch.kind == ProcKind::ELASTIC || ch.kind == ProcKind::IONIZATION) continue;
  1260.             std::fill(ch.cum_rate.begin(), ch.cum_rate.end(), 0.0);
  1261.             std::fill(ch.avg_rate.begin(), ch.avg_rate.end(), 0.0);
  1262.         }        
  1263.     }    
  1264.  
  1265.     fprintf(datafile,"%8d  %8d  %8d\n",cycle,N_e,N_i);
  1266.     print_excitation_densities(levels);
  1267. }
  1268.  
  1269. //---------------------------------------------------------------------//
  1270. // save particle coordinates                                           //
  1271. //---------------------------------------------------------------------//
  1272.  
  1273. void save_particle_data(std::vector<level>& levels, std::vector<Process>& eProc){
  1274.     double   d;
  1275.     FILE   * f;
  1276.     char fname[80];
  1277.    
  1278.     strcpy(fname,"picdata.bin");
  1279.     f = fopen(fname,"wb");
  1280.     fwrite(&Time,sizeof(double),1,f);
  1281.     d = (double)(cycles_done);
  1282.     fwrite(&d,sizeof(double),1,f);
  1283.     d = (double)(N_e);
  1284.     fwrite(&d,sizeof(double),1,f);
  1285.     fwrite(x_e, sizeof(double),N_e,f);
  1286.     fwrite(vx_e,sizeof(double),N_e,f);
  1287.     fwrite(vy_e,sizeof(double),N_e,f);
  1288.     fwrite(vz_e,sizeof(double),N_e,f);
  1289.     d = (double)(N_i);
  1290.     fwrite(&d,sizeof(double),1,f);
  1291.     fwrite(x_i, sizeof(double),N_i,f);
  1292.     fwrite(vx_i,sizeof(double),N_i,f);
  1293.     fwrite(vy_i,sizeof(double),N_i,f);
  1294.     fwrite(vz_i,sizeof(double),N_i,f);
  1295.  
  1296.     // saving excited state densities and rates - Artem
  1297.  
  1298.     for (auto &lvl : levels)
  1299.         fwrite(lvl.density.data(), sizeof(double), N_G, f);
  1300.  
  1301.     for (auto &proc : eProc){
  1302.         if (proc.kind == ProcKind::ELASTIC || proc.kind == ProcKind::IONIZATION) continue;
  1303.         fwrite(proc.cum_rate.data(), sizeof(double), N_G, f);
  1304.         fwrite(proc.avg_rate.data(), sizeof(double), N_G, f);
  1305.     }
  1306.  
  1307.     fclose(f);
  1308.     printf(">> eduPIC: data saved : %d electrons %d ions, excited states densities , %d cycles completed, time is %e [s]\n",N_e,N_i,cycles_done,Time);
  1309. }
  1310.  
  1311. //---------------------------------------------------------------------//
  1312. // load particle coordinates                                           //
  1313. //---------------------------------------------------------------------//
  1314.  
  1315. void load_particle_data(std::vector<level>& levels, std::vector<Process>& eProc){
  1316.     double   d;
  1317.     FILE   * f;
  1318.     char fname[80];
  1319.    
  1320.     strcpy(fname,"picdata.bin");    
  1321.     f = fopen(fname,"rb");
  1322.     if (f==NULL) {printf(">> eduPIC: ERROR: No particle data file found, try running initial cycle using argument '0'\n"); exit(0); }
  1323.     fread(&Time,sizeof(double),1,f);
  1324.     fread(&d,sizeof(double),1,f);
  1325.     cycles_done = int(d);
  1326.     fread(&d,sizeof(double),1,f);
  1327.     N_e = int(d);
  1328.     fread(x_e, sizeof(double),N_e,f);
  1329.     fread(vx_e,sizeof(double),N_e,f);
  1330.     fread(vy_e,sizeof(double),N_e,f);
  1331.     fread(vz_e,sizeof(double),N_e,f);
  1332.     fread(&d,sizeof(double),1,f);
  1333.     N_i = int(d);
  1334.     fread(x_i, sizeof(double),N_i,f);
  1335.     fread(vx_i,sizeof(double),N_i,f);
  1336.     fread(vy_i,sizeof(double),N_i,f);
  1337.     fread(vz_i,sizeof(double),N_i,f);
  1338.  
  1339.     // reading excited states densities and rates info -- Artem
  1340.  
  1341.     for (auto &lvl : levels)
  1342.         fread(lvl.density.data(), sizeof(double), N_G, f);
  1343.  
  1344.     for (auto &proc : eProc){
  1345.         if (proc.kind == ProcKind::ELASTIC || proc.kind == ProcKind::IONIZATION) continue;
  1346.         fread(proc.cum_rate.data(), sizeof(double), N_G, f);
  1347.         fread(proc.avg_rate.data(), sizeof(double), N_G, f);
  1348.     }
  1349.  
  1350.     fclose(f);
  1351.     printf(">> eduPIC: data loaded : %d electrons %d ions, excited states densities, %d cycles completed before, time is %e [s]\n",N_e,N_i,cycles_done,Time);
  1352. }
  1353.  
  1354. //---------------------------------------------------------------------//
  1355. // save density data                                                   //
  1356. //---------------------------------------------------------------------//
  1357.  
  1358. void save_density(void){
  1359.     FILE *f;
  1360.     double c;
  1361.     int m;
  1362.    
  1363.     f = fopen("density.dat","w");
  1364.     c = 1.0 / (double)(no_of_cycles) / (double)(N_T);
  1365.     for(m=0; m<N_G; m++){
  1366.         fprintf(f,"%8.5f  %12e  %12e\n",m * DX, cumul_e_density[m] * c, cumul_i_density[m] * c);
  1367.     }
  1368.     fclose(f);
  1369. }
  1370.  
  1371. // save Final excited states densities - Artem
  1372.  
  1373. void save_final_excited_densities(std::vector<level>& levels) {
  1374.     FILE *f = fopen("excited_densities.dat", "w");
  1375.     for(int p=0; p<N_G; p++) {
  1376.         fprintf(f, "%8.5f %12e %12e\n", p*DX, levels[1].density[p], levels[2].density[p]);
  1377.     }
  1378.     fclose(f);
  1379. }
  1380.  
  1381. //---------------------------------------------------------------------//
  1382. // save EEPF data                                                      //
  1383. //---------------------------------------------------------------------//
  1384.  
  1385. void save_eepf(void) {
  1386.     FILE   *f;
  1387.     int    i;
  1388.     double h,energy;
  1389.    
  1390.     h = 0.0;
  1391.     for (i=0; i<N_EEPF; i++) {h += eepf[i];}
  1392.     h *= DE_EEPF;          
  1393.     f = fopen("eepf.dat","w");
  1394.     for (i=0; i<N_EEPF; i++) {
  1395.         energy = (i + 0.5) * DE_EEPF;
  1396.         fprintf(f,"%e  %e\n", energy, eepf[i] / h / sqrt(energy));
  1397.     }
  1398.     fclose(f);
  1399. }
  1400.  
  1401. //---------------------------------------------------------------------//
  1402. // save IVDF data -- Artem                                             //
  1403. //---------------------------------------------------------------------//
  1404.  
  1405. void save_ivdf(void) {
  1406.  
  1407.     if (mkdir("IVDF", 0755) != 0 && errno != EEXIST) {
  1408.     perror("mkdir IVDF");    
  1409.     }
  1410.  
  1411.     for (int b = 0; b < N_sp; b++){
  1412.  
  1413.     FILE   *f;
  1414.     int    i;
  1415.     double h,velocity;
  1416.    
  1417.     h = 0.0;
  1418.     for (i=0; i<N_IVDF; i++){ h += (ivdf_pos[b][i] + ivdf_neg[b][i]);}
  1419.     h *= DV_IVDF;                       // this stems from: f(V) = N(V)/(bin_width*sum_{V}(N(V)))
  1420.    
  1421.     // build filename: “IVDF/ivdf_0.00–0.01cm.dat”, etc.
  1422.     double x0 = b * DX_PF,
  1423.            x1 = x0 + DX_PF;
  1424.     char fname[128];
  1425.     snprintf(fname, sizeof(fname),
  1426.              "IVDF/ivdf_%.4f-%.4fm.dat", x0, x1);
  1427.    
  1428.     f = fopen(fname,"w");
  1429.     //printing out negative part:
  1430.     for (i=0; i<N_IVDF; i++) {
  1431.         velocity = (-1.0)*(i + 0.5) * DV_IVDF;
  1432.         fprintf(f,"%e  %e\n", velocity, ivdf_neg[b][i] / h );
  1433.     }
  1434.     //printing out positive part
  1435.     for (i=0; i<N_IVDF; i++) {
  1436.         velocity = (i + 0.5) * DV_IVDF;
  1437.         fprintf(f,"%e  %e\n", velocity, ivdf_pos[b][i] / h );
  1438.     }    
  1439.     fclose(f);
  1440.     }
  1441. }
  1442.  
  1443. //---------------------------------------------------------------------//
  1444. // save IEPF data -- Artem                                             //
  1445. //---------------------------------------------------------------------//
  1446.  
  1447. void save_iepf(void) {
  1448.     if (mkdir("IEPF", 0755) != 0 && errno != EEXIST) { perror("mkdir IEPF");}
  1449.     for (int b = 0; b < N_sp; b++){
  1450.         FILE   *f;
  1451.         int    i;
  1452.         double h,energy;
  1453.         h = 0.0;
  1454.         for (i=0; i<N_IEPF; i++){ h += iepf[b][i];}
  1455.         h *= DE_IEPF;
  1456.         if (h < 1.0E-12) continue;                              
  1457.         // build filename: “IEPF/iepf_0.00–0.01cm.dat”, etc.
  1458.         double x0 = b * DX_PF,
  1459.             x1 = x0 + DX_PF;
  1460.         char fname[128];
  1461.         snprintf(fname, sizeof(fname), "IEPF/iepf_%.4f-%.4fm.dat", x0, x1);
  1462.         //printing out part
  1463.         f = fopen(fname,"w");
  1464.         for (i=0; i<N_IEPF; i++) {
  1465.             energy = (i + 0.5) * DE_IEPF;
  1466.             fprintf(f,"%e  %e\n", energy, iepf[b][i] / h );
  1467.         }    
  1468.         fclose(f);
  1469.     }
  1470. }
  1471.  
  1472. //---------------------------------------------------------------------//
  1473. // save IFED data                                                      //
  1474. //---------------------------------------------------------------------//
  1475.  
  1476. void save_ifed(void) {
  1477.     FILE   *f;
  1478.     int    i;
  1479.     double h_pow,h_gnd,energy;
  1480.    
  1481.     h_pow = 0.0;
  1482.     h_gnd = 0.0;
  1483.     for (i=0; i<N_IFED; i++) {h_pow += ifed_pow[i]; h_gnd += ifed_gnd[i];}
  1484.     h_pow *= DE_IFED;
  1485.     h_gnd *= DE_IFED;
  1486.     mean_i_energy_pow = 0.0;
  1487.     mean_i_energy_gnd = 0.0;
  1488.     f = fopen("ifed.dat","w");
  1489.     for (i=0; i<N_IFED; i++) {
  1490.         energy = (i + 0.5) * DE_IFED;
  1491.         fprintf(f,"%6.2f %10.6f %10.6f\n", energy, (double)(ifed_pow[i])/h_pow, (double)(ifed_gnd[i])/h_gnd);
  1492.         mean_i_energy_pow += energy * (double)(ifed_pow[i]) / h_pow;
  1493.         mean_i_energy_gnd += energy * (double)(ifed_gnd[i]) / h_gnd;
  1494.     }
  1495.     fclose(f);
  1496. }
  1497.  
  1498. //--------------------------------------------------------------------//
  1499. // save XT data                                                       //
  1500. //--------------------------------------------------------------------//
  1501.  
  1502. void save_xt_1(xt_distr distr, char *fname) {
  1503.     FILE   *f;
  1504.     int    i, j;
  1505.    
  1506.     f = fopen(fname,"w");
  1507.     for (i=0; i<N_G; i++){
  1508.         for (j=0; j<N_XT; j++){
  1509.             fprintf(f,"%e  ", distr[i][j]);
  1510.         }
  1511.         fprintf(f,"\n");
  1512.     }
  1513.     fclose(f);
  1514. }
  1515.  
  1516. void norm_all_xt(void){
  1517.     double f1, f2;
  1518.     int    i, j;
  1519.    
  1520.     // normalize all XT data
  1521.    
  1522.     f1 = (double)(N_XT) / (double)(no_of_cycles * N_T);
  1523.     f2 = WEIGHT / (ELECTRODE_AREA * DX) / (no_of_cycles * (PERIOD / (double)(N_XT)));
  1524.    
  1525.     for (i=0; i<N_G; i++){
  1526.         for (j=0; j<N_XT; j++){
  1527.             pot_xt[i][j]    *= f1;
  1528.             efield_xt[i][j] *= f1;
  1529.             ne_xt[i][j]     *= f1;
  1530.             ni_xt[i][j]     *= f1;
  1531.             e1_xt[i][j]     *= f1;   // Artem
  1532.             e2_xt[i][j]     *= f1;   // Artem
  1533.             if (counter_e_xt[i][j] > 0) {
  1534.                 ue_xt[i][j]     =  ue_xt[i][j] / counter_e_xt[i][j];
  1535.                 je_xt[i][j]     = -ue_xt[i][j] * ne_xt[i][j] * E_CHARGE;
  1536.                 meanee_xt[i][j] =  meanee_xt[i][j] / counter_e_xt[i][j];
  1537.                 ioniz_rate_xt[i][j] *= f2;
  1538.              } else {
  1539.                 ue_xt[i][j]         = 0.0;
  1540.                 je_xt[i][j]         = 0.0;
  1541.                 meanee_xt[i][j]     = 0.0;
  1542.                 ioniz_rate_xt[i][j] = 0.0;
  1543.             }
  1544.             if (counter_i_xt[i][j] > 0) {
  1545.                 ui_xt[i][j]     = ui_xt[i][j] / counter_i_xt[i][j];
  1546.                 ji_xt[i][j]     = ui_xt[i][j] * ni_xt[i][j] * E_CHARGE;
  1547.                 meanei_xt[i][j] = meanei_xt[i][j] / counter_i_xt[i][j];
  1548.             } else {
  1549.                 ui_xt[i][j]     = 0.0;
  1550.                 ji_xt[i][j]     = 0.0;
  1551.                 meanei_xt[i][j] = 0.0;
  1552.             }
  1553.             powere_xt[i][j] = je_xt[i][j] * efield_xt[i][j];
  1554.             poweri_xt[i][j] = ji_xt[i][j] * efield_xt[i][j];
  1555.         }
  1556.     }
  1557. }
  1558.  
  1559. void save_all_xt(void){
  1560.     char fname[128];
  1561.  
  1562.     if (mkdir("XT_dat", 0755) != 0 && errno != EEXIST) {
  1563.         perror("mkdir XT_dat");
  1564.         exit(1);
  1565.     }
  1566.     // POT
  1567.     // make sure the folder exists (ignore EEXIST)
  1568.     if (mkdir("XT_dat/XT_pot", 0755) != 0 && errno != EEXIST) {
  1569.         perror("mkdir XT_pot");
  1570.     }
  1571.     // build the numbered filename
  1572.     snprintf(fname, sizeof(fname),
  1573.              "XT_dat/XT_pot/pot_xt_%04d.dat",
  1574.              cycles_done);
  1575.     save_xt_1(pot_xt, fname);
  1576.  
  1577.     // NE
  1578.     if (mkdir("XT_dat/XT_ne", 0755) != 0 && errno != EEXIST) {
  1579.         perror("mkdir XT_ne");
  1580.     }
  1581.     snprintf(fname, sizeof(fname),
  1582.              "XT_dat/XT_ne/ne_xt_%04d.dat",
  1583.              cycles_done);
  1584.     save_xt_1(ne_xt, fname);
  1585.  
  1586.     // NI
  1587.     if (mkdir("XT_dat/XT_ni", 0755) != 0 && errno != EEXIST) {
  1588.         perror("mkdir XT_ni");
  1589.     }
  1590.     snprintf(fname, sizeof(fname),
  1591.              "XT_dat/XT_ni/ni_xt_%04d.dat",
  1592.              cycles_done);
  1593.     save_xt_1(ni_xt, fname);
  1594.  
  1595.     // e1
  1596.     if (mkdir("XT_dat/XT_e1", 0755) != 0 && errno != EEXIST) {
  1597.         perror("mkdir XT_e1");
  1598.     }
  1599.     snprintf(fname, sizeof(fname),
  1600.              "XT_dat/XT_e1/e1_xt_%04d.dat",
  1601.              cycles_done);
  1602.     save_xt_1(e1_xt, fname);
  1603.  
  1604.     // e2
  1605.     if (mkdir("XT_dat/XT_e2", 0755) != 0 && errno != EEXIST) {
  1606.         perror("mkdir XT_e2");
  1607.     }
  1608.     snprintf(fname, sizeof(fname),
  1609.              "XT_dat/XT_e2/e2_xt_%04d.dat",
  1610.              cycles_done);
  1611.     save_xt_1(e2_xt, fname);  
  1612.  
  1613.     // E-Field
  1614.     if (mkdir("XT_dat/XT_efield", 0755) != 0 && errno != EEXIST) {
  1615.         perror("mkdir XT_efield");
  1616.     }
  1617.     snprintf(fname, sizeof(fname),
  1618.              "XT_dat/XT_efield/XT_efield_%04d.dat",
  1619.              cycles_done);
  1620.     save_xt_1(efield_xt, fname);    
  1621.  
  1622.     // Ioniz rate
  1623.     if (mkdir("XT_dat/XT_ioniz_rate", 0755) != 0 && errno != EEXIST) {
  1624.         perror("mkdir XT_ioniz_rate");
  1625.     }
  1626.     snprintf(fname, sizeof(fname),
  1627.              "XT_dat/XT_ioniz_rate/XT_ioniz_rate_%04d.dat",
  1628.              cycles_done);
  1629.     save_xt_1(ioniz_rate_xt, fname);    
  1630.  
  1631.     // j-e
  1632.     if (mkdir("XT_dat/XT_je", 0755) != 0 && errno != EEXIST) {
  1633.         perror("mkdir XT_je");
  1634.     }
  1635.     snprintf(fname, sizeof(fname),
  1636.              "XT_dat/XT_je/XT_je_%04d.dat",
  1637.              cycles_done);
  1638.     save_xt_1(je_xt, fname);      
  1639.  
  1640.     // j-i
  1641.     if (mkdir("XT_dat/XT_ji", 0755) != 0 && errno != EEXIST) {
  1642.         perror("mkdir XT_ji");
  1643.     }
  1644.     snprintf(fname, sizeof(fname),
  1645.              "XT_dat/XT_ji/XT_ji_%04d.dat",
  1646.              cycles_done);
  1647.     save_xt_1(ji_xt, fname);    
  1648.  
  1649.     // mean electron energy
  1650.     if (mkdir("XT_dat/XT_meanee", 0755) != 0 && errno != EEXIST) {
  1651.         perror("mkdir XT_meanee");
  1652.     }
  1653.     snprintf(fname, sizeof(fname),
  1654.              "XT_dat/XT_meanee/XT_meanee_%04d.dat",
  1655.              cycles_done);
  1656.     save_xt_1(meanee_xt, fname);    
  1657.  
  1658.     // mean ion energy
  1659.     if (mkdir("XT_dat/XT_meanei", 0755) != 0 && errno != EEXIST) {
  1660.         perror("mkdir XT_meanei");
  1661.     }
  1662.     snprintf(fname, sizeof(fname),
  1663.              "XT_dat/XT_meanei/XT_meanei_%04d.dat",
  1664.              cycles_done);
  1665.     save_xt_1(meanei_xt, fname);        
  1666.  
  1667.     // power e
  1668.     if (mkdir("XT_dat/XT_powere", 0755) != 0 && errno != EEXIST) {
  1669.         perror("mkdir XT_powere");
  1670.     }
  1671.     snprintf(fname, sizeof(fname),
  1672.              "XT_dat/XT_powere/XT_powere_%04d.dat",
  1673.              cycles_done);
  1674.     save_xt_1(powere_xt, fname);      
  1675.  
  1676.     // power i
  1677.     if (mkdir("XT_dat/XT_poweri", 0755) != 0 && errno != EEXIST) {
  1678.         perror("mkdir XT_poweri");
  1679.     }
  1680.     snprintf(fname, sizeof(fname),
  1681.              "XT_dat/XT_poweri/XT_poweri_%04d.dat",
  1682.              cycles_done);
  1683.     save_xt_1(poweri_xt, fname);        
  1684. }
  1685.  
  1686. //---------------------------------------------------------------------//
  1687. // simulation report including stability and accuracy conditions       //
  1688. //---------------------------------------------------------------------//
  1689.  
  1690. void check_and_save_info(std::vector<level>& levels){
  1691.     FILE     *f;
  1692.     double   plas_freq, meane, kT, debye_length, density, ecoll_freq, icoll_freq, sim_time, e_max, v_max, power_e, power_i, c;
  1693.     int      i,j;
  1694.     bool     conditions_OK;
  1695.    
  1696.     density    = cumul_e_density[N_G / 2] / (double)(no_of_cycles) / (double)(N_T);  // e density @ center
  1697.     plas_freq  = E_CHARGE * sqrt(density / EPSILON0 / E_MASS);                       // e plasma frequency @ center
  1698.     meane      = mean_energy_accu_center / (double)(mean_energy_counter_center);     // e mean energy @ center
  1699.     kT         = 2.0 * meane * EV_TO_J / 3.0;                                        // k T_e @ center (approximate)
  1700.     sim_time   = (double)(no_of_cycles) / FREQUENCY;                                 // simulated time
  1701.     ecoll_freq = (double)(N_e_coll) / sim_time / (double)(N_e);                      // e collision frequency
  1702.     icoll_freq = (double)(N_i_coll) / sim_time / (double)(N_i);                      // ion collision frequency
  1703.     debye_length = sqrt(EPSILON0 * kT / density) / E_CHARGE;                         // e Debye length @ center
  1704.    
  1705.     f = fopen("info.txt","w");
  1706.     fprintf(f,"########################## eduPIC simulation report ############################\n");
  1707.     fprintf(f,"Simulation parameters:\n");
  1708.     fprintf(f,"Gap distance                          = %12.3e [m]\n",  L);
  1709.     fprintf(f,"# of grid divisions                   = %12d\n",      N_G);
  1710.     fprintf(f,"Frequency                             = %12.3e [Hz]\n", FREQUENCY);
  1711.     fprintf(f,"# of time steps / period              = %12d\n",      N_T);
  1712.     fprintf(f,"# of electron / ion time steps        = %12d\n",      N_SUB);
  1713.     fprintf(f,"Voltage amplitude                     = %12.3e [V]\n",  VOLTAGE);
  1714.     fprintf(f,"Pressure (Ar)                         = %12.3e [Pa]\n", PRESSURE);
  1715.     fprintf(f,"Temperature                           = %12.3e [K]\n",  T_neutral);
  1716.     fprintf(f,"Superparticle weight                  = %12.3e\n",      WEIGHT);
  1717.     fprintf(f,"# of simulation cycles in this run    = %12d\n",      no_of_cycles);
  1718.     fprintf(f,"--------------------------------------------------------------------------------\n");
  1719.     fprintf(f,"Plasma characteristics:\n");
  1720.     fprintf(f,"Electron density @ center             = %12.3e [m^{-3}]\n", density);
  1721.     fprintf(f,"Plasma frequency @ center             = %12.3e [rad/s]\n",  plas_freq);
  1722.     fprintf(f,"Debye length @ center                 = %12.3e [m]\n",      debye_length);
  1723.     fprintf(f,"Electron collision frequency          = %12.3e [1/s]\n",    ecoll_freq);
  1724.     fprintf(f,"Ion collision frequency               = %12.3e [1/s]\n",    icoll_freq);
  1725.     fprintf(f,"--------------------------------------------------------------------------------\n");
  1726.     fprintf(f,"Stability and accuracy conditions:\n");
  1727.     conditions_OK = true;
  1728.     c = plas_freq * DT_E;
  1729.     fprintf(f,"Plasma frequency @ center * DT_E      = %12.3f (OK if less than 0.20)\n", c);
  1730.     if (c > 0.2) {conditions_OK = false;}
  1731.     c = DX / debye_length;
  1732.     fprintf(f,"DX / Debye length @ center            = %12.3f (OK if less than 1.00)\n", c);
  1733.     if (c > 1.0) {conditions_OK = false;}
  1734.     c = max_electron_coll_freq() * DT_E;
  1735.     fprintf(f,"Max. electron coll. frequency * DT_E  = %12.3f (OK if less than 0.05)\n", c);
  1736.     if (c > 0.05) {conditions_OK = false;}
  1737.     c = max_ion_coll_freq() * DT_I;
  1738.     fprintf(f,"Max. ion coll. frequency * DT_I       = %12.3f (OK if less than 0.05)\n", c);
  1739.     if (c > 0.05) {conditions_OK = false;}
  1740.     if (conditions_OK == false){
  1741.         fprintf(f,"--------------------------------------------------------------------------------\n");
  1742.         fprintf(f,"** STABILITY AND ACCURACY CONDITION(S) VIOLATED - REFINE SIMULATION SETTINGS! **\n");
  1743.         fprintf(f,"--------------------------------------------------------------------------------\n");
  1744.         fclose(f);
  1745.         printf(">> eduPIC: ERROR: STABILITY AND ACCURACY CONDITION(S) VIOLATED!\n");
  1746.         printf(">> eduPIC: for details see 'info.txt' and refine simulation settings!\n");
  1747.     }
  1748.     else
  1749.     {
  1750.         // calculate maximum energy for which the Courant-Friedrichs-Levy condition holds:
  1751.        
  1752.         v_max = DX / DT_E;
  1753.         e_max = 0.5 * E_MASS * v_max * v_max / EV_TO_J;
  1754.         fprintf(f,"Max e- energy for CFL condition       = %12.3f [eV]\n", e_max);
  1755.         fprintf(f,"Check EEPF to ensure that CFL is fulfilled for the majority of the electrons!\n");
  1756.         fprintf(f,"--------------------------------------------------------------------------------\n");
  1757.        
  1758.         // saving of the following data is done here as some of the further lines need data
  1759.         // that are computed / normalized in these functions
  1760.        
  1761.         printf(">> eduPIC: saving diagnostics data\n");
  1762.         save_density();
  1763.         save_final_excited_densities(levels);   // Artem
  1764.         save_eepf();
  1765.         save_ivdf();      
  1766.         save_iepf();
  1767.         save_ifed();
  1768.         norm_all_xt();
  1769.         save_all_xt();
  1770.         fprintf(f,"Particle characteristics at the electrodes:\n");
  1771.         fprintf(f,"Ion flux at powered electrode         = %12.3e [m^{-2} s^{-1}]\n", N_i_abs_pow * WEIGHT / ELECTRODE_AREA / (no_of_cycles * PERIOD));
  1772.         fprintf(f,"Ion flux at grounded electrode        = %12.3e [m^{-2} s^{-1}]\n", N_i_abs_gnd * WEIGHT / ELECTRODE_AREA / (no_of_cycles * PERIOD));
  1773.         fprintf(f,"Mean ion energy at powered electrode  = %12.3e [eV]\n", mean_i_energy_pow);
  1774.         fprintf(f,"Mean ion energy at grounded electrode = %12.3e [eV]\n", mean_i_energy_gnd);
  1775.         fprintf(f,"Electron flux at powered electrode    = %12.3e [m^{-2} s^{-1}]\n", N_e_abs_pow * WEIGHT / ELECTRODE_AREA / (no_of_cycles * PERIOD));
  1776.         fprintf(f,"Electron flux at grounded electrode   = %12.3e [m^{-2} s^{-1}]\n", N_e_abs_gnd * WEIGHT / ELECTRODE_AREA / (no_of_cycles * PERIOD));
  1777.         fprintf(f,"--------------------------------------------------------------------------------\n");
  1778.        
  1779.         // calculate spatially and temporally averaged power absorption by the electrons and ions
  1780.        
  1781.         power_e = 0.0;
  1782.         power_i = 0.0;
  1783.         for (i=0; i<N_G; i++){
  1784.             for (j=0; j<N_XT; j++){
  1785.                 power_e += powere_xt[i][j];
  1786.                 power_i += poweri_xt[i][j];
  1787.             }
  1788.         }
  1789.         power_e /= (double)(N_XT * N_G);
  1790.         power_i /= (double)(N_XT * N_G);
  1791.         fprintf(f,"Absorbed power calculated as <j*E>:\n");
  1792.         fprintf(f,"Electron power density (average)      = %12.3e [W m^{-3}]\n", power_e);
  1793.         fprintf(f,"Ion power density (average)           = %12.3e [W m^{-3}]\n", power_i);
  1794.         fprintf(f,"Total power density(average)          = %12.3e [W m^{-3}]\n", power_e + power_i);
  1795.         fprintf(f,"--------------------------------------------------------------------------------\n");
  1796.         fclose(f);
  1797.     }
  1798. }
  1799.  
  1800. //------------------------------------------------------------------------------------------//
  1801. // main                                                                                     //
  1802. // command line arguments:                                                                  //
  1803. // [1]: number of cycles (0 for init)                                                       //
  1804. // [2]: "m" turns on data collection and saving                                             //
  1805. //------------------------------------------------------------------------------------------//
  1806.  
  1807. int main (int argc, char *argv[]){
  1808.  
  1809.     if (argc == 1) {
  1810.         printf(">> eduPIC: error = need starting_cycle argument\n");
  1811.         return 1;
  1812.     } else {
  1813.         strcpy(st0,argv[1]);
  1814.         arg1 = atol(st0);
  1815.         if (argc > 2) {
  1816.             if (strcmp (argv[2],"m") == 0){
  1817.                 measurement_mode = true;                  // measurements will be done
  1818.             } else {
  1819.                 measurement_mode = false;
  1820.             }
  1821.         }
  1822.     }
  1823.     if (measurement_mode) {
  1824.         printf(">> eduPIC: measurement mode: on\n");
  1825.     } else {
  1826.         printf(">> eduPIC: measurement mode: off\n");
  1827.     }
  1828.  
  1829. //---------------------reading excitation levels information--------------------//
  1830.     std::ifstream levels_data("data/levels.dat");
  1831.     if (!levels_data) {
  1832.         std::cerr << "Failed to open file\n";
  1833.         return 1;
  1834.     }
  1835.     std::string header1;
  1836.     std::getline(levels_data, header1);
  1837.  
  1838.     std::vector<level> levels;
  1839.  
  1840.     level temp1;
  1841.     while (levels_data >> temp1.name >> temp1.th_energy >> temp1.weight) {
  1842.         levels.push_back(temp1);
  1843.     }    
  1844. // ----------------------------------------------------------------------------//
  1845.  
  1846.     std::unordered_map<std::string,int> lvlIndex;                                        // a map that connects levels names and their indices
  1847.     for (int i=0;i<levels.size(); ++i) lvlIndex[levels[i].name] = i;
  1848.  
  1849.     for (auto &lvl : levels) {
  1850.         lvl.density.resize(N_G);
  1851.     }
  1852.  
  1853. // --------- loading transitions information ----------------------------------//
  1854.     std::ifstream transitions_data("data/transitions.dat");
  1855.     if (!transitions_data) {
  1856.         std::cerr << "Failed to open file\n";
  1857.         return 1;
  1858.     }
  1859.     std::string header2;
  1860.     std::getline(transitions_data, header2);
  1861.  
  1862.     std::vector<transition> transitions;
  1863.  
  1864.     transition temp2;
  1865.     while (transitions_data >> temp2.lvl_init >> temp2.lvl_final >> temp2.type >> temp2.thresh_energy >> temp2.A1 >> temp2.A2
  1866.                             >> temp2.A3 >> temp2.A4 >> temp2.A5 >> temp2.A6 >> temp2.is_superelastic) {
  1867.         transitions.push_back(temp2);
  1868.     }
  1869. // -----------------------------------------------------------------------------//  
  1870.     std::vector<Process> eProc;
  1871.  
  1872.     eProc.push_back({ProcKind::ELASTIC,    "1^1S -> 1^1S", static_cast<int>(eProc.size()),  lvlIndex["1^1S"],  lvlIndex["1^1S"],     0.0});
  1873.     eProc.push_back({ProcKind::IONIZATION, "1^1S -> He+" , static_cast<int>(eProc.size()),  lvlIndex["1^1S"],  lvlIndex["He+"],      E_ION_TH});    
  1874.  
  1875.     for (auto const& transition : transitions) {
  1876.         Process p;
  1877.         p.kind          = ((transition.is_superelastic != 0) ? ProcKind::SUPERELASTIC : ProcKind::EXCITATION);
  1878.         p.name          = transition.lvl_init + "→" + transition.lvl_final;
  1879.         p.csSlot        = static_cast<int>(eProc.size());
  1880.         p.init_Lvl      = lvlIndex[transition.lvl_init];
  1881.         p.final_Lvl      = lvlIndex[transition.lvl_final];
  1882.         p.threshold_eV  = transition.thresh_energy;
  1883.         p.A1            = transition.A1;
  1884.         p.A2            = transition.A2;
  1885.         p.A3            = transition.A3;
  1886.         p.A4            = transition.A4;
  1887.         p.A5            = transition.A5;
  1888.         p.A6            = transition.A6;
  1889.         p.type          = transition.type;
  1890.         eProc.push_back(p);
  1891.     }
  1892.    
  1893.     for (auto &pr : eProc) {
  1894.         pr.cum_rate.resize(N_G);
  1895.         pr.avg_rate.resize(N_G);
  1896.     }
  1897.  
  1898.     std::cout << eProc[4].A1 << " " << eProc[4].A2 << " " << eProc[4].A3 << " " << eProc[4].A4 << " " << eProc[4].A5 << " " << eProc[4].A6 << " "
  1899.                 << eProc[4].csSlot << " " << eProc[4].init_Lvl << " "  << eProc[4].final_Lvl << " " << eProc[4].name << " "
  1900.                 << eProc[4].threshold_eV << " " << eProc[4].type << "\n";
  1901.  
  1902.     std::cout << eProc[5].A1 << " " << eProc[5].A2 << " " << eProc[5].A3 << " " << eProc[5].A4 << " " << eProc[5].A5 << " " << eProc[5].A6 << " "
  1903.                 << eProc[5].csSlot << " " << eProc[5].init_Lvl << " "  << eProc[5].final_Lvl << " " << eProc[5].name << " "
  1904.                 << eProc[5].threshold_eV << " " << eProc[5].type << "\n";
  1905.  
  1906.     const int P = static_cast<int>(eProc.size());
  1907.     std::vector<std::vector<float>> sigma_temp(P, std::vector<float>(CS_RANGES,0.0f));
  1908.  
  1909.     set_electron_cross_sections_ar(eProc, levels, sigma_temp);
  1910.     set_ion_cross_sections_ar();
  1911.     calc_total_cross_sections();
  1912.  
  1913.     std::ofstream file0("rates.dat"); file0 << std::scientific << std::setprecision(6);
  1914.     std::ofstream file1("excited_densities.dat"); file1 << std::scientific << std::setprecision(6);
  1915.     std::ofstream file2("ions_info.dat"); file2 << std::scientific << std::setprecision(6);
  1916.     std::ofstream CS_file("CS_check.dat");
  1917.  
  1918.     for (int i = 0; i < CS_RANGES; i++){
  1919.         CS_file << i*DE_CS ;
  1920.         for (int p = 0; p < P; p++){
  1921.             CS_file << " " << sigma_temp[p][i];
  1922.         }
  1923.         CS_file << "\n";
  1924.     }
  1925.     CS_file.close();
  1926.  
  1927.     //test_cross_sections(); return 1;
  1928.     datafile = fopen("conv.dat","a");
  1929.     if (arg1 == 0) {
  1930.         if (FILE *file = fopen("picdata.bin", "r")) { fclose(file);
  1931.             printf(">> eduPIC: Warning: Data from previous calculation are detected.\n");
  1932.             printf("           To start a new simulation from the beginning, please delete all output files before running ./eduPIC 0\n");
  1933.             printf("           To continue the existing calculation, please specify the number of cycles to run, e.g. ./eduPIC 100\n");
  1934.             exit(0);
  1935.         }
  1936.         no_of_cycles = 1;
  1937.         cycle = 1;                                        // init cycle
  1938.         init(N_INIT, levels);                                     // seed initial electrons & ions
  1939.         printf(">> eduPIC: running initializing cycle\n");
  1940.         Time = 0;
  1941.         do_one_cycle(levels, eProc, sigma_temp);
  1942.         print_excitation_densities(levels);
  1943.         for (int i = 0; i < N_G; i++){
  1944.             double x = i*DX;
  1945.             file0 << avg_rate1f[i] << " " << avg_rate1b[i] << " " << avg_rate2f[i] << " " << avg_rate2b[i] << "\n";
  1946.         }
  1947.         cycles_done = 1;
  1948.  
  1949.     } else {
  1950.         no_of_cycles = arg1;                              // run number of cycles specified in command line
  1951.         load_particle_data(levels, eProc);                            // read previous configuration from file
  1952.         counter = cycles_done % N_avg;
  1953.         printf(">> eduPIC: running %d cycle(s)\n",no_of_cycles);
  1954.         for (cycle=cycles_done+1;cycle<=cycles_done+no_of_cycles;cycle++) {
  1955.             do_one_cycle(levels, eProc, sigma_temp);
  1956.         }
  1957.         cycles_done += no_of_cycles;
  1958.         for (int i = 0; i < N_i; i++){
  1959.             file2 << x_i[i] << " " << vx_i[i] << " " << vy_i[i] << " " << vz_i[i] << "\n";
  1960.         }
  1961.     }
  1962.     fclose(datafile);
  1963.     save_particle_data(levels, eProc);
  1964.     if (measurement_mode) {
  1965.         check_and_save_info(levels);
  1966.     }
  1967.     printf(">> eduPIC: simulation of %d cycle(s) is completed.\n",no_of_cycles);
  1968.     file0.close();
  1969.     file1.close();
  1970.     file2.close();
  1971. }
Add Comment
Please, Sign In to add comment