OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
FlatTop.cpp
Go to the documentation of this file.
1#include "FlatTop.h"
2#include <cmath>
3#include <memory>
4#include "Distribution.h"
5#include "SamplingBase.hpp"
6
7using GeneratorPool = typename Kokkos::Random_XorShift64_Pool<>;
8using Dist_t = ippl::random::NormalDistribution<double, 3>;
9
11 std::shared_ptr<ParticleContainer_t> pc, std::shared_ptr<FieldContainer_t> fc,
12 Distribution_t* opalDist)
13 : SamplingBase(pc, fc, opalDist), rand_pool_m(determineRandInit()) {
14 setParameters(opalDist);
15}
16
18 std::shared_ptr<ParticleContainer_t> pc, std::shared_ptr<FieldContainer_t> fc,
19 bool emitting, double sigmaTFall, double sigmaTRise, Vector_t<double, 3> cutoff,
20 double tPulseLengthFWHM, Vector_t<double, 3> sigmaR)
21 : SamplingBase(pc, fc), rand_pool_m(determineRandInit()) {
22 setInternalVariables(emitting, sigmaTFall, sigmaTRise, cutoff, tPulseLengthFWHM, sigmaR);
23}
24
26 std::shared_ptr<ParticleContainer_t> pc, bool emitting, double sigmaTFall,
27 double sigmaTRise, Vector_t<double, 3> cutoff, double tPulseLengthFWHM,
29 : SamplingBase(pc), rand_pool_m(determineRandInit()) {
30 setInternalVariables(emitting, sigmaTFall, sigmaTRise, cutoff, tPulseLengthFWHM, sigmaR);
31}
32
33void FlatTop::setWithDomainDecomp(bool withDomainDecomp) { withDomainDecomp_m = withDomainDecomp; }
34
36 extern Inform* gmsg;
37 size_t randInit;
38 if (Options::seed == -1) {
39 randInit = 1234567;
40 *gmsg << "* Seed = " << randInit << " on all ranks" << endl;
41 } else {
42 randInit = static_cast<size_t>(Options::seed + 100 * ippl::Comm->rank());
43 }
44 return randInit;
45}
46
51
53
54 // make sure only z direction is decomposed
55 fc_m->setDecomp({false, false, true});
56}
57
59 bool emitting, double sigmaTFall, double sigmaTRise, Vector_t<double, 3> cutoff,
60 double tPulseLengthFWHM, Vector_t<double, 3> sigmaR) {
61 emitting_m = emitting;
62 // time span of fall is [0, riseTime, riseTime+flattopTime, fallTime+flattopTime+riseTime ]
63 sigmaTFall_m = sigmaTFall;
64 sigmaTRise_m = sigmaTRise;
65 cutoffR_m = cutoff;
66
67 fallTime_m = sigmaTFall_m * cutoffR_m[2]; // fall is [0, fallTime]
69 tPulseLengthFWHM - std::sqrt(2.0 * std::log(2.0)) * (sigmaTRise_m + sigmaTFall_m);
70 if (flattopTime_m < 0.0) {
71 flattopTime_m = 0.0;
72 }
74
76
77 // These expression are taken from the old OPAL
78 // I think normalizedFlankArea is int_0^{cutoff} exp(-(x/sigma)^2/2 ) / sigma
79 // Instead of int_0^{cutoff} exp(-(x/sigma)^2/2 ) / sqrt(2*pi) / sigma, which is strange!
80 // So the distribution of tails are exp(-(x/sigma)^2/2 ) and not Gaussian!
82 0.5 * std::sqrt(Physics::two_pi) * std::erf(cutoffR_m[2] / std::sqrt(2.0));
84
85 sigmaR_m = sigmaR;
86}
87
88void FlatTop::generateUniformDisk(size_type nlocal, size_t nNew, double dt) {
89 if (nNew == 0) {
90 return;
91 }
92
93 GeneratorPool rand_pool = rand_pool_m;
94 view_type Rview = pc_m->R.getView();
95 view_type Pview = pc_m->P.getView();
96 auto dtView = pc_m->dt.getView();
97
98 double pi = Physics::pi;
102
103 auto range = Kokkos::RangePolicy<>(nlocal, nlocal + nNew);
104
105 // Position sampling is shared: uniform on elliptical disk + R0 offset.
106 Kokkos::parallel_for(
107 "unitDisk_R", range, KOKKOS_LAMBDA(const size_t j) {
108 auto generator = rand_pool.get_state();
109 double r = Kokkos::sqrt(generator.drand(0., 1.));
110 double theta = 2.0 * pi * generator.drand(0., 1.);
111 double frac = generator.drand(0., 1.);
112 rand_pool.free_state(generator);
113
114 Rview(j)[0] = r * Kokkos::cos(theta) * sigmaR[0] + R0[0];
115 Rview(j)[1] = r * Kokkos::sin(theta) * sigmaR[1] + R0[1];
116 Rview(j)[2] = 0.0 + R0[2];
117
118 // Each particle is assigned a fractional timestep dt_i = f * dt where f ~ U(0,1).
119 // This represents the fraction of the next integration step the particle will
120 // experience, as if the particle were born at a random time within [t, t+dt]. The
121 // per-particle dt is used by the Boris integrator (push/kick) and by
122 // scaleDtByCharge for field deposition, so the fractional dt naturally spreads
123 // particles in z and gives fractional charge contribution without needing to sample
124 // Rz explicitly.
125 dtView(j) = frac * dt;
126 });
127
128 // Momentum sampling depends on the emission model chosen in EMISSIONSOURCE.
129 // BEAM reference momentum (avrgpz) is intentionally NOT applied:
130 // for emitted beams only the thermal energy matters (old OPAL behavior).
131 if (emissionModel_m == "ASTRA") {
132 // ASTRA: 3D isotropic thermal emission on forward half-sphere.
133 const double pTot = euclidean_norm(P0);
134 Kokkos::parallel_for(
135 "unitDisk_P_astra", range, KOKKOS_LAMBDA(const size_t j) {
136 auto generator = rand_pool.get_state();
137 double rand1 = generator.drand(0., 1.);
138 double rand2 = generator.drand(0., 1.);
139 rand_pool.free_state(generator);
140
141 double phi = 2.0 * Kokkos::acos(Kokkos::sqrt(rand1));
142 double theta = 2.0 * pi * rand2;
143
144 Pview(j)[0] = pTot * Kokkos::sin(phi) * Kokkos::cos(theta);
145 Pview(j)[1] = pTot * Kokkos::sin(phi) * Kokkos::sin(theta);
146 Pview(j)[2] = pTot * Kokkos::fabs(Kokkos::cos(phi));
147 });
148 } else {
149 // NONE: all "thermal" momentum applied in z direction.
150 Kokkos::parallel_for(
151 "unitDisk_P_none", range, KOKKOS_LAMBDA(const size_t j) { Pview(j) = P0; });
152 }
153 Kokkos::fence();
154
155 // Correct z for the missed timeIntegration1 half-push.
156 //
157 // New particles are born between timeIntegration1 and timeIntegration2.
158 // They only receive the timeIntegration2 push: Δz = 0.5·β_z·c·(frac·dt).
159 // The inter-batch spacing is β_z·c·dt (a full step). Without correction the
160 // batch spread is 0.5·β_z·c·dt — half the spacing — leaving 50% gaps that
161 // appear as visible "discs" under strong acceleration.
162 //
163 // Adding the equivalent of the missed timeIntegration1 half-push at birth:
164 // z_init += 0.5·β_z(birth)·c·(frac·dt)
165 // makes the total spread β_z·c·frac·dt ∈ [0, β_z·c·dt], exactly tiling the
166 // inter-batch gap. The approximation is that β_z is evaluated at birth
167 // momentum; residual error is O(F·dt²) (one-timestep field correction).
168 const double c = Physics::c;
169 Kokkos::parallel_for(
170 "unitDisk_Zcorr", range, KOKKOS_LAMBDA(const size_t j) {
171 double px = Pview(j)[0];
172 double py = Pview(j)[1];
173 double pz = Pview(j)[2];
174 double gamma = Kokkos::sqrt(1.0 + px * px + py * py + pz * pz);
175 double beta_z = pz / gamma;
176 Rview(j)[2] += 0.5 * beta_z * c * dtView(j);
177 });
178 Kokkos::fence();
179}
180
182
183void FlatTop::generateParticles(size_t& numberOfParticles, Vector_t<double, 3> nr) {
184 setNr(nr);
185
186 // initial allocation is similar for both emitting and non-emitting cases
187 allocateParticles(numberOfParticles);
188
189 // This doesn't do anything, since the allocation is now done in TrackRun.
190 /*if(emitting_m){
191 // set nlocal to 0 for the very first time step, before sampling particles
192 //pc_m->setLocalNum(0);
193
194 Kokkos::View<bool*> tmp_invalid("tmp_invalid", 0);
195 // \todo might be abuse of semantics: maybe think about new pc_m->setTotalNum or
196 pc_m->updateTotal function instead? pc_m->destroy(tmp_invalid, pc_m->getLocalNum()); } else {
197 throw OpalException(
198 "FlatTop::generateParticles",
199 "FlatTop does not support generating particles without emitting. Set EMITTED = true in "
200 "the input file.");
201 }*/
202}
203
204double FlatTop::FlatTopProfile(double t) {
205 double t0;
206 if (t < riseTime_m) {
207 t0 = riseTime_m;
208 return exp(-pow((t - t0) / sigmaTRise_m, 2) / 2.);
209 // In the old opal, tails seem to be exp(-x^2/sigma^2/2) rather than Gaussian with
210 // normalizing factor.
211 } else if (t > riseTime_m && t < riseTime_m + flattopTime_m) {
212 return 1.;
213 } else if (t > riseTime_m + flattopTime_m && t < fallTime_m + flattopTime_m + riseTime_m) {
215 return exp(-pow((t - t0) / sigmaTFall_m, 2) / 2.);
216 // In the old opal, tails seem to be exp(-x^2/sigma^2/2) rather than Gaussian with
217 // normalizing factor.
218 } else
219 return 0.;
220}
221
222size_t FlatTop::computeNlocalUniformly(size_t nglobal) {
223 // Use ippl::Comm so we match the communicator used for allocation in TrackRun (maxLocalNum).
224 const int nranks = ippl::Comm->size();
225 const int rank = ippl::Comm->rank();
226
227 const size_t nranks_u = static_cast<size_t>(nranks > 0 ? nranks : 1);
228 const size_type nlocal = nglobal / nranks_u;
229 const size_t remaining = nglobal - nlocal * nranks_u;
230
231 // First 'remaining' ranks get one extra particle.
232 if (remaining > 0 && static_cast<size_t>(rank) < remaining) {
233 return nlocal + 1;
234 }
235 return nlocal;
236}
237
238double FlatTop::integrateTrapezoidal(double x1, double x2, double y1, double y2) {
239 return 0.5 * (y1 + y2) * fabs(x2 - x1);
240}
241
242void FlatTop::initDomainDecomp(double BoxIncr) {
243 auto* mesh = &fc_m->getMesh();
244 auto* FL = &fc_m->getFL();
246 ippl::Vector<double, 3> o;
247 ippl::Vector<double, 3> e;
248 double tol = 1e-15; // enlarge grid by tol to avoid missing particles on boundaries
249 o[0] = -sigmaR[0] - tol;
250 e[0] = sigmaR[0] + tol;
251 o[1] = -sigmaR[1] - tol;
252 e[1] = sigmaR[1] + tol;
253 o[2] = 0.0 - tol;
254 e[2] = Physics::c * emissionTime_m + tol;
255
256 ippl::Vector<double, 3> l = e - o;
257 hr_m = (1.0 + BoxIncr / 100.) * (l / nr_m);
258 mesh->setMeshSpacing(hr_m);
259 mesh->setOrigin(o - 0.5 * hr_m * BoxIncr / 100.);
260 pc_m->getLayout().updateLayout(*FL, *mesh);
261}
262
264 const double tArea = integrateTrapezoidal(t0, tf, FlatTopProfile(t0), FlatTopProfile(tf));
265 // Integer-safe: floor to avoid over-counting, then clamp to totalN_m (avoids fp overflow into
266 // size_type)
267 const double totalNewD = std::floor(static_cast<double>(totalN_m) * tArea / distArea_m);
268 size_type totalNew = static_cast<size_type>(totalNewD);
269 if (totalNew > totalN_m) {
270 totalNew = totalN_m;
271 }
272
273 return pc_m ? static_cast<size_type>(computeLocalEmitCount(static_cast<size_t>(totalNew)))
274 : computeNlocalUniformly(totalNew);
275
276 // The following code is unnecessarily complicated in my opinion as a load balancer will be
277 // called anyways. Apart from that it will mitigate errors in the allocated number of particles
278 // per rank.
279 /*
280 if (totalNew > 0) {
281 if(!withDomainDecomp_m){
282 // the same number of particles per rank
283 nlocalNew = computeNlocalUniformly(totalNew);
284 }
285 else{
286 // select number of particles per rank using estimated domain decomposition at final
287 emission time
288 // find min/max of particle positions for [t,t+dt]
289 Vector_t<double, 3> prmin, prmax;
290 Vector_t<double, 3> sigmaR = sigmaR_m;
291 prmin[0] = -sigmaR[0];
292 prmax[0] = sigmaR[0];
293 prmin[1] = -sigmaR[1];
294 prmax[1] = sigmaR[1];
295 prmin[2] = std::max(0.0, Physics::c * t0);
296 prmax[2] = Physics::c*tf;
297
298 double dx = prmax[0] - prmin[0];
299 double dy = prmax[1] - prmin[1];
300 double dz = prmax[2] - prmin[2];
301
302 if (dx <= 0 || dy <= 0 || dz <= 0) {
303 throw std::runtime_error("Invalid global particle volume: prmax must be greater than
304 prmin.");
305 }
306
307 double globalpvolume = dx * dy * dz;
308
309 // find min/max of subdomains for the current rank
310 auto regions = pc_m->getLayout().getRegionLayout().gethLocalRegions();
311 int rank = ippl::Comm->rank();
312
313 if (rank < 0 || static_cast<size_t>(rank) >= regions.size()) {
314 throw std::runtime_error("Invalid rank index in gethLocalRegions()");
315 }
316
317 Vector_t<double, 3> locrmin, locrmax;
318 for (unsigned d = 0; d < Dim; ++d) {
319 locrmax[d] = regions(rank)[d].max();
320 locrmin[d] = regions(rank)[d].min();
321 }
322
323 if (prmax[0] >= locrmin[0] && prmin[0] <= locrmax[0] &&
324 prmax[1] >= locrmin[1] && prmin[1] <= locrmax[1] &&
325 prmax[2] >= locrmin[2] && prmin[2] <= locrmax[2]) {
326
327 double x1 = std::max(prmin[0], locrmin[0]);
328 double x2 = std::min(prmax[0], locrmax[0]);
329 double y1 = std::max(prmin[1], locrmin[1]);
330 double y2 = std::min(prmax[1], locrmax[1]);
331 double z1 = std::max(prmin[2], locrmin[2]);
332 double z2 = std::min(prmax[2], locrmax[2]);
333
334 if (x2 >= x1 && y2 >= y1 && z2 >= z1) {
335 const double locpvolume = (x2 - x1) * (y2 - y1) * (z2 - z1);
336 if (globalpvolume > 0) {
337 const double frac = locpvolume / globalpvolume;
338 size_type nFromFrac =
339 static_cast<size_type>(std::floor(static_cast<double>(totalNew) * frac)); nlocalNew = (nFromFrac
340 > totalNew) ? totalNew : nFromFrac; } else { nlocalNew = 0;
341 }
342 } else {
343 nlocalNew = 0;
344 }
345 }
346 }
347 }*/
348}
349
350void FlatTop::allocateParticles(size_t numberOfParticles) {
351 totalN_m = numberOfParticles;
352
353 // Initial allocation is now handled centrally in TrackRun / PartBunch via the
354 // bunch's total particle count. Here we only record the desired total number
355 // of particles for this distribution. Actual per-step emission will append
356 // new particles using pc_m->create(nNew), guarded by a global BEAM::NALLOC
357 // limit in ParallelTracker.
358}
359
360void FlatTop::emitParticles(double t, double dt) {
361 Inform msgAll = Inform("FlatTop::emitParticles", INFORM_ALL_NODES);
362
363 // Time profile uses (t - t0) so sampling begins at t0.
364 double tShift = t - t0_m;
365 double dtShift = dt;
366 // Count number of new particles to be emitted in [tShift, tShift+dtShift].
367 size_type nNew = countEnteringParticlesPerRank(tShift, tShift + dtShift);
368 msgAll << level5 << "* " << nNew << " new particles to be emitted" << endl;
369 // if (nNew == 0) { return; } // don't return, see why below
370
371 // Current number of particles per rank.
372 size_type nlocal = pc_m->getLocalNum();
373 msgAll << level5 << "* " << nlocal << " particles already in the container" << endl;
374
375 // Intended design: fill only into pre-allocated slice [nextEmitIndex, nextEmitIndex+nNew)
376 // (no create), when ParticleContainer supports reserve(total) + setActiveSize. Until then
377 // we extend the container each step.
378 // Note that createParticles can be safely called with nNew=0 (no-op), but it NEEDS to be
379 // called, since other ranks might have != 0 leading to a MPI_Allreduce.
380 pc_m->createParticles(nNew);
381
382 // Generate new particles on uniform disc (sample into [nlocal, nlocal+nNew)).
383 // Each particle receives a fractional per-particle dt for sub-timestep spreading.
384 msgAll << level3 << "* generate particles on a disc" << endl;
385 generateUniformDisk(nlocal, nNew, dt);
386
387 fillPolarization(nlocal, nNew);
388
389 pc_m->markMomentsDirty();
390
391 msgAll << level3 << "* " << nNew << " new particles emmitted" << endl;
392}
393
395 size_type nNew;
396 int rank, numRanks;
397 double t = 0.0;
398 double c = Physics::c;
399
400 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
401 MPI_Comm_size(MPI_COMM_WORLD, &numRanks);
402
403 std::string filename = "timeNpart_" + std::to_string(rank) + ".txt";
404 std::ofstream file(filename);
405
406 // Check if the file opened successfully
407 if (!file.is_open()) {
408 throw std::runtime_error("Failed to open file: " + filename);
409 }
410
411 for (size_type i = 0; i < nsteps; i++) {
412 nNew = countEnteringParticlesPerRank(t, t + dt);
413
414 // current number of particles per rank
415 size_type nlocal = pc_m->getLocalNum();
416
417 // extend particle container to accomodate new particles
418 pc_m->createParticles(nNew);
419
420 // generate new particles on uniform disc
421 generateUniformDisk(nlocal, nNew, dt);
422
423 // write to a file
424 auto rViewDevice = pc_m->R.getView();
425 auto rView = Kokkos::create_mirror_view(rViewDevice);
426 Kokkos::deep_copy(rView, rViewDevice);
427
428 for (size_type j = nlocal; j < nlocal + nNew; j++) {
429 file << t << " " << (emissionTime_m - t) * c << " " << rView(j)[0] << " " << rView(j)[1]
430 << "\n";
431 }
432 file.flush(); // Ensure data is written to disk
433
434 t = t + dt;
435 }
436 file.close();
437 ippl::Comm->barrier();
438}
439
440void FlatTop::testEmitParticles(size_type nsteps, double dt) {
441 double t = 0.0;
442
443 for (size_type i = 0; i < nsteps; i++) {
444 emitParticles(t, dt);
445
446 t = t + dt;
447 }
448}
Inform * gmsg
Definition changes.cpp:7
ippl::Vector< T, Dim > Vector_t
typename Kokkos::Random_XorShift64_Pool<> GeneratorPool
typename ippl::detail::ViewType< ippl::Vector< double, Dim >, 1 >::view_type view_type
const int nr
ippl::random::NormalDistribution< double, 3 > Dist_t
Definition FlatTop.cpp:8
Defines the FlatTop class used for sampling emitting particles.
typename Kokkos::Random_XorShift64_Pool<> GeneratorPool
Definition FlatTop.h:18
KOKKOS_INLINE_FUNCTION double euclidean_norm(const Vector_t< T, D > &v)
Definition VectorMath.h:15
double getSigmaTFall() const
double getTPulseLengthFWHM() const
void setTEmission(double tEmission)
ippl::Vector< double, 3 > getSigmaR() const
double getSigmaTRise() const
ippl::Vector< double, 3 > getCutoffR() const
void setNr(Vector_t< double, 3 > nr)
Sets the number of grid points per direction.
Definition FlatTop.cpp:181
void generateUniformDisk(size_type nlocal, size_t nNew, double dt)
Generates particles (x,y) uniformly on a disk distribution.
Definition FlatTop.cpp:88
void testEmitParticles(size_type nsteps, double dt) override
Tests particle emission over a given number of steps.
Definition FlatTop.cpp:440
double distArea_m
Total area of the flattop distribution.
Definition FlatTop.h:118
Vector_t< double, 3 > sigmaR_m
Definition FlatTop.h:130
double FlatTopProfile(double t)
Computes the flat-top profile value at a given time.
Definition FlatTop.cpp:204
bool emitting_m
Flag for particle emission status.
Definition FlatTop.h:124
FlatTop(std::shared_ptr< ParticleContainer_t > pc, std::shared_ptr< FieldContainer_t > fc, Distribution_t *opalDist)
Constructor for FlatTop.
Definition FlatTop.cpp:10
Vector_t< double, 3 > nr_m
Number of grid points per direction.
Definition FlatTop.h:128
static size_t determineRandInit()
Determines the random seed initialization.
Definition FlatTop.cpp:35
void allocateParticles(size_t numberOfParticles)
Allocates memory for a given number of particles.
Definition FlatTop.cpp:350
double emissionTime_m
Total emission time.
Definition FlatTop.h:127
double sigmaTRise_m
Standard deviation for rise time profile.
Definition FlatTop.h:120
size_type totalN_m
Total number of particles.
Definition FlatTop.h:125
GeneratorPool rand_pool_m
Random number generator pool.
Definition FlatTop.h:115
double sigmaTFall_m
Standard deviation for fall time profile.
Definition FlatTop.h:119
bool withDomainDecomp_m
Flag for domain decomposition.
Definition FlatTop.h:126
void initDomainDecomp(double BoxIncr) override
Initializes the domain decomposition.
Definition FlatTop.cpp:242
Vector_t< double, 3 > hr_m
Grid spacing.
Definition FlatTop.h:129
double integrateTrapezoidal(double x1, double x2, double y1, double y2)
Integrates using the trapezoidal rule.
Definition FlatTop.cpp:238
double fallTime_m
Time duration for the fall phase.
Definition FlatTop.h:122
size_t computeNlocalUniformly(size_t nglobal)
Computes the local number of particles uniformly distributed among ranks.
Definition FlatTop.cpp:222
double normalizedFlankArea_m
Normalized area of the distribution flanks.
Definition FlatTop.h:117
void setWithDomainDecomp(bool withDomainDecomp) override
Sets whether to use domain decomposition.
Definition FlatTop.cpp:33
void emitParticles(double t, double dt) override
Emits new particles within a given time interval.
Definition FlatTop.cpp:360
void setInternalVariables(bool emitting, double sigmaTFall, double sigmaTRise, Vector_t< double, 3 > cutoff, double tPulseLengthFWHM, Vector_t< double, 3 > sigmaR)
Definition FlatTop.cpp:58
double riseTime_m
Time duration for the rise phase.
Definition FlatTop.h:123
void generateParticles(size_t &numberOfParticles, Vector_t< double, 3 > nr) override
Generates particles with a given number and grid configuration.
Definition FlatTop.cpp:183
void setParameters(Distribution_t *opalDist)
Sets distribution parameters.
Definition FlatTop.cpp:47
void testNumEmitParticles(size_type nsteps, double dt) override
Tests the number of emitted particles over a given number of steps.
Definition FlatTop.cpp:394
Vector_t< double, 3 > cutoffR_m
Cutoff radius.
Definition FlatTop.h:121
double flattopTime_m
Time duration of when the time profile is flat.
Definition FlatTop.h:116
size_type countEnteringParticlesPerRank(double t0, double tf)
Counts the number of particles entering per rank in a given time interval.
Definition FlatTop.cpp:263
ippl::detail::size_type size_type
Definition FlatTop.h:114
Vector_t< double, 3 > P0_m
Vector_t< double, 3 > R0_m
Emission source offset: position R0, momentum P0, start time t0 (applied in sample step).
std::shared_ptr< FieldContainer_t > fc_m
size_t computeLocalEmitCount(size_t totalToSample) const
Computes the number of particles this rank should emit so that the global total equals totalToSample ...
std::string emissionModel_m
Distribution_t * opalDist_m
void fillPolarization(size_t startIdx, size_t count)
std::shared_ptr< ParticleContainer_t > pc_m
int seed
The current random seed.
Definition Options.cpp:37
constexpr double two_pi
The value of.
Definition Physics.h:40
constexpr double c
The velocity of light in m/s.
Definition Physics.h:60
constexpr double pi
The value of.
Definition Physics.h:36