OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
ThinTracker.cpp
Go to the documentation of this file.
1 // ------------------------------------------------------------------------
2 // $RCSfile: ThinTracker.cpp,v $
3 // ------------------------------------------------------------------------
4 // $Revision: 1.1.1.1 $
5 // ------------------------------------------------------------------------
6 // Copyright: see Copyright.readme
7 // ------------------------------------------------------------------------
8 //
9 // Class: ThinTracker
10 // The visitor class for tracking a bunch of particles through a beamline
11 // using a thin-lens approximation for all elements.
12 //
13 // ------------------------------------------------------------------------
14 // Class category: Algorithms
15 // ------------------------------------------------------------------------
16 //
17 // $Date: 2000/03/27 09:32:33 $
18 // $Author: fci $
19 //
20 // ------------------------------------------------------------------------
21 
22 #include "Algorithms/ThinTracker.h"
23 #include "AbsBeamline/BeamBeam.h"
26 #include "AbsBeamline/Corrector.h"
27 #include "AbsBeamline/Diagnostic.h"
28 #include "AbsBeamline/Drift.h"
29 #include "AbsBeamline/Degrader.h"
32 #include "AbsBeamline/Lambertson.h"
33 #include "AbsBeamline/Marker.h"
34 #include "AbsBeamline/Monitor.h"
35 #include "AbsBeamline/Multipole.h"
36 #include "AbsBeamline/Probe.h"
37 #include "AbsBeamline/RBend.h"
38 #include "AbsBeamline/RFCavity.h"
40 #include "AbsBeamline/SBend.h"
41 #include "AbsBeamline/Separator.h"
42 #include "AbsBeamline/Septum.h"
43 #include "AbsBeamline/Solenoid.h"
46 
49 #include "Algorithms/PartData.h"
53 #include "Beamlines/Beamline.h"
54 #include "Fields/BMultipoleField.h"
55 #include "Physics/Physics.h"
57 #include <cmath>
58 #include <complex>
59 
60 using namespace Physics;
61 
62 
63 // Class ThinTracker
64 // ------------------------------------------------------------------------
65 
66 ThinTracker::ThinTracker(const Beamline &beamline, const PartData &reference,
67  bool revBeam, bool revTrack):
68  Tracker(beamline, reference, revBeam, revTrack)
69 {}
70 
71 
74  const PartData &reference,
75  bool revBeam, bool revTrack):
76  Tracker(beamline, bunch, reference, revBeam, revTrack)
77 {}
78 
79 
81 {}
82 
83 
85  // If x > explim, exp(-x) is outside machine limits.
86  static const double explim = 150.0;
87 
88  // Parameters of the opposite bunch.
89  const double NN = itsReference.getQ() * bb.getBunchCharge();
90  const Vector3D &displacement = bb.getBunchDisplacement();
91  const Matrix3D &sigma = bb.getBunchMoment();
92 
93  if(NN != 0.0) {
94  //
95  // N1 N2 mu_0 c^2 q_e^2 N1 N2
96  // fk = 2 r_e * ------- = ---------------- * ------- =
97  // gamma 2 pi m_0 gamma
98  //
99  // mu_0 c^2 q_e N1 N2 q_e N1 N2
100  // = -------------------- = -------------------- .
101  // 2 pi p_r 2 pi epsilon_0 p_r
102  //
103  // where m_0 is the particle rest mass in Joule.
104  //
105  // and p_r = (gamma m_0) / q_e is the momentum in eV.
106  //
107  const double fk = q_e * NN /
109  const double dx = displacement(0);
110  const double dy = displacement(1);
111  const double sx2 = std::abs(sigma(0, 0));
112  const double sy2 = std::abs(sigma(1, 1));
113  const double sx = sqrt(sx2);
114  const double sy = sqrt(sy2);
115 
116  if(sx2 == sy2) {
117  // Limit for sigma(x)^2 = sigma(y)^2.
118 
119  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); i++) {
120  OpalParticle part = itsBunch_m->get_part(i);
121 
122  double xs = part.x() - dx;
123  double ys = part.y() - dy;
124  double rho2 = xs * xs + ys * ys;
125  double tk = rho2 / (2.0 * sx2);
126  double phi = 0.0;
127 
128  if(tk > explim) {
129  phi = fk / rho2;
130  } else if(tk != 0.0) {
131  phi = fk * (1.0 - exp(- tk)) / rho2;
132  }
133 
134  part.px() += xs * phi;
135  part.py() += ys * phi;
136 
137  itsBunch_m->set_part(part, i);
138  }
139  } else {
140  // Case sigma(x)^2 != sigma(y)^2.
141  const double r = sqrt(2.0 * std::abs(sx2 - sy2));
142  double rk = flip_s * flip_B * fk * sqrt(pi) / r;
143 
144  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); i++) {
145  OpalParticle part = itsBunch_m->get_part(i);
146 
147  double xs = part.x() - dx;
148  double ys = part.y() - dy;
149  double xr = std::abs(xs) / r;
150  double yr = std::abs(ys) / r;
151  std::complex<double> W = Werrf(std::complex<double>(xr, yr));
152 
153  double tk = (xs * xs / sx2 + ys * ys / sy2) / 2.0;
154  if(tk <= explim) {
155  W -= exp(- tk) * Werrf(std::complex<double>(xr * sy / sx, yr * sx / sy));
156  }
157 
158  part.px() += rk * ((xs > 0.0) ? std::imag(W) : - std::imag(W));
159  part.py() += rk * ((ys > 0.0) ? std::real(W) : - std::real(W));
160 
161  itsBunch_m->set_part(part, i);
162  }
163  }
164  }
165 }
166 
167 
169 // applyDrift(flip_s * bstp.getElementLength());
170 }
171 
174 }
175 
178 }
179 
181  //do nothing
182 }
183 
184 
186  // Do nothing.
187 }
188 
189 
191  // Drift through first half of length.
192  double length = flip_s * corr.getElementLength();
193  if(length) applyDrift(length / 2.0);
194 
195  // Apply kick.
196  double scale = (flip_s * flip_B * corr.getElementLength() *
198  const BDipoleField &field = corr.getField();
199 
200  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); i++) {
201  OpalParticle part = itsBunch_m->get_part(i);
202  part.px() -= field.getBy() * scale;
203  part.py() += field.getBx() * scale;
204  itsBunch_m->set_part(part, i);
205  }
206 
207  // Drift through second half of length.
208  if(length) applyDrift(length / 2.0);
209 }
210 
211 
213  // The diagnostic has no effect on particle tracking.
215 }
216 
217 
218 void ThinTracker::visitDrift(const Drift &drift) {
220 }
221 
224 }
225 
227  // Assume the particle go through the magnet's window.
229 }
230 
231 
233  // Do nothing.
234 }
235 
236 
239 }
240 
241 
243  double length = flip_s * mult.getElementLength();
244  const BMultipoleField &field = mult.getField();
245  double scale = (flip_B * itsReference.getQ() * c) / itsReference.getP();
246 
247  if(length) {
248  // Drift through first half of the length.
249  applyDrift(length / 2.0);
250 
251  // Apply multipole kick, field is per unit length.
252  scale *= length;
253  applyThinMultipole(field, scale);
254 
255  // Drift through second half of the length, field is per unit length.
256  applyDrift(length / 2.0);
257  } else {
258  // Thin multipole, field is integral(K*dl).
259  scale *= flip_s;
260  applyThinMultipole(field, scale);
261  }
262 }
263 
264 
266  // Do nothing.
267 }
268 
269 
270 void ThinTracker::visitRBend(const RBend &bend) {
271  // Geometry.
272  const RBendGeometry &geometry = bend.getGeometry();
273  double length = flip_s * geometry.getElementLength();
274  double angle = flip_s * geometry.getBendAngle();
275 
276  // Magnetic field.
277  const BMultipoleField &field = bend.getField();
278 
279  // Drift to mid-plane.
280  applyDrift(length / 2.0);
281 
282  // Apply multipole kick and linear approximation to geometric bend.
283  double scale = (flip_B * itsReference.getQ() * c) / itsReference.getP();
284  if(length) scale *= length;
285 
286  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); i++) {
287  OpalParticle part = itsBunch_m->get_part(i);
288  int order = field.order();
289 
290  if(order > 0) {
291  double x = part.x();
292  double y = part.y();
293  double kx = + field.normal(order);
294  double ky = - field.skew(order);
295 
296  while(--order > 0) {
297  double kxt = x * kx - y * ky;
298  double kyt = x * ky + y * kx;
299  kx = kxt + field.normal(order);
300  ky = kyt - field.skew(order);
301  }
302 
303  part.px() -= kx * scale - angle * (1.0 + part.pt());
304  part.py() += ky * scale;
305  part.t() -= angle * x;
306  }
307  itsBunch_m->set_part(part, i);
308  }
309 
310  // Drift to out-plane.
311  applyDrift(length / 2.0);
312 }
313 
314 
316  // Drift through half length.
317  double length = flip_s * as.getElementLength();
318  if(length) applyDrift(length / 2.0);
319 
320  // Apply accelerating voltage.
321  double freq = as.getFrequency();
322  double peak = flip_s * as.getAmplitude() / itsReference.getP();
323  double kin = itsReference.getM() / itsReference.getP();
324 
325  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); i++) {
326  OpalParticle part = itsBunch_m->get_part(i);
327  double pt = (part.pt() + 1.0);
328  double speed = (c * pt) / sqrt(pt * pt + kin * kin);
329  double phase = as.getPhase() + (freq * part.t()) / speed;
330  part.pt() += peak * sin(phase) / pt;
331  itsBunch_m->set_part(part, i);
332  }
333 
334  if(length) applyDrift(length / 2.0);
335 }
336 
337 
339  // *** MISSING *** Tracking for RF Quadrupole.
341 }
342 
343 
344 void ThinTracker::visitSBend(const SBend &bend) {
345  const PlanarArcGeometry &geometry = bend.getGeometry();
346  double length = flip_s * geometry.getElementLength();
347  double angle = flip_s * geometry.getBendAngle();
348 
349  // Magnetic field.
350  const BMultipoleField &field = bend.getField();
351 
352  // Drift to mid-plane.
353  applyDrift(length / 2.0);
354 
355  // Apply multipole kick and linear approximation to geometric bend.
356  double scale = (flip_B * itsReference.getQ() * c) / itsReference.getP();
357  if(length) scale *= length;
358 
359  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); i++) {
360  OpalParticle part = itsBunch_m->get_part(i);
361  int order = field.order();
362 
363  if(order > 0) {
364  double x = part.x();
365  double y = part.y();
366  double kx = + field.normal(order);
367  double ky = - field.skew(order);
368 
369  while(--order > 0) {
370  double kxt = x * kx - y * ky;
371  double kyt = x * ky + y * kx;
372  kx = kxt + field.normal(order);
373  ky = kyt - field.skew(order);
374  }
375 
376  part.px() -= kx * scale - angle * (1.0 + part.pt());
377  part.py() += ky * scale;
378  part.t() -= angle * x;
379  }
380  itsBunch_m->set_part(part, i);
381  }
382 
383  // Drift to out-plane.
384  applyDrift(length / 2.0);
385 }
386 
387 
389  // Drift through first half of length.
390  double length = flip_s * sep.getElementLength();
391  if(length) {
392  applyDrift(length / 2.0);
393 
394  double scale = (length * itsReference.getQ()) / itsReference.getP();
395  double Ex = scale * sep.getEx();
396  double Ey = scale * sep.getEy();
397 
398  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); i++) {
399  OpalParticle part = itsBunch_m->get_part(i);
400  double pt = 1.0 + part.pt();
401  part.px() += Ex / pt;
402  part.py() += Ey / pt;
403  itsBunch_m->set_part(part, i);
404  }
405 
406  applyDrift(length / 2.0);
407  }
408 }
409 
410 
411 void ThinTracker::visitSeptum(const Septum &sept) {
412  // Assume the particles go through the magnet's window.
414 }
415 
416 
417 void ThinTracker::visitSolenoid(const Solenoid &solenoid) {
418  double length = flip_s * solenoid.getElementLength();
419 
420  if(length) {
421  double ks = (flip_B * itsReference.getQ() * solenoid.getBz() * c) /
422  (2.0 * itsReference.getP());
423 
424  if(ks) {
425  double kin = itsReference.getM() / itsReference.getP();
426  double ref = kin * kin;
427 
428  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); i++) {
429  OpalParticle part = itsBunch_m->get_part(i);
430 
431  double pt = part.pt() + 1.0;
432  double px = part.px() + ks * part.y();
433  double py = part.py() - ks * part.x();
434  double pz = sqrt(pt * pt - px * px - py * py);
435 
436  double k = ks / pz;
437  double C = cos(k * length);
438  double S = sin(k * length);
439 
440  double xt = C * part.x() + S * part.y();
441  double yt = C * part.y() - S * part.x();
442  double pxt = C * part.px() + S * part.py();
443  double pyt = C * part.py() - S * part.px();
444 
445  part.x() = C * xt + (S / k) * pxt;
446  part.y() = C * yt + (S / k) * pyt;
447  part.px() = C * pxt - (S * k) * xt;
448  part.py() = C * pyt - (S * k) * yt;
449  part.t() += length * (pt * ref - (px * px + py * py + 3.0 * pt * pt * ref) / 2.0);
450  itsBunch_m->set_part(part, i);
451  }
452  } else {
453  applyDrift(length);
454  }
455  }
456 }
457 
458 
459 void ThinTracker::applyDrift(double length) {
460  double kin = itsReference.getM() / itsReference.getP();
461  double ref = kin * kin;
462 
463  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); i++) {
464  OpalParticle part = itsBunch_m->get_part(i);
465 
466  double px = part.px();
467  double py = part.py();
468  double pt = part.pt();
469  double lByPz = length / (1.0 + pt);
470  part.x() += px * lByPz;
471  part.y() += py * lByPz;
472  part.t() += length * (pt * ref - (px * px + py * py + 3.0 * pt * pt * ref) / 2.0);
473  itsBunch_m->set_part(part, i);
474  }
475 }
double & py()
Get reference to vertical momentum (no dimension).
Definition: OpalParticle.h:122
virtual const Matrix3D & getBunchMoment() const =0
Get moments.
virtual BMultipoleField & getField() override=0
Get multipole expansion of field.
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
virtual BDipoleField & getField()=0
Return the corrector field.
virtual double getAmplitude() const =0
Get RF amplitude.
double normal(int) const
Get component.
The field of a magnetic dipole.
Definition: BDipoleField.h:31
virtual BMultipoleField & getField() override=0
Get multipole expansion of field.
virtual void visitBeamStripping(const BeamStripping &)
Apply the algorithm to a beam stripping.
Interface for septum magnet.
Definition: Septum.h:11
virtual BMultipoleField & getField() override=0
Get multipole field.
Interface for electrostatic separator.
Definition: Separator.h:33
virtual double getPhase() const =0
Get RF phase.
Interface for beam position monitors.
Definition: Monitor.h:41
Interface for RF Quadrupole.
Definition: RFQuadrupole.h:30
A simple arc in the XZ plane.
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
double & x()
Get reference to horizontal position in m.
Definition: OpalParticle.h:110
constexpr double two_pi
The value of .
Definition: Physics.h:34
virtual double getBz() const =0
Get solenoid field Bz in Teslas.
Interface for RF cavity.
Definition: ParallelPlate.h:36
Tps< T > exp(const Tps< T > &x)
Exponential.
Definition: TpsMath.h:165
virtual double getElementLength() const override
Get design length.
Definition: RFCavity.cpp:818
Particle reference data.
Definition: PartData.h:38
virtual double getEx() const =0
Get horizontal component Ex of field in V/m.
Interface for general corrector.
Definition: Corrector.h:35
virtual double getEy() const =0
Get vertical component Ey of field in V/m.
Abstract collimator.
Definition: RBend.h:73
Interface for beam diagnostics.
Definition: Diagnostic.h:32
virtual void visitBeamBeam(const BeamBeam &)
Apply algorithm to BeamBeam.
Definition: ThinTracker.cpp:84
Interface for a marker.
Definition: Marker.h:32
virtual double getBy() const
Get vertical component.
virtual PlanarArcGeometry & getGeometry() override=0
Get SBend geometry.
Interface for drift space.
Definition: Drift.h:33
Interface for general multipole.
Definition: Multipole.h:46
FLieGenerator< T, N > real(const FLieGenerator< std::complex< T >, N > &)
Take real part of a complex generator.
virtual ~ThinTracker()
Definition: ThinTracker.cpp:80
T deg(T x)
Convert radians to degrees.
Definition: matheval.hpp:82
virtual double getElementLength() const override
Get design length.
Definition: Solenoid.cpp:256
virtual void visitSeptum(const Septum &)
Apply algorithm to Septum.
double getBendAngle() const
Get angle.
Interface for probe.
Definition: Probe.h:16
virtual double getBunchCharge() const =0
Get bunch charge.
virtual void visitParallelPlate(const ParallelPlate &)
Apply algorithm to Solenoid.
double skew(int) const
Get component.
double getQ() const
The constant charge per particle.
Definition: PartData.h:107
virtual void visitSeparator(const Separator &)
Apply algorithm to Separator.
virtual double getElementLength() const
Get design length.
Definition: ElementBase.h:511
OpalParticle get_part(int ii)
virtual void visitCorrector(const Corrector &)
Apply algorithm to Corrector.
const double pi
Definition: fftpack.cpp:894
virtual double getFrequency() const =0
Get RF frequencey.
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:52
Interface for cyclotron collimator.
Definition: CCollimator.h:13
virtual void visitDrift(const Drift &)
Apply algorithm to Drift.
virtual void visitSBend(const SBend &)
Apply algorithm to SBend.
Abstract beam-beam interaction.
Definition: BeamBeam.h:37
virtual void visitFlexibleCollimator(const FlexibleCollimator &)
Apply the algorithm to a flexible collimator.
Definition: SBend.h:68
std::complex< double > Werrf(std::complex< double > z)
Complex error function.
Interface for cyclotron valley.
3-dimensional matrix.
Definition: Matrix3D.h:33
virtual double getBx() const
Get horizontal component.
void applyDrift(double length)
Interface for solenoids.
Definition: Solenoid.h:36
virtual double getElementLength() const
Get element length.
An abstract sequence of beam line components.
Definition: Beamline.h:37
const PartData itsReference
The reference information.
virtual const Vector3D & getBunchDisplacement() const =0
Get displacement.
size_t getLocalNum() const
void set_part(FVector< double, 6 > z, int ii)
virtual double getElementLength() const
Get element length.
double & pt()
Get reference to relative momentum error (no dimension).
Definition: OpalParticle.h:125
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
The geometry for a RBend element.
Definition: RBendGeometry.h:41
A 3-dimension vector.
Definition: Vector3D.h:31
virtual void visitMarker(const Marker &)
Apply algorithm to Marker.
double getP() const
The constant reference momentum per particle.
Definition: PartData.h:117
virtual void visitDegrader(const Degrader &)
Apply the algorithm to a drift.
virtual void visitMonitor(const Monitor &)
Apply algorithm to Monitor.
virtual void visitProbe(const Probe &)
Apply algorithm to Probe.
The magnetic field of a multipole.
Interface for RF cavity.
Definition: RFCavity.h:37
double getM() const
The constant mass per particle.
Definition: PartData.h:112
virtual void visitSolenoid(const Solenoid &)
Apply algorithm to Solenoid.
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
Abstract collimator.
Definition: Degrader.h:37
virtual void visitRFCavity(const RFCavity &)
Apply algorithm to RFCavity.
virtual void visitLambertson(const Lambertson &)
Apply algorithm to Lambertson.
constexpr double q_e
The elementary charge in As.
Definition: Physics.h:76
double & y()
Get reference to vertical displacement in m.
Definition: OpalParticle.h:113
constexpr double epsilon_0
The permittivity of vacuum in As/Vm.
Definition: Physics.h:58
double & t()
Get reference to longitudinal displacement c*t in m.
Definition: OpalParticle.h:116
virtual void visitDiagnostic(const Diagnostic &)
Apply algorithm to Diagnostic.
virtual RBendGeometry & getGeometry() override=0
Get RBend geometry.
virtual double getBendAngle() const
Get angle.
double & px()
Get reference to horizontal momentum (no dimension).
Definition: OpalParticle.h:119
virtual void visitRBend(const RBend &)
Apply algorithm to RBend.
virtual void visitCCollimator(const CCollimator &)
Apply algorithm to Collimator.
FLieGenerator< T, N > imag(const FLieGenerator< std::complex< T >, N > &)
Take imaginary part of a complex generator.
virtual void visitRFQuadrupole(const RFQuadrupole &)
Apply algorithm to RFQuadrupole.
OpalParticle position.
Definition: OpalParticle.h:38
virtual void visitCyclotronValley(const CyclotronValley &)
Apply the algorithm to a CyclotronValley.
PartBunchBase< double, 3 > * itsBunch_m
The bunch of particles to be tracked.
Definition: Tracker.h:174
int order() const
Return order.
virtual void visitMultipole(const Multipole &)
Apply algorithm to Multipole.
void applyThinMultipole(const BMultipoleField &field, double factor)
Definition: Tracker.cpp:154
Interface for a Lambertson septum.
Definition: Lambertson.h:33
Track particles or bunches.
Definition: Tracker.h:84