IPPL (Independent Parallel Particle Layer)
IPPL
Loading...
Searching...
No Matches
PenningTrapManager.h
Go to the documentation of this file.
1#ifndef IPPL_PENNING_TRAP_MANAGER_H
2#define IPPL_PENNING_TRAP_MANAGER_H
3
4#include <memory>
5
6#include "AlpineManager.h"
7#include "FieldContainer.hpp"
8#include "FieldSolver.hpp"
9#include "LoadBalancer.hpp"
10#include "Manager/BaseManager.h"
11#include "ParticleContainer.hpp"
12#include "Random/Distribution.h"
15#include "Random/Randn.h"
16
18
19template <typename T, unsigned Dim>
20class PenningTrapManager : public AlpineManager<T, Dim> {
21public:
26
27 PenningTrapManager(size_type totalP_, int nt_, Vector_t<int, Dim>& nr_, double lbt_,
28 std::string& solver_, std::string& stepMethod_)
29 : AlpineManager<T, Dim>(totalP_, nt_, nr_, lbt_, solver_, stepMethod_) {}
30
31 PenningTrapManager(size_type totalP_, int nt_, Vector_t<int, Dim>& nr_, double lbt_,
32 std::string& solver_, std::string& stepMethod_,
33 std::vector<std::string> preconditioner_params_)
34 : AlpineManager<T, Dim>(totalP_, nt_, nr_, lbt_, solver_, stepMethod_,
35 preconditioner_params_) {}
36
38
39private:
41 double Bext_m;
42 unsigned int nrMax_m;
43 double dxFinest_m;
44 double alpha_m;
45 double DrInv_m;
46
47public:
48 void pre_run() override {
49 Inform m("Pre Run");
50 for (unsigned i = 0; i < Dim; i++) {
51 this->domain_m[i] = ippl::Index(this->nr_m[i]);
52 }
53 this->decomp_m.fill(true);
54
55 this->rmin_m = 0;
56 this->rmax_m = 20;
57
58 length_m = this->rmax_m - this->rmin_m;
59 this->hr_m = length_m / this->nr_m;
60
61 this->Q_m = -1562.5;
62 Bext_m = 5.0;
63 this->origin_m = this->rmin_m;
64
65 nrMax_m = 2048; // Max grid size in our studies
67 this->dt_m = 0.5 * dxFinest_m; // size of timestep
68
69 this->it_m = 0;
70 this->time_m = 0.0;
71
72 this->alpha_m = -0.5 * this->dt_m;
73 DrInv_m = 1.0 / (1 + (std::pow((this->alpha_m * Bext_m), 2)));
74
75 m << "Discretization:" << endl
76 << "nt " << this->nt_m << " Np= " << this->totalP_m << " grid = " << this->nr_m << endl;
77
78 this->isAllPeriodic_m = true;
79
80 this->setFieldContainer(std::make_shared<FieldContainer_t>(
81 this->hr_m, this->rmin_m, this->rmax_m, this->decomp_m, this->domain_m, this->origin_m,
82 this->isAllPeriodic_m));
83
84 this->setParticleContainer(std::make_shared<ParticleContainer_t>(
85 this->fcontainer_m->getMesh(), this->fcontainer_m->getFL()));
86
87 this->fcontainer_m->initializeFields(this->solver_m);
88
89 if (this->getSolver() == "PCG") {
90 this->setFieldSolver(std::make_shared<FieldSolver_t>(
91 this->solver_m, &this->fcontainer_m->getRho(), &this->fcontainer_m->getE(),
92 &this->fcontainer_m->getPhi(), this->preconditioner_params_m));
93 } else {
94 this->setFieldSolver(std::make_shared<FieldSolver_t>(
95 this->solver_m, &this->fcontainer_m->getRho(), &this->fcontainer_m->getE(),
96 &this->fcontainer_m->getPhi()));
97 }
98
99 this->fsolver_m->initSolver();
100
101 this->setLoadBalancer(std::make_shared<LoadBalancer_t>(
102 this->lbt_m, this->fcontainer_m, this->pcontainer_m, this->fsolver_m));
103
105
106 static IpplTimings::TimerRef DummySolveTimer = IpplTimings::getTimer("solveWarmup");
107 IpplTimings::startTimer(DummySolveTimer);
108
109 this->fcontainer_m->getRho() = 0.0;
110
111 this->fsolver_m->runSolver();
112
113 IpplTimings::stopTimer(DummySolveTimer);
114
115 this->par2grid();
116
117 static IpplTimings::TimerRef SolveTimer = IpplTimings::getTimer("solve");
118 IpplTimings::startTimer(SolveTimer);
119
120 this->fsolver_m->runSolver();
121
122 IpplTimings::stopTimer(SolveTimer);
123
124 this->grid2par();
125
126 this->dump();
127
128 m << "Done";
129 }
130
132 Inform m("Initialize Particles");
133
134 auto* mesh = &this->fcontainer_m->getMesh();
135 auto* FL = &this->fcontainer_m->getFL();
137 for (unsigned d = 0; d < Dim; d++) {
138 mu[d] = 0.5 * length_m[d] + this->origin_m[d];
139 }
140 sd[0] = 0.15 * length_m[0];
141 sd[1] = 0.05 * length_m[1];
142 sd[2] = 0.20 * length_m[2];
143
145 double parR[2 * Dim];
146 for (unsigned int i = 0; i < Dim; i++) {
147 parR[i * 2] = mu[i];
148 parR[i * 2 + 1] = sd[i];
149 }
150 DistR_t distR(parR);
151
152 Vector_t<double, Dim> hr = this->hr_m;
153 Vector_t<double, Dim> origin = this->origin_m;
154 static IpplTimings::TimerRef domainDecomposition = IpplTimings::getTimer("loadBalance");
155 if ((this->lbt_m != 1.0) && (ippl::Comm->size() > 1)) {
156 m << "Starting first repartition" << endl;
157 IpplTimings::startTimer(domainDecomposition);
158 this->isFirstRepartition_m = true;
159 const ippl::NDIndex<Dim>& lDom = FL->getLocalNDIndex();
160 const int nghost = this->fcontainer_m->getRho().getNghost();
161 auto rhoview = this->fcontainer_m->getRho().getView();
162
163 using index_array_type = typename ippl::RangePolicy<Dim>::index_array_type;
165 "Assign initial rho based on PDF",
166 this->fcontainer_m->getRho().getFieldRangePolicy(),
167 KOKKOS_LAMBDA(const index_array_type& args) {
168 // local to global index conversion
169 Vector_t<double, Dim> xvec = (args + lDom.first() - nghost + 0.5) * hr + origin;
170
171 // ippl::apply accesses the view at the given indices and obtains a
172 // reference; see src/Expression/IpplOperations.h
173 ippl::apply(rhoview, args) = distR.getFullPdf(xvec);
174 });
175
176 Kokkos::fence();
177
178 this->loadbalancer_m->initializeORB(FL, mesh);
179 this->loadbalancer_m->repartition(FL, mesh, this->isFirstRepartition_m);
180 IpplTimings::stopTimer(domainDecomposition);
181 }
182
183 static IpplTimings::TimerRef particleCreation = IpplTimings::getTimer("particlesCreation");
184 IpplTimings::startTimer(particleCreation);
185
186 // Sample particle positions:
189 size_type totalP = this->totalP_m;
190 int seed = 42;
192 Kokkos::Random_XorShift64_Pool<> rand_pool64((size_type)(seed + 100 * ippl::Comm->rank()));
193
194 using samplingR_t =
195 ippl::random::InverseTransformSampling<double, Dim, Kokkos::DefaultExecutionSpace,
196 DistR_t>;
197 Vector_t<double, Dim> rmin = this->rmin_m;
198 Vector_t<double, Dim> rmax = this->rmax_m;
199 samplingR_t samplingR(distR, rmax, rmin, rlayout, totalP);
200 size_type nlocal = samplingR.getLocalSamplesNum();
201
202 this->pcontainer_m->create(nlocal);
203
204 view_type* R = &(this->pcontainer_m->R.getView());
205 samplingR.generate(*R, rand_pool64);
206
207 view_type* P = &(this->pcontainer_m->P.getView());
208
209 double muP[Dim] = {0.0, 0.0, 0.0};
210 double sdP[Dim] = {1.0, 1.0, 1.0};
211 Kokkos::parallel_for(nlocal, ippl::random::randn<double, Dim>(*P, rand_pool64, muP, sdP));
212
213 Kokkos::fence();
214 ippl::Comm->barrier();
215
216 IpplTimings::stopTimer(particleCreation);
217
218 this->pcontainer_m->q = this->Q_m / this->totalP_m;
219 m << "particles created and initial conditions assigned " << endl;
220 }
221
222 void advance() override {
223 if (this->stepMethod_m == "LeapFrog") {
224 LeapFrogStep();
225 } else {
226 throw IpplException(TestName, "Step method is not set/recognized!");
227 }
228 }
229
231 // LeapFrog time stepping https://en.wikipedia.org/wiki/Leapfrog_integration
232 // Here, we assume a constant charge-to-mass ratio of -1 for
233 // all the particles hence eliminating the need to store mass as
234 // an attribute
235 static IpplTimings::TimerRef PTimer = IpplTimings::getTimer("pushVelocity");
236 static IpplTimings::TimerRef RTimer = IpplTimings::getTimer("pushPosition");
237 static IpplTimings::TimerRef updateTimer = IpplTimings::getTimer("update");
238 static IpplTimings::TimerRef domainDecomposition = IpplTimings::getTimer("loadBalance");
239 static IpplTimings::TimerRef SolveTimer = IpplTimings::getTimer("solve");
240
241 double alpha = this->alpha_m;
242 double Bext = this->Bext_m;
243 double DrInv = this->DrInv_m;
244 double V0 = 30 * this->length_m[2];
245 Vector_t<double, Dim> length = this->length_m;
246 Vector_t<double, Dim> origin = this->origin_m;
247 double dt = this->dt_m;
248 std::shared_ptr<ParticleContainer_t> pc = this->pcontainer_m;
249 std::shared_ptr<FieldContainer_t> fc = this->fcontainer_m;
250
252 auto Rview = pc->R.getView();
253 auto Pview = pc->P.getView();
254 auto Eview = pc->E.getView();
255 Kokkos::parallel_for(
256 "Kick1", pc->getLocalNum(), KOKKOS_LAMBDA(const size_t j) {
257 double Eext_x = -(Rview(j)[0] - origin[0] - 0.5 * length[0])
258 * (V0 / (2 * Kokkos::pow(length[2], 2)));
259 double Eext_y = -(Rview(j)[1] - origin[1] - 0.5 * length[1])
260 * (V0 / (2 * Kokkos::pow(length[2], 2)));
261 double Eext_z = (Rview(j)[2] - origin[2] - 0.5 * length[2])
262 * (V0 / (Kokkos::pow(length[2], 2)));
263
264 Eext_x += Eview(j)[0];
265 Eext_y += Eview(j)[1];
266 Eext_z += Eview(j)[2];
267
268 Pview(j)[0] += alpha * (Eext_x + Pview(j)[1] * Bext);
269 Pview(j)[1] += alpha * (Eext_y - Pview(j)[0] * Bext);
270 Pview(j)[2] += alpha * Eext_z;
271 });
272 Kokkos::fence();
273 ippl::Comm->barrier();
275
276 // drift
278 pc->R = pc->R + dt * pc->P;
280
281 // Since the particles have moved spatially update them to correct processors
282 IpplTimings::startTimer(updateTimer);
283 pc->update();
284 IpplTimings::stopTimer(updateTimer);
285
286 size_type totalP = this->totalP_m;
287 int it = this->it_m;
288 bool isFirstRepartition = false;
289 if (this->loadbalancer_m->balance(totalP, it + 1)) {
290 IpplTimings::startTimer(domainDecomposition);
291 auto* mesh = &fc->getRho().get_mesh();
292 auto* FL = &fc->getFL();
293 this->loadbalancer_m->repartition(FL, mesh, isFirstRepartition);
294 IpplTimings::stopTimer(domainDecomposition);
295 }
296
297 // scatter the charge onto the underlying grid
298 this->par2grid();
299
300 // Field solve
301 IpplTimings::startTimer(SolveTimer);
302 this->fsolver_m->runSolver();
303 IpplTimings::stopTimer(SolveTimer);
304
305 // gather E field
306 this->grid2par();
307
309 auto R2view = pc->R.getView();
310 auto P2view = pc->P.getView();
311 auto E2view = pc->E.getView();
312 Kokkos::parallel_for(
313 "Kick2", pc->getLocalNum(), KOKKOS_LAMBDA(const size_t j) {
314 double Eext_x = -(R2view(j)[0] - origin[0] - 0.5 * length[0])
315 * (V0 / (2 * Kokkos::pow(length[2], 2)));
316 double Eext_y = -(R2view(j)[1] - origin[1] - 0.5 * length[1])
317 * (V0 / (2 * Kokkos::pow(length[2], 2)));
318 double Eext_z = (R2view(j)[2] - origin[2] - 0.5 * length[2])
319 * (V0 / (Kokkos::pow(length[2], 2)));
320
321 Eext_x += E2view(j)[0];
322 Eext_y += E2view(j)[1];
323 Eext_z += E2view(j)[2];
324
325 P2view(j)[0] = DrInv
326 * (P2view(j)[0]
327 + alpha * (Eext_x + P2view(j)[1] * Bext + alpha * Bext * Eext_y));
328 P2view(j)[1] = DrInv
329 * (P2view(j)[1]
330 + alpha * (Eext_y - P2view(j)[0] * Bext - alpha * Bext * Eext_x));
331 P2view(j)[2] += alpha * Eext_z;
332 });
333 Kokkos::fence();
334 ippl::Comm->barrier();
336 }
337
338 void dump() override {
339 static IpplTimings::TimerRef dumpDataTimer = IpplTimings::getTimer("dumpData");
340 IpplTimings::startTimer(dumpDataTimer);
341 dumpData();
342 IpplTimings::stopTimer(dumpDataTimer);
343 }
344
345 void dumpData() {
346 auto Pview = this->pcontainer_m->P.getView();
347 double kinEnergy = 0.0;
348 double potEnergy = 0.0;
349 this->fcontainer_m->getRho() = dot(this->fcontainer_m->getE(), this->fcontainer_m->getE());
350 potEnergy = 0.5 * this->hr_m[0] * this->hr_m[1] * this->hr_m[2]
351 * this->fcontainer_m->getRho().sum();
352
353 Kokkos::parallel_reduce(
354 "Particle Kinetic Energy", this->pcontainer_m->getLocalNum(),
355 KOKKOS_LAMBDA(const int i, double& valL) {
356 double myVal = dot(Pview(i), Pview(i)).apply();
357 valL += myVal;
358 },
359 Kokkos::Sum<double>(kinEnergy));
360
361 kinEnergy *= 0.5;
362 double gkinEnergy = 0.0;
363
364 ippl::Comm->reduce(kinEnergy, gkinEnergy, 1, std::plus<double>());
365
366 const int nghostE = this->fcontainer_m->getE().getNghost();
367 auto Eview = this->fcontainer_m->getE().getView();
368 Vector_t<T, Dim> normE;
369
370 using index_array_type = typename ippl::RangePolicy<Dim>::index_array_type;
371 for (unsigned d = 0; d < Dim; ++d) {
372 T temp = 0.0;
374 "Vector E reduce", ippl::getRangePolicy(Eview, nghostE),
375 KOKKOS_LAMBDA(const index_array_type& args, T& valL) {
376 // ippl::apply accesses the view at the given indices and obtains a
377 // reference; see src/Expression/IpplOperations.h
378 T myVal = std::pow(ippl::apply(Eview, args)[d], 2);
379 valL += myVal;
380 },
381 Kokkos::Sum<T>(temp));
382 Kokkos::fence();
383 T globaltemp = 0.0;
384 ippl::Comm->reduce(temp, globaltemp, 1, std::plus<double>());
385
386 normE[d] = std::sqrt(globaltemp);
387 ippl::Comm->barrier();
388 }
389
390 if (ippl::Comm->rank() == 0) {
391 std::stringstream fname;
392 fname << "data/ParticleField_";
393 fname << ippl::Comm->size();
394 fname << "_manager";
395 fname << ".csv";
396
397 Inform csvout(NULL, fname.str().c_str(), Inform::APPEND);
398 csvout.precision(10);
399 csvout.setf(std::ios::scientific, std::ios::floatfield);
400
401 if (std::fabs(this->time_m) < 1e-14) {
402 csvout << "time, Potential energy, Kinetic energy, Total energy, Rho_norm2";
403 for (unsigned d = 0; d < Dim; d++) {
404 csvout << ", E" << static_cast<char>((Dim <= 3 ? 'x' : '1') + d) << "_norm2";
405 }
406 csvout << endl;
407 }
408
409 csvout << this->time_m << " " << potEnergy << " " << gkinEnergy << " "
410 << potEnergy + gkinEnergy << " " << this->rhoNorm_m << " ";
411 for (unsigned d = 0; d < Dim; d++) {
412 csvout << normE[d] << " ";
413 }
414 csvout << endl;
415 csvout.flush();
416 }
417 ippl::Comm->barrier();
418 }
419};
420#endif
typename ippl::detail::ViewType< ippl::Vector< double, Dim >, 1 >::view_type view_type
typename ippl::detail::ViewType< ippl::Vector< double, Dim >, 1 >::view_type view_type
constexpr unsigned Dim
ippl::detail::size_type size_type
Definition datatypes.h:23
const char * TestName
ippl::Vector< T, Dim > Vector_t
Definition datatypes.h:38
Inform & endl(Inform &inf)
Definition Inform.cpp:42
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) apply(const View &view, const Coords &coords)
RangePolicy< View::rank, typenameView::execution_space, PolicyArgs... >::policy_type getRangePolicy(const View &view, int shift=0)
void parallel_for(const std::string &name, const ExecPolicy &policy, const FunctorType &functor)
void parallel_reduce(const std::string &name, const ExecPolicy &policy, const FunctorType &functor, ReducerArgument &&... reducer)
std::unique_ptr< mpi::Communicator > Comm
Definition Ippl.h:22
std::size_t size_type
Definition IpplTypes.h:13
KOKKOS_INLINE_FUNCTION Vector< int, Dim > first() const
Definition NDIndex.hpp:170
void setParticleContainer(std::shared_ptr< ParticleContainer< T, Dim > > pcontainer)
Definition PicManager.h:55
A class for inverse transform sampling.
Functor to generate random numbers from a normal distribution.
Definition Randn.h:26
@ APPEND
Definition Inform.h:45
FmtFlags_t setf(FmtFlags_t setbits, FmtFlags_t field)
Definition Inform.h:101
void flush()
Definition Inform.h:113
int precision() const
Definition Inform.h:111
Timing::TimerRef TimerRef
static TimerRef getTimer(const char *nm)
static void stopTimer(TimerRef t)
static void startTimer(TimerRef t)
::ippl::Vector< index_type, Dim > index_array_type
void par2grid() override
Particle-to-grid operation.
Vector_t< double, Dim > origin_m
ippl::NDIndex< Dim > domain_m
Vector_t< int, Dim > nr_m
Vector_t< double, Dim > hr_m
Vector_t< double, Dim > rmin_m
const std::string & getSolver() const
bool isFirstRepartition_m
std::string stepMethod_m
std::array< bool, Dim > decomp_m
std::string solver_m
size_type totalP_m
Vector_t< double, Dim > rmax_m
AlpineManager(size_type totalP_, int nt_, Vector_t< int, Dim > &nr_, double lbt_, std::string &solver_, std::string &stepMethod_, std::vector< std::string > preconditioner_params={})
void grid2par() override
Grid-to-particle operation.
PenningTrapManager(size_type totalP_, int nt_, Vector_t< int, Dim > &nr_, double lbt_, std::string &solver_, std::string &stepMethod_)
FieldContainer< T, Dim > FieldContainer_t
ParticleContainer< T, Dim > ParticleContainer_t
void pre_run() override
A method that should be used for setting up the simulation.
LoadBalancer< T, Dim > LoadBalancer_t
PenningTrapManager(size_type totalP_, int nt_, Vector_t< int, Dim > &nr_, double lbt_, std::string &solver_, std::string &stepMethod_, std::vector< std::string > preconditioner_params_)
void advance() override
A method that should be used to execute/advance a step of simulation.
FieldSolver< T, Dim > FieldSolver_t
Vector_t< double, Dim > length_m