OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
Multipole.cpp
Go to the documentation of this file.
1
22#include "PartBunch/PartBunch.h"
24
25// Unused Headers
26#include "Fields/Fieldmap.h"
27#include "Physics/Physics.h"
28
29// orders
30namespace {
32}
33
34// GPU factorial
35namespace {
36 KOKKOS_INLINE_FUNCTION
37 double factorial(unsigned int n) {
38 if (n == 0) return 1.0;
39 if (n == 1) return 1.0;
40 if (n == 2) return 2.0;
41 if (n == 3) return 6.0;
42 if (n == 4) return 24.0;
43 if (n == 5) return 120.0;
44 if (n > 5) Kokkos::abort("factorial out of bounds");
45 return 0;
46 }
47} // namespace
48
49/* ============================== Constructors ============================== */
51
58 : Component(right),
59 NormalComponents("Multipole::Normal", right.NormalComponents.extent(0)),
60 NormalComponentErrors("Multipole::NormalErr", right.NormalComponentErrors.extent(0)),
61 SkewComponents("Multipole::Skew", right.SkewComponents.extent(0)),
62 SkewComponentErrors("Multipole::SkewErr", right.SkewComponentErrors.extent(0)),
63
64 max_SkewComponent_m(right.max_SkewComponent_m),
65 max_NormalComponent_m(right.max_NormalComponent_m),
66
67 nSlices_m(right.nSlices_m) {
68 Kokkos::deep_copy(NormalComponents, right.NormalComponents);
69 Kokkos::deep_copy(NormalComponentErrors, right.NormalComponentErrors);
70 Kokkos::deep_copy(SkewComponents, right.SkewComponents);
71 Kokkos::deep_copy(SkewComponentErrors, right.SkewComponentErrors);
72}
73
79Multipole::Multipole(const std::string& name)
80 : Component(name),
81 NormalComponents("Multipole::Normal", 1),
82 NormalComponentErrors("Multipole::NormalErr", 1),
83 SkewComponents("Multipole::Skew", 1),
84 SkewComponentErrors("Multipole::SkewErr", 1),
85 max_SkewComponent_m(1),
86 max_NormalComponent_m(1),
87 nSlices_m(1) {
88 // Initialize with 0.0
89 Kokkos::deep_copy(NormalComponents, 0.0);
90 Kokkos::deep_copy(SkewComponents, 0.0);
91}
92
94/* ========================================================================== */
95/* =========================== Multipole Expansion ========================== */
96
97// @note n=0 corresponds to the dipol, n=1 to the quadrupole, etc...
98
109double Multipole::getNormalComponent(int n) const {
110 if (n < 0) {
112 "Multipole::getNormalComponent",
113 "component index " + std::to_string(n) + " out of bounds");
114 } else if (n < max_NormalComponent_m) {
115 double val;
116 Kokkos::deep_copy(val, Kokkos::subview(NormalComponents, n));
117 return val;
118 } else {
119 return 0.0;
120 }
121}
122
133double Multipole::getSkewComponent(int n) const {
134 if (n < 0) {
136 "Multipole::getSkewComponent",
137 "component index " + std::to_string(n) + " out of bounds");
138 } else if (n < max_SkewComponent_m) {
139 double val;
140 Kokkos::deep_copy(val, Kokkos::subview(SkewComponents, n));
141 return val;
142 } else {
143 return 0.0;
144 }
145}
146
158void Multipole::setNormalComponent(int n, double v, double vError) {
159 if (n < 0) {
161 "Multipole::setNormalComponent",
162 "component index " + std::to_string(n) + " out of bounds");
163 }
164
165 if (n >= max_NormalComponent_m) {
166 max_NormalComponent_m = n + 1;
169 }
170
171 double valComp, valErr;
172
173 // TODO: Check the physics here (copied from OPAL)
174 if (n == DIPOLE) {
175 valComp = (v + vError) / 2.0;
176 valErr = valComp;
177 } else {
178 double fact = factorial(n);
179 valComp = (v + vError) / fact;
180 valErr = vError / fact;
181 }
182
183 // Copy to Device
184 auto sub_comp = Kokkos::subview(NormalComponents, n);
185 auto sub_err = Kokkos::subview(NormalComponentErrors, n);
186
187 Kokkos::deep_copy(sub_comp, valComp);
188 Kokkos::deep_copy(sub_err, valErr);
189}
190
202void Multipole::setSkewComponent(int n, double v, double vError) {
203 if (n < 0) {
205 "Multipole::setSkewComponent",
206 "component index " + std::to_string(n) + " out of bounds");
207 }
208
209 if (n >= max_SkewComponent_m) {
210 max_SkewComponent_m = n + 1;
211 Kokkos::resize(SkewComponents, max_SkewComponent_m);
213 }
214
215 double valComp, valErr;
216 // TODO: Check physics (copied from OPAL)
217 if (n == DIPOLE) {
218 valComp = (v + vError) / 2.0;
219 valErr = valComp;
220 } else {
221 double fact = factorial(n);
222 valComp = (v + vError) / fact;
223 valErr = vError / fact;
224 }
225
226 auto sub_comp = Kokkos::subview(SkewComponents, n);
227 auto sub_err = Kokkos::subview(SkewComponentErrors, n);
228
229 Kokkos::deep_copy(sub_comp, valComp);
230 Kokkos::deep_copy(sub_err, valErr);
231}
232
233/* ========================================================================== */
234/* ============================== Apply Functions =========================== */
235
245bool Multipole::apply(const std::shared_ptr<ParticleContainer_t>& pc) {
246 *gmsg << level3 << "Multipole::apply() called" << endl;
247 // Get the views
248 auto Rview = pc->R.getView();
249 auto Eview = pc->E.getView();
250 auto Bview = pc->B.getView();
251 const size_t nLocal = pc->getLocalNum();
252
253 // Local variables that are copied into the kernel
254 double elemLength = getElementLength();
255
256 // Capture member variables by value for the kernel
257 auto normalComponents = NormalComponents;
258 auto skewComponents = SkewComponents;
259 int maxNormal = max_NormalComponent_m;
260 int maxSkew = max_SkewComponent_m;
261
262 // Kernel launch over all particles
263 Kokkos::parallel_for(
264 "Multipole::apply()", nLocal, KOKKOS_LAMBDA(const size_t i) {
265 // Check bounds
266 if (Rview(i)(2) > 0 && Rview(i)(2) <= elemLength) {
267 Vector_t<double, 3> Ef(0.0), Bf(0.0);
268 // Compute field at particle position
270 Rview(i), Ef, Bf, normalComponents, skewComponents, maxNormal, maxSkew);
271 for (unsigned d = 0; d < 3; ++d) {
272 Eview(i)(d) += Ef(d);
273 Bview(i)(d) += Bf(d);
274 }
275 }
276 });
277 return false;
278}
279
291 const size_t& i, const double&, Vector_t<double, 3>& E, Vector_t<double, 3>& B) {
292 // Get container
293 std::shared_ptr<ParticleContainer_t> pc = RefPartBunch_m->getParticleContainer();
294 auto Rview = pc->R.getView();
295 const Vector_t<double, 3> R = Rview(i);
296
297 // Check bounds
298 if (R(2) < 0.0 || R(2) > getElementLength()) return false;
300
301 // Compute fields
302 Vector_t<double, 3> Ef(0.0), Bf(0.0);
303 computeFieldHost(R, Ef, Bf);
304
305 // Apply fields
306 for (unsigned int d = 0; d < 3; ++d) {
307 E[d] += Ef(d);
308 B[d] += Bf(d);
309 }
310
311 return false;
312}
313
326 const Vector_t<double, 3>& R, const Vector_t<double, 3>&, const double&,
328 // Check bounds
329 if (R(2) < 0.0 || R(2) > getElementLength()) return false;
331
332 // Compute field
333 computeFieldHost(R, E, B);
334
335 return false;
336}
337
350 const Vector_t<double, 3>& R, const Vector_t<double, 3>&, const double&,
352 // Check bounds
353 if (R(2) < 0.0 || R(2) > getElementLength()) return false;
354 if (!isInsideTransverse(R)) return true;
355
356 /*
357 for (int i = 0; i < max_NormalComponent_m; ++i) {
358 NormalComponents[i] -= NormalComponentErrors[i];
359 }
360 for (int i = 0; i < max_SkewComponent_m; ++i) {
361 SkewComponents[i] -= SkewComponentErrors[i];
362 }
363 */
364
365 // Compute field
366 computeFieldHost(R, E, B);
367
368 /*
369 for (int i = 0; i < max_NormalComponent_m; ++i) {
370 NormalComponents[i] += NormalComponentErrors[i];
371 }
372 for (int i = 0; i < max_SkewComponent_m; ++i) {
373 SkewComponents[i] += SkewComponentErrors[i];
374 }
375 */
376
377 return false;
378}
379/* ========================================================================== */
380/* ============================== Functions ================================= */
386void Multipole::accept(BeamlineVisitor& visitor) const { visitor.visitMultipole(*this); }
387
404KOKKOS_INLINE_FUNCTION
407 const Kokkos::View<double*> NormalComponents, const Kokkos::View<double*> SkewComponents,
408 int max_NormalComponent, int max_SkewComponent) {
409 // Use primitive double arrays instead of Vector_t to avoid constructor/destructor calls on GPU
410 // stack
411 double Rn_x[MAX_MP_ORDER + 1];
412 double Rn_y[MAX_MP_ORDER + 1];
413 double fact[MAX_MP_ORDER + 1];
414
415 // --- NORMAL COMPONENTS ---
416 {
417 Rn_x[0] = 1.0;
418 Rn_y[0] = 1.0; // Equivalent to Vector_t(1.0)
419 fact[0] = 1.0;
420
421 // Use local variable for loop bound to help optimizer
422 int count = (max_NormalComponent > MAX_MP_ORDER) ? MAX_MP_ORDER : max_NormalComponent;
423
424 for (int i = 0; i < count; ++i) {
425 // Access Kokkos::View directly (device memory)
426 double norm = NormalComponents(i);
427
428 switch (i) {
429 case DIPOLE:
430 B(1) += norm;
431 break;
432
433 case QUADRUPOLE:
434 B(0) += norm * R(1);
435 B(1) += norm * R(0);
436 break;
437 }
438
439 Rn_x[i + 1] = Rn_x[i] * R(0);
440 Rn_y[i + 1] = Rn_y[i] * R(1);
441 fact[i + 1] = fact[i] / (double)(i + 1);
442 }
443 }
444
445 // --- SKEW COMPONENTS ---
446 {
447 Rn_x[0] = 1.0;
448 Rn_y[0] = 1.0;
449 fact[0] = 1.0;
450
451 int count = (max_SkewComponent > MAX_MP_ORDER) ? MAX_MP_ORDER : max_SkewComponent;
452
453 for (int i = 0; i < count; ++i) {
454 double skew = SkewComponents(i);
455
456 switch (i) {
457 case DIPOLE:
458 B(0) -= skew;
459 break;
460
461 case QUADRUPOLE:
462 B(0) -= skew * R(0);
463 B(1) += skew * R(1);
464 break;
465 }
466
467 Rn_x[i + 1] = Rn_x[i] * R(0);
468 Rn_y[i + 1] = Rn_y[i] * R(1);
469 fact[i + 1] = fact[i] / (double)(i + 1);
470 }
471 }
472}
473
486 double fact[MAX_MP_ORDER + 1];
487
488 auto NormalComponents_host = Kokkos::create_mirror_view(NormalComponents);
489 Kokkos::deep_copy(NormalComponents_host, NormalComponents);
490
491 auto SkewComponents_host = Kokkos::create_mirror_view(SkewComponents);
492 Kokkos::deep_copy(SkewComponents_host, SkewComponents);
493
494 // --- NORMAL COMPONENTS ---
495 {
496 Rn[0] = Vector_t<double, 3>(1.0);
497 fact[0] = 1.0;
498
499 // Use local variable for loop bound to help optimizer
501
502 for (int i = 0; i < count; ++i) {
503 // Access Kokkos::View directly (device memory)
504 double norm = NormalComponents_host(i);
505
506 switch (i) {
507 case DIPOLE:
508 B(1) += norm;
509 break;
510
511 case QUADRUPOLE:
512 B(0) += norm * R(1);
513 B(1) += norm * R(0);
514 break;
515
516 case SEXTUPOLE:
517 B(0) += 2 * norm * R(0) * R(1);
518 B(1) += norm * (Rn[2](0) - Rn[2](1));
519 break;
520
521 case OCTUPOLE:
522 B(0) += norm * (3 * Rn[2](0) * Rn[1](1) - Rn[3](1));
523 B(1) += norm * (Rn[3](0) - 3 * Rn[1](0) * Rn[2](1));
524 break;
525
526 case DECAPOLE:
527 B(0) += 4 * norm * (Rn[3](0) * Rn[1](1) - Rn[1](0) * Rn[3](1));
528 B(1) += norm * (Rn[4](0) - 6 * Rn[2](0) * Rn[2](1) + Rn[4](1));
529 break;
530
531 default: {
532 double powMinusOne = 1.0;
533 double Bx = 0.0, By = 0.0;
534 for (int j = 1; j <= (i + 1) / 2; ++j) {
535 Bx += powMinusOne * norm
536 * (Rn[i - 2 * j + 1](0) * fact[i - 2 * j + 1] * Rn[2 * j - 1](1)
537 * fact[2 * j - 1]);
538 By += powMinusOne * norm
539 * (Rn[i - 2 * j + 2](0) * fact[i - 2 * j + 2] * Rn[2 * j - 2](1)
540 * fact[2 * j - 2]);
541 powMinusOne *= -1.0;
542 }
543
544 if ((i + 1) / 2 == i / 2) {
545 int j = (i + 2) / 2;
546 By += powMinusOne * norm
547 * (Rn[i - 2 * j + 2](0) * fact[i - 2 * j + 2] * Rn[2 * j - 2](1)
548 * fact[2 * j - 2]);
549 }
550 B(0) += Bx;
551 B(1) += By;
552 }
553 }
554
555 Rn[i + 1](0) = Rn[i](0) * R(0);
556 Rn[i + 1](1) = Rn[i](1) * R(1);
557 fact[i + 1] = fact[i] / (double)(i + 1);
558 }
559 }
560
561 // --- SKEW COMPONENTS ---
562 {
563 Rn[0] = Vector_t<double, 3>(1.0);
564 fact[0] = 1.0;
565
567
568 for (int i = 0; i < count; ++i) {
569 double skew = SkewComponents_host(i);
570
571 switch (i) {
572 case DIPOLE:
573 B(0) -= skew;
574 break;
575
576 case QUADRUPOLE:
577 B(0) -= skew * R(0);
578 B(1) += skew * R(1);
579 break;
580
581 case SEXTUPOLE:
582 B(0) -= skew * (Rn[2](0) - Rn[2](1));
583 B(1) += 2 * skew * R(0) * R(1);
584 break;
585
586 case OCTUPOLE:
587 B(0) -= skew * (Rn[3](0) - 3 * Rn[1](0) * Rn[2](1));
588 B(1) += skew * (3 * Rn[2](0) * Rn[1](1) - Rn[3](1));
589 break;
590
591 case DECAPOLE:
592 B(0) -= skew * (Rn[4](0) - 6 * Rn[2](0) * Rn[2](1) + Rn[4](1));
593 B(1) += 4 * skew * (Rn[3](0) * Rn[1](1) - Rn[1](0) * Rn[3](1));
594 break;
595
596 default: {
597 double powMinusOne = 1.0;
598 double Bx = 0.0, By = 0.0;
599 for (int j = 1; j <= (i + 1) / 2; ++j) {
600 Bx -= powMinusOne * skew
601 * (Rn[i - 2 * j + 2](0) * fact[i - 2 * j + 2] * Rn[2 * j - 2](1)
602 * fact[2 * j - 2]);
603 By += powMinusOne * skew
604 * (Rn[i - 2 * j + 1](0) * fact[i - 2 * j + 1] * Rn[2 * j - 1](1)
605 * fact[2 * j - 1]);
606 powMinusOne *= -1.0;
607 }
608
609 if ((i + 1) / 2 == i / 2) {
610 int j = (i + 2) / 2;
611 Bx -= powMinusOne * skew
612 * (Rn[i - 2 * j + 2](0) * fact[i - 2 * j + 2] * Rn[2 * j - 2](1)
613 * fact[2 * j - 2]);
614 }
615
616 B(0) += Bx;
617 B(1) += By;
618 }
619 }
620
621 Rn[i + 1](0) = Rn[i](0) * R(0);
622 Rn[i + 1](1) = Rn[i](1) * R(1);
623 fact[i + 1] = fact[i] / (double)(i + 1);
624 }
625 }
626}
634void Multipole::initialise(PartBunch_t* bunch, double& startField, double& endField) {
635 RefPartBunch_m = bunch;
636 endField = startField + getElementLength();
637 online_m = true;
638}
639
640void Multipole::finalise() { online_m = false; }
641
642bool Multipole::bends() const { return false; }
643
644void Multipole::getFieldExtend(double& zBegin, double& zEnd) const {
645 zBegin = 0.0;
646 zEnd = getElementLength();
647}
648
650
652 if (r(2) >= 0.0 && r(2) < getElementLength()) {
653 return isInsideTransverse(r);
654 }
655
656 return false;
657}
658
659bool Multipole::isFocusing(int component) const {
660 if (component < 0)
661 throw GeneralOpalException("Multipole::isFocusing", "component negative");
662 else if (static_cast<unsigned long>(component) >= NormalComponents.extent(0))
663 throw GeneralOpalException("Multipole::isFocusing", "component too big");
664
665 // Fix: Use getNormalComponent() to safely retrieve the value from GPU memory
666 return getNormalComponent(component) * std::pow(-1, component + 1)
667 * RefPartBunch_m->getParticleContainer()->getChargePerParticle()
668 > 0.0;
669}
670
671/* ========================================================================== */
672/* =========================== Unused Functions ============================= */
673
674// set the number of slices for map tracking
675void Multipole::setNSlices(const std::size_t& nSlices) { nSlices_m = nSlices; }
676
677// get the number of slices for map tracking
678std::size_t Multipole::getNSlices() const { return nSlices_m; }
679/* ========================================================================== */
Inform * gmsg
Definition changes.cpp:7
ippl::Vector< T, Dim > Vector_t
ElementType
Definition ElementBase.h:94
@ OCTUPOLE
Definition IndexMap.cpp:172
@ DIPOLE
Definition IndexMap.cpp:169
@ DECAPOLE
Definition IndexMap.cpp:173
@ SEXTUPOLE
Definition IndexMap.cpp:171
@ QUADRUPOLE
Definition IndexMap.cpp:170
constexpr int MAX_MP_ORDER
Definition Multipole.h:9
Template PIC bunch: IPPL PicManager, shared field mesh/solver, and multiple particle containers.
virtual void visitMultipole(const Multipole &)=0
Apply the algorithm to a multipole.
bool online_m
Definition Component.h:226
PartBunch_t * RefPartBunch_m
Definition Component.h:225
bool getFlagDeleteOnTransverseExit() const
virtual double getElementLength() const
Get design length.
bool isInsideTransverse(const Vector_t< double, 3 > &r) const
Interface for general multipole.
Definition Multipole.h:30
void setSkewComponent(int n, double)
Definition Multipole.h:216
virtual void accept(BeamlineVisitor &) const override
Accepts a BeamlineVisitor.
int max_NormalComponent_m
Definition Multipole.h:205
void setNormalComponent(int n, double)
Definition Multipole.h:214
virtual void initialise(PartBunch_t *bunch, double &startField, double &endField) override
Setup, multipole goes online.
bool isFocusing(int n) const
virtual bool applyToReferenceParticle(const Vector_t< double, 3 > &R, const Vector_t< double, 3 > &P, const double &t, Vector_t< double, 3 > &E, Vector_t< double, 3 > &B) override
Apply to reference particle with position R and momemtum P.
virtual void finalise() override
void setNSlices(const std::size_t &nSlices)
virtual bool bends() const override
void computeFieldHost(Vector_t< double, 3 > R, Vector_t< double, 3 > &E, Vector_t< double, 3 > &B) const
Computes the E and B field at position R of the reference particle.
Kokkos::View< double * > SkewComponents
Definition Multipole.h:202
virtual ElementType getType() const override
Get element type std::string.
virtual bool isInside(const Vector_t< double, 3 > &r) const override
virtual bool apply(const std::shared_ptr< ParticleContainer_t > &pc) override
Apply to all particles. Kernel launch moved inside the function.
Kokkos::View< double * > SkewComponentErrors
Definition Multipole.h:203
Kokkos::View< double * > NormalComponentErrors
Definition Multipole.h:201
Kokkos::View< double * > NormalComponents
Definition Multipole.h:200
static KOKKOS_INLINE_FUNCTION void computeField(Vector_t< double, 3 > R, Vector_t< double, 3 > &E, Vector_t< double, 3 > &B, const Kokkos::View< double * > NormalComponents, const Kokkos::View< double * > SkewComponents, int max_NormalComponent, int max_SkewComponent)
Computes the E and B field at position R.
double getSkewComponent(int n) const
Gets the n-th skew component of the multipole expansion.
int max_SkewComponent_m
Definition Multipole.h:204
virtual void getFieldExtend(double &zBegin, double &zEnd) const override
Return the field-support extent of the component.
std::size_t getNSlices() const
virtual ~Multipole()
Definition Multipole.cpp:93
double getNormalComponent(int n) const
Gets the n-th normal component of the multipole expansion.
std::size_t nSlices_m
Definition Multipole.h:207
Vector_t< double, Dim > R(size_t)
Do not use; throws (access positions via ParticleContainer::R).
Definition PartBunch.h:611
double getChargePerParticle(size_t containerIndex=0) const
Get the charge per particle for a given particle container.
Definition PartBunch.h:406