ImpactX
Loading...
Searching...
No Matches
DipEdge.H
Go to the documentation of this file.
1/* Copyright 2022-2023 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 <AMReX_Enum.H>
22#include <AMReX_Extension.H>
23#include <AMReX_Math.H>
24#include <AMReX_REAL.H>
25#include <AMReX_SIMD.H>
26
27#include <cmath>
28#include <stdexcept>
29
30
31namespace impactx::elements
32{
33 namespace dipedge
34 {
36 linear,
37 nonlinear
38 );
39 AMREX_ENUM(Location,
40 entry,
41 exit
42 );
43 }
44
45 struct DipEdge
46 : public mixin::Named,
47 public mixin::BeamOptic<DipEdge>,
48 public mixin::LinearTransport<DipEdge>,
49 public mixin::Thin,
50 public mixin::Alignment,
52 // At least on Intel AVX512, there is a small overhead to vectorize this element, see
53 // https://github.com/BLAST-ImpactX/impactx/pull/1002
54 // verify again after https://github.com/BLAST-ImpactX/impactx/pull/1158
55 // public amrex::simd::Vectorized<amrex::simd::native_simd_size_particlereal>
56 {
57 static constexpr auto type = "DipEdge";
59
100 dipedge::Model model,
101 dipedge::Location location,
102 bool modify_ref_part = false,
105 amrex::ParticleReal rotation_degree = 0,
106 std::optional<std::string> name = std::nullopt
107 )
108 : Named(std::move(name)),
109 Alignment(dx, dy, rotation_degree),
110 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)
111 {
112 }
113
119 void reverse ()
120 {
121 m_psi = -m_psi;
122 m_K2 = -m_K2;
123 m_location = (m_location == dipedge::Location::entry)
124 ? dipedge::Location::exit
125 : dipedge::Location::entry;
126 }
127
129 using BeamOptic::operator();
130
138 void compute_constants ([[maybe_unused]] RefPart const & refpart)
139 {
140 using namespace amrex::literals; // for _rt and _prt
141 using amrex::Math::powi;
142
143 Alignment::compute_constants(refpart);
144
145 // constants for linear map:
146
147 // edge focusing matrix elements (zero gap)
148 m_R21 = std::tan(m_psi) / m_rc;
149
150 // first-order effect of nonzero gap
151 auto const [sin_psi, cos_psi] = amrex::Math::sincos(m_psi);
152 amrex::ParticleReal const vf = (1.0_prt + powi<2>(sin_psi))
153 / powi<3>(cos_psi)
154 * m_g * m_K2 / powi<2>(m_rc);
155
156 m_R43 = vf - m_R21;
157
158 // constants for nonlinear map:
159
160 // access reference particle values to find beta
161 m_beta = refpart.beta();
162
163 // expressions from eq (14) of Mitchell and Hwang, NAPAC2025
164 amrex::ParticleReal const tan_psi = sin_psi/cos_psi;
165 amrex::ParticleReal const sec_psi = 1_prt/cos_psi;
166
167 // the scale constant specifying entry or exit
168 amrex::ParticleReal const loc = m_location == dipedge::Location::entry ? 1_prt : -1_prt;
169
170 m_c1 = (m_g / m_rc) * m_K1 / cos_psi;
171 m_c2_times_1plusdelta = powi<2>(m_g/m_rc) * m_K0 * sin_psi * powi<3>(sec_psi)/2_prt;
172 m_c3_times_1plusdelta = loc * powi<2>(m_g)/m_rc * m_K0 * powi<2>(sec_psi);
173 m_c4_times_1plusdelta = loc * (m_g / m_rc) * m_K1 * sin_psi / (powi<2>(cos_psi));
174 m_c5 = tan_psi / (2_prt * m_rc);
175 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;
176 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));
177 m_c8_times_1plusdelta = (1_prt/6_prt) * powi<3>(tan_psi) / (2_prt * powi<2>(m_rc));
178 m_c9_times_1plusdelta = 0.5_prt * tan_psi * powi<2>(sec_psi) / (2_prt * powi<2>(m_rc));
179 m_c10_times_1plusdelta = loc * powi<2>(tan_psi) / (2_prt * m_rc);
180 m_c11_times_1plusdelta = loc * 1_prt / (2_prt * m_rc);
181 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;
182 m_c13 = powi<2>(sin_psi) / (2_prt*powi<3>(cos_psi)) * powi<2>(m_g)/(m_rc*m_R) * m_K4;
183 m_c14 = 0.5_prt * sin_psi/(powi<3>(cos_psi)) * m_g/(m_rc*m_R) * m_K5;
184 m_c15 = (m_K6 / (m_rc*m_R)) / (powi<3>(cos_psi));
185 }
186
201 template<typename T_Real=amrex::ParticleReal, typename T_IdCpu=uint64_t>
204 T_Real & AMREX_RESTRICT x,
205 T_Real & AMREX_RESTRICT y,
206 [[maybe_unused]] T_Real & AMREX_RESTRICT t,
207 T_Real & AMREX_RESTRICT px,
208 T_Real & AMREX_RESTRICT py,
209 [[maybe_unused]] T_Real & AMREX_RESTRICT pt,
210 [[maybe_unused]] T_IdCpu & AMREX_RESTRICT idcpu,
211 [[maybe_unused]] RefPart const & AMREX_RESTRICT refpart
212 ) const
213 {
214
215 using namespace amrex::literals; // for _rt and _prt
216 using namespace std; // for cmath(float)
217 using amrex::Math::powi;
218
219 // shift due to alignment errors of the element
220 shift_in(x, y, px, py);
221
222 if (m_model == dipedge::Model::linear) {
223 // apply linear model
224 px = px + m_R21 * x;
225 py = py + m_R43 * y;
226 } else {
227 // apply nonlinear model
228 T_Real xout = x;
229 T_Real pxout = px;
230 T_Real yout = y;
231 T_Real pyout = py;
232 T_Real tout = t;
233 T_Real ptout = pt;
234
235 // compute particle momentum deviation delta + 1 (reciprocal)
236 T_Real const inv_delta1 = 1_prt / sqrt(1_prt - 2_prt*pt/m_beta + powi<2>(pt));
237
238 T_Real const c2 = m_c2_times_1plusdelta * inv_delta1;
239 T_Real const c3 = m_c3_times_1plusdelta * inv_delta1;
240 //T_Real const c4 = m_c4_times_1plusdelta * inv_delta1; //not currently used
241 //T_Real const c6 = m_c6_times_1plusdelta * inv_delta1; //not currently used
242 T_Real const c7 = m_c7_times_1plusdelta * inv_delta1;
243 T_Real const c8 = m_c8_times_1plusdelta * inv_delta1;
244 T_Real const c9 = m_c9_times_1plusdelta * inv_delta1;
245 T_Real const c10 = m_c10_times_1plusdelta * inv_delta1;
246 T_Real const c11 = m_c11_times_1plusdelta * inv_delta1;
247 T_Real const c12 = m_c12_times_1plusdelta * inv_delta1;
248
249 // update transverse coordinates
250 xout = x - c3 - c10*powi<2>(x) + (c10 + c11)*powi<2>(y);
251 yout = y + 2_prt * c10 * x * y;
252
253 // update transverse momenta
254 T_Real const D = 1_prt - 4_prt*c10*c11*powi<2>(y) - 4_prt*powi<2>(c10)*(powi<2>(x)+powi<2>(y));
255 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)
256 + (-m_c15/2_prt + c10*m_c5 - c11*m_c5 - c9)*powi<2>(y);
257 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);
258 T_Real const dXdx = 1_prt - 2_prt*c10*x;
259 T_Real const dXdy = 2_prt*(c10 + c11)*y;
260 T_Real const dYdx = 2_prt*c10*y;
261 T_Real const dYdy = 1_prt + 2_prt*c10*x;
262
263 pxout = (dYdy * (px - dRdx) - dYdx * (py - dRdy)) / D;
264 pyout = (-dXdy * (px - dRdx) + dXdx * (py - dRdy)) / D;
265
266 // update temporal coordinate
267 T_Real const dDelta_dpt = (pt - 1_prt/m_beta) * inv_delta1;
268 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)
269 + (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);
270
271 // optional removal of reference particle shift
274 T_Real const dx_ref = (m_modify_ref_part) ? -c3_ref : 0_prt;
275 T_Real const dpx_ref = (m_modify_ref_part) ? m_c13 - c2_ref - c3_ref*m_c5 : 0_prt;
276 T_Real const dt_ref = -c3_ref * dpx_ref / m_beta;
277 xout -= dx_ref;
278 pxout -= dpx_ref;
279 tout -= dt_ref;
280
281 x = xout;
282 px = pxout;
283 y = yout;
284 py = pyout;
285 t = tout;
286 pt = ptout;
287 }
288
289 // undo shift due to alignment errors of the element
290 shift_out(x, y, px, py);
291 }
292
293
299 void operator() (RefPart & AMREX_RESTRICT refpart) const
300 {
301 using namespace amrex::literals; // for _rt and _prt
302 using amrex::Math::powi;
303
304 // assign input reference particle values
305 [[maybe_unused]] amrex::ParticleReal const x = refpart.x;
306 [[maybe_unused]] amrex::ParticleReal const z = refpart.z;
307 [[maybe_unused]] amrex::ParticleReal const t = refpart.t;
308 [[maybe_unused]] amrex::ParticleReal const px = refpart.px;
309 [[maybe_unused]] amrex::ParticleReal const pz = refpart.pz;
310 [[maybe_unused]] amrex::ParticleReal const pt = refpart.pt;
311
312 if (m_modify_ref_part && m_model == dipedge::Model::nonlinear) {
313 // calculate expensive terms
314 amrex::ParticleReal const loc = m_location == dipedge::Location::entry ? 1_prt : -1_prt;
315 auto const [sin_psi, cos_psi] = amrex::Math::sincos(m_psi);
316 amrex::ParticleReal const tan_psi = sin_psi/cos_psi;
317 amrex::ParticleReal const sec_psi = 1_prt/cos_psi;
318 amrex::ParticleReal const c2_ref = powi<2>(m_g/m_rc) * m_K0 * sin_psi * powi<3>(sec_psi)/2_prt;
319 amrex::ParticleReal const c3_ref = loc * powi<2>(m_g)/m_rc * m_K0 * powi<2>(sec_psi);
320 amrex::ParticleReal const c5_ref = tan_psi / (2_prt * m_rc);
321 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;
322
323 amrex::ParticleReal const pnorm = std::sqrt(pt*pt-1_prt);
324 amrex::ParticleReal const dx_ref = -c3_ref;
325 amrex::ParticleReal const dpx_ref = c13_ref - c2_ref - c3_ref*c5_ref;
326 amrex::ParticleReal const dpz_ref = pnorm*(-1_prt + std::sqrt(1_prt - powi<2>(dpx_ref/pnorm)));
327 amrex::ParticleReal const dt_ref = -c3_ref * dpx_ref / refpart.beta();
328
329 // advance position and momentum
330 refpart.x = x + (pz/pnorm)*dx_ref;
331 refpart.z = z - (px/pnorm)*dx_ref;
332 refpart.t = t + dt_ref;
333 refpart.px = px + (pz/pnorm)*dpx_ref + (px/pnorm)*dpz_ref;
334 refpart.pz = pz - (px/pnorm)*dpx_ref + (pz/pnorm)*dpz_ref;
335 // refpart.pt = pt; // Unchanged
336 }
337
338 }
339
340
342 using LinearTransport::operator();
343
350 Map6x6
351 transport_map ([[maybe_unused]] RefPart const & AMREX_RESTRICT refpart) const
352 {
353 using namespace amrex::literals; // for _rt and _prt
354 using amrex::Math::powi;
355
356 // initialize linear map matrix elements
358
359 // edge focusing matrix elements (zero gap)
360 amrex::ParticleReal const R21 = std::tan(m_psi) / m_rc;
361
362 // first-order effect of nonzero gap
363 auto const [sin_psi, cos_psi] = amrex::Math::sincos(m_psi);
364 amrex::ParticleReal const vf = (1.0_prt + powi<2>(sin_psi))
365 / powi<3>(cos_psi)
366 * m_g * m_K2 / powi<2>(m_rc);
367
368 amrex::ParticleReal const R43 = vf - R21;
369
370 // set non-identity matrix elements
371 R(2,1) = R21;
372 R(4,3) = R43;
373
374 return R;
375 }
376
388 dipedge::Model m_model;
389 dipedge::Location m_location;
391
392 private:
393 // constants that are independent of the individually tracked particle,
394 // see: compute_constants() to refresh
400 };
401
402} // namespace impactx
403
405
406#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
__host__ __device__ std::pair< double, double > sincos(double x)
constexpr T powi(T x) noexcept
__host__ __device__ GpuComplex< T > sqrt(const GpuComplex< T > &a_z) noexcept
Definition DipEdge.H:34
AMREX_ENUM(Model, linear, nonlinear)
Definition All.H:56
@ 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:56
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 &AMREX_RESTRICT pt, T_IdCpu &AMREX_RESTRICT idcpu, RefPart const &AMREX_RESTRICT refpart) const
Definition DipEdge.H:203
amrex::ParticleReal m_c11_times_1plusdelta
Definition DipEdge.H:399
static constexpr auto type
Definition DipEdge.H:57
amrex::ParticleReal m_g
bend radius in m
Definition DipEdge.H:379
amrex::ParticleReal m_K5
fringe field integral
Definition DipEdge.H:386
amrex::ParticleReal m_K3
fringe field integral
Definition DipEdge.H:384
amrex::ParticleReal m_c5
Definition DipEdge.H:396
amrex::ParticleReal m_c9_times_1plusdelta
Definition DipEdge.H:398
amrex::ParticleReal m_c15
Definition DipEdge.H:396
amrex::ParticleReal m_c14
Definition DipEdge.H:396
amrex::ParticleReal m_K0
scale length in m
Definition DipEdge.H:381
amrex::ParticleReal m_beta
Definition DipEdge.H:395
amrex::ParticleReal m_R21
apply fringe field to reference particle (true/false)
Definition DipEdge.H:395
amrex::ParticleReal m_R43
Definition DipEdge.H:395
amrex::ParticleReal m_c4_times_1plusdelta
Definition DipEdge.H:397
amrex::ParticleReal m_c13
Definition DipEdge.H:396
amrex::ParticleReal m_c1
Definition DipEdge.H:396
amrex::ParticleReal m_K4
fringe field integral
Definition DipEdge.H:385
dipedge::Location m_location
fringe field model: linear or nonlinear
Definition DipEdge.H:389
amrex::ParticleReal m_K2
fringe field integral
Definition DipEdge.H:383
amrex::ParticleReal m_c8_times_1plusdelta
Definition DipEdge.H:398
amrex::ParticleReal m_c12_times_1plusdelta
Definition DipEdge.H:399
amrex::ParticleReal m_c3_times_1plusdelta
Definition DipEdge.H:397
dipedge::Model m_model
fringe field integral
Definition DipEdge.H:388
void compute_constants(RefPart const &refpart)
Definition DipEdge.H:138
AMREX_GPU_HOST AMREX_FORCE_INLINE Map6x6 transport_map(RefPart const &AMREX_RESTRICT refpart) const
Definition DipEdge.H:351
amrex::ParticleReal m_c7_times_1plusdelta
Definition DipEdge.H:398
amrex::ParticleReal m_K6
fringe field integral
Definition DipEdge.H:387
void reverse()
Definition DipEdge.H:119
amrex::ParticleReal m_c10_times_1plusdelta
Definition DipEdge.H:398
amrex::ParticleReal m_c2_times_1plusdelta
Definition DipEdge.H:397
amrex::ParticleReal m_psi
Definition DipEdge.H:377
DipEdge(amrex::ParticleReal psi, amrex::ParticleReal rc, amrex::ParticleReal g, amrex::ParticleReal R, amrex::ParticleReal K0, amrex::ParticleReal K1, amrex::ParticleReal K2, amrex::ParticleReal K3, amrex::ParticleReal K4, amrex::ParticleReal K5, amrex::ParticleReal K6, dipedge::Model model, dipedge::Location location, bool modify_ref_part=false, amrex::ParticleReal dx=0, amrex::ParticleReal dy=0, amrex::ParticleReal rotation_degree=0, std::optional< std::string > name=std::nullopt)
Definition DipEdge.H:88
amrex::ParticleReal m_c6_times_1plusdelta
Definition DipEdge.H:397
amrex::ParticleReal m_R
gap parameter in m
Definition DipEdge.H:380
bool m_modify_ref_part
fringe field location: entry, or exit
Definition DipEdge.H:390
amrex::ParticleReal m_rc
pole face angle in rad
Definition DipEdge.H:378
ImpactXParticleContainer::ParticleType PType
Definition DipEdge.H:58
amrex::ParticleReal m_K1
fringe field integral
Definition DipEdge.H:382
Definition alignment.H:27
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void shift_out(T_Real &AMREX_RESTRICT x, T_Real &AMREX_RESTRICT y, T_Real &AMREX_RESTRICT px, T_Real &AMREX_RESTRICT py) const
Definition alignment.H:109
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal dy() const
Definition alignment.H:146
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal dx() const
Definition alignment.H:136
Alignment(amrex::ParticleReal dx, amrex::ParticleReal dy, amrex::ParticleReal rotation_degree)
Definition alignment.H:36
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void shift_in(T_Real &AMREX_RESTRICT x, T_Real &AMREX_RESTRICT y, T_Real &AMREX_RESTRICT px, T_Real &AMREX_RESTRICT py) const
Definition alignment.H:78
Definition beamoptic.H:436
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 thin.H:24