CPP-snippets 0.0.1
A silly C++ project to use for demonstrating code integration
Loading...
Searching...
No Matches
Particles Class Reference

Stores and advances a dynamically sized set of 1D superparticles. More...

#include <particles.hpp>

Public Member Functions

 Particles ()=default
 Default constructor, creates an empty particle set.
 
 Particles (int N, double q, double m, double w)
 Construct a particle set with N superparticles.
 
void resize (int N)
 Resize particle arrays to contain N superparticles.
 
double interp_linear (const Eigen::VectorXd &x_nodes, const Eigen::VectorXd &E_nodes, double x)
 
void emit (int N, double xpos, double dt, double energy_eV, const Eigen::VectorXd &node_pos, const Eigen::VectorXd &efield, std::mt19937 &rng)
 Emit N particles at position xpos with Maxwellian velocity distribution.
 
void push_leapfrog (double dt, const Eigen::VectorXd &node_pos, const Eigen::VectorXd &efield)
 Advance particles using leapfrog (velocity half-step scheme).
 
Eigen::Vector4d fluxes (const Eigen::Vector4d &el_pos, std::mt19937 &rng, double p_ce, double p_ag)
 Compute absorbed particle fluxes on four electrodes.
 
Eigen::VectorXd deposit_charge (const Eigen::VectorXd &node_pos, const Eigen::VectorXd &dx) const
 Deposit particle charge onto grid nodes (CIC scheme).
 
Eigen::VectorXd energy_distribution (int n_bins, double W_max) const
 

Data Fields

Eigen::VectorXd m_positions
 Particle positions (1D).
 
Eigen::VectorXd m_pos_prev
 Previous positions for boundary‐crossing detection.
 
Eigen::VectorXd m_vel_half
 Half-step velocities for leapfrog integration.
 
double m_charge
 Charge of a single physical particle (C).
 
double m_mass
 Mass of a single physical particle (kg).
 
double m_weight = 1.0
 Superparticle statistical weight.
 

Detailed Description

Stores and advances a dynamically sized set of 1D superparticles.

Handles particle emission, leapfrog pushing under electric fields, charge deposition onto grid nodes, and absorbing boundary flux diagnostics.

Definition at line 13 of file particles.hpp.

Constructor & Destructor Documentation

◆ Particles()

Particles ( int N,
double q,
double m,
double w )

Construct a particle set with N superparticles.

Parameters
NNumber of particles.
qCharge per physical particle.
mMass per physical particle.
wStatistical weight per superparticle.

Definition at line 11 of file particles.cpp.

12 : m_positions(N), m_vel_half(N), m_pos_prev(N),
13 m_charge(q_in), m_mass(m_in), m_weight(w_in) { }
double m_weight
Superparticle statistical weight.
Definition particles.hpp:41
Eigen::VectorXd m_positions
Particle positions (1D).
Definition particles.hpp:26
double m_mass
Mass of a single physical particle (kg).
Definition particles.hpp:38
double m_charge
Charge of a single physical particle (C).
Definition particles.hpp:35
Eigen::VectorXd m_pos_prev
Previous positions for boundary‐crossing detection.
Definition particles.hpp:29
Eigen::VectorXd m_vel_half
Half-step velocities for leapfrog integration.
Definition particles.hpp:32

Member Function Documentation

◆ deposit_charge()

Eigen::VectorXd deposit_charge ( const Eigen::VectorXd & node_pos,
const Eigen::VectorXd & dx ) const

Deposit particle charge onto grid nodes (CIC scheme).

Parameters
node_posNode positions.
dxCell widths.
Returns
Charge density vector (C/m) at nodes.

Definition at line 216 of file particles.cpp.

217 {
218 std::cout << " + depositing particle charges (OMP)" << std::endl;
219
220 int Nnodes = node_pos.size();
221 int Np = m_positions.size();
222
223 Eigen::VectorXd rho = Eigen::VectorXd::Zero(Nnodes);
224
225 double qsp = m_charge * m_weight;
226
227 #pragma omp parallel
228 {
229 // Thread-local accumulator
230 Eigen::VectorXd local_rho = Eigen::VectorXd::Zero(Nnodes);
231
232 #pragma omp for nowait
233 for (int p = 0; p < Np; p++) {
234
235 double xpos = m_positions[p];
236 int j = find_cell_index(node_pos, xpos);
237
238 double xL = node_pos[j];
239 double xR = node_pos[j+1];
240
241 double wL = (xR - xpos) / dx[j];
242 double wR = (xpos - xL) / dx[j];
243
244 local_rho[j] += qsp * wL;
245 local_rho[j + 1] += qsp * wR;
246 }
247
248 // reduction (atomic add)
249 #pragma omp critical
250 {
251 rho += local_rho;
252 }
253 }
254
255 // Convert charge to density
256 #pragma omp parallel for
257 for (int j = 0; j < Nnodes - 1; j++) {
258 if (dx[j] > 0) {
259 rho[j] /= dx[j];
260 rho[j + 1] /= dx[j];
261 }
262 }
263
264 return rho;
265}

