OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
ThickMapper.cpp
Go to the documentation of this file.
1 // ------------------------------------------------------------------------
2 // $RCSfile: ThickMapper.cpp,v $
3 // ------------------------------------------------------------------------
4 // $Revision: 1.3.4.3 $
5 // ------------------------------------------------------------------------
6 // Copyright: see Copyright.readme
7 // ------------------------------------------------------------------------
8 //
9 // Class: ThickMapper
10 // The visitor class for building a map of given order for a beamline
11 // using a finite-length lenses for all elements.
12 // Multipole-like elements are done by expanding the Lie series.
13 //
14 // ------------------------------------------------------------------------
15 //
16 // $Date: 2004/11/12 20:10:11 $
17 // $Author: adelmann $
18 //
19 // ------------------------------------------------------------------------
20 
21 #include "Algorithms/ThickMapper.h"
22 
24 #include "AbsBeamline/Corrector.h"
25 #include "AbsBeamline/Degrader.h"
26 #include "AbsBeamline/Diagnostic.h"
27 #include "AbsBeamline/Drift.h"
30 #include "AbsBeamline/Lambertson.h"
31 #include "AbsBeamline/Marker.h"
32 #include "AbsBeamline/Monitor.h"
33 #include "AbsBeamline/Multipole.h"
34 #include "AbsBeamline/Probe.h"
35 #include "AbsBeamline/RBend.h"
36 #include "AbsBeamline/RFCavity.h"
38 #include "AbsBeamline/SBend.h"
39 #include "AbsBeamline/Separator.h"
40 #include "AbsBeamline/Septum.h"
41 #include "AbsBeamline/Solenoid.h"
44 
48 #include "Beamlines/Beamline.h"
49 
50 #include "Fields/BMultipoleField.h"
51 #include "FixedAlgebra/FTps.h"
52 #include "FixedAlgebra/FTpsMath.h"
53 #include "FixedAlgebra/FVps.h"
54 
55 #include "Physics/Physics.h"
56 
57 #include "Utilities/NumToStr.h"
58 
59 class Beamline;
60 class PartData;
61 using Physics::c;
62 
63 #define PSdim 6
69 
70 namespace {
71  Vector implicitIntStep(const Vector &zin, const VSeries &f, const MxSeries gradf, double ds,
72  int nx = 20);
73  Vector implicitInt4(const Vector &zin, const VSeries &f, double s, double ds,
74  int nx = 20, int cx = 4);
75 };
76 
77 // Class ThickMapper
78 // ------------------------------------------------------------------------
79 
81  const PartData &reference,
82  bool backBeam, bool backTrack):
83  Mapper(beamline, reference, backBeam, backTrack)
84 {}
85 
86 
88 {}
89 
90 
92  // *** MISSING *** Map for beam-beam.
93 }
94 
96  // *** MISSING *** Map for beam Stripping.
97 }
98 
101 }
102 
103 
105  double length = flip_s * corr.getElementLength();
106 
107  // Drift through first half of length.
108  if(length != 0.0) applyDrift(length / 2.0);
109 
110  // Apply kick.
111  double scale =
113  const BDipoleField &field = corr.getField();
114  itsMap[PX] -= field.getBy() * scale;
115  itsMap[PY] += field.getBx() * scale;
116 
117  // Drift through second half of length.
118  if(length != 0.0) applyDrift(length / 2.0);
119 }
120 
123 }
124 
126  // The diagnostic has no effect on the map.
128 }
129 
130 
131 void ThickMapper::visitDrift(const Drift &drift) {
133 }
134 
137 }
138 
140  // Assume the particle go through the magnet's window.
142 }
143 
144 
145 void ThickMapper::visitMarker(const Marker &marker) {
146  // Do nothing.
147 }
148 
149 
152 }
153 
154 
156  //std::cerr << "==> In ThickMapper::visitMultipole(const Multipole &mult)" << std::endl;
157  // Geometry and Field
158  double length = flip_s * mult.getElementLength();
159  double scale = (flip_B * itsReference.getQ() * c) / itsReference.getP();
160  const BMultipoleField &field = mult.getField();
161  int order = field.order();
162 
163  if(length == 0.0) {
164  // Thin multipole, field coefficients are integral(B*dl).
165  scale *= flip_s;
166  applyThinMultipole(field, scale);
167  } else {
168  // Finite-length multipole, field coefficients are B.
169  // Apply entrance fringe field.
170  applyEntranceFringe(0.0, 0.0, field, scale);
171 
172  // Remove closed orbit from map.
173  Vector co = itsMap.constantTerm();
174  itsMap.setMinOrder(1);
175 
176  // Construct the Hamiltonian H about that closed orbit.
177  // (1) Define variables.
178  static const Series px = Series::makeVariable(PX);
179  static const Series py = Series::makeVariable(PY);
180  static const Series pt = Series::makeVariable(PT) + 1.0; // 1 + \delta = p/p0
181  static const Map ident;
182  Map translate = co + ident;
183  // (2) Construct kinematic terms.
184  double kin = itsReference.getM() / itsReference.getP(); // = 1/(beta*gamma)
185  Series E_trans = sqrt((pt * pt + kin * kin).substitute(translate), order) / itsReference.getBeta();
186  Series pz_trans = sqrt((pt * pt - px * px - py * py).substitute(translate), order);
187  // (3) Build vector potential in straight reference frame.
188  Series As = buildMultipoleVectorPotential(field) * scale;
190  // (4) Build Hamiltonian.
191  Series H_trans = E_trans - pz_trans + As.substitute(translate);
192 
193  // Propagate closed orbit.
194  // Build J.grad(H).
195  VSeries JgradH;
196  for(int i = 0; i < PSdim; i += 2) {
197  JgradH[ i ] = H_trans.derivative(i + 1);
198  JgradH[i+1] = -H_trans.derivative(i);
199  }
200  // Do integration to propagate closed orbit using implicitInt4().
201  static const Vector zeroV;
202  Vector new_co = co + implicitInt4(zeroV, JgradH, length, 0.5 * length);
203 
204  // Propagate map.
205  H_trans.setMinOrder(2); // Can do this because we assume zin is on the closed orbit.
206  itsMap = ExpMap((-length) * H_trans).substitute(itsMap);
207 
208  // Add new closed orbit to propagated map.
209  itsMap += new_co;
210 
211  // Apply exit fringe field.
212  applyExitFringe(0.0, 0.0, field, scale);
213  }
214  //std::cerr << "==> Leaving ThickMapper::visitMultipole(...)" << std::endl;
215 }
216 
217 void ThickMapper::visitProbe(const Probe &prob) {
218  // Do nothing.
219 }
220 
222  // Do nothing here.
223 }
224 
225 void ThickMapper::visitRBend(const RBend &bend) {
226  //std::cerr << "==> In ThickMapper::visitRBend(const RBend &bend)" << std::endl;
227  // Geometry and Field.
228  const RBendGeometry &geometry = bend.getGeometry();
229  double length = flip_s * geometry.getElementLength();
230  double scale = (flip_B * itsReference.getQ() * c) / itsReference.getP();
231  const BMultipoleField &field = bend.getField();
232  int order = field.order();
233  double beta_inv = 1.0 / itsReference.getBeta();
234 
235  // Compute slices and stepsize.
236  int nSlices = (int) bend.getSlices();
237  double stepsize = bend.getStepsize();
238  if(stepsize != 0.0) {
239  int nst = (int) ceil(length / stepsize);
240  nSlices = std::max(nSlices, nst);
241  }
242  double d_ell = length / nSlices;
243 
244  if(length == 0.0) {
245  // Thin RBend.
246  double half_angle = flip_s * geometry.getBendAngle() / 2.0;
247  Euclid3D rotat = Euclid3D::YRotation(- half_angle);
248  applyTransform(rotat, 0.0); // Transform from in-plane to mid-plane.
249  applyThinMultipole(field, scale); // Apply multipole kick.
250  applyTransform(rotat, 0.0); // Transform from mid-plane to out-plane.
251  } else {
252  // Finite-length RBend.
253  // Define variables, ...
254  static const Series x = Series::makeVariable(X);
255  static const Series px = Series::makeVariable(PX);
256  static const Series py = Series::makeVariable(PY);
257  static const Series de = Series::makeVariable(PT);
258  static const Series de2 = de * de;
259  static const Series pxy2 = px * px + py * py;
260  static const Map ident;
261  // kinematic terms, ...
262  double kin = itsReference.getM() / itsReference.getP(); // mc^2/pc = 1/(beta*gamma)
263  double kin2 = kin * kin;
264  // and the vector potential, actually -As in straight reference frame.
265  Series As = buildMultipoleVectorPotential(field) * scale;
267 
268  if(back_path) {
269  // Apply rotation global to local.
270  applyTransform(Inverse(geometry.getExitPatch()));
271  // Apply entrance fringe field.
272  applyEntranceFringe(bend.getExitFaceRotation(), scale, field, scale);
273  } else {
274  // Apply rotation global to local.
275  applyTransform(geometry.getEntrancePatch());
276  // Apply exit fringe field.
277  applyEntranceFringe(bend.getEntryFaceRotation(), scale, field, scale);
278  }
279 
280  // Remove closed orbit from map.
281  Vector co = itsMap.constantTerm();
282  itsMap.setMinOrder(1);
283 
284  // Propagate closed orbit and map.
285  for(int slice = 0; slice < nSlices; ++slice) {
286  // Construct translated Hamiltonian about closed orbit.
287  double p1 = 1.0 + co[PT];
288  Series de12 = p1 * p1 + 2.0 * p1 * de + de2;
289  Series H_trans = beta_inv * sqrt(de12 + kin2, order)
290  - sqrt(de12 - co[PX] * co[PX] - co[PY] * co[PY]
291  - 2.0 * (co[PX] * px + co[PY] * py) - pxy2, order)
292  + As.substitute(co + ident);
293  // Build J.grad(H).
294  VSeries JgradH;
295  for(int i = 0; i < PSdim; i += 2) {
296  JgradH[ i ] = H_trans.derivative(i + 1);
297  JgradH[i+1] = -H_trans.derivative(i);
298  }
299  // Do integration to propagate closed orbit using implicitInt4().
300  static const Vector zeroV;
301  Vector new_co = co + implicitInt4(zeroV, JgradH, d_ell, 0.5 * d_ell);
302  // Propagate map using mid-point of closed-orbit path.
303  Vector mid_co = (co + new_co) / 2.0;
304  p1 = 1.0 + mid_co[PT];
305  de12 = p1 * p1 + 2.0 * p1 * de + de2;
306  H_trans = beta_inv * sqrt(de12 + kin2, order)
307  - sqrt(de12 - mid_co[PX] * mid_co[PX] - mid_co[PY] * mid_co[PY]
308  - 2.0 * (mid_co[PX] * px + mid_co[PY] * py) - pxy2, order)
309  + As.substitute(mid_co + ident);
310  H_trans.setMinOrder(2); // Can do this because we expand about the closed orbit.
311  itsMap = ExpMap((-d_ell) * H_trans).substitute(itsMap);
312  // Set closed-orbit to new value.
313  co = new_co;
314  }
315 
316  // Add new closed orbit to propagated map.
317  itsMap += co;
318 
319  if(back_path) {
320  // Apply exit fringe field.
321  applyExitFringe(bend.getEntryFaceRotation(), scale, field, scale);
322  // Transform from local to global.
324  } else {
325  // Apply entrance fringe field.
326  applyEntranceFringe(bend.getExitFaceRotation(), scale, field, scale);
327  // Transform from local to global.
328  applyTransform(geometry.getExitPatch());
329  }
330  }
331 }
332 
333 
335  double length = flip_s * as.getElementLength();
336 
337  // Drift through half length.
338  if(length != 0.0) applyDrift(length / 2.0);
339 
340  // Apply accelerating voltage.
341  double freq = as.getFrequency();
342  double kin = itsReference.getM() / itsReference.getP();
343  double peak = flip_s * as.getAmplitude() / itsReference.getP();
344 
345  Series pt = itsMap[PT] + 1.0;
346  Series speed = (c * pt) / sqrt(pt * pt + kin * kin);
347  Series phase = as.getPhase() + freq * itsMap[T] / speed;
348  itsMap[PT] += peak * sin(phase) / pt;
349 
350  // Drift through half length.
351  if(length != 0.0) applyDrift(length / 2.0);
352 }
353 
354 
356  // *** MISSING *** Map for RF quadrupole.
358 }
359 
360 
361 void ThickMapper::visitSBend(const SBend &bend) {
362  //std::cerr << "==> In ThickMapper::visitSBend(const SBend &bend)" << std::endl;
363  // Geometry and Field.
364  const PlanarArcGeometry &geometry = bend.getGeometry();
365  double length = flip_s * geometry.getElementLength();
366  double scale = (flip_B * itsReference.getQ() * c) / itsReference.getP();
367  const BMultipoleField &field = bend.getField();
368  int order = field.order();
369  double beta_inv = 1.0 / itsReference.getBeta();
370 
371  // Compute slices and stepsize.
372  int nSlices = (int) bend.getSlices();
373  double stepsize = bend.getStepsize();
374  if(stepsize != 0.0) {
375  int nst = (int) ceil(length / stepsize);
376  nSlices = std::max(nSlices, nst);
377  }
378  double d_ell = length / nSlices;
379 
380  if(length == 0.0) {
381  // Thin SBend.
382  double half_angle = geometry.getBendAngle() / 2.0;
383  Euclid3D rotat = Euclid3D::YRotation(- half_angle);
384  applyTransform(rotat, 0.0); // Transform from in-plane to mid-plane.
385  applyThinSBend(field, scale, 0.0); // Apply multipole kick.
386  applyTransform(rotat, 0.0); // Transform from mid-plane to out-plane.
387  } else {
388  // Finite-length SBend.
389  // Define variables, ...
390  static const Series x = Series::makeVariable(X);
391  static const Series px = Series::makeVariable(PX);
392  static const Series py = Series::makeVariable(PY);
393  static const Series de = Series::makeVariable(PT);
394  static const Series de2 = de * de;
395  static const Series pxy2 = px * px + py * py;
396  static const Map ident;
397  // kinematic terms, ...
398  double h = geometry.getCurvature();
399  double kin = itsReference.getM() / itsReference.getP(); // mc^2/pc = 1/(beta*gamma)
400  double kin2 = kin * kin;
401  Series hx1 = (1.0 + h * x);
402  // and the vector potential, actually -(1+h*x)*As in reference frame of curvature h.
403  Series As = buildSBendVectorPotential(field, h) * scale;
405 
406  // Apply entrance fringe field.
408  bend.getEntryFaceCurvature(), field, scale);
409 
410  // Remove closed orbit from map.
411  Vector co = itsMap.constantTerm();
412  itsMap.setMinOrder(1);
413 
414  // Propagate closed orbit and map.
415  for(int slice = 0; slice < nSlices; ++slice) {
416  // Construct translated Hamiltonian about closed orbit.
417  double p1 = 1.0 + co[PT];
418  Series de12 = p1 * p1 + 2.0 * p1 * de + de2;
419  Series H_trans = beta_inv * sqrt(de12 + kin2, order)
420  - (hx1 + h * co[X])
421  * sqrt(de12 - co[PX] * co[PX] - co[PY] * co[PY]
422  - 2.0 * (co[PX] * px + co[PY] * py) - pxy2, order)
423  + As.substitute(co + ident);
424  // Build J.grad(H).
425  VSeries JgradH;
426  for(int i = 0; i < PSdim; i += 2) {
427  JgradH[ i ] = H_trans.derivative(i + 1);
428  JgradH[i+1] = -H_trans.derivative(i);
429  }
430  // Do integration to propagate closed orbit using implicitInt4().
431  static const Vector zeroV;
432  Vector new_co = co + implicitInt4(zeroV, JgradH, d_ell, 0.5 * d_ell);
433  // Propagate map using mid-point of closed-orbit path.
434  Vector mid_co = (co + new_co) / 2.0;
435  p1 = 1.0 + mid_co[PT];
436  de12 = p1 * p1 + 2.0 * p1 * de + de2;
437  H_trans = beta_inv * sqrt(de12 + kin2, order)
438  - (hx1 + h * mid_co[X])
439  * sqrt(de12 - mid_co[PX] * mid_co[PX] - mid_co[PY] * mid_co[PY]
440  - 2.0 * (mid_co[PX] * px + mid_co[PY] * py) - pxy2, order)
441  + As.substitute(mid_co + ident);
442  H_trans.setMinOrder(2); // Can do this because we expand about the closed orbit.
443  itsMap = ExpMap((-d_ell) * H_trans).substitute(itsMap);
444  // Set closed-orbit to new value.
445  co = new_co;
446  }
447 
448  // Add new closed orbit to propagated map.
449  itsMap += co;
450 
451  // Apply exit fringe field.
453  bend.getExitFaceCurvature(), field, scale);
454  }
455  //std::cerr << "==> Leaving ThickMapper::visitSBend(...)" << std::endl;
456 }
457 
458 
460  double length = flip_s * sep.getElementLength();
461 
462  if(length != 0.0) {
463  // Drift through first half of length.
464  applyDrift(length / 2.0);
465 
466  // electrostatic kick.
467  double scale = (length * itsReference.getQ()) / itsReference.getP();
468  double Ex = scale * sep.getEx();
469  double Ey = scale * sep.getEy();
470  Series pt = 1.0 + itsMap[PT];
471  itsMap[PX] += Ex / pt;
472  itsMap[PY] += Ey / pt;
473 
474  // Drift through second half of length.
475  applyDrift(length / 2.0);
476  }
477 }
478 
479 
480 void ThickMapper::visitSeptum(const Septum &sept) {
481  // Assume the particle go through the magnet's window.
483 }
484 
485 
486 void ThickMapper::visitSolenoid(const Solenoid &solenoid) {
487  double length = flip_s * solenoid.getElementLength();
488 
489  if(length != 0.0) {
490  double ks = (flip_B * itsReference.getQ() * solenoid.getBz() * c) /
491  (2.0 * itsReference.getP());
492 
493  if(ks) {
494  double kin = itsReference.getM() / itsReference.getP();
495  double refTime = length / itsReference.getBeta();
496 
497  Series pt = itsMap[PT] + 1.0;
498  Series px = itsMap[PX] + ks * itsMap[Y];
499  Series py = itsMap[PY] - ks * itsMap[X];
500  Series pz = sqrt(pt * pt - px * px - py * py);
501  Series E = sqrt(pt * pt + kin * kin);
502 
503  Series k = ks / pz;
504  Series C = cos(k * length);
505  Series S = sin(k * length);
506 
507  Series xt = C * itsMap[X] + S * itsMap[Y];
508  Series yt = C * itsMap[Y] - S * itsMap[X];
509  Series pxt = C * itsMap[PX] + S * itsMap[PY];
510  Series pyt = C * itsMap[PY] - S * itsMap[PX];
511 
512  itsMap[X] = C * xt + (S / k) * pxt;
513  itsMap[Y] = C * yt + (S / k) * pyt;
514  itsMap[PX] = C * pxt - (S * k) * xt;
515  itsMap[PY] = C * pyt - (S * k) * yt;
516  itsMap[T] += pt * (refTime / E - length / pz);
517  } else {
518  applyDrift(length);
519  }
520  }
521 }
522 
523 
525  // Do nothing.
526 }
527 
528 
529 void ThickMapper::applyDrift(double length) {
530  if(length != 0.0) {
531  // HACK: fix a maximum order for computations.
532  int order = 6;
533 
534  // Remove closed orbit from map.
535  Vector co = itsMap.constantTerm();
536  itsMap.setMinOrder(1);
537 
538  // Construct the Hamiltonian H about that closed orbit.
539  // (1) Define variables.
540  static const Series px = Series::makeVariable(PX);
541  static const Series py = Series::makeVariable(PY);
542  static const Series pt = Series::makeVariable(PT) + 1.0;
543  static const Series x = Series::makeVariable(X);
544  static const Map ident;
545  Map translate = co + ident;
546  // (2) Construct kinematic terms.
547  double kin = itsReference.getM() / itsReference.getP(); // 1/(beta*gamma)
548  Series E_trans = sqrt((pt * pt + kin * kin).substitute(translate), order)
549  / itsReference.getBeta();
550  Series pz_trans = sqrt((pt * pt - px * px - py * py).substitute(translate), order);
551  // (3) Build Hamiltonian.
552  Series H_trans = E_trans - pz_trans;
553 
554  // Propagate closed orbit.
555  // Build J.grad(H).
556  VSeries JgradH;
557  for(int i = 0; i < PSdim; i += 2) {
558  JgradH[ i ] = H_trans.derivative(i + 1);
559  JgradH[i+1] = -H_trans.derivative(i);
560  }
561  // Do integration to propagate closed orbit using implicitInt4().
562  static const Vector zeroV;
563  Vector new_co = co + implicitInt4(zeroV, JgradH, length, 0.5 * length);
564 
565  // Propagate map.
566  H_trans.setMinOrder(2); // Can do this because we assume zin is on the closed orbit.
567  itsMap = ExpMap((-length) * H_trans).substitute(itsMap);
568 
569  // Add new closed orbit to propagated map.
570  itsMap += new_co;
571  }
572 }
573 
574 
575 void ThickMapper::applyEntranceFringe(double angle, double curve,
576  const BMultipoleField &field, double scale) {
577  // *** MISSING *** Higher order terms for entrance fringe.
578  double ca = cos(angle);
579  double sa = sin(angle);
580  double ta = tan(angle);
581 
582  int order = field.order();
583  static const Series x = Series::makeVariable(X);
584  Series by = field.normal(order);
585  for(int i = order; --i >= 1;) by = by * x + field.normal(i);
586  by *= scale;
587 
588  const Series p = itsMap[PT] + 1.0;
589  // rotate to magnet face
590  Series ps = sqrt(p * p - itsMap[PX] * itsMap[PX] - itsMap[PY] * itsMap[PY], order);
591  itsMap[X] = itsMap[X] / (ca * (1.0 - ta * itsMap[PX] / ps));
592  itsMap[PX] = ca * itsMap[PX] + sa * ps;
593  Series ellovpp = sa * itsMap[X] / ps;
594  itsMap[Y] += ellovpp * itsMap[PY];
595  itsMap[T] -= ellovpp * p;
596  // fringe
597  Series psy = sqrt(p * p - itsMap[PX] * itsMap[PX], order);
598  itsMap[PY] -= by.substitute(itsMap) * itsMap[Y] * itsMap[PX] / psy;
599  // rotate from magnet face
600  ps = sqrt(p * p - itsMap[PX] * itsMap[PX] - itsMap[PY] * itsMap[PY], order);
601  itsMap[X] = itsMap[X] / (ca * (1.0 + ta * itsMap[PX] / ps));
602  itsMap[PX] = ca * itsMap[PX] - sa * ps;
603  ellovpp = sa * itsMap[X] / ps;
604  itsMap[Y] -= ellovpp * itsMap[PY];
605  itsMap[T] += ellovpp * p;
606  // edge effect
607  itsMap[PX] += by.substitute(itsMap) * ta * itsMap[X];
608 }
609 
610 
611 void ThickMapper::applyExitFringe(double angle, double curve,
612  const BMultipoleField &field, double scale) {
613  // *** MISSING *** Higher order terms for exit fringe.
614  double ca = cos(angle);
615  double sa = sin(angle);
616  double ta = tan(angle);
617 
618  int order = field.order();
619  static const Series x = Series::makeVariable(X);
620  Series by = field.normal(order);
621  for(int i = order; --i >= 1;) by = by * x + field.normal(i);
622  by *= scale;
623 
624  const Series p = itsMap[PT] + 1.0;
625  // edge effect
626  itsMap[PX] += by.substitute(itsMap) * ta * itsMap[X];
627  // rotate to magnet face
628  Series ps = sqrt(p * p - itsMap[PX] * itsMap[PX] - itsMap[PY] * itsMap[PY], order);
629  itsMap[X] = itsMap[X] / (ca * (1.0 + ta * itsMap[PX] / ps));
630  itsMap[PX] = ca * itsMap[PX] - sa * ps;
631  Series ellovpp = sa * itsMap[X] / ps;
632  itsMap[Y] -= ellovpp * itsMap[PY];
633  itsMap[T] += ellovpp * p;
634  // fringe
635  Series psy = sqrt(p * p - itsMap[PX] * itsMap[PX], order);
636  itsMap[PY] += by.substitute(itsMap) * itsMap[Y] * itsMap[PX] / psy;
637  // rotate from magnet face
638  ps = sqrt(p * p - itsMap[PX] * itsMap[PX] - itsMap[PY] * itsMap[PY], order);
639  itsMap[X] = itsMap[X] / (ca * (1.0 - ta * itsMap[PX] / ps));
640  itsMap[PX] = ca * itsMap[PX] + sa * ps;
641  ellovpp = sa * itsMap[X] / ps;
642  itsMap[Y] += ellovpp * itsMap[PY];
643  itsMap[T] -= ellovpp * p;
644 }
645 
646 namespace {
647  Vector implicitInt4(const Vector &zin, const VSeries &f, double s, double ds, int nx, int cx) {
648  //std::cerr << "==> In implicitInt4(zin,f,s,ds,nx,cx) ..." << std::endl;
649  // Default: nx = 20, cx = 4
650 
651  // This routine integrates the N-dimensional autonomous differential equation
652  // z' = f(z) for a distance s, in steps of size ds. It uses a "Yoshida-fied"
653  // version of implicitInt2 to obtain zf accurate through fourth-order in the
654  // step-size ds. When f derives from a Hamiltonian---i.e., f = J.grad(H)---
655  // then this routine performs symplectic integration. The optional arguments
656  // nx and cx have the same meaning as in implicitInt2().
657 
658  // The Yoshida constants: 2ya+yb=1; 2ya^3+yb^3=0.
659  static const double yt = pow(2., 1 / 3.);
660  static const double ya = 1 / (2. - yt);
661  static const double yb = -yt * ya;
662 
663  // Build matrix grad(f).
664  MxSeries gradf;
665  for(int i = 0; i < PSdim; ++i)
666  for(int j = 0; j < PSdim; ++j)
667  gradf[i][j] = f[i].derivative(j);
668 
669  // Initialize accumulated length, current step-size, and number of cuts.
670  double as = std::abs(s), st = 0., dsc = std::abs(ds);
671  if(s < 0.) dsc = -dsc;
672  int ci = 0;
673 
674  // Integrate each step.
675  Vector zf = zin;
676  while(std::abs(st) < as) {
677  Vector zt;
678  bool ok = true;
679  try {
680  if(std::abs(st + dsc) > as) dsc = s - st;
681  zt = ::implicitIntStep(zf, f, gradf, ya * dsc, nx);
682  zt = ::implicitIntStep(zt, f, gradf, yb * dsc, nx);
683  zt = ::implicitIntStep(zt, f, gradf, ya * dsc, nx);
684  } catch(ConvergenceError &cnverr) {
685  if(++ci > cx) {
686  std::string msg = "Convergence not achieved within " + NumToStr<int>(cx) + " cuts of step-size!";
687  throw ConvergenceError("ThickMapper::implicitInt4()", msg);
688  }
689  //std::cerr << " cutting step size in half" << std::endl;
690  dsc *= 0.5;
691  ok = false;
692  }
693  if(ok) {zf = zt; st += dsc;}
694  }
695 
696  //std::cerr << "==> Leaving implicitInt4(zin,f,s,ds,nx,cx)" << std::endl;
697  return zf;
698  }
699 
700  Vector implicitIntStep(const Vector &zin, const VSeries &f, const MxSeries gradf, double ds, int nx) {
701  //std::cerr << "==> In implicitIntStep(zin,f,gradf,ds,nx) ..." << std::endl;
702  //std::cerr << " ds = " << ds << std::endl;
703  //std::cerr << " zin =\n" << zin << std::endl;
704  // This routine integrates the N-dimensional autonomous differential equation
705  // z' = f(z) for a single step of size ds, using Newton's method to solve the
706  // implicit equation zf = zin + ds*f((zin+zf)/2). For reasons of efficiency,
707  // its arguments include the matrix gradf = grad(f). The (optional) argument
708  // nx limits the number of Newton iterations. This routine returns a result
709  // zf accurate through second-order in the step-size ds. When f derives from
710  // a Hamiltonian---i.e., f=J.grad(H)---then this routine performs symplectic
711  // integration.
712 
713  // Set up flags, etc., for convergence (bounce) test.
714  FVector<bool, PSdim> bounce(false);
715  Vector dz, dz_old;
716  int bcount = 0;
717  static const double thresh = 1.e-8;
718 
719  // Use second-order Runge-Kutta integration to determine a good initial guess.
720  double ds2 = 0.5 * ds;
721  Vector z = f.constantTerm(zin);
722  z = zin + ds2 * (z + f.constantTerm(zin + ds * z));
723 
724  // Newton iterations:
725  // z :-> [I-ds/2.grad(f)]^{-1}.[zin+ds.f((zin+z)/2)-ds/2.grad(f).z]
726  Vector zf;
727  int ni = 0;
728  while(bcount < PSdim) {
729  if(ni == nx) {
730  std::string msg = "Convergence not achieved within " + NumToStr<int>(nx) + " iterations!";
731  throw ConvergenceError("ThickMapper::implicitIntStep()", msg);
732  }
733 
734  // Build gf = -ds/2.grad(f)[(zin+z)/2] and idgf_inv = [I-ds/2.grad(f)]^{-1}[(zin+z)/2].
735  Vector zt = 0.5 * (zin + z);
736  Matrix gf, idgf, idgf_inv;
737  for(int i = 0; i < PSdim; ++i)
738  for(int j = 0; j < PSdim; ++j)
739  gf[i][j] = -ds2 * gradf[i][j].evaluate(zt);
740  idgf = gf;
741  for(int i = 0; i < PSdim; ++i) idgf[i][i] += 1.;
742  FLUMatrix<double, PSdim> lu(idgf);
743  idgf_inv = lu.inverse();
744 
745  // Execute Newton step.
746  zf = idgf_inv * (zin + ds * f.constantTerm(zt) + gf * z);
747 
748  //std::cerr << " -(ds/2)grad(f) =\n" << gf << std::endl;
749  //std::cerr << " f =\n" << f.constantTerm(zt) << std::endl;
750  //std::cerr << "zk =\n" << zf << std::endl;
751 
752  // Test for convergence ("bounce" test).
753  dz_old = dz;
754  dz = zf - z;
755  if(ni) { // (we need at least two iterations before testing makes sense)
756  for(int i = 0; i < PSdim; ++i) {
757  if(!bounce[i] && (dz[i] == 0. || (std::abs(dz[i]) < thresh && std::abs(dz[i]) >= std::abs(dz_old[i]))))
758  {bounce[i] = true; ++bcount;}
759  }
760  }
761  z = zf;
762  ++ni;
763  }
764 
765  //std::cerr << " zf =\n" << zf << std::endl;
766  //std::cerr << "==> Leaving implicitIntStep(zin,f,gradf,ds,nx)" << std::endl;
767  return zf;
768  }
769 };
virtual void visitDrift(const Drift &)
Apply the algorithm to a Drift.
virtual double getExitFaceRotation() const =0
Get exit pole face rotation.
virtual void visitBeamBeam(const BeamBeam &)
Apply the algorithm to a BeamBeam.
Definition: ThickMapper.cpp:91
virtual BMultipoleField & getField() override=0
Get multipole expansion of field.
virtual void visitLambertson(const Lambertson &)
Apply the algorithm to a Lambertson.
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
void applyThinSBend(const BMultipoleField &field, double scale, double h)
Thin SBend kick.
Definition: Mapper.cpp:158
void setMinOrder(int order)
Set minimum order.
Definition: FTps.hpp:321
virtual BDipoleField & getField()=0
Return the corrector field.
virtual void visitMarker(const Marker &)
Apply the algorithm to a Marker.
FVps< double, 6 > VSeries
Definition: ThickMapper.cpp:67
virtual double getAmplitude() const =0
Get RF amplitude.
double normal(int) const
Get component.
virtual void visitRBend(const RBend &)
Apply the algorithm to a RBend.
The field of a magnetic dipole.
Definition: BDipoleField.h:31
virtual BMultipoleField & getField() override=0
Get multipole expansion of field.
A templated representation for matrices.
Definition: IdealMapper.h:26
Interface for septum magnet.
Definition: Septum.h:11
virtual BMultipoleField & getField() override=0
Get multipole field.
virtual void visitSeparator(const Separator &)
Apply the algorithm to a Separator.
Interface for electrostatic separator.
Definition: Separator.h:33
Euclid3D getExitPatch() const
Get patch.
virtual double getSlices() const =0
Get number of slices.
virtual double getPhase() const =0
Get RF phase.
A templated representation for vectors.
Definition: PartBunchBase.h:26
Interface for beam position monitors.
Definition: Monitor.h:41
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:123
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
virtual double getBz() const =0
Get solenoid field Bz in Teslas.
PETE_TUTree< FnCeil, typename T::PETE_Expr_t > ceil(const PETE_Expr< T > &l)
Definition: PETE.h:811
Interface for RF cavity.
Definition: ParallelPlate.h:36
virtual void visitSBend(const SBend &)
Apply the algorithm to a SBend.
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
virtual void visitParallelPlate(const ParallelPlate &)
Apply the algorithm to a ParallelPlate.
Interface for beam diagnostics.
Definition: Diagnostic.h:32
Euclid3D getEntrancePatch() const
Get patch.
Interface for a marker.
Definition: Marker.h:32
virtual double getBy() const
Get vertical component.
virtual PlanarArcGeometry & getGeometry() override=0
Get SBend geometry.
virtual void visitMonitor(const Monitor &)
Apply the algorithm to a Monitor.
Tps< T > tan(const Tps< T > &x)
Tangent.
Definition: TpsMath.h:147
virtual double getEntryFaceRotation() const =0
Get pole entry face rotation.
Interface for drift space.
Definition: Drift.h:33
virtual void visitDiagnostic(const Diagnostic &)
Apply the algorithm to a Diagnostic.
virtual void visitBeamStripping(const BeamStripping &)
Apply the algorithm to a BeamStripping.
Definition: ThickMapper.cpp:95
double getBeta() const
The relativistic beta per particle.
Definition: PartData.h:127
virtual double getStepsize() const =0
Get stepsize.
Interface for general multipole.
Definition: Multipole.h:46
FTps< double, 6 > Series
Definition: LieMapper.cpp:60
FVector< double, 6 > Vector
Definition: ThickMapper.cpp:64
static Euclid3D YRotation(double angle)
Make rotation.
Definition: Euclid3D.cpp:164
T deg(T x)
Convert radians to degrees.
Definition: matheval.hpp:82
Euclid3D Inverse(const Euclid3D &t)
Euclidean inverse.
Definition: Euclid3D.h:204
virtual double getElementLength() const override
Get design length.
Definition: Solenoid.cpp:256
virtual double getStepsize() const =0
Get stepsize.
virtual double getExitFaceRotation() const =0
Get exit pole face rotation.
double getBendAngle() const
Get angle.
Interface for probe.
Definition: Probe.h:16
virtual void visitFlexibleCollimator(const FlexibleCollimator &)
Apply the algorithm to a flexible collimator.
A templated representation of a LU-decomposition.
Definition: FLUMatrix.h:42
FVector< T, N > constantTerm() const
Extract the constant part of the map.
Definition: FVps.hpp:540
double getQ() const
The constant charge per particle.
Definition: PartData.h:107
virtual double getElementLength() const
Get design length.
Definition: ElementBase.h:511
virtual double getSlices() const =0
Get number of slices.
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 visitCorrector(const Corrector &)
Apply the algorithm to a Corrector.
Displacement and rotation in space.
Definition: Euclid3D.h:68
Convergence error exception.
Abstract beam-beam interaction.
Definition: BeamBeam.h:37
#define PSdim
Definition: ThickMapper.cpp:63
Definition: SBend.h:68
FMatrix< FTps< double, 6 >, 6, 6 > MxSeries
Definition: ThickMapper.cpp:68
FTps< double, 6 > buildSBendVectorPotential(const BMultipoleField &, double h)
Construct the vector potential for an SBend.
virtual void visitRFQuadrupole(const RFQuadrupole &)
Apply the algorithm to a RFQuadrupole.
Interface for cyclotron valley.
virtual void visitDegrader(const Degrader &)
Apply the algorithm to a Degrader.
void applyThinMultipole(const BMultipoleField &field, double factor)
Thin multipole kick.
Definition: Mapper.cpp:135
virtual double getBx() const
Get horizontal component.
FVps< T, N > ExpMap(const FTps< T, N > &H, int trunc=FTps< T, N >::EXACT)
Build the exponential series.
Definition: FTps.hpp:1994
virtual void visitSolenoid(const Solenoid &)
Apply the algorithm to a Solenoid.
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
Build transfer map.
Definition: Mapper.h:85
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
virtual double getEntryFaceCurvature() const =0
Get entry pole face curvature.
FTps substitute(const FMatrix< T, N, N > &M, int n) const
Substitute.
Definition: FTps.hpp:1075
virtual double getElementLength() const
Get element length.
void applyEntranceFringe(double edge, double curve, const BMultipoleField &field, double scale)
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
virtual double getEntryFaceRotation() const =0
Get pole entry face rotation.
FTps derivative(int var) const
Partial derivative.
Definition: FTps.hpp:1396
The geometry for a RBend element.
Definition: RBendGeometry.h:41
double getP() const
The constant reference momentum per particle.
Definition: PartData.h:117
void applyTransform(const Euclid3D &, double refLength=0.0)
Apply transform.
Definition: Mapper.cpp:168
The magnetic field of a multipole.
FTps< double, 6 > buildMultipoleVectorPotential(const BMultipoleField &)
Construct the vector potential for a Multipole.
Interface for RF cavity.
Definition: RFCavity.h:37
double getM() const
The constant mass per particle.
Definition: PartData.h:112
virtual void visitSeptum(const Septum &)
Apply the algorithm to a Septum.
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
FVps< double, 6 > Map
Definition: ThickMapper.cpp:67
Abstract collimator.
Definition: Degrader.h:37
virtual double getExitFaceCurvature() const =0
Get exit pole face curvature.
void setMinOrder(int order)
Set minimum order.
Definition: FVps.hpp:175
FMatrix< double, 6, 6 > Matrix
Definition: ThickMapper.cpp:65
virtual void visitProbe(const Probe &)
Apply the algorithm to a Probe.
double getCurvature() const
Get curvature.
Matrix.
Definition: Matrix.h:38
virtual void visitCCollimator(const CCollimator &)
Apply the algorithm to a collimator.
Definition: ThickMapper.cpp:99
virtual void visitMultipole(const Multipole &)
Apply the algorithm to a Multipole.
static const int EXACT
Representation of infinite precision.
Definition: FTps.h:383
virtual ~ThickMapper()
Definition: ThickMapper.cpp:87
virtual RBendGeometry & getGeometry() override=0
Get RBend geometry.
virtual double getBendAngle() const
Get angle.
void applyExitFringe(double edge, double curve, const BMultipoleField &field, double scale)
void setTruncOrder(int order)
Set truncation order.
Definition: FTps.hpp:393
virtual void visitCyclotronValley(const CyclotronValley &)
Apply the algorithm to a CyclotronValley.
Truncated power series in N variables of type T.
void applyDrift(double length)
Vector.
const PartData itsReference
The reference information.
virtual void visitRFCavity(const RFCavity &)
Apply the algorithm to a RFCavity.
Vector truncated power series in n variables.
static FTps makeVariable(int var)
Make variable.
int order() const
Return order.
FVps< double, 6 > itsMap
The transfer map being built.
Definition: Mapper.h:152
Interface for a Lambertson septum.
Definition: Lambertson.h:33