OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
ClosedOrbitFinder.h
Go to the documentation of this file.
1 
13 #ifndef CLOSEDORBITFINDER_H
14 #define CLOSEDORBITFINDER_H
15 
16 #include <algorithm>
17 #include <array>
18 #include <cmath>
19 #include <functional>
20 #include <limits>
21 #include <numeric>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "Utilities/Options.h"
27 #include "Utilities/Options.h"
29 
31 
32 #include "AbsBeamline/Cyclotron.h"
33 
34 // include headers for integration
35 #include <boost/numeric/odeint/integrate/integrate_n_steps.hpp>
36 #include <boost/filesystem.hpp>
37 
38 extern Inform *gmsg;
39 
41 template<typename Value_type, typename Size_type, class Stepper>
43 {
44  public:
46  typedef Value_type value_type;
48  typedef Size_type size_type;
50  typedef std::vector<value_type> container_type;
52  typedef std::vector<value_type> state_type;
53 
54  typedef std::function<void(const state_type&, state_type&, const double)> function_t;
55 
57 
67  Cyclotron* cycl, bool domain = true, int Nsectors = 1);
68 
71 
73  container_type getPathLength(const value_type& angle = 0);
74 
76  container_type getFieldIndex(const value_type& angle = 0);
77 
79  std::pair<value_type,value_type> getTunes();
80 
85 
89 
94 
99 
102 
105 
107  bool isConverged();
108 
110 
118  bool findOrbit(value_type accuracy, size_type maxit,
119  value_type ekin,
120  value_type dE = 1.0, value_type rguess = -1.0,
121  bool isTuneMode = false);
122 
124  void computeOrbitProperties(const value_type& E);
125 
126  private:
128 
133  value_type computeTune(const std::array<value_type,2>&, value_type, size_type);
134 
135  // Compute closed orbit for given energy
137  const value_type&, size_type);
138 
140 // void computeVerticalOscillations();
141 
143  container_type rotate(value_type angle, container_type& orbitProperty);
144 
146  std::array<value_type,2> x_m; // x_m = [x1, x2]
148  std::array<value_type,2> px_m; // px_m = [px1, px2]
150  std::array<value_type,2> z_m; // z_m = [z1, z2]
152  std::array<value_type,2> pz_m; // pz_m = [pz1, pz2]
153 
168 
172  size_type nzcross_m; //#crossings of zero-line in x- and z-direction
173 
176 
178  /* (see paper of Dr. C. Baumgarten: "Transverse-Longitudinal
179  * Coupling by Space Charge in Cyclotrons" (2012), formula (1))
180  */
186 
189 
192 
198  /* value_type lastOrbitVal_m; */
199 
205  /* value_type lastMomentumVal_m; */
206 
211  bool domain_m;
217 
220 
226  std::function<double(double)> acon_m = [](double wo) { return Physics::c / wo; };
227 
229 
232  std::function<double(double, double)> bcon_m = [](double e0, double wo) {
233  return e0 * 1.0e7 / (/* physics::q0 */ 1.0 * Physics::c * Physics::c / wo);
234  };
235 
237 };
238 
239 // -----------------------------------------------------------------------------------------------------------------------
240 // PUBLIC MEMBER FUNCTIONS
241 // -----------------------------------------------------------------------------------------------------------------------
242 
243 template<typename Value_type, typename Size_type, class Stepper>
244 ClosedOrbitFinder<Value_type,
245  Size_type,
247  size_type N, Cyclotron* cycl,
248  bool domain, int nSectors)
249  : nxcross_m(0)
250  , nzcross_m(0)
251  , E0_m(E0)
252  , wo_m(cycl->getRfFrequ()*1E6/cycl->getCyclHarm()*2.0*Physics::pi)
253  , N_m(N)
254  , dtheta_m(Physics::two_pi/value_type(N))
255  , ravg_m(0)
256  , phase_m(0)
257  /* , lastOrbitVal_m(0.0) */
258  /* , lastMomentumVal_m(0.0) */
259  , domain_m(domain)
260  , nSectors_m(nSectors)
261  , stepper_m()
262  , cycl_m(cycl)
263 {
264 
265  if ( cycl_m->getFMLowE() > cycl_m->getFMHighE() )
266  throw OpalException("ClosedOrbitFinder::ClosedOrbitFinder()",
267  "Incorrect cyclotron energy (MeV) bounds: Maximum cyclotron energy smaller than minimum cyclotron energy.");
268 
269  // if domain_m = true --> integrate over a single sector
270  if (domain_m) {
271  N_m /= cycl_m->getSymmetry();
272  }
273 
275  cycl_m->getBScale());
276 
277  // reserve storage for the orbit and momentum (--> size = 0, capacity = N_m+1)
278  /*
279  * we need N+1 storage, since dtheta = 2pi/N (and not 2pi/(N-1)) that's why we need N+1 integration steps
280  * to return to the origin (but the return size is N_m)
281  */
282  r_m.reserve(N_m + 1);
283  pr_m.reserve(N_m + 1);
284  vz_m.reserve(N_m + 1);
285  vpz_m.reserve(N_m + 1);
286 
287  // reserve memory of N_m for the properties (--> size = 0, capacity = N_m)
288  h_m.reserve(N_m);
289  ds_m.reserve(N_m);
290  fidx_m.reserve(N_m);
291 }
292 
293 template<typename Value_type, typename Size_type, class Stepper>
296 {
297  if (angle != 0.0)
298  return rotate(angle, h_m);
299  else
300  return h_m;
301 }
302 
303 template<typename Value_type, typename Size_type, class Stepper>
306 {
307  if (angle != 0.0)
308  return rotate(angle, ds_m);
309  else
310  return ds_m;
311 }
312 
313 template<typename Value_type, typename Size_type, class Stepper>
316 {
317  if (angle != 0.0)
318  return rotate(angle, fidx_m);
319  return fidx_m;
320 }
321 
322 template<typename Value_type, typename Size_type, class Stepper>
324  // compute radial tune
325  value_type nur = computeTune(x_m,px_m[1],nxcross_m);
326 
327  // compute vertical tune
328  value_type nuz = computeTune(z_m,pz_m[1],nzcross_m);
329 
330  return std::make_pair(nur,nuz);
331 }
332 
333 template<typename Value_type, typename Size_type, class Stepper>
336 {
337  if (angle != 0.0)
338  return rotate(angle, r_m);
339  else
340  return r_m;
341 }
342 
343 template<typename Value_type, typename Size_type, class Stepper>
346 {
347  container_type pr = pr_m;
348 
349  if (angle != 0.0)
350  pr = rotate(angle, pr);
351 
352  // change units from meters to \beta * \gamma
353  /* in Gordon paper:
354  *
355  * p = \gamma * \beta * a
356  *
357  * where a = c / \omega_{0} with \omega_{0} = 2 * \pi * \nu_{0} = 2 * \pi * \nu_{rf} / h
358  *
359  * c: speed of light
360  * h: harmonic number
361  * v_{rf}: nomial rf frequency
362  *
363  * Units:
364  *
365  * [a] = m --> [p] = m
366  *
367  * The momentum in \beta * \gamma is obtained by dividing by "a"
368  */
369  value_type factor = 1.0 / acon_m(wo_m);
370  std::for_each(pr.begin(), pr.end(), [factor](value_type& p) { p *= factor; });
371 
372  return pr;
373 }
374 
375 template<typename Value_type, typename Size_type, class Stepper>
378 {
379  return ravg_m;
380 }
381 
382 template<typename Value_type, typename Size_type, class Stepper>
385 {
386  return phase_m;
387 }
388 
389 // -----------------------------------------------------------------------------------------------------------------------
390 // PRIVATE MEMBER FUNCTIONS
391 // -----------------------------------------------------------------------------------------------------------------------
392 
393 
394 
395 template<typename Value_type, typename Size_type, class Stepper>
397  size_type maxit,
398  value_type ekin,
399  value_type dE,
400  value_type rguess,
401  bool isTuneMode)
402 {
403  /* REMARK TO GORDON
404  * q' = 1/b = 1/bcon
405  * a' = a = acon
406  */
407 
408  // resize vectors (--> size = N_m+1, capacity = N_m+1), note: we do N_m+1 integration steps
409  r_m.resize(N_m+1);
410  pr_m.resize(N_m+1);
411  vz_m.resize(N_m+1);
412  vpz_m.resize(N_m+1);
413 
414  // store acon locally
415  value_type acon = acon_m(wo_m); // [acon] = m
416  // amplitude of error; Gordon, formula (18) (a = a')
418 
419  /*
420  * Christian:
421  * N = 1440 ---> N = 720 ---> dtheta = 2PI/720 --> nsteps = 721
422  *
423  * 0, 2, 4, ... ---> jeden zweiten berechnene: 1, 3, 5, ... interpolieren --> 1440 Werte
424  *
425  * Matthias:
426  * N = 1440 --> dtheta = 2PI/1440 --> nsteps = 1441
427  *
428  * 0, 1, 2, 3, 4, 5, ... --> 1440 Werte
429  *
430  */
431 
432  value_type E; // starting energy
433  value_type E_fin; // final energy
434 
435  if ( isTuneMode ) {
436  E = cycl_m->getFMLowE();
437  E_fin = cycl_m->getFMHighE();
438  } else {
439  E = ekin;
440  E_fin = ekin;
441  }
442 
443  namespace fs = boost::filesystem;
444  fs::path dir = OpalData::getInstance()->getInputBasename();
445  dir = dir.parent_path() / "data";
446  std::string tunefile = (dir / "tunes.dat").string();
447 
448  if ( isTuneMode ) {
449  std::ofstream out(tunefile, std::ios::out);
450 
451  out << std::left
452  << std::setw(15) << "# energy[MeV]"
453  << std::setw(15) << "radius_ini[m]"
454  << std::setw(15) << "radius_avg[m]"
455  << std::setw(15) << "nu_r"
456  << std::setw(15) << "nu_z"
457  << std::endl;
458  }
459  // initial guess
460  container_type init;
461  enum Guess {NONE, FIRST, SECOND};
462  Guess guess = NONE;
463  value_type rn1, pn1; // normalised r, pr value of previous closed orbit
464  value_type rn2, pn2; // normalised r, pr value of second to previous closed orbit
465 
466  // iterate until suggested energy (start with minimum energy)
467  // increase energy by dE
468  for (; E <= E_fin ; E+=dE) {
470 
471  // energy dependent values
472  value_type en = E / E0_m; // en = E/E0 = E/(mc^2) (E0 is potential energy)
473  value_type gamma = en + 1.0;
474  value_type gamma2 = gamma * gamma; // = gamma^2
475  value_type beta = std::sqrt(1.0 - 1.0 / gamma2);
476  value_type p = acon_m(wo_m) * std::sqrt(en * (2.0 + en)); // momentum [p] = m; Gordon, formula (3)
477 
478  if (guess == NONE) {
479  // set initial values for radius and radial momentum for lowest energy Emin
480  // orbit, [r] = m; Gordon, formula (20)
481  // radial momentum; Gordon, formula (20)
482  // r pr z pz
483  init = {beta * acon, 0.0, 0.0, 1.0};
484  if (rguess >= 0.0) {
485  init[0] = rguess * 0.001;
486  }
487  guess = FIRST;
488  } else if (guess == FIRST) {
489  // new initial values based on previous one, formula (21)
490  init[0] = (beta*acon) * rn1;
491  init[1] = p*pn1;
492  guess = SECOND;
493  } else if (guess == SECOND) {
494  // second extrapolation, formula (21)
495  init[0] = (beta*acon) * (rn1 + (rn1-rn2));
496  init[1] = p*(pn1 + (pn1-pn2));
497  }
498 
499  std::fill( r_m.begin(), r_m.end(), 0);
500  std::fill( pr_m.begin(), pr_m.end(), 0);
501  std::fill( vz_m.begin(), vz_m.end(), 0);
502  std::fill(vpz_m.begin(), vpz_m.end(), 0);
503 
504  // (re-)set inital values for r and pr
505  r_m[0] = init[0];
506  pr_m[0] = init[1];
507  vz_m[0] = init[2];
508  vpz_m[0] = init[3];
509 
510  if ( !this->findOrbitOfEnergy_m(E, init, error, accuracy, maxit) ) {
511  *gmsg << "ClosedOrbitFinder didn't converge for energy " + std::to_string(E) + " MeV." << endl;
512  guess = NONE;
513  continue;
514  }
515  // store for next initial guess
516  rn2 = rn1;
517  pn2 = pn1;
518  rn1 = r_m[0] / (acon * beta);
519  pn1 = pr_m[0] / p;
520 
521  if ( isTuneMode ) {
522 
523  this->computeOrbitProperties(E);
524  value_type reo = this->getOrbit( cycl_m->getPHIinit())[0];
525  value_type peo = this->getMomentum(cycl_m->getPHIinit())[0];
526  std::pair<value_type , value_type > tunes = this->getTunes();
527 
528  *gmsg << std::left
529  << "* ----------------------------" << endl
530  << "* Closed orbit info (Gordon units):" << endl
531  << "*" << endl
532  << "* kinetic energy: " << std::setw(12) << E << " [MeV]" << endl
533  << "* average radius: " << std::setw(12) << ravg_m << " [m]" << endl
534  << "* initial radius: " << std::setw(12) << reo << " [m]" << endl
535  << "* initial momentum: " << std::setw(12) << peo << " [Beta Gamma]" << endl
536  << "* frequency error: " << phase_m << endl
537  << "* horizontal tune: " << tunes.first << endl
538  << "* vertical tune: " << tunes.second << endl
539  << "* ----------------------------" << endl << endl;
540 
541  std::ofstream out(tunefile, std::ios::app);
542  out << std::left
543  << std::setw(15) << E
544  << std::setw(15) << reo
545  << std::setw(15) << ravg_m
546  << std::setw(15) << tunes.first
547  << std::setw(15) << tunes.second << std::endl;
548  out.close();
549  }
550  }
551 
552  /* store last entry, since it is needed in computeVerticalOscillations(), because we have to do the same
553  * number of integrations steps there.
554  */
555  /* lastOrbitVal_m = r_m[N_m]; // needed in computeVerticalOscillations() */
556  /* lastMomentumVal_m = pr_m[N_m]; // needed in computeVerticalOscillations() */
557 
558  // remove last entry (since we don't have to store [0,2pi], but [0,2pi[) --> size = N_m, capacity = N_m+1
559  r_m.pop_back();
560  pr_m.pop_back();
561 
562  /* domain_m = true --> only integrated over a single sector
563  * --> multiply by cycl_m->getSymmetry() to get correct phase_m
564  */
565  if (domain_m)
566  phase_m *= cycl_m->getSymmetry();
567 
568  // returns true if converged, otherwise false
569  return error < accuracy;
570 }
571 
572 template<typename Value_type, typename Size_type, class Stepper>
574  const value_type& E,
575  container_type& init,
576  value_type& error,
577  const value_type& accuracy,
578  size_type maxit)
579 {
580  /* *gmsg << "rguess : " << init[0] << endl; */
581  /* *gmsg << "prguess: " << init[1] << endl; */
582 
583  value_type bint, brint, btint;
584  value_type invbcon = 1.0 / bcon_m(E0_m, wo_m); // [bcon] = MeV*s/(C*m^2) = 10^6 T = 10^7 kG (kilo Gauss)
585 
586  value_type xold = 0.0; // for counting nxcross
587  value_type zold = 0.0; // for counting nzcross
588 
589  // index for reaching next element of the arrays r and pr (no nicer way found yet)
590  size_type idx = 0;
591  // observer for storing the current value after each ODE step (e.g. Runge-Kutta step) into the containers of r and pr
592  auto store = [&](state_type& y, const value_type t)
593  {
594  r_m[idx] = y[0];
595  pr_m[idx] = y[1];
596  vz_m[idx] = y[6];
597  vpz_m[idx] = y[7];
598 
599  // count number of crossings (excluding starting point --> idx>0)
600  nxcross_m += (idx > 0) * (y[4] * xold < 0);
601  xold = y[4];
602 
603  // number of times z2 changes sign
604  nzcross_m += (idx > 0) * (y[10] * zold < 0);
605  zold = y[10];
606 
607  ++idx;
608  };
609 
610  // define initial state container for integration: y = {r, pr, x1, px1, x2, px2,
611  // z, pz, z1, pz1, z2, pz2,
612  // phase}
613  state_type y(11);
614 
615  // difference of last and first value of r (1. element) and pr (2. element)
616  container_type err(2);
617  // correction term for initial values: r = r + dr, pr = pr + dpr; Gordon, formula (17)
618  container_type delta = {0.0, 0.0};
619  // if niterations > maxit --> stop iteration
620  size_type niterations = 0;
621 
622  // energy dependent values
623  value_type en = E / E0_m; // en = E/E0 = E/(mc^2) (E0 is potential energy)
624  value_type gamma = en + 1.0;
625  value_type p = acon_m(wo_m) * std::sqrt(en * (2.0 + en)); // momentum [p] = m; Gordon, formula (3)
626  value_type gamma2 = gamma * gamma; // = gamma^2
627  value_type invgamma4 = 1.0 / (gamma2 * gamma2); // = 1/gamma^4
628  value_type p2 = p * p; // p^2 = p*p
629 
630  // helper constants
631  value_type pr2; // squared radial momentum (pr^2 = pr*pr)
632  value_type ptheta, invptheta; // azimuthal momentum
633 
634  // define the six ODEs (using lambda function)
635  function_t orbit_integration = [&](const state_type &y,
636  state_type &dydt,
637  const double theta)
638  {
639  pr2 = y[1] * y[1];
640  if (p2 < pr2) {
641  //*gmsg << theta << " " << p2 << " " << pr2 << endl;
642  throw OpalException("ClosedOrbitFinder::findOrbitOfEnergy_m()",
643  "p_{r}^2 > p^{2} (defined in Gordon paper) --> Square root of negative number.");
644  }
645  // Gordon, formula (5c)
646  ptheta = std::sqrt(p2 - pr2);
647  invptheta = 1.0 / ptheta;
648  // average field over the number of sectors
649  brint=0.0, btint=0.0, bint=0.0;
650  for (int i = 0; i<nSectors_m; i++) {
651  double angle = theta + i * Physics::two_pi / nSectors_m;
652  double tmpbr, tmpbt, tmpb;
653  // interpolate values of magnetic field
654  cycl_m->apply(y[0], y[6], angle, tmpbr, tmpbt, tmpb);
655  brint += tmpbr;
656  btint += tmpbt;
657  bint += tmpb;
658  }
659  brint /= nSectors_m;
660  btint /= nSectors_m;
661  bint /= nSectors_m;
662  // multiply by 1 / b
663  bint *= invbcon;
664  brint *= invbcon;
665  btint *= invbcon;
666 
667  // Gordon, formula (5a)
668  dydt[0] = y[0] * y[1] * invptheta;
669  // Gordon, formula (5b) (typo in paper! second equal sign is a minus)
670  dydt[1] = ptheta - y[0] * bint;
671  // Gordon, formulas (9a) and (9b)
672  for (size_type i = 2; i < 5; i += 2) {
673  dydt[i] = (y[1] * y[i] + y[0] * p2 * y[i+1] * invptheta * invptheta) * invptheta;
674  dydt[i+1] = - y[1] * y[i+1] * invptheta - (bint + y[0] * brint) * y[i];
675  }
676 
677  // Gordon, formulas (22a) and (22b)
678  for (size_type i = 6; i < 12; i += 2) {
679  dydt[i] = y[0] * y[i+1] * invptheta;
680  dydt[i+1] = (y[0] * brint - y[1] * invptheta * btint) * y[i];
681  }
682 
683  // integrate phase
684  dydt[12] = y[0] * invptheta * gamma - 1;
685 
686  };
687 
688  // integrate until error smaller than user-defined accuracy
689  do {
690  // (re-)set initial values
691  x_m[0] = 1.0; // x1; Gordon, formula (10)
692  px_m[0] = 0.0; // px1; Gordon, formula (10)
693  x_m[1] = 0.0; // x2; Gordon, formula (10)
694  px_m[1] = 1.0; // px2; Gordon, formula (10)
695  z_m[0] = 1.0;
696  pz_m[0] = 0.0;
697  z_m[1] = 0.0;
698  pz_m[1] = 1.0;
699  phase_m = 0.0;
700  nxcross_m = 0; // counts the number of crossings of x-axis (excluding first step)
701  nzcross_m = 0;
702  idx = 0; // index for looping over r and pr arrays
703 
704  // fill container with initial states
705  y = {init[0],init[1],
706  x_m[0], px_m[0], x_m[1], px_m[1],
707  init[2], init[3],
708  z_m[0], pz_m[0], z_m[1], pz_m[1],
709  phase_m
710  };
711 
712  try {
713  // integrate from 0 to 2*pi / nSectors (one has to get back to the "origin")
714  boost::numeric::odeint::integrate_n_steps(stepper_m, orbit_integration,y,0.0,dtheta_m,N_m,store);
715  } catch(OpalException & ex) {
716  *gmsg << ex.where() << " " << ex.what() << endl;
717  break;
718  }
719 
720  // write new state
721  x_m[0] = y[2];
722  px_m[0] = y[3];
723  x_m[1] = y[4];
724  px_m[1] = y[5];
725 
726  z_m[0] = y[8];
727  pz_m[0] = y[9];
728  z_m[1] = y[10];
729  pz_m[1] = y[11];
730  phase_m = y[12] * Physics::u_two_pi; // / (2.0 * Physics::pi);
731 
732  // compute error (compare values of orbit and momentum for theta = 0 and theta = 2*pi)
733  // (Note: size = N_m+1 --> last entry is N_m)
734  err[0] = r_m[N_m] - r_m[0]; // Gordon, formula (14)
735  err[1] = pr_m[N_m] - pr_m[0]; // Gordon, formula (14)
736 
737  // correct initial values of r and pr
738  value_type invdenom = 1.0 / (x_m[0] + px_m[1] - 2.0);
739  delta[0] = ((px_m[1] - 1.0) * err[0] - x_m[1] * err[1]) * invdenom; // dr; Gordon, formula (16a)
740  delta[1] = (( x_m[0] - 1.0) * err[1] - px_m[0] * err[0]) * invdenom; // dpr; Gordon, formula (16b)
741 
742  // improved initial values; Gordon, formula (17) (here it's used for higher energies)
743  init[0] += delta[0];
744  init[1] += delta[1];
745 
746  // compute amplitude of the error (Gordon, formula (18)
747  error = std::sqrt(delta[0] * delta[0] + delta[1] * delta[1] * invgamma4) / r_m[0];
748 
749  // *gmsg << "iteration " << niterations << " error: " << error << endl;
750 
751  } while ((error > accuracy) && (niterations++ < maxit));
752 
753  if (error > accuracy)
754  *gmsg << "findOrbit not converged after " << niterations << " iterations with error: " << error << ". Needed accuracy " << accuracy << endl;
755 
756  return (error < accuracy);
757 }
758 
759 template<typename Value_type, typename Size_type, class Stepper>
760 Value_type ClosedOrbitFinder<Value_type, Size_type, Stepper>::computeTune(const std::array<value_type,2>& y,
761  value_type py2, size_type ncross)
762 {
763  // Y = [y1, y2; py1, py2]
764 
765  // cos(mu)
766  value_type cos = 0.5 * (y[0] + py2);
767 
768  value_type mu;
769 
770  // sign of sin(mu) has to be equal to y2
771  bool negative = std::signbit(y[1]);
772 
773  bool uneven = (ncross % 2);
774 
775  value_type abscos = std::fabs(cos);
776  value_type muPrime;
777  if (abscos > 1.0) {
778  // store the number of crossings
779  if (uneven)
780  ncross = ncross + 1;
781 
782  // Gordon, formula (36b)
783  muPrime = -std::acosh(abscos); // mu'
784 
785  } else {
786  muPrime = (uneven) ? std::acos(-cos) : std::acos(cos); // mu'
787  /* It has to be fulfilled: 0<= mu' <= pi
788  * But since |cos(mu)| <= 1, we have
789  * -1 <= cos(mu) <= 1 --> 0 <= mu <= pi (using above programmed line), such
790  * that condition is already fulfilled.
791  */
792  }
793 
794  // Gordon, formula (36)
795  mu = ncross * Physics::pi + muPrime;
796 
797  if (abscos < 1.0) {
798  // if sign(y[1]) > 0 && sign(sin(mu)) < 0
799  if (!negative && std::signbit(std::sin(mu))) {
800  mu = ncross * Physics::pi - muPrime;
801  } else if (negative && !std::signbit(std::sin(mu))) {
802  mu = ncross * Physics::pi - muPrime + Physics::two_pi;
803  }
804  }
805 
806  // nu = mu/theta, where theta = integration domain
807 
808  /* domain_m = true --> only integrated over a single sector --> multiply by cycl_m->getSymmetry() to
809  * get correct tune.
810  */
811  if (domain_m)
812  mu *= cycl_m->getSymmetry();
813 
814  return mu * Physics::u_two_pi;
815 }
816 
817 template<typename Value_type, typename Size_type, class Stepper>
819  /*
820  * The formulas for h, fidx and ds are from the paper:
821  * "Tranverse-Longitudinal Coupling by Space Charge in Cyclotrons"
822  * written by Dr. Christian Baumgarten (2012, PSI)
823  * p. 6
824  */
825 
826  // READ IN MAGNETIC FIELD: ONLY FOR STAND-ALONE PROGRAM
827  value_type bint, brint, btint; // B, dB/dr, dB/dtheta
828 
829  value_type invbcon = 1.0 / bcon_m(E0_m, wo_m);
830  value_type en = E / E0_m; // en = E/E0 = E/(mc^2) (E0 is potential energy)
831  value_type p = acon_m(wo_m) * std::sqrt(en * (2.0 + en)); // momentum [p] = m; Gordon, formula (3)
832  value_type p2 = p * p;
833  value_type theta = 0.0; // angle for interpolating
834  value_type ptheta;
835 
836  // resize of container (--> size = N_m, capacity = N_m)
837  h_m.resize(N_m);
838  fidx_m.resize(N_m);
839  ds_m.resize(N_m);
840 
841  for (size_type i = 0; i < N_m; ++i) {
842  // interpolate magnetic field
843  cycl_m->apply(r_m[i], vz_m[i], theta, brint, btint, bint);
844  bint *= invbcon;
845  brint *= invbcon;
846  btint *= invbcon;
847 
848  // inverse bending radius
849  h_m[i] = bint / p;
850 
851  // local field index
852  if (p < pr_m[i])
853  throw OpalException("ClosedOrbitFinder::computeOrbitProperties()",
854  "p_{r}^2 > p^{2} " + std::to_string(p) + " " + std::to_string(pr_m[i]) + " (defined in Gordon paper) --> Square root of negative number.");
855 
856  ptheta = std::sqrt(p2 - pr_m[i] * pr_m[i]);
857 
858  fidx_m[i] = (brint * ptheta - btint * pr_m[i] / r_m[i]) / p2; //(bint*bint);
859 
860  // path length element
861  ds_m[i] = std::hypot(r_m[i] * pr_m[i] / ptheta,r_m[i]) * dtheta_m; // C++11 function
862 
863  // increase angle
864  theta += dtheta_m;
865  }
866 
867  // compute average radius
868  ravg_m = std::accumulate(r_m.begin(),r_m.end(),0.0) / value_type(r_m.size());
869 }
870 
871 template<typename Value_type, typename Size_type, class Stepper>
874 
875  container_type orbitPropertyCopy = orbitProperty;
876 
877  // compute the number of steps per degree
878  value_type deg_step = N_m / 360.0;
879 
880  // compute starting point
881  size_type start = deg_step * angle;
882 
883  // copy end to start
884  std::copy(orbitProperty.begin() + start, orbitProperty.end(), orbitPropertyCopy.begin());
885 
886  // copy start to end
887  std::copy_n(orbitProperty.begin(), start, orbitPropertyCopy.end() - start);
888 
889  return orbitPropertyCopy;
890 
891 }
892 
893 #endif
constexpr double u_two_pi
The value of .
Definition: Physics.h:37
PETE_TUTree< FnArcCos, typename T::PETE_Expr_t > acos(const PETE_Expr< T > &l)
Definition: PETE.h:808
std::function< double(double, double)> bcon_m
Cyclotron unit (Tesla)
Finds a closed orbit of a cyclotron for a given energy.
std::array< value_type, 2 > pz_m
Stores current momenta in vertical direction for the solutions of the ODE with different initial valu...
container_type rotate(value_type angle, container_type &orbitProperty)
This function computes nzcross_ which is used to compute the tune in z-direction and the frequency er...
Interface for a Cyclotron.
Definition: Cyclotron.h:91
Stepper stepper_m
Defines the stepper for integration of the ODE&#39;s.
PETE_TUTree< FnFabs, typename T::PETE_Expr_t > fabs(const PETE_Expr< T > &l)
Definition: PETE.h:815
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:123
The base class for all OPAL exceptions.
Definition: OpalException.h:28
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
constexpr double two_pi
The value of .
Definition: Physics.h:34
std::vector< value_type > container_type
Type of container for storing quantities (path length, orbit, etc.)
container_type getPathLength(const value_type &angle=0)
Returns the step lengths of the path (size of container N+1)
Inform * gmsg
Definition: Main.cpp:21
virtual double getSymmetry() const
Definition: Cyclotron.cpp:244
ClosedOrbitFinder(value_type E0, size_type N, Cyclotron *cycl, bool domain=true, int Nsectors=1)
Sets the initial values for the integration and calls findOrbit().
const std::string & getCyclotronType() const
Definition: Cyclotron.cpp:253
FRONT * fs
Definition: hypervolume.cpp:59
container_type ds_m
Stores the step length.
bool findOrbit(value_type accuracy, size_type maxit, value_type ekin, value_type dE=1.0, value_type rguess=-1.0, bool isTuneMode=false)
Computes the closed orbit.
container_type r_m
Stores the radial orbit (size: N_m+1)
std::vector< value_type > state_type
Type for holding state of ODE values.
container_type fidx_m
Stores the field index.
size_type nxcross_m
Counts the number of zero-line crossings in horizontal direction (used for computing horizontal tune)...
std::function< void(const state_type &, state_type &, const double)> function_t
value_type getAverageRadius()
Returns the average orbit radius.
bool findOrbitOfEnergy_m(const value_type &, container_type &, value_type &, const value_type &, size_type)
virtual double getFMHighE() const
Definition: Cyclotron.cpp:339
static OpalData * getInstance()
Definition: OpalData.cpp:209
std::pair< value_type, value_type > getTunes()
Returns the radial and vertical tunes (in that order)
std::function< double(double)> acon_m
void computeOrbitProperties(const value_type &E)
Fills in the values of h_m, ds_m, fidx_m.
constexpr double pi
The value of .
Definition: Physics.h:31
virtual double getBScale() const
Definition: Cyclotron.cpp:269
container_type vpz_m
Stores the vertical momentum.
size_type N_m
Number of integration steps.
container_type getOrbit(value_type angle=0)
const double pi
Definition: fftpack.cpp:894
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:52
container_type getFieldIndex(const value_type &angle=0)
Returns the field index (size of container N+1)
size_type nzcross_m
Counts the number of zero-line crossings in vertical direction (used for computing vertical tune) ...
value_type getFrequencyError()
Returns the frequency error.
container_type h_m
Stores the inverse bending radius.
virtual const std::string & what() const
Return the message string for the exception.
Value_type value_type
Type of variables.
virtual double getFMLowE() const
Definition: Cyclotron.cpp:336
int getFieldFlag(const std::string &type) const
Definition: Cyclotron.cpp:184
value_type ravg_m
Is the average radius.
value_type wo_m
Is the nominal orbital frequency.
container_type getMomentum(value_type angle=0)
T * value_type(const SliceIterator< T > &)
value_type computeTune(const std::array< value_type, 2 > &, value_type, size_type)
This function is called by the function getTunes().
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
std::array< value_type, 2 > px_m
Stores current momenta in horizontal direction for the solutions of the ODE with different initial va...
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
container_type getInverseBendingRadius(const value_type &angle=0)
Returns the inverse bending radius (size of container N+1)
bool isConverged()
Returns true if a closed orbit could be found.
std::array< value_type, 2 > x_m
Stores current position in horizontal direction for the solutions of the ODE with different initial v...
bool for_each(const BareFieldIterator< T, D > &p, SameFieldID s, C)
Definition: AssignDefs.h:30
value_type dtheta_m
Is the angle step size.
std::string getInputBasename()
get input file name without extension
Definition: OpalData.cpp:717
Definition: Inform.h:41
std::array< value_type, 2 > z_m
Stores current position in vertical direction for the solutions of the ODE with different initial val...
container_type vz_m
Stores the vertical oribt (size: N_m+1)
Size_type size_type
Type for specifying sizes.
container_type pr_m
Stores the radial momentum.
value_type E0_m
Is the rest mass [MeV / c**2].
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
void read(const int &fieldflag, const double &scaleFactor)
Definition: Cyclotron.cpp:733
value_type phase_m
Is the phase.
virtual const std::string & where() const
Return the name of the method or function which detected the exception.