Penning Trap Simulation
Simulate particle behavior inside a Penning Trap
Loading...
Searching...
No Matches
PenningTrap.cpp
Go to the documentation of this file.
1
13#include "PenningTrap.hpp"
14#include "typedefs.hpp"
15#include <algorithm>
16#include <functional>
17#include <sys/types.h>
18#include <vector>
19
20PenningTrap::PenningTrap(double B_0, double V_0, double d, double t)
21{
22 this->B_0 = B_0;
23 this->V_0 = V_0;
24 this->d = d;
25 this->t = t;
26 this->perturbation = [](double t) { return 1.; };
27}
28
29PenningTrap::PenningTrap(uint i, double B_0, double V_0, double d, double t)
30 : PenningTrap::PenningTrap(B_0, V_0, d)
31{
32 for (size_t j = 0; j < i; j++) {
33 this->particles.push_back(
34 Particle(vec3(vec3().randn() * .1 * this->d),
35 vec3(vec3().randn() * .1 * this->d)));
36 }
37}
38
39PenningTrap::PenningTrap(std::vector<Particle> particles, double B_0,
40 double V_0, double d, double t)
41 : PenningTrap::PenningTrap(B_0, V_0, d)
42{
43 this->particles = particles;
44}
45
46void PenningTrap::set_pertubation(double f, double omega_V)
47{
48 this->perturbation = [f, omega_V](double t) {
49 return 1 + f * std::cos(omega_V * t);
50 };
51}
52
53void PenningTrap::reinitialize(double f, double omega_V, double t)
54{
55 this->t = t;
56 this->set_pertubation(f, omega_V);
57
58 for (size_t i = 0; i < this->particles.size(); i++) {
59 this->particles[i].r_vec = vec3().randn() * .1 * this->d;
60 }
61}
62
63vec3 PenningTrap::v_func(uint i, uint j, double dt)
64{
65 switch (i) {
66 case 0:
67 return .5 * dt * this->k_v[0][j];
68 case 1:
69 return .5 * dt * this->k_v[1][j];
70 case 2:
71 return dt * this->k_v[2][j];
72 case 3:
73 return vec3((dt / 6.)
74 * (this->k_v[0][j] + 2. * this->k_v[1][j]
75 + 2. * this->k_v[2][j] + this->k_v[3][j]));
76 default:
77 std::cout << "Not valid!" << std::endl;
78 abort();
79 }
80}
81
82vec3 PenningTrap::r_func(uint i, uint j, double dt)
83{
84 switch (i) {
85 case 0:
86 return .5 * dt * this->k_r[0][j];
87 case 1:
88 return .5 * dt * this->k_r[1][j];
89 case 2:
90 return dt * this->k_r[2][j];
91 case 3:
92 return vec3((dt / 6.)
93 * (this->k_r[0][j] + 2. * this->k_r[1][j]
94 + 2. * this->k_r[2][j] + this->k_r[3][j]));
95 default:
96 std::cout << "Not valid!" << std::endl;
97 abort();
98 }
99}
100
102{
103 this->particles.push_back(particle);
104}
105
107{
108 r(2) *= -2.;
109
110 return vec3((this->V_0 * this->perturbation(this->t) / (this->d * this->d))
111 * r);
112}
113
115{
116 return vec3{0., 0., this->B_0};
117}
118
120{
121 // Calculate the difference between the particles' position
122 vec3 res = this->particles[i].r_vec - this->particles[j].r_vec;
123
124 // Get the distance between the particles
125 double norm = arma::norm(res, 2);
126
127 return vec3((this->particles[j].q / (norm * norm * norm)) * res);
128}
129
131{
132 Particle *p = &this->particles[i];
133
134 if (arma::norm(p->r_vec) > this->d) {
135 return vec3{0., 0., 0.};
136 }
137
138 return vec3(p->q
139 * (this->external_E_field(p->r_vec)
140 + arma::cross(p->v_vec, this->external_B_field(p->r_vec))));
141}
142
144{
145 vec3 res;
146
147 for (size_t j = 0; j < this->particles.size(); j++) {
148 if (i != j)
149 res += this->force_on_particle(i, j);
150 }
151
152 return vec3(res * (K_E * this->particles[i].q));
153}
154
156{
157 if (arma::norm(this->particles[i].r_vec) > this->d) {
158 return vec3{0., 0., 0.};
159 }
160 return vec3(this->total_force_external(i) - this->total_force_particles(i));
161}
162
163void PenningTrap::evolve_RK4(double dt, bool particle_interaction)
164{
165
166 std::vector<Particle> original_particles = this->particles;
167 std::vector<Particle> tmp_particles = this->particles;
168
169 vec3 (PenningTrap::*force)(uint) = particle_interaction
172
173 size_t size = this->particles.size();
174
175 // Allocating takes a long time, so reuse sim_arr if possible
176 if (this->k_v.size() != 4 || this->k_r.size() != 4
177 || this->k_v[0].size() != size || this->k_r[0].size() != size) {
178 this->k_v = sim_arr(4, sim_cols(size));
179 this->k_r = sim_arr(4, sim_cols(size));
180 }
181
182 // Each k_{i+1} is dependent on k_i, so outer loop is not parallelizable
183 for (size_t i = 0; i < 4; i++) {
184 // Inner loop is able to be parallelized
185#pragma omp parallel for
186 for (size_t j = 0; j < size; j++) {
187 this->k_v[i][j] = (this->*force)(j) / this->particles[j].m;
188 this->k_r[i][j] = this->particles[j].v_vec;
189
190 tmp_particles[j].v_vec =
191 original_particles[j].v_vec + this->v_func(i, j, dt);
192 tmp_particles[j].r_vec =
193 original_particles[j].r_vec + this->r_func(i, j, dt);
194 }
195 this->particles = tmp_particles;
196 }
197
198 this->t += dt;
199}
200
201void PenningTrap::evolve_forward_euler(double dt, bool particle_interaction)
202{
203 size_t size = this->particles.size();
204 vec3 force_res[size];
205
206 vec3 (PenningTrap::*force)(uint) = particle_interaction
209
210 // Calculating the force for each particle is independent and therefore
211 // a good candidate for parallel execution
212#pragma omp parallel for
213 for (size_t i = 0; i < size; i++) {
214 force_res[i] = (this->*force)(i);
215 }
216
217 // Updating the particles is also independent, so we can parallelize
218 // this as well
219#pragma omp parallel for
220 for (size_t i = 0; i < size; i++) {
221 this->particles[i].r_vec += dt * this->particles[i].v_vec;
222 this->particles[i].v_vec += dt * force_res[i] / this->particles[i].m;
223 }
224
225 this->t += dt;
226}
227
228simulation_t PenningTrap::simulate(double time, uint steps, std::string method,
229 bool particle_interaction)
230{
231 double dt = time / (double)steps;
232
233 uint size = this->particles.size();
234
235 simulation_t res{sim_arr(size, sim_cols(steps)),
236 sim_arr(size, sim_cols(steps))};
237
238 void (PenningTrap::*func)(double, bool);
239 if (method == "rk4") {
241 } else if (method == "euler") {
243 } else {
244 std::cout << "Not a valid method!" << std::endl;
245 abort();
246 }
247
248 for (size_t j = 0; j < steps; j++) {
249 for (size_t i = 0; i < size; i++) {
250 res.r_vecs[i][j] = this->particles[i].r_vec;
251 res.v_vecs[i][j] = this->particles[i].v_vec;
252 }
253 (this->*func)(dt, particle_interaction);
254 }
255
256 return res;
257}
258
259void PenningTrap::write_simulation_to_dir(std::string path, double time,
260 uint steps, std::string method,
261 bool particle_interaction)
262{
263 if (path.back() != '/') {
264 path += '/';
265 }
266 if (mkpath(path, 0777) != 0) {
267 std::cout << "Failed to make path" << std::endl;
268 abort();
269 }
270
271 simulation_t res =
272 this->simulate(time, steps, method, particle_interaction);
273
274 std::ofstream ofile;
275
276 // Writing each particle to its own file is independent and can be run in
277 // parallel.
278#pragma omp parallel for private(ofile)
279 for (size_t i = 0; i < this->particles.size(); i++) {
280 ofile.open(path + "particle_" + std::to_string(i) + "_r.txt");
281 for (vec3 &vec : res.r_vecs[i]) {
282 ofile << scientific_format(vec(0), 10, 8) << ','
283 << scientific_format(vec(1), 10, 8) << ','
284 << scientific_format(vec(2), 10, 8) << '\n';
285 }
286 ofile.close();
287 ofile.open(path + "particle_" + std::to_string(i) + "_v.txt");
288 for (vec3 &vec : res.v_vecs[i]) {
289 ofile << scientific_format(vec(0), 10, 8) << ','
290 << scientific_format(vec(1), 10, 8) << ','
291 << scientific_format(vec(2), 10, 8) << '\n';
292 }
293 ofile.close();
294 }
295}
296
297double PenningTrap::fraction_of_particles_left(double time, uint steps,
298 std::string method,
299 bool particle_interaction)
300{
301 double dt = time / (double)steps;
302
303 void (PenningTrap::*func)(double, bool);
304 if (method == "rk4") {
306 } else if (method == "euler") {
308 } else {
309 std::cout << "Not a valid method!" << std::endl;
310 abort();
311 }
312
313 for (size_t j = 0; j < steps; j++) {
314 (this->*func)(dt, particle_interaction);
315 }
316
317 int particles_left = 0;
318
319 // A reduction is perfect here
320#pragma omp parallel for reduction(+ : particles_left)
321 for (size_t i = 0; i < this->particles.size(); i++) {
322 if (arma::norm(this->particles[i].r_vec) < this->d) {
323 particles_left++;
324 }
325 }
326
327 return (double)particles_left / (double)this->particles.size();
328}
A class for simulating a Penning trap.
A class that holds attributes of a particle.
Definition: Particle.hpp:23
vec3 r_vec
position
Definition: Particle.hpp:25
vec3 v_vec
velocity
Definition: Particle.hpp:26
double q
Charge.
Definition: Particle.hpp:27
A class that simulates a Penning trap.
Definition: PenningTrap.hpp:32
std::vector< Particle > particles
The particles in the Penning trap.
Definition: PenningTrap.hpp:39
double B_0
Magnetic field strength.
Definition: PenningTrap.hpp:34
vec3 total_force_external(uint i)
Calculate the total external force on a particle.
sim_arr k_r
Definition: PenningTrap.hpp:42
vec3 total_force_particles(uint i)
Calculate the total force on a particle p_i from other particles.
vec3 external_B_field(vec3 r)
Calculate B at point r.
void evolve_RK4(double dt, bool particle_interaction=true)
Go forward one timestep using the RK4 method.
vec3 v_func(uint i, uint j, double dt)
Helper for evolve_RK4 when calculating values.
Definition: PenningTrap.cpp:63
vec3 external_E_field(vec3 r)
Calculate E at point r.
PenningTrap(double B_0=T, double V_0=(25. *V)/1000., double d=500., double t=0.)
Constructor for the PenningTrap class.
Definition: PenningTrap.cpp:20
double d
Characteristic dimension.
Definition: PenningTrap.hpp:37
void add_particle(Particle particle)
Add a particle to the system.
double V_0
Applied potential.
Definition: PenningTrap.hpp:35
simulation_t simulate(double time, uint steps, std::string method="rk4", bool particle_interaction=true)
Simulate the particle system inside the Penning trap over a certain amount of time.
vec3 force_on_particle(uint i, uint j)
Calculate the force between 2 particles.
vec3 r_func(uint i, uint j, double dt)
Helper for evolve_RK4 when calculating values.
Definition: PenningTrap.cpp:82
double t
Current time.
Definition: PenningTrap.hpp:38
vec3 total_force(uint i)
calculate the total force on a particle p_i.
void set_pertubation(double f, double omega_V)
Time dependent perturbation to V_0.
Definition: PenningTrap.cpp:46
void reinitialize(double f, double omega_V, double t=0.)
Give all particles new positions and velocities, and change t and V_0.
Definition: PenningTrap.cpp:53
void evolve_forward_euler(double dt, bool particle_interaction=true)
Go forward one timestep using the forward Euler method.
double fraction_of_particles_left(double time, uint steps, std::string method="rk4", bool particle_interaction=true)
Simulate and calculate what fraction of particles are still left inside the Penning trap after the si...
void write_simulation_to_dir(std::string path, double time, uint steps, std::string method="rk4", bool particle_interaction=true)
Simulate and write the displacement of all particles to files.
std::function< double(double)> perturbation
Time-dependent perturbation.
Definition: PenningTrap.hpp:36
sim_arr k_v
Definition: PenningTrap.hpp:40
#define K_E
Coulomb constant. unit: .
Definition: constants.hpp:17
Typedef for PenningTrap::simulation return value.
Definition: typedefs.hpp:40
Useful typedefs for cleaner code.
arma::vec::fixed< 3 > vec3
Typedef for a fixed 3d arma vector.
Definition: typedefs.hpp:23
std::vector< vec3 > sim_cols
Typedef for the column of the result vector from simulating particles.
Definition: typedefs.hpp:28
std::vector< sim_cols > sim_arr
Typedef for the result of the simulate method.
Definition: typedefs.hpp:36
bool mkpath(std::string path, int mode=0777)
Make path given.
Definition: utils.cpp:72
std::string scientific_format(double d, int width=20, int prec=10)
Turns a double into a string written in scientific format.
Definition: utils.cpp:15