1818#include " mixin/named.H"
1919#include " mixin/nofinalize.H"
2020#include " mixin/lineartransport.H"
21+ #include " mixin/spintransport.H"
2122
2223#include < AMReX_Extension.H>
2324#include < AMReX_Math.H>
@@ -36,6 +37,7 @@ namespace impactx::elements
3637 public mixin::Thick,
3738 public mixin::Alignment,
3839 public mixin::PipeAperture,
40+ public mixin::SpinTransport,
3941 public mixin::NoFinalize
4042 // At least on Intel AVX512, there is a small overhead to vectorize this element for DP and a gain for SP, see
4143 // https://github.com/BLAST-ImpactX/impactx/pull/1002
@@ -289,6 +291,84 @@ namespace impactx::elements
289291 return R;
290292 }
291293
294+ /* * This operator pushes the particle spin.
295+ *
296+ * @param x particle position in x
297+ * @param y particle position in y
298+ * @param t particle position in t
299+ * @param px particle momentum in x
300+ * @param py particle momentum in y
301+ * @param pt particle momentum in t
302+ * @param sx particle spin x-component
303+ * @param sy particle spin y-component
304+ * @param sz particle spin z-component
305+ * @param idcpu particle global index
306+ * @param refpart reference particle (unused)
307+ */
308+ template <typename T_Real=amrex::ParticleReal, typename T_IdCpu=uint64_t >
309+ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
310+ void operator () (
311+ T_Real & AMREX_RESTRICT x,
312+ T_Real & AMREX_RESTRICT y,
313+ T_Real & AMREX_RESTRICT t,
314+ T_Real & AMREX_RESTRICT px,
315+ T_Real & AMREX_RESTRICT py,
316+ T_Real & AMREX_RESTRICT pt,
317+ T_Real & AMREX_RESTRICT sx,
318+ T_Real & AMREX_RESTRICT sy,
319+ T_Real & AMREX_RESTRICT sz,
320+ T_IdCpu & AMREX_RESTRICT idcpu,
321+ [[maybe_unused]] RefPart const & AMREX_RESTRICT refpart
322+ ) const
323+ {
324+ using namespace amrex ::literals; // for _rt and _prt
325+
326+ // initialize the three components of the axis-angle vector
327+ T_Real lambdax = 0_prt;
328+ T_Real lambday = 0_prt;
329+ T_Real lambdaz = 0_prt;
330+
331+ // the value of the gyromagnetic anomaly (TO BE PASSED FROM USER INPUT)
332+ // currently, the default value for electrons is used
333+ amrex::ParticleReal G = 0.00115965218046 ;
334+ amrex::ParticleReal const gyro_const = 1_prt + G * refpart.gamma ();
335+
336+ // length of the current slice
337+ amrex::ParticleReal const slice_ds = m_ds / nslice ();
338+
339+ // compute phase advance per unit length in s (in rad/m)
340+ amrex::ParticleReal const omega = std::sqrt (std::abs (m_k));
341+
342+ // compute trigonometric quantities
343+ sin_omega_ds = std::sin (omega*m_slice_ds);
344+ cos_omega_ds = std::cos (omega*m_slice_ds);
345+ sinh_omega_ds = std::sinh (omega*m_slice_ds);
346+ cosh_omega_ds = std::cosh (omega*m_slice_ds);
347+
348+ if (m_k > 0 .0_prt)
349+ {
350+
351+ // horizontally focusing quad case
352+ lambdax = -gyro_const * ( py*(cosh_omega_ds - 1_prt) + y*omega*sinh_omega_ds );
353+ lambday = -gyro_const * ( px*(1_prt - cos_omega_ds) + x*omega*sin_omega_ds );
354+
355+ } else if (m_k < 0 .0_prt)
356+ {
357+
358+ // horizontally defocusing quad case
359+ lambdax = -gyro_const * ( px*(1_prt - cos_omega_ds) + x*omega*sin_omega_ds );
360+ lambday = -gyro_const * ( py*(cosh_omega_ds - 1_prt) + y*omega*sinh_omega_ds );
361+
362+ } else {
363+ // treat as a drift
364+ }
365+
366+ // push the spin vector using the generator just determined
367+ rotate_spin (lambdax,lambday,lambdaz,sx,sy,sz);
368+
369+ }
370+
371+
292372 /* * This pushes the covariance matrix. */
293373 using LinearTransport::operator ();
294374
0 commit comments