ImpactX
Loading...
Searching...
No Matches
DipEdge.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_DIPEDGE_H
11#define IMPACTX_DIPEDGE_H
12
14#include "mixin/alignment.H"
15#include "mixin/beamoptic.H"
16#include "mixin/thin.H"
18#include "mixin/named.H"
19#include "mixin/nofinalize.H"
20
21#include <ablastr/constant.H>
22
23#include <AMReX_Enum.H>
24#include <AMReX_Extension.H>
25#include <AMReX_Math.H>
26#include <AMReX_REAL.H>
27#include <AMReX_SIMD.H>
28
29#include <cmath>
30#include <stdexcept>
31
32namespace impactx::elements
33{
34 struct DipEdge
35 : public mixin::Named,
36 public mixin::BeamOptic<DipEdge>,
37 public mixin::LinearTransport<DipEdge>,
38 public mixin::Thin,
39 public mixin::Alignment,
41 // At least on Intel AVX512, there is a small overhead to vectorize this element, see
42 // https://github.com/BLAST-ImpactX/impactx/pull/1002
43 // verify again after https://github.com/BLAST-ImpactX/impactx/pull/1158
44 // public amrex::simd::Vectorized<amrex::simd::native_simd_size_particlereal>
45 {
46 static constexpr auto type = "DipEdge";
48
50 linear,
51 nonlinear,
52 );
53
55 entry,
56 exit,
57 );
58
59 static constexpr amrex::ParticleReal DEFAULT_R = 1;
62 static constexpr amrex::ParticleReal DEFAULT_K1 = 0;
63 static constexpr amrex::ParticleReal DEFAULT_K2 = 1;
64 static constexpr amrex::ParticleReal DEFAULT_K3 = amrex::ParticleReal(1.0 / 6.0);
65 static constexpr amrex::ParticleReal DEFAULT_K4 = 0;
66 static constexpr amrex::ParticleReal DEFAULT_K5 = 0;
67 static constexpr amrex::ParticleReal DEFAULT_K6 = 0;
68 static constexpr Model DEFAULT_model = Model::linear;
69 static constexpr Location DEFAULT_location = Location::entry;
70 static constexpr bool DEFAULT_modify_ref_part = false;
71
112 Model model = DEFAULT_model,
113 Location location = DEFAULT_location,
114 bool modify_ref_part = DEFAULT_modify_ref_part,
118 std::optional<std::string> name = DEFAULT_name
119 )
120 : Named(std::move(name)),
121 Alignment(dx, dy, rotation_degree),
122 m_psi(psi), m_rc(rc), m_g(g), m_R(R), m_K0(K0), m_K1(K1), m_K2(K2), m_K3(K3), m_K4(K4), m_K5(K5), m_K6(K6), m_model(model), m_location(location), m_modify_ref_part(modify_ref_part)
123 {
124 }
125
131 void reverse ()
132 {
133 m_psi = -m_psi;
134 m_K2 = -m_K2;
135 m_location = (m_location == Location::entry)
136 ? Location::exit
137 : Location::entry;
138 }
139
141 using BeamOptic::operator();
142
150 void compute_constants ([[maybe_unused]] RefPart const & refpart)
151 {
152 using namespace amrex::literals; // for _rt and _prt
153 using amrex::Math::powi;
154
155 Alignment::compute_constants(refpart);
156
157 // constants for linear map:
158
159 // edge focusing matrix elements (zero gap)
160 m_R21 = std::tan(m_psi) / m_rc;
161
162 // first-order effect of nonzero gap
163 auto const [sin_psi, cos_psi] = amrex::Math::sincos(m_psi);
164 amrex::ParticleReal const vf = (1.0_prt + powi<2>(sin_psi))
165 / powi<3>(cos_psi)
166 * m_g * m_K2 / powi<2>(m_rc);
167
168 m_R43 = vf - m_R21;
169
170 // constants for nonlinear map:
171
172 // access reference particle values to find beta
173 m_beta = refpart.beta();
174
175 // expressions from eq (14) of Mitchell and Hwang, NAPAC2025
176 amrex::ParticleReal const tan_psi = sin_psi/cos_psi;
177 amrex::ParticleReal const sec_psi = 1_prt/cos_psi;
178
179 // the scale constant specifying entry or exit
180 amrex::ParticleReal const loc = m_location == Location::entry ? 1_prt : -1_prt;
181
182 m_c1 = (m_g / m_rc) * m_K1 / cos_psi;
183 m_c2_times_1plusdelta = powi<2>(m_g/m_rc) * m_K0 * sin_psi * powi<3>(sec_psi)/2_prt;
184 m_c3_times_1plusdelta = loc * powi<2>(m_g)/m_rc * m_K0 * powi<2>(sec_psi);
185 m_c4_times_1plusdelta = loc * (m_g / m_rc) * m_K1 * sin_psi / (powi<2>(cos_psi));
186 m_c5 = tan_psi / (2_prt * m_rc);
187 m_c6_times_1plusdelta = 0.5_prt * powi<2>(sin_psi)/(2_prt * m_rc * powi<3>(cos_psi)) * m_g/m_rc * m_K1;
188 m_c7_times_1plusdelta = 0.5_prt * powi<3>(sec_psi) * (m_g * m_K1 / (2_prt*powi<2>(m_rc)) + (1_prt+powi<2>(sin_psi))*m_g*m_K2/powi<2>(m_rc));
189 m_c8_times_1plusdelta = (1_prt/6_prt) * powi<3>(tan_psi) / (2_prt * powi<2>(m_rc));
190 m_c9_times_1plusdelta = 0.5_prt * tan_psi * powi<2>(sec_psi) / (2_prt * powi<2>(m_rc));
191 m_c10_times_1plusdelta = loc * powi<2>(tan_psi) / (2_prt * m_rc);
192 m_c11_times_1plusdelta = loc * 1_prt / (2_prt * m_rc);
193 m_c12_times_1plusdelta = (m_g > 0_prt) ? 1_prt / (24_prt) * (4_prt/cos_psi - 8_prt/(powi<3>(cos_psi))) * m_K3/(powi<2>(m_rc) * m_g) : 0_prt;
194 m_c13 = powi<2>(sin_psi) / (2_prt*powi<3>(cos_psi)) * powi<2>(m_g)/(m_rc*m_R) * m_K4;
195 m_c14 = 0.5_prt * sin_psi/(powi<3>(cos_psi)) * m_g/(m_rc*m_R) * m_K5;
196 m_c15 = (m_K6 / (m_rc*m_R)) / (powi<3>(cos_psi));
197 }
198
213 template<typename T_Real=amrex::ParticleReal, typename T_IdCpu=uint64_t>
216 T_Real & AMREX_RESTRICT x,
217 T_Real & AMREX_RESTRICT y,
218 T_Real & AMREX_RESTRICT t,
219 T_Real & AMREX_RESTRICT px,
220 T_Real & AMREX_RESTRICT py,
221 T_Real const & AMREX_RESTRICT pt,
222 [[maybe_unused]] T_IdCpu const & AMREX_RESTRICT idcpu,
223 [[maybe_unused]] RefPart const & AMREX_RESTRICT refpart
224 ) const
225 {
226
227 using namespace amrex::literals; // for _rt and _prt
228 using namespace std; // for cmath(float)
229 using amrex::Math::powi;
230
231 if (m_model == Model::linear) {
232 // apply linear model
233 px = px + m_R21 * x;
234 py = py + m_R43 * y;
235 } else {
236 // apply nonlinear model
237 T_Real xout = x;
238 T_Real pxout = px;
239 T_Real yout = y;
240 T_Real pyout = py;
241 T_Real tout = t;
242 // T_Real ptout = pt;
243
244 // compute particle momentum deviation delta + 1 (reciprocal)
245 T_Real const inv_delta1 = 1_prt / sqrt(1_prt - 2_prt*pt/m_beta + powi<2>(pt));
246
247 T_Real const c2 = m_c2_times_1plusdelta * inv_delta1;
248 T_Real const c3 = m_c3_times_1plusdelta * inv_delta1;
249 //T_Real const c4 = m_c4_times_1plusdelta * inv_delta1; //not currently used
250 //T_Real const c6 = m_c6_times_1plusdelta * inv_delta1; //not currently used
251 T_Real const c7 = m_c7_times_1plusdelta * inv_delta1;
252 T_Real const c8 = m_c8_times_1plusdelta * inv_delta1;
253 T_Real const c9 = m_c9_times_1plusdelta * inv_delta1;
254 T_Real const c10 = m_c10_times_1plusdelta * inv_delta1;
255 T_Real const c11 = m_c11_times_1plusdelta * inv_delta1;
256 T_Real const c12 = m_c12_times_1plusdelta * inv_delta1;
257
258 // update transverse coordinates
259 xout = x - c3 - c10*powi<2>(x) + (c10 + c11)*powi<2>(y);
260 yout = y + 2_prt * c10 * x * y;
261
262 // update transverse momenta
263 T_Real const D = 1_prt - 4_prt*c10*c11*powi<2>(y) - 4_prt*powi<2>(c10)*(powi<2>(x)+powi<2>(y));
264 T_Real const dRdx = -m_c13 + c2 + c3*m_c5 + 2_prt*(m_c14 - m_c5)*x + 3_prt*(m_c15/6_prt + c10*m_c5 + c8)*powi<2>(x)
265 + (-m_c15/2_prt + c10*m_c5 - c11*m_c5 - c9)*powi<2>(y);
266 T_Real const dRdy = 2_prt*(-m_c14 + m_c5 - c7)*y + 2_prt*(-m_c15/2_prt + c10*m_c5 - c11*m_c5 - c9)*x*y - 4_prt*c12*powi<3>(y);
267 T_Real const dXdx = 1_prt - 2_prt*c10*x;
268 T_Real const dXdy = 2_prt*(c10 + c11)*y;
269 T_Real const dYdx = 2_prt*c10*y;
270 T_Real const dYdy = 1_prt + 2_prt*c10*x;
271
272 pxout = (dYdy * (px - dRdx) - dYdx * (py - dRdy)) / D;
273 pyout = (-dXdy * (px - dRdx) + dXdx * (py - dRdy)) / D;
274
275 // update temporal coordinate
276 T_Real const dDelta_dpt = (pt - 1_prt/m_beta) * inv_delta1;
277 tout = t - dDelta_dpt * inv_delta1 * ((c2 + c3*m_c5)*x + (c10*m_c5 + c8)*powi<3>(x) - c7*powi<2>(y) - c12*powi<4>(y)
278 + (c10*m_c5 - c11*m_c5 - c9)*x*powi<2>(y) + pxout*(-c3 + (c11 + c10)*powi<2>(y) - c10*powi<2>(x)) + 2_prt*pyout*c10*x*y);
279
280 // optional removal of reference particle shift
283 T_Real const dx_ref = (m_modify_ref_part) ? -c3_ref : 0_prt;
284 T_Real const dpx_ref = (m_modify_ref_part) ? m_c13 - c2_ref - c3_ref*m_c5 : 0_prt;
285 T_Real const dt_ref = -c3_ref * dpx_ref / m_beta;
286 xout -= dx_ref;
287 pxout -= dpx_ref;
288 tout -= dt_ref;
289
290 x = xout;
291 px = pxout;
292 y = yout;
293 py = pyout;
294 t = tout;
295 // pt = ptout;
296 }
297 }
298
304 void operator() (RefPart & AMREX_RESTRICT refpart) const
305 {
306 using namespace amrex::literals; // for _rt and _prt
307 using amrex::Math::powi;
308
309 // assign input reference particle values
310 amrex::ParticleReal const x = refpart.x;
311 amrex::ParticleReal const z = refpart.z;
312 amrex::ParticleReal const t = refpart.t;
313 amrex::ParticleReal const px = refpart.px;
314 amrex::ParticleReal const pz = refpart.pz;
315 amrex::ParticleReal const pt = refpart.pt;
316
317 if (m_modify_ref_part && m_model == Model::nonlinear) {
318 // calculate expensive terms
319 amrex::ParticleReal const loc = m_location == Location::entry ? 1_prt : -1_prt;
320 auto const [sin_psi, cos_psi] = amrex::Math::sincos(m_psi);
321 amrex::ParticleReal const tan_psi = sin_psi/cos_psi;
322 amrex::ParticleReal const sec_psi = 1_prt/cos_psi;
323 amrex::ParticleReal const c2_ref = powi<2>(m_g/m_rc) * m_K0 * sin_psi * powi<3>(sec_psi)/2_prt;
324 amrex::ParticleReal const c3_ref = loc * powi<2>(m_g)/m_rc * m_K0 * powi<2>(sec_psi);
325 amrex::ParticleReal const c5_ref = tan_psi / (2_prt * m_rc);
326 amrex::ParticleReal const c13_ref = powi<2>(sin_psi) / (2_prt*powi<3>(cos_psi)) * powi<2>(m_g)/(m_rc*m_R) * m_K4;
327
328 amrex::ParticleReal const pnorm = std::sqrt(pt*pt-1_prt);
329 amrex::ParticleReal const dx_ref = -c3_ref;
330 amrex::ParticleReal const dpx_ref = c13_ref - c2_ref - c3_ref*c5_ref;
331 amrex::ParticleReal const dpz_ref = pnorm*(-1_prt + std::sqrt(1_prt - powi<2>(dpx_ref/pnorm)));
332 amrex::ParticleReal const dt_ref = -c3_ref * dpx_ref / refpart.beta();
333
334 // advance position and momentum
335 refpart.x = x + (pz/pnorm)*dx_ref;
336 refpart.z = z - (px/pnorm)*dx_ref;
337 refpart.t = t + dt_ref;
338 refpart.px = px + (pz/pnorm)*dpx_ref + (px/pnorm)*dpz_ref;
339 refpart.pz = pz - (px/pnorm)*dpx_ref + (pz/pnorm)*dpz_ref;
340 // refpart.pt = pt; // Unchanged
341 }
342
343 }
344
346 using LinearTransport::operator();
347
354 Map6x6
355 transport_map ([[maybe_unused]] RefPart const & AMREX_RESTRICT refpart) const
356 {
357 using namespace amrex::literals; // for _rt and _prt
358 using amrex::Math::powi;
359
360 // initialize linear map matrix elements
362
363 // edge focusing matrix elements (zero gap)
364 amrex::ParticleReal const R21 = std::tan(m_psi) / m_rc;
365
366 // first-order effect of nonzero gap
367 auto const [sin_psi, cos_psi] = amrex::Math::sincos(m_psi);
368 amrex::ParticleReal const vf = (1.0_prt + powi<2>(sin_psi))
369 / powi<3>(cos_psi)
370 * m_g * m_K2 / powi<2>(m_rc);
371
372 amrex::ParticleReal const R43 = vf - R21;
373
374 // set non-identity matrix elements
375 R(2,1) = R21;
376 R(4,3) = R43;
377
378 // apply the transverse rotation (roll) alignment error
379 return rotate_aligned_map(R);
380 }
381
393 Model m_model;
394 Location m_location;
396
397 private:
398 // constants that are independent of the individually tracked particle,
399 // see: compute_constants() to refresh
405 };
406
407} // namespace impactx
408
410
411#endif // IMPACTX_DIPEDGE_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
amrex_particle_real ParticleReal
constexpr T powi(T x) noexcept
__host__ __device__ std::pair< double, double > sincos(double x)
__host__ __device__ GpuComplex< T > sqrt(const GpuComplex< T > &a_z) noexcept
Definition All.H:55
@ 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
Definition DipEdge.H:45
amrex::ParticleReal m_c11_times_1plusdelta
Definition DipEdge.H:404
static constexpr auto type
Definition DipEdge.H:46
amrex::ParticleReal m_g
bend radius in m
Definition DipEdge.H:384
Location m_location
fringe field model: linear or nonlinear
Definition DipEdge.H:394
amrex::ParticleReal m_K5
fringe field integral
Definition DipEdge.H:391
amrex::ParticleReal m_K3
fringe field integral
Definition DipEdge.H:389
amrex::ParticleReal m_c5
Definition DipEdge.H:401
amrex::ParticleReal m_c9_times_1plusdelta
Definition DipEdge.H:403
amrex::ParticleReal m_c15
Definition DipEdge.H:401
static constexpr bool DEFAULT_modify_ref_part
Definition DipEdge.H:70
amrex::ParticleReal m_c14
Definition DipEdge.H:401
amrex::ParticleReal m_K0
scale length in m
Definition DipEdge.H:386
amrex::ParticleReal m_beta
Definition DipEdge.H:400
amrex::ParticleReal m_R21
apply fringe field to reference particle (true/false)
Definition DipEdge.H:400
amrex::ParticleReal m_R43
Definition DipEdge.H:400
amrex::ParticleReal m_c4_times_1plusdelta
Definition DipEdge.H:402
static constexpr amrex::ParticleReal DEFAULT_R
Definition DipEdge.H:59
static constexpr amrex::ParticleReal DEFAULT_K1
Definition DipEdge.H:62
static constexpr amrex::ParticleReal DEFAULT_K5
Definition DipEdge.H:66
amrex::ParticleReal m_c13
Definition DipEdge.H:401
static constexpr Model DEFAULT_model
Definition DipEdge.H:68
amrex::ParticleReal m_c1
Definition DipEdge.H:401
amrex::ParticleReal m_K4
fringe field integral
Definition DipEdge.H:390
amrex::ParticleReal m_K2
fringe field integral
Definition DipEdge.H:388
amrex::ParticleReal m_c8_times_1plusdelta
Definition DipEdge.H:403
AMREX_ENUM_IN_CLASS(Location, entry, exit,)
static constexpr amrex::ParticleReal DEFAULT_K2
Definition DipEdge.H:63
amrex::ParticleReal m_c12_times_1plusdelta
Definition DipEdge.H:404
AMREX_ENUM_IN_CLASS(Model, linear, nonlinear,)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void operator()(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 const &AMREX_RESTRICT pt, T_IdCpu const &AMREX_RESTRICT idcpu, RefPart const &AMREX_RESTRICT refpart) const
Definition DipEdge.H:215
amrex::ParticleReal m_c3_times_1plusdelta
Definition DipEdge.H:402
static constexpr amrex::ParticleReal DEFAULT_K3
Definition DipEdge.H:64
DipEdge(amrex::ParticleReal psi, amrex::ParticleReal rc, amrex::ParticleReal g, amrex::ParticleReal R=DEFAULT_R, amrex::ParticleReal K0=DEFAULT_K0, amrex::ParticleReal K1=DEFAULT_K1, amrex::ParticleReal K2=DEFAULT_K2, amrex::ParticleReal K3=DEFAULT_K3, amrex::ParticleReal K4=DEFAULT_K4, amrex::ParticleReal K5=DEFAULT_K5, amrex::ParticleReal K6=DEFAULT_K6, Model model=DEFAULT_model, Location location=DEFAULT_location, bool modify_ref_part=DEFAULT_modify_ref_part, amrex::ParticleReal dx=DEFAULT_dx, amrex::ParticleReal dy=DEFAULT_dy, amrex::ParticleReal rotation_degree=DEFAULT_rotation_degree, std::optional< std::string > name=DEFAULT_name)
Definition DipEdge.H:100
void compute_constants(RefPart const &refpart)
Definition DipEdge.H:150
AMREX_GPU_HOST AMREX_FORCE_INLINE Map6x6 transport_map(RefPart const &AMREX_RESTRICT refpart) const
Definition DipEdge.H:355
amrex::ParticleReal m_c7_times_1plusdelta
Definition DipEdge.H:403
amrex::ParticleReal m_K6
fringe field integral
Definition DipEdge.H:392
void reverse()
Definition DipEdge.H:131
amrex::ParticleReal m_c10_times_1plusdelta
Definition DipEdge.H:403
amrex::ParticleReal m_c2_times_1plusdelta
Definition DipEdge.H:402
amrex::ParticleReal m_psi
Definition DipEdge.H:382
static constexpr amrex::ParticleReal DEFAULT_K6
Definition DipEdge.H:67
amrex::ParticleReal m_c6_times_1plusdelta
Definition DipEdge.H:402
amrex::ParticleReal m_R
gap parameter in m
Definition DipEdge.H:385
bool m_modify_ref_part
fringe field location: entry, or exit
Definition DipEdge.H:395
amrex::ParticleReal m_rc
pole face angle in rad
Definition DipEdge.H:383
ImpactXParticleContainer::ParticleType PType
Definition DipEdge.H:47
static constexpr Location DEFAULT_location
Definition DipEdge.H:69
amrex::ParticleReal m_K1
fringe field integral
Definition DipEdge.H:387
static constexpr amrex::ParticleReal DEFAULT_K0
Definition DipEdge.H:60
static constexpr amrex::ParticleReal DEFAULT_K4
Definition DipEdge.H:65
Model m_model
fringe field integral
Definition DipEdge.H:393
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
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 thin.H:24