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 T_Real & AMREX_RESTRICT t,
207 T_Real & AMREX_RESTRICT px,
208 T_Real & AMREX_RESTRICT py,
209 T_Real const & AMREX_RESTRICT pt,
210 [[maybe_unused]] T_IdCpu const & 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 if (m_model == dipedge::Model::linear) {
220 // apply linear model
221 px = px + m_R21 * x;
222 py = py + m_R43 * y;
223 } else {
224 // apply nonlinear model
225 T_Real xout = x;
226 T_Real pxout = px;
227 T_Real yout = y;
228 T_Real pyout = py;
229 T_Real tout = t;
230 // T_Real ptout = pt;
231
232 // compute particle momentum deviation delta + 1 (reciprocal)
233 T_Real const inv_delta1 = 1_prt / sqrt(1_prt - 2_prt*pt/m_beta + powi<2>(pt));
234
235 T_Real const c2 = m_c2_times_1plusdelta * inv_delta1;
236 T_Real const c3 = m_c3_times_1plusdelta * inv_delta1;
237 //T_Real const c4 = m_c4_times_1plusdelta * inv_delta1; //not currently used
238 //T_Real const c6 = m_c6_times_1plusdelta * inv_delta1; //not currently used
239 T_Real const c7 = m_c7_times_1plusdelta * inv_delta1;
240 T_Real const c8 = m_c8_times_1plusdelta * inv_delta1;
241 T_Real const c9 = m_c9_times_1plusdelta * inv_delta1;
242 T_Real const c10 = m_c10_times_1plusdelta * inv_delta1;
243 T_Real const c11 = m_c11_times_1plusdelta * inv_delta1;
244 T_Real const c12 = m_c12_times_1plusdelta * inv_delta1;
245
246 // update transverse coordinates
247 xout = x - c3 - c10*powi<2>(x) + (c10 + c11)*powi<2>(y);
248 yout = y + 2_prt * c10 * x * y;
249
250 // update transverse momenta
251 T_Real const D = 1_prt - 4_prt*c10*c11*powi<2>(y) - 4_prt*powi<2>(c10)*(powi<2>(x)+powi<2>(y));
252 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)
253 + (-m_c15/2_prt + c10*m_c5 - c11*m_c5 - c9)*powi<2>(y);
254 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);
255 T_Real const dXdx = 1_prt - 2_prt*c10*x;
256 T_Real const dXdy = 2_prt*(c10 + c11)*y;
257 T_Real const dYdx = 2_prt*c10*y;
258 T_Real const dYdy = 1_prt + 2_prt*c10*x;
259
260 pxout = (dYdy * (px - dRdx) - dYdx * (py - dRdy)) / D;
261 pyout = (-dXdy * (px - dRdx) + dXdx * (py - dRdy)) / D;
262
263 // update temporal coordinate
264 T_Real const dDelta_dpt = (pt - 1_prt/m_beta) * inv_delta1;
265 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)
266 + (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);
267
268 // optional removal of reference particle shift
271 T_Real const dx_ref = (m_modify_ref_part) ? -c3_ref : 0_prt;
272 T_Real const dpx_ref = (m_modify_ref_part) ? m_c13 - c2_ref - c3_ref*m_c5 : 0_prt;
273 T_Real const dt_ref = -c3_ref * dpx_ref / m_beta;
274 xout -= dx_ref;
275 pxout -= dpx_ref;
276 tout -= dt_ref;
277
278 x = xout;
279 px = pxout;
280 y = yout;
281 py = pyout;
282 t = tout;
283 // pt = ptout;
284 }
285 }
286
287
293 void operator() (RefPart & AMREX_RESTRICT refpart) const
294 {
295 using namespace amrex::literals; // for _rt and _prt
296 using amrex::Math::powi;
297
298 // assign input reference particle values
299 amrex::ParticleReal const x = refpart.x;
300 amrex::ParticleReal const z = refpart.z;
301 amrex::ParticleReal const t = refpart.t;
302 amrex::ParticleReal const px = refpart.px;
303 amrex::ParticleReal const pz = refpart.pz;
304 amrex::ParticleReal const pt = refpart.pt;
305
306 if (m_modify_ref_part && m_model == dipedge::Model::nonlinear) {
307 // calculate expensive terms
308 amrex::ParticleReal const loc = m_location == dipedge::Location::entry ? 1_prt : -1_prt;
309 auto const [sin_psi, cos_psi] = amrex::Math::sincos(m_psi);
310 amrex::ParticleReal const tan_psi = sin_psi/cos_psi;
311 amrex::ParticleReal const sec_psi = 1_prt/cos_psi;
312 amrex::ParticleReal const c2_ref = powi<2>(m_g/m_rc) * m_K0 * sin_psi * powi<3>(sec_psi)/2_prt;
313 amrex::ParticleReal const c3_ref = loc * powi<2>(m_g)/m_rc * m_K0 * powi<2>(sec_psi);
314 amrex::ParticleReal const c5_ref = tan_psi / (2_prt * m_rc);
315 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;
316
317 amrex::ParticleReal const pnorm = std::sqrt(pt*pt-1_prt);
318 amrex::ParticleReal const dx_ref = -c3_ref;
319 amrex::ParticleReal const dpx_ref = c13_ref - c2_ref - c3_ref*c5_ref;
320 amrex::ParticleReal const dpz_ref = pnorm*(-1_prt + std::sqrt(1_prt - powi<2>(dpx_ref/pnorm)));
321 amrex::ParticleReal const dt_ref = -c3_ref * dpx_ref / refpart.beta();
322
323 // advance position and momentum
324 refpart.x = x + (pz/pnorm)*dx_ref;
325 refpart.z = z - (px/pnorm)*dx_ref;
326 refpart.t = t + dt_ref;
327 refpart.px = px + (pz/pnorm)*dpx_ref + (px/pnorm)*dpz_ref;
328 refpart.pz = pz - (px/pnorm)*dpx_ref + (pz/pnorm)*dpz_ref;
329 // refpart.pt = pt; // Unchanged
330 }
331
332 }
333
334
336 using LinearTransport::operator();
337
344 Map6x6
345 transport_map ([[maybe_unused]] RefPart const & AMREX_RESTRICT refpart) const
346 {
347 using namespace amrex::literals; // for _rt and _prt
348 using amrex::Math::powi;
349
350 // initialize linear map matrix elements
352
353 // edge focusing matrix elements (zero gap)
354 amrex::ParticleReal const R21 = std::tan(m_psi) / m_rc;
355
356 // first-order effect of nonzero gap
357 auto const [sin_psi, cos_psi] = amrex::Math::sincos(m_psi);
358 amrex::ParticleReal const vf = (1.0_prt + powi<2>(sin_psi))
359 / powi<3>(cos_psi)
360 * m_g * m_K2 / powi<2>(m_rc);
361
362 amrex::ParticleReal const R43 = vf - R21;
363
364 // set non-identity matrix elements
365 R(2,1) = R21;
366 R(4,3) = R43;
367
368 // apply the transverse rotation (roll) alignment error
369 return rotate_aligned_map(R);
370 }
371
383 dipedge::Model m_model;
384 dipedge::Location m_location;
386
387 private:
388 // constants that are independent of the individually tracked particle,
389 // see: compute_constants() to refresh
395 };
396
397} // namespace impactx
398
400
401#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 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::ParticleReal m_c11_times_1plusdelta
Definition DipEdge.H:394
static constexpr auto type
Definition DipEdge.H:57
amrex::ParticleReal m_g
bend radius in m
Definition DipEdge.H:374
amrex::ParticleReal m_K5
fringe field integral
Definition DipEdge.H:381
amrex::ParticleReal m_K3
fringe field integral
Definition DipEdge.H:379
amrex::ParticleReal m_c5
Definition DipEdge.H:391
amrex::ParticleReal m_c9_times_1plusdelta
Definition DipEdge.H:393
amrex::ParticleReal m_c15
Definition DipEdge.H:391
amrex::ParticleReal m_c14
Definition DipEdge.H:391
amrex::ParticleReal m_K0
scale length in m
Definition DipEdge.H:376
amrex::ParticleReal m_beta
Definition DipEdge.H:390
amrex::ParticleReal m_R21
apply fringe field to reference particle (true/false)
Definition DipEdge.H:390
amrex::ParticleReal m_R43
Definition DipEdge.H:390
amrex::ParticleReal m_c4_times_1plusdelta
Definition DipEdge.H:392
amrex::ParticleReal m_c13
Definition DipEdge.H:391
amrex::ParticleReal m_c1
Definition DipEdge.H:391
amrex::ParticleReal m_K4
fringe field integral
Definition DipEdge.H:380
dipedge::Location m_location
fringe field model: linear or nonlinear
Definition DipEdge.H:384
amrex::ParticleReal m_K2
fringe field integral
Definition DipEdge.H:378
amrex::ParticleReal m_c8_times_1plusdelta
Definition DipEdge.H:393
amrex::ParticleReal m_c12_times_1plusdelta
Definition DipEdge.H:394
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:203
amrex::ParticleReal m_c3_times_1plusdelta
Definition DipEdge.H:392
dipedge::Model m_model
fringe field integral
Definition DipEdge.H:383
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:345
amrex::ParticleReal m_c7_times_1plusdelta
Definition DipEdge.H:393
amrex::ParticleReal m_K6
fringe field integral
Definition DipEdge.H:382
void reverse()
Definition DipEdge.H:119
amrex::ParticleReal m_c10_times_1plusdelta
Definition DipEdge.H:393
amrex::ParticleReal m_c2_times_1plusdelta
Definition DipEdge.H:392
amrex::ParticleReal m_psi
Definition DipEdge.H:372
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:392
amrex::ParticleReal m_R
gap parameter in m
Definition DipEdge.H:375
bool m_modify_ref_part
fringe field location: entry, or exit
Definition DipEdge.H:385
amrex::ParticleReal m_rc
pole face angle in rad
Definition DipEdge.H:373
ImpactXParticleContainer::ParticleType PType
Definition DipEdge.H:58
amrex::ParticleReal m_K1
fringe field integral
Definition DipEdge.H:377
Definition alignment.H:29
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal dy() const
Definition alignment.H:189
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::ParticleReal dx() const
Definition alignment.H:179
AMREX_GPU_HOST AMREX_FORCE_INLINE Map6x6 rotate_aligned_map(Map6x6 const &R) const
Definition alignment.H:263
Alignment(amrex::ParticleReal dx, amrex::ParticleReal dy, amrex::ParticleReal rotation_degree)
Definition alignment.H:39
Definition beamoptic.H:529
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