◆ emit()

void emit ( int N,
double xpos,
double dt,
double energy_eV,
const Eigen::VectorXd & node_pos,
const Eigen::VectorXd & efield,
std::mt19937 & rng )

Emit N particles at position xpos with Maxwellian velocity distribution.

Parameters
NNumber of emitted particles.
xposInjection position.
dtInjection time intervall.
energy_eVThermal energy (eV) for Maxwellian sampling.
rngRandom generator for velocity/time sampling.

Definition at line 71 of file particles.cpp.

73 {
74
75 std::cout << " + Emmiting " << N << " new particles" << std::endl;
76 int n_nodes = node_pos.size();
77 int old_N = m_positions.size();
78 int new_N = old_N + N;
79
80 m_pos_prev.conservativeResize(new_N);
81 m_positions.conservativeResize(new_N);
82 m_vel_half.conservativeResize(new_N);
83
84 double kT = energy_eV * constants::q_e;
85 double sigma_v = std::sqrt(kT / m_mass);
86
87 double t_emit, v_emit = 0;
88 double a = (m_charge * efield[0]) / m_mass;
89 bool overshoot = false;
90
91 // IMPORTANT: RNG must be thread-local
92 std::normal_distribution<double> normal(0.0, sigma_v); // Maxwellian velocity
93 std::uniform_real_distribution<double> uniform(0.0, dt); // Uniform emission time
94
95 #pragma omp parallel
96 {
97 std::mt19937 local_rng(rng()); // copy engine per thread
98
99 #pragma omp for
100 for (int i = 0; i < N; i++) {
101 int idx = old_N + i;
102
103 t_emit = uniform(local_rng); // relative time of emission inside timestep
104 v_emit = std::fabs(normal(local_rng)); // thermionic emission speed
105 double p_pos = xpos;
106 double p_vel = v_emit;
107
108 // initial values
109 m_pos_prev[idx] = xpos;
110 m_positions[idx] = xpos;
111 m_vel_half[idx] = v_emit;
112
113 // preadvance position and velocity in efield solution with substepping
114 double dt_sub = dt / std::ceil(n_nodes/2);
115 double local_a = 0;
116 for (int j = 0; j < std::ceil(n_nodes/2); j++) {
117 local_a = (m_charge * this->interp_linear(node_pos, efield, p_pos)) / m_mass;
118 m_vel_half[idx] += local_a * dt_sub;
119 m_positions[idx] += m_vel_half[idx] * dt_sub;
120
121 }
122 }
123 }
124}

◆ energy_distribution()

Eigen::VectorXd energy_distribution ( int n_bins,
double W_max ) const

Definition at line 275 of file particles.cpp.

275 {
276 Eigen::VectorXd energies = get_kinetic_energies();
277
278 Eigen::VectorXd hist = Eigen::VectorXd::Zero(n_bins);
279 double dW = W_max / n_bins;
280
281 for (int i = 0; i < energies.size(); ++i) {
282 int bin = std::min(static_cast<int>(energies[i] / dW), n_bins - 1);
283 hist(bin) += m_weight; // scale by superparticle weight
284 }
285
286 // normalize if desired (number of particles per unit energy)
287 // hist /= dW;
288 return hist;
289}

◆ fluxes()

Eigen::Vector4d fluxes ( const Eigen::Vector4d & el_pos,
std::mt19937 & rng,
double p_ce,
double p_ag )

Compute absorbed particle fluxes on four electrodes.

Flux order: 0: filament
1: control electrode
2: acceleration grid
3: ion collector

Randomized absorption at the control electrode and acceleration grid is controlled by probabilities p_ce and p_ag.

Parameters
el_posPositions of the four electrodes.
rngRandom generator for absorption events.
p_ceAbsorption probability at control electrode.
p_agAbsorption probability at acceleration grid.
Returns
Vector of absorbed charges per boundary.

Definition at line 153 of file particles.cpp.

