ImpactX
Loading...
Searching...
No Matches
ExactCFbend.H
Go to the documentation of this file.
1/* Copyright 2022-2026 The Regents of the University of California, through Lawrence
2 * Berkeley National Laboratory (subject to receipt of any required
3 * approvals from the U.S. Dept. of Energy). All rights reserved.
4 *
5 * This file is part of ImpactX.
6 *
7 * Authors: Chad Mitchell, Axel Huebl
8 * License: BSD-3-Clause-LBNL
9 */
10#ifndef IMPACTX_EXACTCFBEND_H
11#define IMPACTX_EXACTCFBEND_H
12
15#include "mixin/alignment.H"
16#include "mixin/beamoptic.H"
17#include "mixin/dynamicdata.H"
19#include "mixin/spintransport.H"
20#include "mixin/named.H"
21#include "mixin/nofinalize.H"
22#include "mixin/pipeaperture.H"
23#include "mixin/thick.H"
24#include "mixin/TrackedVector.H"
25
26#include <AMReX_Extension.H>
27#include <AMReX_REAL.H>
28#include <AMReX_Print.H>
29#include <AMReX_GpuComplex.H>
30#include <AMReX_Math.H>
31
32#include <cmath>
33#include <memory>
34#include <stdexcept>
35#include <vector>
36
37namespace impactx::elements
38{
39
50
52 : public mixin::Named,
53 public mixin::BeamOptic<ExactCFbend>,
54 public mixin::LinearTransport<ExactCFbend>,
55 public mixin::Thick,
56 public mixin::Alignment,
57 public mixin::NoFinalize,
60 {
61 static constexpr auto type = "ExactCFbend";
63
65
93 std::vector<amrex::ParticleReal> k_normal,
94 std::vector<amrex::ParticleReal> k_skew,
95 int unit,
98 amrex::ParticleReal rotation_degree = 0,
101 int int_order = 2,
102 int mapsteps = 1,
103 int nslice = 1,
104 std::optional<std::string> name = std::nullopt
105 )
106 : Named(std::move(name)),
107 Thick(ds, nslice),
108 Alignment(dx, dy, rotation_degree),
110 m_unit(unit), m_int_order(int_order), m_mapsteps(mapsteps),
111 m_id(DynamicData::allocate_id())
112 {
113 if (m_int_order != 2 && m_int_order != 4 && m_int_order != 6)
114 throw std::runtime_error("ExactCFbend: The order used for symplectic integration must be 2, 4 or 6.");
115
116 m_ncoef = int(k_normal.size());
117 if (m_ncoef != int(k_skew.size()))
118 throw std::runtime_error("ExactCFbend: normal and skew coefficient arrays must have same length!");
119
120 auto& coef = DynamicData::emplace(
121 m_id,
122 std::move(k_normal),
123 std::move(k_skew)
124 );
125 m_k_normal_h_data = coef.k_normal.host_const().data();
126 m_k_skew_h_data = coef.k_skew.host_const().data();
127 }
128
130 void reverse () { Thick::reverse(); }
131
133 using BeamOptic::operator();
134
142 void compute_constants (RefPart const & refpart)
143 {
144 using namespace amrex::literals; // for _rt and _prt
145
146 Alignment::compute_constants(refpart);
147
148 // Ensure dynamic coefficient data is on GPU and we have fresh pointers to it
149 auto const & coef = *DynamicData::get(m_id);
150 m_k_normal_d_data = coef.k_normal.device_const().data();
151 m_k_skew_d_data = coef.k_skew.device_const().data();
152
153 // access reference particle values to find relativistic factors
154 m_beta = refpart.beta();
155 m_ibeta = 1_prt / m_beta;
156 m_ibetgam2 = 1_prt / amrex::Math::powi<2>(refpart.beta_gamma());
157 m_brho = refpart.rigidity_Tm();
158
159 amrex::ParticleReal const * k_normal = m_k_normal_h_data;
160
161 // find magnetic field and curvature; normalize units to MAD-X convention if needed
162 amrex::ParticleReal curvature = m_unit == 1 ? k_normal[0] / m_brho : k_normal[0];
163 m_rc = curvature == 0.0 ? 0.0_prt : 1.0_prt/curvature;
164
165 // arc length and angle of the current slice
166 m_slice_ds = m_ds / nslice();
167 }
168
190 [[maybe_unused]] uint64_t const & AMREX_RESTRICT idcpu,
191 RefPart const & AMREX_RESTRICT refpart
192 ) const
193 {
194 using namespace amrex::literals; // for _rt and _prt
195
196 // numerical integration parameters
197 amrex::ParticleReal const zin = 0_prt;
198 amrex::ParticleReal const zout = m_slice_ds;
199 int const nsteps = m_mapsteps;
200
201 // initialize phase space 6-vector
203 x, px, y, py, t, pt
204 };
205
206 // call integrator to advance through slice (int_order = 2 or 4, otherwise default 2)
207 if (m_int_order == 2) {
208 integrators::symp2_integrate_particle(particle,zin,zout,nsteps,refpart,*this);
209 } else if (m_int_order == 4) {
210 integrators::symp4_integrate_particle(particle,zin,zout,nsteps,refpart,*this);
211 } else if (m_int_order == 6) {
212 integrators::symp6_integrate_particle(particle,zin,zout,nsteps,refpart,*this);
213 }
214
215 // assign updated values
216 x = particle(1);
217 px = particle(2);
218 y = particle(3);
219 py = particle(4);
220 t = particle(5);
221 pt = particle(6);
222 }
223
234 void map1 (amrex::ParticleReal const tau,
236 amrex::ParticleReal & zeval,
237 [[maybe_unused]] RefPart const & AMREX_RESTRICT refpart) const
238 {
239 using namespace amrex::literals; // for _rt and _prt
240 using namespace amrex::Math; // for powi
241
242 amrex::ParticleReal const x = particle(1);
243 amrex::ParticleReal const px = particle(2);
244 amrex::ParticleReal const y = particle(3);
245 amrex::ParticleReal const py = particle(4);
246 amrex::ParticleReal const t = particle(5);
247 amrex::ParticleReal const pt = particle(6);
248
249 amrex::ParticleReal xout = x;
250 amrex::ParticleReal pxout = px;
251 amrex::ParticleReal yout = y;
252 amrex::ParticleReal pyout = py;
253 amrex::ParticleReal tout = t;
254 amrex::ParticleReal ptout = pt;
255
256 // treat the special case of zero field
257 if (m_rc==0_prt) {
258 // compute the radical in the denominator (= pz):
259 amrex::ParticleReal const inv_pzden = 1_prt / std::sqrt(
260 powi<2>(pt - m_ibeta) -
261 m_ibetgam2 -
262 powi<2>(px) -
263 powi<2>(py)
264 );
265
266 // advance position and momentum (exact drift)
267 xout = x + tau * px * inv_pzden;
268 yout = y + tau * py * inv_pzden;
269 tout = t - tau * (m_ibeta + (pt - m_ibeta) * inv_pzden);
270
271 // assign updated momenta
272 pxout = px;
273 pyout = py;
274 ptout = pt;
275
276 } else {
277
278 // assign intermediate quantities
279 amrex::ParticleReal const pperp2 = powi<2>(pt)-2.0_prt * m_ibeta * pt - powi<2>(py)+1.0_prt;
280 amrex::ParticleReal const px2 = powi<2>(px);
281
282 // trigonometric evaluations
283 amrex::ParticleReal const slice_phi = tau / m_rc;
284 auto const [sin_phi, cos_phi] = amrex::Math::sincos(slice_phi);
285
286 // determine if particle lies within the domain of map definition
287 if (pperp2 > px2)
288 {
289 amrex::ParticleReal const pperp = std::sqrt(pperp2);
290 amrex::ParticleReal const pzi = std::sqrt(powi<2>(pperp) - powi<2>(px));
291 amrex::ParticleReal const rho = m_rc + x;
292
293 // update momenta
294 pxout = px * cos_phi + (pzi - rho / m_rc) * sin_phi;
295 pyout = py;
296 ptout = pt;
297
298 // angle of momentum rotation
299 amrex::ParticleReal const px2f = powi<2>(pxout);
300 // determine if particle lies within domain of map definition
301 if (pperp2 > px2f)
302 {
303 amrex::ParticleReal const pzf = std::sqrt(powi<2>(pperp)-powi<2>(pxout));
304 amrex::ParticleReal const theta = slice_phi + std::asin(px/pperp) - std::asin(pxout/pperp);
305
306 // update position coordinates
307 xout = -m_rc + rho*cos_phi + m_rc*(pzf + px*sin_phi - pzi*cos_phi);
308 yout = y + theta * m_rc * py;
309 tout = t - theta * m_rc * (pt - 1.0_prt * m_ibeta) - slice_phi * m_rc * m_ibeta;
310
311 } // else { amrex::Print() << "Warning: outside map domain." << "\n"; }
312 } // else { amrex::Print() << "Warning: outside map domain." << "\n"; }
313 }
314
315 // push particle coordinates
316 particle(1) = xout;
317 particle(2) = pxout;
318 particle(3) = yout;
319 particle(4) = pyout;
320 particle(5) = tout;
321 particle(6) = ptout;
322
323 zeval += 0_prt;
324 }
325
337 void map2 (amrex::ParticleReal const tau,
339 amrex::ParticleReal & zeval,
340 [[maybe_unused]] RefPart const & AMREX_RESTRICT refpart) const
341 {
342 using namespace amrex::literals; // for _rt and _prt
343 using namespace amrex::Math; // for powi
344
345 amrex::ParticleReal const x = particle(1);
346 amrex::ParticleReal const px = particle(2);
347 amrex::ParticleReal const y = particle(3);
348 amrex::ParticleReal const py = particle(4);
349 amrex::ParticleReal const t = particle(5);
350 amrex::ParticleReal const pt = particle(6);
351
352 amrex::ParticleReal xout = x;
353 amrex::ParticleReal pxout = px;
354 amrex::ParticleReal yout = y;
355 amrex::ParticleReal pyout = py;
356 amrex::ParticleReal tout = t;
357 amrex::ParticleReal ptout = pt;
358
359 amrex::ParticleReal const * k_normal = m_k_normal_d_data;
360 amrex::ParticleReal const * k_skew = m_k_skew_d_data;
361
362 // defined dimensionless variables scaled by radius of curvature
363 amrex::ParticleReal rho = 1_prt + x / m_rc;
364 amrex::ParticleReal y_scl = y / m_rc;
365 amrex::ParticleReal ln_rho = std::log(rho);
366
367 // initialize the momentum kick
368 amrex::ParticleReal dpx = 0_prt;
369 amrex::ParticleReal dpy = 0_prt;
370
371 // loop over array of multipole coefficients
372 for (int m = 1; m < m_ncoef; m++) {
373
374 // normalize units to MAD-X convention if needed
375 amrex::ParticleReal kn = m_unit == 1 ? k_normal[m] / m_brho : k_normal[m];
376 amrex::ParticleReal ks = m_unit == 1 ? k_skew[m] / m_brho : k_skew[m];
377
378 int n = m+1; // multipole order
379
380 // define dimensionless multipole coefficients (scaled by radius of curvature)
381 kn = std::pow(m_rc, static_cast<amrex::ParticleReal>(m)) * kn;
382 ks = std::pow(m_rc, static_cast<amrex::ParticleReal>(m)) * ks;
383
384 switch (n) {
385
386 case 1: // dipole-like contribution (not used - contributing to map 1)
387 dpx += -rho * kn;
388 dpy += ks;
389 break;
390 case 2: // quadrupole-like contribution
391 dpx += -rho*ln_rho*kn + y_scl*rho*ks;
392 dpy += y_scl*kn + 0.5_prt*(rho*rho - 1_prt)*ks;
393 break;
394 case 3: // sextupole-like contribution
395 dpx += 0.25_prt*rho*(1_prt + 2_prt*y_scl*y_scl - rho*rho + 2_prt*ln_rho)*kn + y_scl*rho*ln_rho*ks;
396 dpy += 0.5_prt*y_scl*(rho*rho - 1_prt)*kn + 0.25_prt*(1_prt - 2_prt*y_scl*y_scl - rho*rho + 2_prt*rho*rho*ln_rho)*ks;
397 break;
398 case 4: // octupole-like contribution
399 dpx += -0.25_prt*(rho - powi<3>(rho) + (rho - 2_prt*y_scl*y_scl*rho + powi<3>(rho))*ln_rho)*kn
400 -1_prt/12_prt*y_scl*rho*(3_prt + 2_prt*y_scl*y_scl - 3_prt*rho*rho + 6_prt*ln_rho)*ks;
401 dpy += -1_prt/12_prt*y_scl*(-3_prt + 2_prt*y_scl*y_scl + 3_prt*rho*rho - 6_prt*rho*rho*ln_rho)*kn
402 -1_prt/16_prt*((-1_prt + 4_prt*y_scl*y_scl - rho*rho)*(-1_prt + rho*rho) + 4_prt*rho*rho*ln_rho)*ks;
403 break;
404 case 5: // decapole-like contribution
405 dpx += -1_prt/192_prt*rho*(8_prt*powi<4>(y_scl) - 24_prt*powi<2>(y_scl)*(-1_prt + rho*rho)
406 + 3_prt*(-5_prt + 4_prt*powi<2>(rho) + powi<4>(rho))
407 +12_prt*(-1_prt + 4_prt*y_scl*y_scl - 2_prt*rho*rho)*ln_rho)*kn
408 +1_prt/12_prt*y_scl*rho*(3_prt*(-1_prt + rho*rho) + (2_prt*y_scl*y_scl - 3_prt*(1_prt + rho*rho))*ln_rho)*ks;
409 dpy += -1_prt/48_prt*y_scl*((-1_prt + rho*rho)*(4_prt*y_scl*y_scl - 3_prt*(1_prt + rho*rho)) + 12_prt*rho*rho*ln_rho)*kn
410 + 1_prt/192_prt*(3_prt + 8_prt*powi<4>(y_scl) + 12_prt*rho*rho
411 - 15_prt*powi<4>(rho) + 24_prt*y_scl*y_scl*(-1_prt + rho*rho)
412 +12_prt*rho*rho*(2_prt - 4_prt*y_scl*y_scl + rho*rho)*ln_rho)*ks;
413 break;
414 // default:
415 // TODO: print a warning here that order > 5 is not currently supported
416 }
417
418 }
419
420 // advance momentum by a step of length tau
421 pxout = pxout + tau * dpx;
422 pyout = pyout + tau * dpy;
423
424 //update the particle coordinates
425 particle(1) = xout;
426 particle(2) = pxout;
427 particle(3) = yout;
428 particle(4) = pyout;
429 particle(5) = tout;
430 particle(6) = ptout;
431
432 zeval += tau;
433 }
434
435
445 template<typename T_Real>
447 void map3 (amrex::ParticleReal const tau,
449 amrex::SmallVector<T_Real, 3, 1> & particle_spin,
450 amrex::ParticleReal & zeval,
451 RefPart const & AMREX_RESTRICT refpart) const
452 {
453 using namespace amrex::literals; // for _rt and _prt
454 using namespace std; // for cmath(float)
455 using amrex::Math::powi;
456
457 // reference particle relativistic factors
458 amrex::ParticleReal const gamma_ref = refpart.gamma();
459 amrex::ParticleReal const gyro_anomaly = refpart.gyromagnetic_anomaly;
460
461 // initialize the three components of the axis-angle vector
462 T_Real lambdax = 0_prt;
463 T_Real lambday = 0_prt;
464 T_Real lambdaz = 0_prt;
465
466 T_Real const x = particle(1);
467 T_Real const px = particle(2);
468 T_Real const y = particle(3);
469 T_Real const py = particle(4);
470 //T_Real const t = particle(5); // not used
471 T_Real const pt = particle(6);
472 T_Real sx = particle_spin(1);
473 T_Real sy = particle_spin(2);
474 T_Real sz = particle_spin(3);
475
476#if AMREX_DEVICE_COMPILE
477 const amrex::ParticleReal *const k_normal = m_k_normal_d_data;
478 const amrex::ParticleReal *const k_skew = m_k_skew_d_data;
479#else
480 const amrex::ParticleReal *const k_normal = m_k_normal_h_data;
481 const amrex::ParticleReal *const k_skew = m_k_skew_h_data;
482#endif
483
484 // defined dimensionless variables scaled by radius of curvature
485 amrex::ParticleReal rho = 1_prt + x / m_rc;
486 amrex::ParticleReal y_scl = y / m_rc;
487 amrex::ParticleReal ln_rho = std::log(rho);
488
489 // Initialization of magnetic field variables:
490 T_Real Bx = 0_prt;
491 T_Real By = 0_prt;
492 T_Real Bz = 0_prt;
493
494 // loop over array of multipole coefficients
495 // NOTE: Here we start at m = 0 in order to include the dipole contribution to the spin
496 for (int m = 0; m < m_ncoef; m++) {
497
498 // normalize units to MAD-X convention if needed
499 amrex::ParticleReal kn = m_unit == 1 ? k_normal[m] / m_brho : k_normal[m];
500 amrex::ParticleReal ks = m_unit == 1 ? k_skew[m] / m_brho : k_skew[m];
501
502 int n = m+1; // multipole order
503
504 // define dimensionless multipole coefficients (scaled by radius of curvature)
505 kn = std::pow(m_rc, static_cast<amrex::ParticleReal>(m)) * kn;
506 ks = std::pow(m_rc, static_cast<amrex::ParticleReal>(m)) * ks;
507
508 // Below, we use the consistency relationships Bx = dpy/rho and By = -dpx/rho. See T. Zolkin, PRAB 20, 043501 (2017).
509
510 switch (n) {
511
512 case 1: // dipole-like contribution (not used - contributing to map 1)
513 Bx += ks;
514 By += rho * kn;
515 break;
516 case 2: // quadrupole-like contribution
517 Bx += y_scl*kn + 0.5_prt*(rho*rho - 1_prt)*ks;
518 By += -(-rho*ln_rho*kn + y_scl*rho*ks);
519 break;
520 case 3: // sextupole-like contribution
521 Bx += 0.5_prt*y_scl*(rho*rho - 1_prt)*kn + 0.25_prt*(1_prt - 2_prt*y_scl*y_scl - rho*rho + 2_prt*rho*rho*ln_rho)*ks;
522 By += -(0.25_prt*rho*(1_prt + 2_prt*y_scl*y_scl - rho*rho + 2_prt*ln_rho)*kn + y_scl*rho*ln_rho*ks);
523 break;
524 case 4: // octupole-like contribution
525 Bx += -1_prt/12_prt*y_scl*(-3_prt + 2_prt*y_scl*y_scl + 3_prt*rho*rho - 6_prt*rho*rho*ln_rho)*kn
526 -1_prt/16_prt*((-1_prt + 4_prt*y_scl*y_scl - rho*rho)*(-1_prt + rho*rho) + 4_prt*rho*rho*ln_rho)*ks;
527 By += -(-0.25_prt*(rho - powi<3>(rho) + (rho - 2_prt*y_scl*y_scl*rho + powi<3>(rho))*ln_rho)*kn
528 -1_prt/12_prt*y_scl*rho*(3_prt + 2_prt*y_scl*y_scl - 3_prt*rho*rho + 6_prt*ln_rho)*ks);
529 break;
530 case 5: // decapole-like contribution
531 Bx += -1_prt/48_prt*y_scl*((-1_prt + rho*rho)*(4_prt*y_scl*y_scl - 3_prt*(1_prt + rho*rho)) + 12_prt*rho*rho*ln_rho)*kn
532 + 1_prt/192_prt*(3_prt + 8_prt*powi<4>(y_scl) + 12_prt*rho*rho
533 - 15_prt*powi<4>(rho) + 24_prt*y_scl*y_scl*(-1_prt + rho*rho)
534 +12_prt*rho*rho*(2_prt - 4_prt*y_scl*y_scl + rho*rho)*ln_rho)*ks;
535 By += -(-1_prt/192_prt*rho*(8_prt*powi<4>(y_scl) - 24_prt*powi<2>(y_scl)*(-1_prt + rho*rho)
536 + 3_prt*(-5_prt + 4_prt*powi<2>(rho) + powi<4>(rho))
537 +12_prt*(-1_prt + 4_prt*y_scl*y_scl - 2_prt*rho*rho)*ln_rho)*kn
538 +1_prt/12_prt*y_scl*rho*(3_prt*(-1_prt + rho*rho) + (2_prt*y_scl*y_scl - 3_prt*(1_prt + rho*rho))*ln_rho)*ks);
539 break;
540 }
541
542 }
543
544 // Scaling magnetic field to units used in the T-BMT representation (e.g. q*B/mc).
545
546 Bx = Bx * m_beta * gamma_ref / rho;
547 By = By * m_beta * gamma_ref / rho;
548
549 // Electric field normalized by q/mc^2:
550 T_Real const Ex = 0.0_prt;
551 T_Real const Ey = 0.0_prt;
552 T_Real const Ez = 0.0_prt;
553
554 // Quantities required to evaluate the full Thomas-BMT precession vector.
555 T_Real const Pnorm = sqrt(1_prt - 2_prt * pt * m_ibeta + pt*pt);
556 T_Real const iPnorm = 1_prt/Pnorm;
557 T_Real const Ps = sqrt(Pnorm*Pnorm - px*px - py*py);
558 T_Real const gamma = gamma_ref * (1_prt - pt*m_beta);
559 T_Real const ux = px * iPnorm;
560 T_Real const uy = py * iPnorm;
561 T_Real const uz = Ps * iPnorm;
562
563 // Design-orbit curvature
564 amrex::ParticleReal const h = 1_prt/m_rc;
565
566 // Evaluation of the full Thomas-BMT precession vector.
567 tbmt_precession_vector(x,ux,uy,uz,gamma,h,gyro_anomaly,Bx,By,Bz,Ex,Ey,Ez,lambdax,lambday,lambdaz);
568
569 // Generator of the spin rotation for a single step
570 lambdax *= tau;
571 lambday *= tau;
572 lambdaz *= tau;
573
574 // push the spin vector using the generator just determined
575 rotate_spin(lambdax,lambday,lambdaz,sx,sy,sz);
576
577 // update the spin variables
578 particle_spin(1) = sx;
579 particle_spin(2) = sy;
580 particle_spin(3) = sz;
581
582 // The coordinates and momenta are not affected.
583
584 zeval += 0_prt;
585 }
586
587
593 void operator() (RefPart & AMREX_RESTRICT refpart) const
594 {
595 using namespace amrex::literals; // for _rt and _prt
596
597 // assign input reference particle values
598 amrex::ParticleReal const x = refpart.x;
599 amrex::ParticleReal const px = refpart.px;
600 amrex::ParticleReal const y = refpart.y;
601 amrex::ParticleReal const py = refpart.py;
602 amrex::ParticleReal const z = refpart.z;
603 amrex::ParticleReal const pz = refpart.pz;
604 amrex::ParticleReal const t = refpart.t;
605 amrex::ParticleReal const pt = refpart.pt;
606 amrex::ParticleReal const s = refpart.s;
607 amrex::ParticleReal const brho = refpart.rigidity_Tm();
608
609#if AMREX_DEVICE_COMPILE
610 amrex::ParticleReal const * k_normal = m_k_normal_d_data;
611#else
612 amrex::ParticleReal const * k_normal = m_k_normal_h_data;
613#endif
614 // find magnetic field and curvature; normalize units to MAD-X convention if needed
615 amrex::ParticleReal const curvature = m_unit == 1 ? k_normal[0] / brho : k_normal[0];
616 amrex::ParticleReal const rc = curvature == 0.0 ? 0.0_prt : 1.0_prt/curvature;
617
618 // length of the current slice
619 amrex::ParticleReal const slice_ds = m_ds / nslice();
620
621 // special case of zero field = an exact drift
622 if (rc==0_prt) {
623 // advance position and momentum (drift)
624 amrex::ParticleReal const step = slice_ds / std::sqrt(amrex::Math::powi<2>(pt) - 1.0_prt);
625 refpart.x = x + step*px;
626 refpart.y = y + step*py;
627 refpart.z = z + step*pz;
628 refpart.t = t - step*pt;
629
630 } else {
631
632 // assign intermediate parameters
633 amrex::ParticleReal const B = refpart.beta_gamma() /rc;
634 amrex::ParticleReal const theta = slice_ds / rc;
635
636 // calculate expensive terms once
637 auto const [sin_theta, cos_theta] = amrex::Math::sincos(theta);
638
639 // advance position and momentum (bend)
640 refpart.px = px*cos_theta - pz*sin_theta;
641 refpart.py = py;
642 refpart.pz = pz*cos_theta + px*sin_theta;
643 refpart.pt = pt;
644
645 refpart.x = x + (refpart.pz - pz)/B;
646 refpart.y = y + (theta/B)*py;
647 refpart.z = z - (refpart.px - px)/B;
648 refpart.t = t - (theta/B)*pt;
649
650 }
651
652 // advance integrated path length
653 refpart.s = s + slice_ds;
654 }
655
656
657
663 Map6x6
664 transport_map (RefPart const & AMREX_RESTRICT refpart) const // TODO: update as well, but needs more careful placement of calc_constants
665 {
666 using namespace amrex::literals; // for _rt and _prt
667
668 // length of the current slice
669 amrex::ParticleReal const slice_ds = m_ds / nslice();
670
671 // find beta*gamma^2, beta
672 amrex::ParticleReal const betgam2 = amrex::Math::powi<2>(refpart.pt) - 1_prt;
673 amrex::ParticleReal const bet = refpart.beta();
674 amrex::ParticleReal const ibetgam2 = 1_prt / betgam2;
675
676 amrex::ParticleReal const * k_normal = m_k_normal_h_data;
677
678 // find magnetic field and curvature; normalize units to MAD-X convention if needed
679 amrex::ParticleReal const curvature = m_unit == 1 ? k_normal[0] / m_brho : k_normal[0];
680 amrex::ParticleReal const rc = curvature == 0.0 ? 0.0_prt : 1.0_prt/curvature;
681 amrex::ParticleReal const kn = m_unit == 1 ? k_normal[1] / m_brho : k_normal[1];
683
684 // update horizontal and longitudinal phase space variables
685 amrex::ParticleReal const gx = kn + amrex::Math::powi<-2>(rc);
686 amrex::ParticleReal const omega_x = std::sqrt(std::abs(gx));
687
688 // update vertical phase space variables
689 amrex::ParticleReal const gy = -kn;
690 amrex::ParticleReal const omega_y = std::sqrt(std::abs(gy));
691
692 // trigonometry
693 auto const [sinx, cosx] = amrex::Math::sincos(omega_x * slice_ds);
694 amrex::ParticleReal const sinhx = std::sinh(omega_x * slice_ds);
695 amrex::ParticleReal const coshx = std::cosh(omega_x * slice_ds);
696 auto const [siny, cosy] = amrex::Math::sincos(omega_y * slice_ds);
697 amrex::ParticleReal const sinhy = std::sinh(omega_y * slice_ds);
698 amrex::ParticleReal const coshy = std::cosh(omega_y * slice_ds);
699
700 amrex::ParticleReal const igbrc = 1_prt / (gx * bet * rc);
701 amrex::ParticleReal const iobrc = 1_prt / (omega_x * bet * rc);
702 amrex::ParticleReal const igobr = 1_prt / (gx * omega_x * b2rc2);
703
704 // initialize linear map matrix elements
706
707 R(1,1) = gx > 0_prt ? cosx : coshx;
708 R(1,2) = gx > 0_prt ? sinx / omega_x : sinhx / omega_x;
709 R(1,6) = gx > 0_prt ? -(1_prt - cosx) * igbrc : -(1_prt - coshx) * igbrc;
710 R(2,1) = gx > 0_prt ? -omega_x * sinx : omega_x * sinhx;
711 R(2,2) = gx > 0_prt ? cosx : coshx;
712 R(2,6) = gx > 0_prt ? -sinx * iobrc : -sinhx * iobrc;
713 R(3,3) = gy > 0_prt ? cosy : coshy;
714 R(3,4) = gy > 0_prt ? siny / omega_y : sinhy / omega_y;
715 R(4,3) = gy > 0_prt ? -omega_y * siny : omega_y * sinhy;
716 R(4,4) = gy > 0_prt ? cosy : coshy;
717 R(5,1) = gx > 0_prt ? sinx * iobrc : sinhx * iobrc;
718 R(5,2) = gx > 0_prt ? (1_prt - cosx) * igbrc : (1_prt - coshx) * igbrc;
719 R(5,6) = gx > 0_prt ?
720 slice_ds * ibetgam2 + (sinx - omega_x * slice_ds) * igobr :
721 slice_ds * ibetgam2 + (sinhx - omega_x * slice_ds) * igobr;
722
723 // apply the transverse rotation (roll) alignment error
724 return rotate_aligned_map(R);
725 }
726
727
742 template<typename T_Real=amrex::ParticleReal, typename T_IdCpu=uint64_t>
745 T_Real & AMREX_RESTRICT x,
746 T_Real & AMREX_RESTRICT y,
747 T_Real & AMREX_RESTRICT t,
748 T_Real & AMREX_RESTRICT px,
749 T_Real & AMREX_RESTRICT py,
750 T_Real & AMREX_RESTRICT pt,
751 T_Real & AMREX_RESTRICT sx,
752 T_Real & AMREX_RESTRICT sy,
753 T_Real & AMREX_RESTRICT sz,
754 [[maybe_unused]] T_IdCpu const & AMREX_RESTRICT idcpu,
755 RefPart const & AMREX_RESTRICT refpart
756 ) const
757 {
758 using namespace amrex::literals; // for _rt and _prt
759
760 // numerical integration parameters
761 amrex::ParticleReal const zin = 0_prt;
762 amrex::ParticleReal const zout = m_slice_ds;
763 int const nsteps = m_mapsteps;
764
765 // initialize phase space 6-vector
767 x, px, y, py, t, pt
768 };
769
770 // initialize spin 3-vector
772 sx, sy, sz
773 };
774
775 // call integrator to advance through slice
776 integrators::symp_integrate_particle_spin(particle,particle_spin,zin,zout,nsteps,m_int_order,refpart,*this);
777
778 // assign updated values
779 x = particle(1);
780 px = particle(2);
781 y = particle(3);
782 py = particle(4);
783 t = particle(5);
784 pt = particle(6);
785 sx = particle_spin(1);
786 sy = particle_spin(2);
787 sz = particle_spin(3);
788 }
789
790
792 using LinearTransport::operator();
793
794
795 int m_unit;
798 int m_id;
799
800 int m_ncoef = 0;
805
806 private:
807 // constants that are independent of the individually tracked particle,
808 // see: compute_constants() to refresh
815 };
816
817} // namespace impactx
818
821
822#endif // IMPACTX_EXACTCFBEND_H
#define AMREX_FORCE_INLINE
#define AMREX_RESTRICT
#define AMREX_GPU_HOST_DEVICE
#define AMREX_GPU_HOST
#define IMPACTX_PUSH_EXTERN_TEMPLATE(ElementType)
Definition PushAll.H:78
#define IMPACTX_GPUDATA_EXTERN(ElementType)
Definition dynamicdata.H:169
amrex_particle_real ParticleReal
constexpr T powi(T x) noexcept
__host__ __device__ std::pair< double, double > sincos(double x)
SmallMatrix< T, N, 1, Order::F, StartIndex > SmallVector
__host__ __device__ GpuComplex< T > sqrt(const GpuComplex< T > &a_z) noexcept
Definition All.H:56
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void symp2_integrate_particle(amrex::SmallVector< T_Real, 6, 1 > &particle, amrex::ParticleReal const zin, amrex::ParticleReal const zout, int const nsteps, RefPart const &refpart, T_Element const &element)
Definition Integrators.H:182
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void symp4_integrate_particle(amrex::SmallVector< T_Real, 6, 1 > &particle, amrex::ParticleReal const zin, amrex::ParticleReal const zout, int const nsteps, RefPart const &refpart, T_Element const &element)
Definition Integrators.H:231
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void symp_integrate_particle_spin(amrex::SmallVector< T_Real, 6, 1 > &particle, amrex::SmallVector< T_Real, 3, 1 > &particle_spin, amrex::ParticleReal const zin, amrex::ParticleReal const zout, int const nsteps, int int_order, RefPart const &refpart, T_Element const &element)
Definition Integrators.H:493
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void symp6_integrate_particle(amrex::SmallVector< T_Real, 6, 1 > &particle, amrex::ParticleReal const zin, amrex::ParticleReal const zout, int const nsteps, RefPart const &refpart, T_Element const &element)
Definition Integrators.H:292
@ s
fixed s as the independent variable
Definition ImpactXParticleContainer.H:37
@ t
fixed t as the independent variable
Definition ImpactXParticleContainer.H:38
amrex::SmallMatrix< amrex::ParticleReal, 6, 6, amrex::Order::F, 1 > Map6x6
Definition CovarianceMatrix.H:20
static constexpr __host__ __device__ SmallMatrix< T, NRows, NCols, ORDER, StartIndex > Identity() noexcept
Definition ReferenceParticle.H:33
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal beta_gamma() const
Definition ReferenceParticle.H:167
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal rigidity_Tm() const
Definition ReferenceParticle.H:260
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal beta() const
Definition ReferenceParticle.H:151
Definition ExactCFbend.H:46
mixin::TrackedVector< amrex::ParticleReal > k_normal
Definition ExactCFbend.H:47
mixin::TrackedVector< amrex::ParticleReal > k_skew
Definition ExactCFbend.H:48
Definition ExactCFbend.H:60
ImpactXParticleContainer::ParticleType PType
Definition ExactCFbend.H:62
int m_int_order
unit specification for Multipole strength
Definition ExactCFbend.H:796
amrex::ParticleReal m_ibeta
beta
Definition ExactCFbend.H:812
amrex::ParticleReal const * m_k_skew_d_data
non-owning pointer to device normal coefficients
Definition ExactCFbend.H:804
int m_id
number of integration steps per slice
Definition ExactCFbend.H:798
amrex::ParticleReal const * m_k_skew_h_data
non-owning pointer to host normal coefficients
Definition ExactCFbend.H:802
amrex::ParticleReal m_rc
magnetic ridigity in T-m
Definition ExactCFbend.H:814
int m_mapsteps
order used for the symplectic integration (2 or 4)
Definition ExactCFbend.H:797
mixin::GPUDataRegistry< CFbendCoefficients > DynamicData
Definition ExactCFbend.H:64
amrex::ParticleReal m_beta
1 / m_betgam2
Definition ExactCFbend.H:811
amrex::ParticleReal m_slice_ds
non-owning pointer to device skew coefficients
Definition ExactCFbend.H:809
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void spin_and_phasespace_push(T_Real &AMREX_RESTRICT x, T_Real &AMREX_RESTRICT y, T_Real &AMREX_RESTRICT t, T_Real &AMREX_RESTRICT px, T_Real &AMREX_RESTRICT py, T_Real &AMREX_RESTRICT pt, T_Real &AMREX_RESTRICT sx, T_Real &AMREX_RESTRICT sy, T_Real &AMREX_RESTRICT sz, T_IdCpu const &AMREX_RESTRICT idcpu, RefPart const &AMREX_RESTRICT refpart) const
Definition ExactCFbend.H:744
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void map3(amrex::ParticleReal const tau, amrex::SmallVector< T_Real, 6, 1 > &particle, amrex::SmallVector< T_Real, 3, 1 > &particle_spin, amrex::ParticleReal &zeval, RefPart const &AMREX_RESTRICT refpart) const
Definition ExactCFbend.H:447
int m_ncoef
unique ExactMultipole id used for data lookup map
Definition ExactCFbend.H:800
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void map2(amrex::ParticleReal const tau, amrex::SmallVector< amrex::ParticleReal, 6, 1 > &particle, amrex::ParticleReal &zeval, RefPart const &AMREX_RESTRICT refpart) const
Definition ExactCFbend.H:337
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void operator()(amrex::ParticleReal &AMREX_RESTRICT x, amrex::ParticleReal &AMREX_RESTRICT y, amrex::ParticleReal &AMREX_RESTRICT t, amrex::ParticleReal &AMREX_RESTRICT px, amrex::ParticleReal &AMREX_RESTRICT py, amrex::ParticleReal &AMREX_RESTRICT pt, uint64_t const &AMREX_RESTRICT idcpu, RefPart const &AMREX_RESTRICT refpart) const
Definition ExactCFbend.H:183
amrex::ParticleReal m_ibetgam2
m_ds / nslice();
Definition ExactCFbend.H:810
void compute_constants(RefPart const &refpart)
Definition ExactCFbend.H:142
amrex::ParticleReal const * m_k_normal_d_data
non-owning pointer to host skew coefficients
Definition ExactCFbend.H:803
AMREX_GPU_HOST AMREX_FORCE_INLINE Map6x6 transport_map(RefPart const &AMREX_RESTRICT refpart) const
Definition ExactCFbend.H:664
amrex::ParticleReal m_brho
1 / m_beta
Definition ExactCFbend.H:813
amrex::ParticleReal const * m_k_normal_h_data
number of Fourier coefficients
Definition ExactCFbend.H:801
ExactCFbend(amrex::ParticleReal ds, std::vector< amrex::ParticleReal > k_normal, std::vector< amrex::ParticleReal > k_skew, int unit, amrex::ParticleReal dx=0, amrex::ParticleReal dy=0, amrex::ParticleReal rotation_degree=0, amrex::ParticleReal aperture_x=0, amrex::ParticleReal aperture_y=0, int int_order=2, int mapsteps=1, int nslice=1, std::optional< std::string > name=std::nullopt)
Definition ExactCFbend.H:91
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void map1(amrex::ParticleReal const tau, amrex::SmallVector< amrex::ParticleReal, 6, 1 > &particle, amrex::ParticleReal &zeval, RefPart const &AMREX_RESTRICT refpart) const
Definition ExactCFbend.H:234
void reverse()
Definition ExactCFbend.H:130
int m_unit
Definition ExactCFbend.H:795
static constexpr auto type
Definition ExactCFbend.H:61
Definition alignment.H:29
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal dy() const
Definition alignment.H:189
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal dx() const
Definition alignment.H:179
AMREX_GPU_HOST AMREX_FORCE_INLINE Map6x6 rotate_aligned_map(Map6x6 const &R) const
Definition alignment.H:263
Alignment(amrex::ParticleReal dx, amrex::ParticleReal dy, amrex::ParticleReal rotation_degree)
Definition alignment.H:39
Definition beamoptic.H:529
static std::shared_ptr< CFbendCoefficients > const & get(int id)
Definition dynamicdata.H:98
static CFbendCoefficients & emplace(int id, Args &&... args)
Definition dynamicdata.H:125
Definition lineartransport.H:50
Definition named.H:29
AMREX_GPU_HOST Named(std::optional< std::string > name)
Definition named.H:57
AMREX_FORCE_INLINE std::string name() const
Definition named.H:122
Definition nofinalize.H:22
Definition pipeaperture.H:26
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal aperture_x() const
Definition pipeaperture.H:90
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal aperture_y() const
Definition pipeaperture.H:101
PipeAperture(amrex::ParticleReal aperture_x, amrex::ParticleReal aperture_y)
Definition pipeaperture.H:32
Definition spintransport.H:36
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void tbmt_precession_vector(T_Real const &AMREX_RESTRICT x, T_Real const &AMREX_RESTRICT ux, T_Real const &AMREX_RESTRICT uy, T_Real const &AMREX_RESTRICT uz, T_Real const &AMREX_RESTRICT gamma, amrex::ParticleReal const &AMREX_RESTRICT h, amrex::ParticleReal const &AMREX_RESTRICT gyro_anomaly, T_Real const &AMREX_RESTRICT Bx, T_Real const &AMREX_RESTRICT By, T_Real const &AMREX_RESTRICT Bz, T_Real const &AMREX_RESTRICT Ex, T_Real const &AMREX_RESTRICT Ey, T_Real const &AMREX_RESTRICT Ez, T_Real &AMREX_RESTRICT Omegax, T_Real &AMREX_RESTRICT Omegay, T_Real &AMREX_RESTRICT Omegaz) const
Definition spintransport.H:121
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void rotate_spin(T_Real const &AMREX_RESTRICT lambdax, T_Real const &AMREX_RESTRICT lambday, T_Real const &AMREX_RESTRICT lambdaz, T_Real &AMREX_RESTRICT sx, T_Real &AMREX_RESTRICT sy, T_Real &AMREX_RESTRICT sz) const
Definition spintransport.H:48
Definition thick.H:24
Thick(amrex::ParticleReal ds, int nslice)
Definition thick.H:30
amrex::ParticleReal m_ds
Definition thick.H:68
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal ds() const
Definition thick.H:53
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int nslice() const
Definition thick.H:43
Definition TrackedVector.H:49