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
25#include <AMReX_Extension.H>
26#include <AMReX_REAL.H>
27#include <AMReX_Print.H>
28#include <AMReX_GpuComplex.H>
29#include <AMReX_Math.H>
30#include <AMReX_TrackedVector.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
66 static constexpr int DEFAULT_unit = 0;
67 static constexpr int DEFAULT_int_order = 2;
68 static constexpr int DEFAULT_mapsteps = 10;
69
97 std::vector<amrex::ParticleReal> k_normal,
98 std::vector<amrex::ParticleReal> k_skew,
99 int unit = DEFAULT_unit,
105 int int_order = DEFAULT_int_order,
106 int mapsteps = DEFAULT_mapsteps,
108 std::optional<std::string> name = DEFAULT_name
109 )
110 : Named(std::move(name)),
111 Thick(ds, nslice),
112 Alignment(dx, dy, rotation_degree),
114 m_unit(unit), m_int_order(int_order), m_mapsteps(mapsteps),
115 m_id(DynamicData::allocate_id())
116 {
117 if (m_int_order != 2 && m_int_order != 4 && m_int_order != 6)
118 throw std::runtime_error("ExactCFbend: The order used for symplectic integration must be 2, 4 or 6.");
119
120 m_ncoef = int(k_normal.size());
121 if (m_ncoef != int(k_skew.size()))
122 throw std::runtime_error("ExactCFbend: normal and skew coefficient arrays must have same length!");
123
124 auto& coef = DynamicData::emplace(
125 m_id,
126 std::move(k_normal),
127 std::move(k_skew)
128 );
129 m_k_normal_h_data = coef.k_normal.host_const().data();
130 m_k_skew_h_data = coef.k_skew.host_const().data();
131 }
132
134 void reverse () { Thick::reverse(); }
135
137 using BeamOptic::operator();
138
146 void compute_constants (RefPart const & refpart)
147 {
148 using namespace amrex::literals; // for _rt and _prt
149
150 Alignment::compute_constants(refpart);
151
152 // Ensure dynamic coefficient data is on GPU and we have fresh pointers to it
153 auto const & coef = *DynamicData::get(m_id);
154 m_k_normal_d_data = coef.k_normal.device_const().data();
155 m_k_skew_d_data = coef.k_skew.device_const().data();
156
157 // access reference particle values to find relativistic factors
158 m_beta = refpart.beta();
159 m_ibeta = 1_prt / m_beta;
160 m_ibetgam2 = 1_prt / amrex::Math::powi<2>(refpart.beta_gamma());
161 m_brho = refpart.rigidity_Tm();
162
163 amrex::ParticleReal const * k_normal = m_k_normal_h_data;
164
165 // find magnetic field and curvature; normalize units to MAD-X convention if needed
166 amrex::ParticleReal curvature = m_unit == 1 ? k_normal[0] / m_brho : k_normal[0];
167 m_rc = curvature == 0.0 ? 0.0_prt : 1.0_prt/curvature;
168
169 // arc length and angle of the current slice
170 m_slice_ds = m_ds / nslice();
171 }
172
194 [[maybe_unused]] uint64_t const & AMREX_RESTRICT idcpu,
195 RefPart const & AMREX_RESTRICT refpart
196 ) const
197 {
198 using namespace amrex::literals; // for _rt and _prt
199
200 // numerical integration parameters
201 amrex::ParticleReal const zin = 0_prt;
202 amrex::ParticleReal const zout = m_slice_ds;
203 int const nsteps = m_mapsteps;
204
205 // initialize phase space 6-vector
207 x, px, y, py, t, pt
208 };
209
210 // call integrator to advance through slice (int_order = 2 or 4, otherwise default 2)
211 if (m_int_order == 2) {
212 integrators::symp2_integrate_particle(particle,zin,zout,nsteps,refpart,*this);
213 } else if (m_int_order == 4) {
214 integrators::symp4_integrate_particle(particle,zin,zout,nsteps,refpart,*this);
215 } else if (m_int_order == 6) {
216 integrators::symp6_integrate_particle(particle,zin,zout,nsteps,refpart,*this);
217 }
218
219 // assign updated values
220 x = particle(1);
221 px = particle(2);
222 y = particle(3);
223 py = particle(4);
224 t = particle(5);
225 pt = particle(6);
226 }
227
238 void map1 (amrex::ParticleReal const tau,
240 amrex::ParticleReal & zeval,
241 [[maybe_unused]] RefPart const & AMREX_RESTRICT refpart) const
242 {
243 using namespace amrex::literals; // for _rt and _prt
244 using namespace amrex::Math; // for powi
245
246 amrex::ParticleReal const x = particle(1);
247 amrex::ParticleReal const px = particle(2);
248 amrex::ParticleReal const y = particle(3);
249 amrex::ParticleReal const py = particle(4);
250 amrex::ParticleReal const t = particle(5);
251 amrex::ParticleReal const pt = particle(6);
252
253 amrex::ParticleReal xout = x;
254 amrex::ParticleReal pxout = px;
255 amrex::ParticleReal yout = y;
256 amrex::ParticleReal pyout = py;
257 amrex::ParticleReal tout = t;
258 amrex::ParticleReal ptout = pt;
259
260 // treat the special case of zero field
261 if (m_rc==0_prt) {
262 // compute the radical in the denominator (= pz):
263 amrex::ParticleReal const inv_pzden = 1_prt / std::sqrt(
264 powi<2>(pt - m_ibeta) -
265 m_ibetgam2 -
266 powi<2>(px) -
267 powi<2>(py)
268 );
269
270 // advance position and momentum (exact drift)
271 xout = x + tau * px * inv_pzden;
272 yout = y + tau * py * inv_pzden;
273 tout = t - tau * (m_ibeta + (pt - m_ibeta) * inv_pzden);
274
275 // assign updated momenta
276 pxout = px;
277 pyout = py;
278 ptout = pt;
279
280 } else {
281
282 // assign intermediate quantities
283 amrex::ParticleReal const pperp2 = powi<2>(pt)-2.0_prt * m_ibeta * pt - powi<2>(py)+1.0_prt;
284 amrex::ParticleReal const px2 = powi<2>(px);
285
286 // trigonometric evaluations
287 amrex::ParticleReal const slice_phi = tau / m_rc;
288 auto const [sin_phi, cos_phi] = amrex::Math::sincos(slice_phi);
289
290 // determine if particle lies within the domain of map definition
291 if (pperp2 > px2)
292 {
293 amrex::ParticleReal const pperp = std::sqrt(pperp2);
294 amrex::ParticleReal const pzi = std::sqrt(powi<2>(pperp) - powi<2>(px));
295 amrex::ParticleReal const rho = m_rc + x;
296
297 // update momenta
298 pxout = px * cos_phi + (pzi - rho / m_rc) * sin_phi;
299 pyout = py;
300 ptout = pt;
301
302 // angle of momentum rotation
303 amrex::ParticleReal const px2f = powi<2>(pxout);
304 // determine if particle lies within domain of map definition
305 if (pperp2 > px2f)
306 {
307 amrex::ParticleReal const pzf = std::sqrt(powi<2>(pperp)-powi<2>(pxout));
308 amrex::ParticleReal const theta = slice_phi + std::asin(px/pperp) - std::asin(pxout/pperp);
309
310 // update position coordinates
311 xout = -m_rc + rho*cos_phi + m_rc*(pzf + px*sin_phi - pzi*cos_phi);
312 yout = y + theta * m_rc * py;
313 tout = t - theta * m_rc * (pt - 1.0_prt * m_ibeta) - slice_phi * m_rc * m_ibeta;
314
315 } // else { amrex::Print() << "Warning: outside map domain." << "\n"; }
316 } // else { amrex::Print() << "Warning: outside map domain." << "\n"; }
317 }
318
319 // push particle coordinates
320 particle(1) = xout;
321 particle(2) = pxout;
322 particle(3) = yout;
323 particle(4) = pyout;
324 particle(5) = tout;
325 particle(6) = ptout;
326
327 zeval += 0_prt;
328 }
329
341 void map2 (amrex::ParticleReal const tau,
343 amrex::ParticleReal & zeval,
344 [[maybe_unused]] RefPart const & AMREX_RESTRICT refpart) const
345 {
346 using namespace amrex::literals; // for _rt and _prt
347 using namespace amrex::Math; // for powi
348
349 amrex::ParticleReal const x = particle(1);
350 amrex::ParticleReal const px = particle(2);
351 amrex::ParticleReal const y = particle(3);
352 amrex::ParticleReal const py = particle(4);
353 amrex::ParticleReal const t = particle(5);
354 amrex::ParticleReal const pt = particle(6);
355
356 amrex::ParticleReal xout = x;
357 amrex::ParticleReal pxout = px;
358 amrex::ParticleReal yout = y;
359 amrex::ParticleReal pyout = py;
360 amrex::ParticleReal tout = t;
361 amrex::ParticleReal ptout = pt;
362
363 amrex::ParticleReal const * k_normal = m_k_normal_d_data;
364 amrex::ParticleReal const * k_skew = m_k_skew_d_data;
365
366 // defined dimensionless variables scaled by radius of curvature
367 amrex::ParticleReal rho = 1_prt + x / m_rc;
368 amrex::ParticleReal y_scl = y / m_rc;
369 amrex::ParticleReal ln_rho = std::log(rho);
370
371 // initialize the momentum kick
372 amrex::ParticleReal dpx = 0_prt;
373 amrex::ParticleReal dpy = 0_prt;
374
375 // loop over array of multipole coefficients
376 for (int m = 1; m < m_ncoef; m++) {
377
378 // normalize units to MAD-X convention if needed
379 amrex::ParticleReal kn = m_unit == 1 ? k_normal[m] / m_brho : k_normal[m];
380 amrex::ParticleReal ks = m_unit == 1 ? k_skew[m] / m_brho : k_skew[m];
381
382 int n = m+1; // multipole order
383
384 // define dimensionless multipole coefficients (scaled by radius of curvature)
385 kn = std::pow(m_rc, static_cast<amrex::ParticleReal>(m)) * kn;
386 ks = std::pow(m_rc, static_cast<amrex::ParticleReal>(m)) * ks;
387
388 switch (n) {
389
390 case 1: // dipole-like contribution (not used - contributing to map 1)
391 dpx += -rho * kn;
392 dpy += ks;
393 break;
394 case 2: // quadrupole-like contribution
395 dpx += -rho*ln_rho*kn + y_scl*rho*ks;
396 dpy += y_scl*kn + 0.5_prt*(rho*rho - 1_prt)*ks;
397 break;
398 case 3: // sextupole-like contribution
399 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;
400 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;
401 break;
402 case 4: // octupole-like contribution
403 dpx += -0.25_prt*(rho - powi<3>(rho) + (rho - 2_prt*y_scl*y_scl*rho + powi<3>(rho))*ln_rho)*kn
404 -1_prt/12_prt*y_scl*rho*(3_prt + 2_prt*y_scl*y_scl - 3_prt*rho*rho + 6_prt*ln_rho)*ks;
405 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
406 -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;
407 break;
408 case 5: // decapole-like contribution
409 dpx += -1_prt/192_prt*rho*(8_prt*powi<4>(y_scl) - 24_prt*powi<2>(y_scl)*(-1_prt + rho*rho)
410 + 3_prt*(-5_prt + 4_prt*powi<2>(rho) + powi<4>(rho))
411 +12_prt*(-1_prt + 4_prt*y_scl*y_scl - 2_prt*rho*rho)*ln_rho)*kn
412 +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;
413 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
414 + 1_prt/192_prt*(3_prt + 8_prt*powi<4>(y_scl) + 12_prt*rho*rho
415 - 15_prt*powi<4>(rho) + 24_prt*y_scl*y_scl*(-1_prt + rho*rho)
416 +12_prt*rho*rho*(2_prt - 4_prt*y_scl*y_scl + rho*rho)*ln_rho)*ks;
417 break;
418 // default:
419 // TODO: print a warning here that order > 5 is not currently supported
420 }
421
422 }
423
424 // advance momentum by a step of length tau
425 pxout = pxout + tau * dpx;
426 pyout = pyout + tau * dpy;
427
428 //update the particle coordinates
429 particle(1) = xout;
430 particle(2) = pxout;
431 particle(3) = yout;
432 particle(4) = pyout;
433 particle(5) = tout;
434 particle(6) = ptout;
435
436 zeval += tau;
437 }
438
448 template<typename T_Real>
450 void map3 (amrex::ParticleReal const tau,
452 amrex::SmallVector<T_Real, 3, 1> & particle_spin,
453 amrex::ParticleReal & zeval,
454 RefPart const & AMREX_RESTRICT refpart) const
455 {
456 using namespace amrex::literals; // for _rt and _prt
457 using namespace std; // for cmath(float)
458 using amrex::Math::powi;
459
460 // reference particle relativistic factors
461 amrex::ParticleReal const gamma_ref = refpart.gamma();
462 amrex::ParticleReal const gyro_anomaly = refpart.gyromagnetic_anomaly;
463
464 // initialize the three components of the axis-angle vector
465 T_Real lambdax = 0_prt;
466 T_Real lambday = 0_prt;
467 T_Real lambdaz = 0_prt;
468
469 T_Real const x = particle(1);
470 T_Real const px = particle(2);
471 T_Real const y = particle(3);
472 T_Real const py = particle(4);
473 //T_Real const t = particle(5); // not used
474 T_Real const pt = particle(6);
475 T_Real sx = particle_spin(1);
476 T_Real sy = particle_spin(2);
477 T_Real sz = particle_spin(3);
478
479#if AMREX_DEVICE_COMPILE
480 const amrex::ParticleReal *const k_normal = m_k_normal_d_data;
481 const amrex::ParticleReal *const k_skew = m_k_skew_d_data;
482#else
483 const amrex::ParticleReal *const k_normal = m_k_normal_h_data;
484 const amrex::ParticleReal *const k_skew = m_k_skew_h_data;
485#endif
486
487 // defined dimensionless variables scaled by radius of curvature
488 amrex::ParticleReal rho = 1_prt + x / m_rc;
489 amrex::ParticleReal y_scl = y / m_rc;
490 amrex::ParticleReal ln_rho = std::log(rho);
491
492 // Initialization of magnetic field variables:
493 T_Real Bx = 0_prt;
494 T_Real By = 0_prt;
495 T_Real Bz = 0_prt;
496
497 // loop over array of multipole coefficients
498 // NOTE: Here we start at m = 0 in order to include the dipole contribution to the spin
499 for (int m = 0; m < m_ncoef; m++) {
500
501 // normalize units to MAD-X convention if needed
502 amrex::ParticleReal kn = m_unit == 1 ? k_normal[m] / m_brho : k_normal[m];
503 amrex::ParticleReal ks = m_unit == 1 ? k_skew[m] / m_brho : k_skew[m];
504
505 int n = m+1; // multipole order
506
507 // define dimensionless multipole coefficients (scaled by radius of curvature)
508 kn = std::pow(m_rc, static_cast<amrex::ParticleReal>(m)) * kn;
509 ks = std::pow(m_rc, static_cast<amrex::ParticleReal>(m)) * ks;
510
511 // Below, we use the consistency relationships Bx = dpy/rho and By = -dpx/rho. See T. Zolkin, PRAB 20, 043501 (2017).
512
513 switch (n) {
514
515 case 1: // dipole-like contribution (not used - contributing to map 1)
516 Bx += ks;
517 By += rho * kn;
518 break;
519 case 2: // quadrupole-like contribution
520 Bx += y_scl*kn + 0.5_prt*(rho*rho - 1_prt)*ks;
521 By += -(-rho*ln_rho*kn + y_scl*rho*ks);
522 break;
523 case 3: // sextupole-like contribution
524 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;
525 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);
526 break;
527 case 4: // octupole-like contribution
528 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
529 -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;
530 By += -(-0.25_prt*(rho - powi<3>(rho) + (rho - 2_prt*y_scl*y_scl*rho + powi<3>(rho))*ln_rho)*kn
531 -1_prt/12_prt*y_scl*rho*(3_prt + 2_prt*y_scl*y_scl - 3_prt*rho*rho + 6_prt*ln_rho)*ks);
532 break;
533 case 5: // decapole-like contribution
534 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
535 + 1_prt/192_prt*(3_prt + 8_prt*powi<4>(y_scl) + 12_prt*rho*rho
536 - 15_prt*powi<4>(rho) + 24_prt*y_scl*y_scl*(-1_prt + rho*rho)
537 +12_prt*rho*rho*(2_prt - 4_prt*y_scl*y_scl + rho*rho)*ln_rho)*ks;
538 By += -(-1_prt/192_prt*rho*(8_prt*powi<4>(y_scl) - 24_prt*powi<2>(y_scl)*(-1_prt + rho*rho)
539 + 3_prt*(-5_prt + 4_prt*powi<2>(rho) + powi<4>(rho))
540 +12_prt*(-1_prt + 4_prt*y_scl*y_scl - 2_prt*rho*rho)*ln_rho)*kn
541 +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);
542 break;
543 }
544
545 }
546
547 // Scaling magnetic field to units used in the T-BMT representation (e.g. q*B/mc).
548
549 Bx = Bx * m_beta * gamma_ref / rho;
550 By = By * m_beta * gamma_ref / rho;
551
552 // Electric field normalized by q/mc^2:
553 T_Real const Ex = 0.0_prt;
554 T_Real const Ey = 0.0_prt;
555 T_Real const Ez = 0.0_prt;
556
557 // Quantities required to evaluate the full Thomas-BMT precession vector.
558 T_Real const Pnorm = sqrt(1_prt - 2_prt * pt * m_ibeta + pt*pt);
559 T_Real const iPnorm = 1_prt/Pnorm;
560 T_Real const Ps = sqrt(Pnorm*Pnorm - px*px - py*py);
561 T_Real const gamma = gamma_ref * (1_prt - pt*m_beta);
562 T_Real const ux = px * iPnorm;
563 T_Real const uy = py * iPnorm;
564 T_Real const uz = Ps * iPnorm;
565
566 // Design-orbit curvature
567 amrex::ParticleReal const h = 1_prt/m_rc;
568
569 // Evaluation of the full Thomas-BMT precession vector.
570 tbmt_precession_vector(x,ux,uy,uz,gamma,h,gyro_anomaly,Bx,By,Bz,Ex,Ey,Ez,lambdax,lambday,lambdaz);
571
572 // Generator of the spin rotation for a single step
573 lambdax *= tau;
574 lambday *= tau;
575 lambdaz *= tau;
576
577 // push the spin vector using the generator just determined
578 rotate_spin(lambdax,lambday,lambdaz,sx,sy,sz);
579
580 // update the spin variables
581 particle_spin(1) = sx;
582 particle_spin(2) = sy;
583 particle_spin(3) = sz;
584
585 // The coordinates and momenta are not affected.
586
587 zeval += 0_prt;
588 }
589
595 void operator() (RefPart & AMREX_RESTRICT refpart) const
596 {
597 using namespace amrex::literals; // for _rt and _prt
598
599 // assign input reference particle values
600 amrex::ParticleReal const x = refpart.x;
601 amrex::ParticleReal const px = refpart.px;
602 amrex::ParticleReal const y = refpart.y;
603 amrex::ParticleReal const py = refpart.py;
604 amrex::ParticleReal const z = refpart.z;
605 amrex::ParticleReal const pz = refpart.pz;
606 amrex::ParticleReal const t = refpart.t;
607 amrex::ParticleReal const pt = refpart.pt;
608 amrex::ParticleReal const s = refpart.s;
609 amrex::ParticleReal const brho = refpart.rigidity_Tm();
610
611#if AMREX_DEVICE_COMPILE
612 amrex::ParticleReal const * k_normal = m_k_normal_d_data;
613#else
614 amrex::ParticleReal const * k_normal = m_k_normal_h_data;
615#endif
616 // find magnetic field and curvature; normalize units to MAD-X convention if needed
617 amrex::ParticleReal const curvature = m_unit == 1 ? k_normal[0] / brho : k_normal[0];
618 amrex::ParticleReal const rc = curvature == 0.0 ? 0.0_prt : 1.0_prt/curvature;
619
620 // length of the current slice
621 amrex::ParticleReal const slice_ds = m_ds / nslice();
622
623 // special case of zero field = an exact drift
624 if (rc==0_prt) {
625 // advance position and momentum (drift)
626 amrex::ParticleReal const step = slice_ds / std::sqrt(amrex::Math::powi<2>(pt) - 1.0_prt);
627 refpart.x = x + step*px;
628 refpart.y = y + step*py;
629 refpart.z = z + step*pz;
630 refpart.t = t - step*pt;
631
632 } else {
633
634 // assign intermediate parameters
635 amrex::ParticleReal const B = refpart.beta_gamma() /rc;
636 amrex::ParticleReal const theta = slice_ds / rc;
637
638 // calculate expensive terms once
639 auto const [sin_theta, cos_theta] = amrex::Math::sincos(theta);
640
641 // advance position and momentum (bend)
642 refpart.px = px*cos_theta - pz*sin_theta;
643 refpart.py = py;
644 refpart.pz = pz*cos_theta + px*sin_theta;
645 refpart.pt = pt;
646
647 refpart.x = x + (refpart.pz - pz)/B;
648 refpart.y = y + (theta/B)*py;
649 refpart.z = z - (refpart.px - px)/B;
650 refpart.t = t - (theta/B)*pt;
651
652 }
653
654 // advance integrated path length
655 refpart.s = s + slice_ds;
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 int m_unit;
797 int m_id;
798
799 int m_ncoef = 0;
804
805 private:
806 // constants that are independent of the individually tracked particle,
807 // see: compute_constants() to refresh
814 };
815
816} // namespace impactx
817
820
821#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:55
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
amrex::Gpu::TrackedVector< amrex::ParticleReal > k_skew
Definition ExactCFbend.H:48
amrex::Gpu::TrackedVector< amrex::ParticleReal > k_normal
Definition ExactCFbend.H:47
Definition ExactCFbend.H:60
ImpactXParticleContainer::ParticleType PType
Definition ExactCFbend.H:62
int m_int_order
unit specification for Multipole strength
Definition ExactCFbend.H:795
amrex::ParticleReal m_ibeta
beta
Definition ExactCFbend.H:811
static constexpr int DEFAULT_mapsteps
Definition ExactCFbend.H:68
amrex::ParticleReal const * m_k_skew_d_data
non-owning pointer to device normal coefficients
Definition ExactCFbend.H:803
int m_id
number of integration steps per slice
Definition ExactCFbend.H:797
amrex::ParticleReal const * m_k_skew_h_data
non-owning pointer to host normal coefficients
Definition ExactCFbend.H:801
amrex::ParticleReal m_rc
magnetic ridigity in T-m
Definition ExactCFbend.H:813
int m_mapsteps
order used for the symplectic integration (2 or 4)
Definition ExactCFbend.H:796
ExactCFbend(amrex::ParticleReal ds, std::vector< amrex::ParticleReal > k_normal, std::vector< amrex::ParticleReal > k_skew, int unit=DEFAULT_unit, amrex::ParticleReal dx=DEFAULT_dx, amrex::ParticleReal dy=DEFAULT_dy, amrex::ParticleReal rotation_degree=DEFAULT_rotation_degree, amrex::ParticleReal aperture_x=DEFAULT_aperture_x, amrex::ParticleReal aperture_y=DEFAULT_aperture_y, int int_order=DEFAULT_int_order, int mapsteps=DEFAULT_mapsteps, int nslice=DEFAULT_nslice, std::optional< std::string > name=DEFAULT_name)
Definition ExactCFbend.H:95
mixin::GPUDataRegistry< CFbendCoefficients > DynamicData
Definition ExactCFbend.H:64
amrex::ParticleReal m_beta
1 / m_betgam2
Definition ExactCFbend.H:810
amrex::ParticleReal m_slice_ds
non-owning pointer to device skew coefficients
Definition ExactCFbend.H:808
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:450
int m_ncoef
unique ExactMultipole id used for data lookup map
Definition ExactCFbend.H:799
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:341
static constexpr int DEFAULT_unit
Definition ExactCFbend.H:66
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:187
amrex::ParticleReal m_ibetgam2
m_ds / nslice();
Definition ExactCFbend.H:809
void compute_constants(RefPart const &refpart)
Definition ExactCFbend.H:146
amrex::ParticleReal const * m_k_normal_d_data
non-owning pointer to host skew coefficients
Definition ExactCFbend.H:802
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:812
static constexpr int DEFAULT_int_order
Definition ExactCFbend.H:67
amrex::ParticleReal const * m_k_normal_h_data
number of Fourier coefficients
Definition ExactCFbend.H:800
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:238
void reverse()
Definition ExactCFbend.H:134
int m_unit
Definition ExactCFbend.H:794
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:193
static constexpr amrex::ParticleReal DEFAULT_dy
Definition alignment.H:34
static constexpr amrex::ParticleReal DEFAULT_dx
Definition alignment.H:33
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal dx() const
Definition alignment.H:183
AMREX_GPU_HOST AMREX_FORCE_INLINE Map6x6 rotate_aligned_map(Map6x6 const &R) const
Definition alignment.H:267
Alignment(amrex::ParticleReal dx, amrex::ParticleReal dy, amrex::ParticleReal rotation_degree)
Definition alignment.H:43
static constexpr amrex::ParticleReal DEFAULT_rotation_degree
Definition alignment.H:35
Definition beamoptic.H:567
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
static constexpr std::nullopt_t DEFAULT_name
Definition named.H:30
AMREX_GPU_HOST Named(std::optional< std::string > name)
Definition named.H:59
AMREX_FORCE_INLINE std::string name() const
Definition named.H:124
Definition nofinalize.H:22
Definition pipeaperture.H:26
static constexpr amrex::ParticleReal DEFAULT_aperture_x
Definition pipeaperture.H:27
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal aperture_x() const
Definition pipeaperture.H:93
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal aperture_y() const
Definition pipeaperture.H:104
static constexpr amrex::ParticleReal DEFAULT_aperture_y
Definition pipeaperture.H:28
PipeAperture(amrex::ParticleReal aperture_x, amrex::ParticleReal aperture_y)
Definition pipeaperture.H:35
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
static constexpr int DEFAULT_nslice
Definition thick.H:25
Thick(amrex::ParticleReal ds, int nslice)
Definition thick.H:32
amrex::ParticleReal m_ds
Definition thick.H:70
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal ds() const
Definition thick.H:55
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int nslice() const
Definition thick.H:45