153 {
154
155 int N = m_positions.size();
156 int initial_size = N;
157
158 int fil_particles = 0;
159 int ce_particles = 0;
160 int ag_particles = 0;
161 int ic_particles = 0;
162
163 int i = 0;
164 while (i < N) {
165
166 bool removed = false;
167
168 if (m_positions[i] < el_pos[0]) {
169 // --- Particle hit filament ---
170 fil_particles++;
171 removed = true;
172 }
173 else if (m_positions[i] > el_pos.tail(1).value()) {
174 // --- Particle hit ion collector ---
175 ic_particles++;
176 removed = true;
177 }
178 else if ((m_pos_prev[i] - el_pos[1]) * (m_positions[i] - el_pos[1]) <= 0.0) {
179 if (std::generate_canonical<double, 53>(rng) < p_ce) {
180 ce_particles++;
181 removed = true;
182 }
183 }
184 else if ((m_pos_prev[i] - el_pos[2]) * (m_positions[i] - el_pos[2]) <= 0.0) {
185 // --- Particle crossed acceleration grid ---
186 if (std::generate_canonical<double, 53>(rng) < p_ag) {
187 ag_particles++;
188 removed = true;
189 }
190 }
191
192 if (removed) {
193 // Swap-and-pop to remove particle efficiently
194 int last = N - 1;
195 m_positions[i] = m_positions[last];
196 m_pos_prev[i] = m_pos_prev[last];
197 m_vel_half[i] = m_vel_half[last];
198 --N; // reduce particle count
199 }
200 else {
201 ++i;
202 }
203 }
204
205 // Resize vectors to remove absorbed particles
206 m_positions.conservativeResize(N);
207 m_pos_prev.conservativeResize(N);
208 m_vel_half.conservativeResize(N);
209
210 Eigen::Vector4d particle_fluxes(fil_particles, ce_particles, ag_particles, ic_particles);
211
212 std::cout << " + removed " << initial_size - N << " particles " << std::endl;
213 return particle_fluxes * m_charge * m_weight;
214}

◆ interp_linear()

double interp_linear ( const Eigen::VectorXd & x_nodes,
const Eigen::VectorXd & E_nodes,
double x )

Definition at line 45 of file particles.cpp.

45 {
46 const int N = x_nodes.size();
47
48 // --- bounds check (clamp) ---
49 if (x <= x_nodes[0]) return val_nodes[0];
50 if (x >= x_nodes[N - 1]) return val_nodes[N - 1];
51
52 // --- locate cell via binary search ---
53 auto it = std::lower_bound(x_nodes.data(), x_nodes.data() + N, x);
54 int i = std::max(0, int(it - x_nodes.data() - 1));
55
56 // --- linear interpolation ---
57 double x0 = x_nodes[i];
58 double x1 = x_nodes[i+1];
59 double E0 = val_nodes[i];
60 double E1 = val_nodes[i+1];
61
62 double t = (x - x0) / (x1 - x0);
63 return E0 + t * (E1 - E0);
64}

◆ push_leapfrog()

void push_leapfrog ( double dt,
const Eigen::VectorXd & node_pos,
const Eigen::VectorXd & efield )

Advance particles using leapfrog (velocity half-step scheme).

Parameters
dtTime step.
efieldCell averaged electric fields.
node_posNodal positions.

Definition at line 130 of file particles.cpp.

131 {
132
133 int Np = m_positions.size();
134 m_pos_prev = m_positions; // store previous positions for diagnostics
135
136 #pragma omp parallel for
137 for (int i = 0; i < Np; i++) {
138 // 1. Interpolate electric field at particle position
139 double local_efield = interp_linear(node_pos, efield, m_positions[i]);
140
141 // 2. Compute acceleration
142 double local_a = m_charge * local_efield / m_mass;
143
144 // 3. Update half-step velocity
145 m_vel_half[i] += local_a * dt;
146
147 // 4. Update position
148 m_positions[i] += m_vel_half[i] * dt;
149 }
150
151}

◆ resize()

void resize ( int N)

Resize particle arrays to contain N superparticles.

Parameters
NNew total particle count.

Definition at line 18 of file particles.cpp.

18 {
19 m_pos_prev.resize(N);
20 m_positions.resize(N);
21 m_vel_half.resize(N);
22}

Field Documentation

◆ m_charge

double m_charge

Charge of a single physical particle (C).

Definition at line 35 of file particles.hpp.

◆ m_mass

double m_mass

Mass of a single physical particle (kg).

Definition at line 38 of file particles.hpp.

◆ m_pos_prev

Eigen::VectorXd m_pos_prev

Previous positions for boundary‐crossing detection.

Definition at line 29 of file particles.hpp.

◆ m_positions

Eigen::VectorXd m_positions

Particle positions (1D).

Definition at line 26 of file particles.hpp.

◆ m_vel_half

Eigen::VectorXd m_vel_half

Half-step velocities for leapfrog integration.

Definition at line 32 of file particles.hpp.

◆ m_weight

double m_weight = 1.0

Superparticle statistical weight.

Definition at line 41 of file particles.hpp.


The documentation for this class was generated from the following files: