1515#include " mixin/beamoptic.H"
1616#include " mixin/thin.H"
1717#include " mixin/lineartransport.H"
18+ #include " mixin/spintransport.H"
1819#include " mixin/named.H"
1920#include " mixin/nofinalize.H"
2021
@@ -35,6 +36,7 @@ namespace impactx::elements
3536 public mixin::LinearTransport<TaperedPL>,
3637 public mixin::Thin,
3738 public mixin::Alignment,
39+ public mixin::SpinTransport,
3840 public mixin::NoFinalize
3941 // At least on Intel AVX512, there is a small overhead to vectorize this element, see
4042 // https://github.com/BLAST-ImpactX/impactx/pull/1002
@@ -76,6 +78,28 @@ namespace impactx::elements
7678 /* * Reverse the element (negate strength) */
7779 void reverse () { m_k = -m_k; }
7880
81+ /* * Compute and cache the constants for the push.
82+ *
83+ * In particular, used to pre-compute and cache variables that are
84+ * independent of the individually tracked particle.
85+ *
86+ * @param refpart reference particle
87+ */
88+ void compute_constants (RefPart const & refpart)
89+ {
90+ using namespace amrex ::literals; // for _rt and _prt
91+
92+ Alignment::compute_constants (refpart);
93+
94+ // normalize focusing strength units to MAD-X convention if needed
95+ m_g = m_unit == 1 ? m_k / refpart.rigidity_Tm () : m_k;
96+
97+ // additional constants needed for spin push
98+ m_beta = refpart.beta ();
99+ m_ibeta = 1_prt / m_beta;
100+
101+ }
102+
79103 /* * Push all particles */
80104 using BeamOptic::operator ();
81105
@@ -150,6 +174,81 @@ namespace impactx::elements
150174 /* * This pushes the reference particle. */
151175 using Thin::operator ();
152176
177+
178+ /* * This operator pushes the particle spin.
179+ *
180+ * @param x particle position in x
181+ * @param y particle position in y
182+ * @param t particle position in t
183+ * @param px particle momentum in x
184+ * @param py particle momentum in y
185+ * @param pt particle momentum in t
186+ * @param sx particle spin x-component
187+ * @param sy particle spin y-component
188+ * @param sz particle spin z-component
189+ * @param idcpu particle global index
190+ * @param refpart reference particle
191+ */
192+ template <typename T_Real=amrex::ParticleReal, typename T_IdCpu=uint64_t >
193+ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
194+ void spin_and_phasespace_push (
195+ T_Real & AMREX_RESTRICT x,
196+ T_Real & AMREX_RESTRICT y,
197+ T_Real & AMREX_RESTRICT t,
198+ T_Real & AMREX_RESTRICT px,
199+ T_Real & AMREX_RESTRICT py,
200+ T_Real & AMREX_RESTRICT pt,
201+ T_Real & AMREX_RESTRICT sx,
202+ T_Real & AMREX_RESTRICT sy,
203+ T_Real & AMREX_RESTRICT sz,
204+ [[maybe_unused]] T_IdCpu & AMREX_RESTRICT idcpu,
205+ RefPart const & AMREX_RESTRICT refpart
206+ ) const
207+ {
208+ using namespace amrex ::literals; // for _rt and _prt
209+ using amrex::Math::powi;
210+
211+ // initialize the three components of the axis-angle vector
212+ T_Real lambdax = 0_prt;
213+ T_Real lambday = 0_prt;
214+ T_Real lambdaz = 0_prt;
215+
216+ // compute multipole magnetic field normalized by q/mc:
217+ amrex::ParticleReal gamma_ref = refpart.gamma ();
218+ T_Real const Bx = m_g * ( y + m_taper * x * y ) * gamma_ref * m_beta;
219+ T_Real const By = -m_g * ( x + m_taper*0 .5_prt * (powi<2 >(x) + powi<2 >(y)) ) * gamma_ref * m_beta;
220+ T_Real const Bz = 0_prt;
221+
222+ // Electric field normalized by q/mc^2:
223+ T_Real const Ex = 0 .0_prt;
224+ T_Real const Ey = 0 .0_prt;
225+ T_Real const Ez = 0 .0_prt;
226+
227+ // Quantities required to evaluate the full Thomas-BMT precession vector.
228+ T_Real const Pnorm = sqrt (1_prt - 2_prt * pt * m_ibeta + pt*pt);
229+ T_Real const iPnorm = 1_prt/Pnorm;
230+ T_Real const Ps = sqrt (Pnorm*Pnorm - px*px - py*py);
231+ T_Real const gamma = gamma_ref * (1_prt - pt*m_beta);
232+ T_Real const ux = px * iPnorm;
233+ T_Real const uy = py * iPnorm;
234+ T_Real const uz = Ps * iPnorm;
235+
236+ // Zero curvature in a quadrupole
237+ amrex::ParticleReal const h = 0 .0_prt;
238+
239+ // Evaluation of the full Thomas-BMT precession vector.
240+ amrex::ParticleReal gyro_anomaly = refpart.gyromagnetic_anomaly ;
241+ tbmt_precession_vector (x, ux, uy, uz, gamma, h, gyro_anomaly, Bx, By, Bz, Ex, Ey, Ez, lambdax, lambday, lambdaz);
242+
243+ // push the spin vector using the generator just determined
244+ rotate_spin (lambdax, lambday, lambdaz, sx, sy, sz);
245+
246+ // phase space push
247+ (*this )(x, y, t, px, py, pt, idcpu, refpart);
248+
249+ }
250+
251+
153252 /* * This pushes the covariance matrix. */
154253 using LinearTransport::operator ();
155254
@@ -209,6 +308,12 @@ for beam-quality preservation between plasma-accelerator stages",
209308 amrex::ParticleReal m_k; // ! linear focusing strength (field gradient)
210309 amrex::ParticleReal m_taper; // ! horizontal taper parameter
211310 int m_unit; // ! units for linear focusing strength (field gradient)
311+
312+ private:
313+ // constants that are independent of the individually tracked particle
314+ amrex::ParticleReal m_beta; // ! relativistic beta
315+ amrex::ParticleReal m_ibeta; // ! inverse of beta
316+ amrex::ParticleReal m_g; // ! quadrupole strength
212317 };
213318
214319} // namespace impactx
0 commit comments