OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
SigmaGenerator.h
Go to the documentation of this file.
1 
14 #ifndef SIGMAGENERATOR_H
15 #define SIGMAGENERATOR_H
16 
17 #include <array>
18 #include <cmath>
19 #include <fstream>
20 #include <functional>
21 #include <iomanip>
22 #include <iterator>
23 #include <limits>
24 #include <list>
25 #include <numeric>
26 #include <sstream>
27 #include <string>
28 #include <utility>
29 #include <vector>
30 
31 #include "Physics/Physics.h"
32 #include "Utilities/Options.h"
33 #include "Utilities/Options.h"
35 
36 #include <boost/numeric/ublas/matrix.hpp>
37 #include <boost/numeric/ublas/matrix_sparse.hpp>
38 #include <boost/numeric/ublas/vector.hpp>
39 
40 #include <boost/numeric/ublas/vector_proxy.hpp>
41 #include <boost/numeric/ublas/triangular.hpp>
42 #include <boost/numeric/ublas/lu.hpp>
43 #include <boost/numeric/ublas/io.hpp>
44 
45 #include <gsl/gsl_matrix.h>
46 #include <gsl/gsl_math.h>
47 #include <gsl/gsl_eigen.h>
48 
49 #include <boost/numeric/odeint/stepper/runge_kutta4.hpp>
50 #if BOOST_VERSION >= 106000
51 #include <boost/numeric/odeint/integrate/check_adapter.hpp>
52 #endif
53 
55 #include "ClosedOrbitFinder.h"
56 #include "MapGenerator.h"
57 #include "Harmonics.h"
58 
59 
60 extern Inform *gmsg;
61 
63 template<typename Value_type, typename Size_type>
65 {
66 public:
68  typedef Value_type value_type;
72  typedef Size_type size_type;
74  typedef boost::numeric::ublas::matrix<value_type> matrix_type;
75 
76  typedef std::complex<value_type> complex_t;
77 
79  typedef boost::numeric::ublas::matrix<complex_t> cmatrix_type;
80 
82  typedef boost::numeric::ublas::compressed_matrix<complex_t,
83  boost::numeric::ublas::row_major
86  typedef boost::numeric::ublas::vector<value_type> vector_type;
88  typedef std::vector<value_type> container_type;
94  typedef std::function<Series(value_type,value_type,value_type)> Hamiltonian;
96  typedef std::function<Series(value_type,value_type,value_type)> SpaceCharge;
97 
99 
114  value_type E, value_type m, const Cyclotron* cycl,
115  size_type N, size_type Nsectors, size_type truncOrder, bool write = true);
116 
118 
131  bool match(value_type accuracy, size_type maxit, size_type maxitOrbit,
132  Cyclotron* cycl, value_type denergy, value_type rguess, bool harmonic, bool full);
133 
139  void eigsolve_m(const matrix_type& Mturn, sparse_matrix_type& R);
140 
146  bool invertMatrix_m(const sparse_matrix_type& R,
147  sparse_matrix_type& invR);
148 
150 
156  void decouple(const matrix_type& Mturn, sparse_matrix_type& R, sparse_matrix_type& invR);
157 
159 
164  value_type isEigenEllipse(const matrix_type& Mturn, const matrix_type& sigma);
165 
168 
170  size_type getIterations() const;
171 
173  value_type getError() const;
174 
176  std::array<value_type,3> getEmittances() const;
177 
178  const double& getInjectionRadius() const {
179  return rinit_m;
180  }
181 
182  const double& getInjectionMomentum() const {
183  return prinit_m;
184  }
185 
186  private:
190  std::array<value_type,3> emittance_m;
219 
222 
225 
228 
230  bool write_m;
231 
234 
236  std::vector<matrix_type> sigmas_m;
237 
240 
243 
246 
247  double rinit_m;
248  double prinit_m;
249 
258 
260 
268 
270 
274  void updateSigma(const std::vector<matrix_type>&,
275  const std::vector<matrix_type>&);
276 
278 
283 
284 
286 
291 
293 
296  std::string float2string(value_type val);
297 
299 
308  void writeOrbitOutput_m(const std::pair<value_type,value_type>& tunes,
309  const value_type& ravg,
310  const value_type& freqError,
311  const container_type& r_turn,
312  const container_type& peo,
313  const container_type& h_turn,
314  const container_type& fidx_turn,
315  const container_type& ds_turn);
316 };
317 
318 // -----------------------------------------------------------------------------------------------------------------------
319 // PUBLIC MEMBER FUNCTIONS
320 // -----------------------------------------------------------------------------------------------------------------------
321 
322 template<typename Value_type, typename Size_type>
324  value_type ex,
325  value_type ey,
326  value_type ez,
327  value_type E,
328  value_type m,
329  const Cyclotron* cycl,
330  size_type N,
331  size_type Nsectors,
332  size_type truncOrder,
333  bool write)
334  : I_m(I)
335  , wo_m(cycl->getRfFrequ()*1E6/cycl->getCyclHarm()*2.0*Physics::pi)
336  , E_m(E)
337  , gamma_m(E/m+1.0)
338  , gamma2_m(gamma_m*gamma_m)
339  , nh_m(cycl->getCyclHarm())
340  , beta_m(std::sqrt(1.0-1.0/gamma2_m))
341  , m_m(m)
342  , niterations_m(0)
343  , converged_m(false)
344  , Emin_m(cycl->getFMLowE())
345  , Emax_m(cycl->getFMHighE())
346  , N_m(N)
347  , nSectors_m(Nsectors)
348  , nStepsPerSector_m(N/cycl->getSymmetry())
349  , nSteps_m(N)
350  , error_m(std::numeric_limits<value_type>::max())
351  , truncOrder_m(truncOrder)
352  , write_m(write)
353  , sigmas_m(nStepsPerSector_m)
354  , rinit_m(0.0)
355  , prinit_m(0.0)
356 {
357  // set emittances (initialization like that due to old compiler version)
358  // [ex] = [ey] = [ez] = pi*mm*mrad --> [emittance] = mm mrad
359  emittance_m[0] = ex * Physics::pi;
360  emittance_m[1] = ey * Physics::pi;
361  emittance_m[2] = ez * Physics::pi;
362 
363  // minimum beta*gamma
364  value_type minGamma = Emin_m / m_m + 1.0;
365  value_type bgam = std::sqrt(minGamma * minGamma - 1.0);
366 
367  // normalized emittance (--> multiply with beta*gamma)
368  // [emittance] = mm mrad
369  emittance_m[0] *= bgam;
370  emittance_m[1] *= bgam;
371  emittance_m[2] *= bgam;
372 
373  // Define the Hamiltonian
375 
376  // infinitesimal elements
383 
384  H_m = [&](value_type h, value_type kx, value_type ky) {
385  return 0.5*px_m*px_m + 0.5*kx*x_m*x_m - h*x_m*delta_m +
386  0.5*py_m*py_m + 0.5*ky*y_m*y_m +
388  };
389 
390  Hsc_m = [&](value_type sigx, value_type sigy, value_type sigz) {
391  // convert m from MeV/c^2 to eV*s^{2}/m^{2}
392  value_type m = m_m * 1.0e6 / (Physics::c * Physics::c);
393 
394  // formula (57)
395  value_type lam = 2.0 * Physics::pi*Physics::c / (wo_m * nh_m); // wavelength, [lam] = m
396  value_type K3 = 3.0 * /* physics::q0 */ 1.0 * I_m * lam / (20.0 * std::sqrt(5.0) * Physics::pi * Physics::epsilon_0 * m *
397  Physics::c * Physics::c * Physics::c * beta_m * beta_m * gamma_m * gamma2_m); // [K3] = m
398 
399  value_type milli = 1.0e-3;
400 
401  // formula (30), (31)
402  // [sigma(0,0)] = mm^{2} --> [sx] = [sy] = [sz] = mm
403  // multiply with 0.001 to get meter --> [sx] = [sy] = [sz] = m
404  value_type sx = std::sqrt(std::fabs(sigx)) * milli;
405  value_type sy = std::sqrt(std::fabs(sigy)) * milli;
406  value_type sz = std::sqrt(std::fabs(sigz)) * milli;
407 
408  value_type tmp = sx * sy; // [tmp] = m^{2}
409 
410  value_type f = std::sqrt(tmp) / (3.0 * gamma_m * sz); // [f] = 1
411  value_type kxy = K3 * std::fabs(1.0 - f) / ((sx + sy) * sz); // [kxy] = 1/m
412 
413  value_type Kx = kxy / sx;
414  value_type Ky = kxy / sy;
415  value_type Kz = K3 * f / (tmp * sz);
416 
417  return -0.5 * Kx * x_m * x_m
418  -0.5 * Ky * y_m * y_m
419  -0.5 * Kz * z_m * z_m * gamma2_m;
420  };
421 }
422 
423 template<typename Value_type, typename Size_type>
425  size_type maxit,
426  size_type maxitOrbit,
427  Cyclotron* cycl,
428  value_type denergy,
429  value_type rguess,
430  bool harmonic, bool full)
431 {
432  /* compute the equilibrium orbit for energy E_
433  * and get the the following properties:
434  * - inverse bending radius h
435  * - step sizes of path ds
436  * - tune nuz
437  */
438 
439  try {
440  if ( !full )
441  nSteps_m = nStepsPerSector_m;
442 
443  // object for space charge map and cyclotron map
445  size_type,
446  Series,
447  Map,
448  Hamiltonian,
449  SpaceCharge> mapgen(nSteps_m);
450 
451  // compute cyclotron map and space charge map for each angle and store them into a vector
452  std::vector<matrix_type> Mcycs(nSteps_m), Mscs(nSteps_m);
453 
454  container_type h(nSteps_m), r(nSteps_m), ds(nSteps_m), fidx(nSteps_m);
455  value_type ravg = 0.0, const_ds = 0.0;
456  std::pair<value_type,value_type> tunes;
457 
458  if (!harmonic) {
459  ClosedOrbitFinder<value_type, size_type,
460  boost::numeric::odeint::runge_kutta4<container_type> > cof(m_m, N_m, cycl, false, nSectors_m);
461 
462  if ( !cof.findOrbit(accuracy, maxitOrbit, E_m, denergy, rguess) ) {
463  throw OpalException("SigmaGenerator::match()",
464  "Closed orbit finder didn't converge.");
465  }
466 
467  cof.computeOrbitProperties(E_m);
468 
469  // properties of one turn
470  tunes = cof.getTunes();
471  ravg = cof.getAverageRadius(); // average radius
472 
473  value_type angle = cycl->getPHIinit();
474  container_type h_turn = cof.getInverseBendingRadius(angle);
475  container_type r_turn = cof.getOrbit(angle);
476  container_type ds_turn = cof.getPathLength(angle);
477  container_type fidx_turn = cof.getFieldIndex(angle);
478 
479  container_type peo = cof.getMomentum(angle);
480 
481  // write properties to file
482  if (write_m)
483  writeOrbitOutput_m(tunes, ravg, cof.getFrequencyError(),
484  r_turn, peo, h_turn, fidx_turn, ds_turn);
485 
486  // write to terminal
487  *gmsg << "* ----------------------------" << endl
488  << "* Closed orbit info:" << endl
489  << "*" << endl
490  << "* average radius: " << ravg << " [m]" << endl
491  << "* initial radius: " << r_turn[0] << " [m]" << endl
492  << "* initial momentum: " << peo[0] << " [Beta Gamma]" << endl
493  << "* frequency error: " << cof.getFrequencyError()*100 <<" [ % ] "<< endl
494  << "* horizontal tune: " << tunes.first << endl
495  << "* vertical tune: " << tunes.second << endl
496  << "* ----------------------------" << endl << endl;
497 
498  // copy properties
499  std::copy_n(r_turn.begin(), nSteps_m, r.begin());
500  std::copy_n(h_turn.begin(), nSteps_m, h.begin());
501  std::copy_n(fidx_turn.begin(), nSteps_m, fidx.begin());
502  std::copy_n(ds_turn.begin(), nSteps_m, ds.begin());
503 
504  rinit_m = r[0];
505  prinit_m = peo[0];
506 
507  } else {
508  *gmsg << "Not yet supported." << endl;
509  return false;
510  }
511 
512  // initialize sigma matrices (for each angle one) (first guess)
513  initialize(tunes.second,ravg);
514 
515  // for writing
516  std::ofstream writeMturn, writeMcyc, writeMsc;
517 
518  if (write_m) {
519 
520  std::string energy = float2string(E_m);
521 
522  writeMturn.open("data/OneTurnMapForEnergy"+energy+"MeV.dat",std::ios::app);
523  writeMsc.open("data/SpaceChargeMapPerAngleForEnergy"+energy+"MeV.dat",std::ios::app);
524  writeMcyc.open("data/CyclotronMapPerAngleForEnergy"+energy+"MeV.dat",std::ios::app);
525 
526  writeMturn << "--------------------------------" << std::endl;
527  writeMturn << "Iteration: 0 " << std::endl;
528  writeMturn << "--------------------------------" << std::endl;
529 
530  writeMsc << "--------------------------------" << std::endl;
531  writeMsc << "Iteration: 0 " << std::endl;
532  writeMsc << "--------------------------------" << std::endl;
533  }
534 
535  // calculate only for a single sector (a nSector_-th) of the whole cyclotron
536  for (size_type i = 0; i < nSteps_m; ++i) {
537  if (!harmonic) {
538  Mcycs[i] = mapgen.generateMap(H_m(h[i],
539  h[i]*h[i]+fidx[i],
540  -fidx[i]),
541  ds[i],truncOrder_m);
542 
543  Mscs[i] = mapgen.generateMap(Hsc_m(sigmas_m[i](0,0),
544  sigmas_m[i](2,2),
545  sigmas_m[i](4,4)),
546  ds[i],truncOrder_m);
547  } else {
548  Mscs[i] = mapgen.generateMap(Hsc_m(sigmas_m[i](0,0),
549  sigmas_m[i](2,2),
550  sigmas_m[i](4,4)),
551  const_ds,truncOrder_m);
552  }
553 
554  if (write_m) {
555  writeMcyc << Mcycs[i] << std::endl;
556  writeMsc << Mscs[i] << std::endl;
557  }
558  }
559 
560  // one turn matrix
561  mapgen.combine(Mscs,Mcycs);
562  matrix_type Mturn = mapgen.getMap();
563 
564  if (write_m)
565  writeMturn << Mturn << std::endl;
566 
567  // (inverse) transformation matrix
568  sparse_matrix_type R(6, 6), invR(6, 6);
569 
570  // new initial sigma matrix
571  matrix_type newSigma(6,6);
572 
573  // for exiting loop
574  bool stop = false;
575 
576  value_type weight = 0.5;
577 
578  while (error_m > accuracy && !stop) {
579  // decouple transfer matrix and compute (inverse) tranformation matrix
580  decouple(Mturn,R,invR);
581 
582  // construct new initial sigma-matrix
583  newSigma = updateInitialSigma(Mturn, R, invR);
584 
585  // compute new sigma matrices for all angles (except for initial sigma)
586  updateSigma(Mscs,Mcycs);
587 
588  // compute error
589  error_m = L2ErrorNorm(sigmas_m[0],newSigma);
590 
591  // write new initial sigma-matrix into vector
592  sigmas_m[0] = weight*newSigma + (1.0-weight)*sigmas_m[0];
593 
594  if (write_m) {
595  writeMsc << "--------------------------------" << std::endl;
596  writeMsc << "Iteration: " << niterations_m + 1 << std::endl;
597  writeMsc << "--------------------------------" << std::endl;
598  }
599 
600  // compute new space charge maps
601  for (size_type i = 0; i < nSteps_m; ++i) {
602  if (!harmonic) {
603  Mscs[i] = mapgen.generateMap(Hsc_m(sigmas_m[i](0,0),
604  sigmas_m[i](2,2),
605  sigmas_m[i](4,4)),
606  ds[i],truncOrder_m);
607  } else {
608  Mscs[i] = mapgen.generateMap(Hsc_m(sigmas_m[i](0,0),
609  sigmas_m[i](2,2),
610  sigmas_m[i](4,4)),
611  const_ds,truncOrder_m);
612  }
613 
614  if (write_m)
615  writeMsc << Mscs[i] << std::endl;
616  }
617 
618  // construct new one turn transfer matrix M
619  mapgen.combine(Mscs,Mcycs);
620  Mturn = mapgen.getMap();
621 
622  if (write_m) {
623  writeMturn << "--------------------------------" << std::endl;
624  writeMturn << "Iteration: " << niterations_m + 1 << std::endl;
625  writeMturn << "--------------------------------" << std::endl;
626  writeMturn << Mturn << std::endl;
627  }
628 
629  // check if number of iterations has maxit exceeded.
630  stop = (niterations_m++ > maxit);
631  }
632 
633  // store converged sigma-matrix
634  sigma_m.resize(6,6,false);
635  sigma_m.swap(newSigma);
636 
637  // returns if the sigma matrix has converged
638  converged_m = error_m < accuracy;
639 
640  // Close files
641  if (write_m) {
642  writeMturn.close();
643  writeMsc.close();
644  writeMcyc.close();
645  }
646 
647  } catch(const std::exception& e) {
648  std::cerr << e.what() << std::endl;
649  }
650 
651  if ( converged_m && write_m ) {
652  // write tunes
653  std::ofstream writeSigmaMatched("data/MatchedDistributions.dat", std::ios::app);
654 
655  std::array<double,3> emit = this->getEmittances();
656 
657  writeSigmaMatched << "* Converged (Ex, Ey, Ez) = (" << emit[0]
658  << ", " << emit[1] << ", " << emit[2]
659  << ") pi mm mrad for E= " << E_m << " (MeV)"
660  << std::endl << "* Sigma-Matrix " << std::endl;
661 
662  for(unsigned int i = 0; i < sigma_m.size1(); ++ i) {
663  writeSigmaMatched << std::setprecision(4) << std::setw(11)
664  << sigma_m(i,0);
665  for(unsigned int j = 1; j < sigma_m.size2(); ++ j) {
666  writeSigmaMatched << " & " << std::setprecision(4)
667  << std::setw(11) << sigma_m(i,j);
668  }
669  writeSigmaMatched << " \\\\" << std::endl;
670  }
671 
672  writeSigmaMatched << std::endl;
673  writeSigmaMatched.close();
674  }
675 
676  return converged_m;
677 }
678 
679 
680 template<typename Value_type, typename Size_type>
683 {
684  typedef gsl_matrix* gsl_matrix_t;
685  typedef gsl_vector_complex* gsl_cvector_t;
686  typedef gsl_matrix_complex* gsl_cmatrix_t;
687  typedef gsl_eigen_nonsymmv_workspace* gsl_wspace_t;
688  typedef boost::numeric::ublas::vector<complex_t> complex_vector_type;
689 
690  gsl_cvector_t evals = gsl_vector_complex_alloc(6);
691  gsl_cmatrix_t evecs = gsl_matrix_complex_alloc(6, 6);
692  gsl_wspace_t wspace = gsl_eigen_nonsymmv_alloc(6);
693  gsl_matrix_t M = gsl_matrix_alloc(6, 6);
694 
695  // go to GSL
696  for (size_type i = 0; i < 6; ++i){
697  for (size_type j = 0; j < 6; ++j) {
698  gsl_matrix_set(M, i, j, Mturn(i,j));
699  }
700  }
701 
702  /*int status = */gsl_eigen_nonsymmv(M, evals, evecs, wspace);
703 
704 // if ( !status )
705 // throw OpalException("SigmaGenerator::eigsolve_m()",
706 // "Couldn't perform eigendecomposition!");
707 
708  /*status = *///gsl_eigen_nonsymmv_sort(evals, evecs, GSL_EIGEN_SORT_ABS_ASC);
709 
710 // if ( !status )
711 // throw OpalException("SigmaGenerator::eigsolve_m()",
712 // "Couldn't sort eigenvalues and eigenvectors!");
713 
714  // go to UBLAS
715  for( size_type i = 0; i < 6; i++){
716  gsl_vector_complex_view evec_i = gsl_matrix_complex_column(evecs, i);
717 
718  for(size_type j = 0;j < 6; j++){
719  gsl_complex zgsl = gsl_vector_complex_get(&evec_i.vector, j);
720  complex_t z(GSL_REAL(zgsl), GSL_IMAG(zgsl));
721  R(i,j) = z;
722  }
723  }
724 
725  // Sorting the Eigenvectors
726  // This is an arbitrary threshold that has worked for me. (We should fix this)
727  value_type threshold = 10e-12;
728  bool isZdirection = false;
729  std::vector<complex_vector_type> zVectors{};
730  std::vector<complex_vector_type> xyVectors{};
731 
732  for(size_type i = 0; i < 6; i++){
733  complex_t z = R(i,0);
734  if(std::abs(z) < threshold) z = 0.;
735  if(z == 0.) isZdirection = true;
736  complex_vector_type v(6);
737  if(isZdirection){
738  for(size_type j = 0;j < 6; j++){
739  complex_t z = R(i,j);
740  v(j) = z;
741  }
742  zVectors.push_back(v);
743  }
744  else{
745  for(size_type j = 0; j < 6; j++){
746  complex_t z = R(i,j);
747  v(j) = z;
748  }
749  xyVectors.push_back(v);
750  }
751  isZdirection = false;
752  }
753 
754  //if z-direction not found, then the system does not behave as expected
755  if(zVectors.size() != 2)
756  throw OpalException("SigmaGenerator::eigsolve_m()",
757  "Couldn't find the vertical Eigenvectors.");
758 
759  // Norms the Eigenvectors
760  for(size_type i = 0; i < 4; i++){
761  value_type norm{0};
762  for(size_type j = 0; j < 6; j++) norm += std::norm(xyVectors[i](j));
763  for(size_type j = 0; j < 6; j++) xyVectors[i](j) /= std::sqrt(norm);
764  }
765  for(size_type i = 0; i < 2; i++){
766  value_type norm{0};
767  for(size_type j = 0; j < 6; j++) norm += std::norm(zVectors[i](j));
768  for(size_type j = 0; j < 6; j++) zVectors[i](j) /= std::sqrt(norm);
769  }
770 
771  for(value_type i = 0; i < 6; i++){
772  R(i,0) = xyVectors[0](i);
773  R(i,1) = xyVectors[1](i);
774  R(i,2) = zVectors[0](i);
775  R(i,3) = zVectors[1](i);
776  R(i,4) = xyVectors[2](i);
777  R(i,5) = xyVectors[3](i);
778  }
779 
780  gsl_vector_complex_free(evals);
781  gsl_matrix_complex_free(evecs);
782  gsl_eigen_nonsymmv_free(wspace);
783  gsl_matrix_free(M);
784 }
785 
786 
787 template<typename Value_type, typename Size_type>
789  sparse_matrix_type& invR)
790 {
791  typedef boost::numeric::ublas::permutation_matrix<size_type> pmatrix_t;
792 
793  //creates a working copy of R
794  cmatrix_type A(R);
795 
796  //permutation matrix for the LU-factorization
797  pmatrix_t pm(A.size1());
798 
799  //LU-factorization
800  int res = lu_factorize(A,pm);
801 
802  if( res != 0)
803  return false;
804 
805  // create identity matrix of invR
806  invR.assign(boost::numeric::ublas::identity_matrix<complex_t>(A.size1()));
807 
808  // backsubstitute to get the inverse
809  boost::numeric::ublas::lu_substitute(A, pm, invR);
810 
811  return true;
812 }
813 
814 
815 template<typename Value_type, typename Size_type>
818  sparse_matrix_type& invR)
819 {
820  this->eigsolve_m(Mturn, R);
821 
822  if ( !this->invertMatrix_m(R, invR) )
823  throw OpalException("SigmaGenerator::decouple()",
824  "Couldn't compute inverse matrix!");
825 }
826 
827 template<typename Value_type, typename Size_type>
830  const matrix_type& sigma)
831 {
832  // compute sigma matrix after one turn
833  matrix_type newSigma = matt_boost::gemmm<matrix_type>(Mturn,
834  sigma,
835  boost::numeric::ublas::trans(Mturn));
836 
837  // return L2 error
838  return L2ErrorNorm(sigma,newSigma);
839 }
840 
841 template<typename Value_type, typename Size_type>
844 {
845  return sigma_m;
846 }
847 
848 template<typename Value_type, typename Size_type>
851 {
852  return (converged_m) ? niterations_m : size_type(0);
853 }
854 
855 template<typename Value_type, typename Size_type>
858 {
859  return error_m;
860 }
861 
862 template<typename Value_type, typename Size_type>
863 inline std::array<Value_type,3>
865 {
866  value_type bgam = gamma_m*beta_m;
867  return std::array<value_type,3>{{
868  emittance_m[0]/Physics::pi/bgam,
869  emittance_m[1]/Physics::pi/bgam,
870  emittance_m[2]/Physics::pi/bgam
871  }};
872 }
873 
874 // -----------------------------------------------------------------------------------------------------------------------
875 // PRIVATE MEMBER FUNCTIONS
876 // -----------------------------------------------------------------------------------------------------------------------
877 
878 template<typename Value_type, typename Size_type>
880  value_type ravg)
881 {
882  /*
883  * The initialization is based on the analytical solution of
884  * using a spherical symmetric beam in the paper:
885  * Transverse-longitudinal coupling by space charge in cyclotrons
886  * by Dr. Christian Baumgarten
887  * (formulas: (46), (56), (57))
888  */
889 
890 
891  /* Units:
892  * ----------------------------------------------
893  * [wo] = 1/s
894  * [nh] = 1
895  * [q0] = e
896  * [I] = A
897  * [eps0] = (A*s)^{2}/(N*m^{2})
898  * [E0] = MeV/(c^{2}) (with speed of light c)
899  * [beta] = 1
900  * [gamma] = 1
901  * [m] = kg
902  *
903  * [lam] = m
904  * [K3] = m
905  * [alpha] = 10^{3}/(pi*mrad)
906  * ----------------------------------------------
907  */
908 
909  // helper constants
910  value_type invbg = 1.0 / (beta_m * gamma_m);
911  value_type micro = 1.0e-6;
912  value_type mega = 1.0e6;
913  //value_type kilo = 1.0e3;
914 
915  // convert mass m_m from MeV/c^2 to eV*s^{2}/m^{2}
916  value_type m = m_m * mega/(Physics::c * Physics::c); // [m] = eV*s^{2}/m^{2}, [m_m] = MeV/c^2
917 
918  /* Emittance [ex] = [ey] = [ez] = mm mrad (emittance_m are normalized emittances
919  * (i.e. emittance multiplied with beta*gamma)
920  */
921  value_type ex = emittance_m[0] * invbg; // [ex] = mm mrad
922  value_type ey = emittance_m[1] * invbg; // [ey] = mm mrad
923  value_type ez = emittance_m[2] * invbg; // [ez] = mm mrad
924 
925  // convert normalized emittances: mm mrad --> m rad (mm mrad: millimeter milliradian)
926  ex *= micro;
927  ey *= micro;
928  ez *= micro;
929 
930  // initial guess of emittance, [e] = m rad
931  value_type e = std::cbrt(ex * ey * ez); // cbrt computes cubic root (C++11) <cmath>
932 
933  // cyclotron radius [rcyc] = m
934  value_type rcyc = ravg / beta_m;
935 
936  // "average" inverse bending radius
937  value_type h = 1.0 / ravg; // [h] = 1/m
938 
939  // formula (57)
940  value_type lam = 2.0 * Physics::pi * Physics::c / (wo_m * nh_m); // wavelength, [lam] = m
941  value_type K3 = 3.0 * /* physics::q0 */ 1.0 * I_m * lam / (20.0 * std::sqrt(5.0) * Physics::pi * Physics::epsilon_0 * m *
942  Physics::c * Physics::c * Physics::c * beta_m * beta_m * gamma2_m * gamma_m); // [K3] = m
943 
944  value_type alpha = /* physics::q0 */ 1.0 * Physics::mu_0 * I_m / (5.0 * std::sqrt(10.0) * m * Physics::c *
945  gamma_m * nh_m) * std::sqrt(rcyc * rcyc * rcyc / (e * e * e)); // [alpha] = 1/rad --> [alpha] = 1
946 
947  value_type sig0 = std::sqrt(2.0 * rcyc * e) / gamma_m; // [sig0] = m*sqrt(rad) --> [sig0] = m
948 
949  // formula (56)
950  value_type sig; // [sig] = m
951  if (alpha >= 2.5) {
952  sig = sig0 * std::cbrt(1.0 + alpha); // cbrt computes cubic root (C++11) <cmath>
953  } else if (alpha >= 0) {
954  sig = sig0 * (1 + alpha * (0.25 - 0.03125 * alpha));
955  } else {
956  throw OpalException("SigmaGenerator::initialize()",
957  "Negative alpha value: " + std::to_string(alpha) + " < 0");
958  }
959 
960  // K = Kx = Ky = Kz
961  value_type K = K3 * gamma_m / (3.0 * sig * sig * sig); // formula (46), [K] = 1/m^{2}
962  value_type kx = h * h * gamma2_m; // formula (46) (assumption of an isochronous cyclotron), [kx] = 1/m^{2}
963 
964  value_type a = 0.5 * kx - K; // formula (22) (with K = Kx = Kz), [a] = 1/m^{2}
965  value_type b = K * K; // formula (22) (with K = Kx = Kz and kx = h^2*gamma^2), [b] = 1/m^{4}
966 
967 
968  // b must be positive, otherwise no real-valued frequency
969  if (b < 0)
970  throw OpalException("SigmaGenerator::initialize()",
971  "Negative value --> No real-valued frequency.");
972 
973  value_type tmp = a * a - b; // [tmp] = 1/m^{4}
974  if (tmp < 0)
975  throw OpalException("SigmaGenerator::initialize()",
976  "Square root of negative number.");
977 
978  tmp = std::sqrt(tmp); // [tmp] = 1/m^{2}
979 
980  if (a < tmp)
981  throw OpalException("Error in SigmaGenerator::initialize()",
982  "Square root of negative number.");
983 
984  if (h * h * nuz * nuz <= K)
985  throw OpalException("SigmaGenerator::initialize()",
986  "h^{2} * nu_{z}^{2} <= K (i.e. square root of negative number)");
987 
988  value_type Omega = std::sqrt(a + tmp); // formula (22), [Omega] = 1/m
989  value_type omega = std::sqrt(a - tmp); // formula (22), [omega] = 1/m
990 
991  value_type A = h / (Omega * Omega + K); // formula (26), [A] = m
992  value_type B = h / (omega * omega + K); // formula (26), [B] = m
993  value_type invAB = 1.0 / (B - A); // [invAB] = 1/m
994 
995  // construct initial sigma-matrix (formula (29, 30, 31)
996  // Remark: We multiply with 10^{6} (= mega) to convert emittances back.
997  // 1 m^{2} = 10^{6} mm^{2}
998  matrix_type sigma = boost::numeric::ublas::zero_matrix<value_type>(6);
999 
1000  // formula (30), [sigma(0,0)] = m^2 rad * 10^{6} = mm^{2} rad = mm mrad
1001  sigma(0,0) = invAB * (B * ex / Omega + A * ez / omega) * mega;
1002 
1003  // [sigma(0,5)] = [sigma(5,0)] = m rad * 10^{6} = mm mrad // 1000: m --> mm and 1000 to promille
1004  sigma(0,5) = sigma(5,0) = invAB * (ex / Omega + ez / omega) * mega;
1005 
1006  // [sigma(1,1)] = rad * 10^{6} = mrad (and promille)
1007  sigma(1,1) = invAB * (B * ex * Omega + A * ez * omega) * mega;
1008 
1009  // [sigma(1,4)] = [sigma(4,1)] = m rad * 10^{6} = mm mrad
1010  sigma(1,4) = sigma(4,1) = invAB * (ex * Omega+ez * omega) / (K * gamma2_m) * mega;
1011 
1012  // formula (31), [sigma(2,2)] = m rad * 10^{6} = mm mrad
1013  sigma(2,2) = ey / (std::sqrt(h * h * nuz * nuz - K)) * mega;
1014 
1015  sigma(3,3) = (ey * mega) * (ey * mega) / sigma(2,2);
1016 
1017  // [sigma(4,4)] = m^{2} rad * 10^{6} = mm^{2} rad = mm mrad
1018  sigma(4,4) = invAB * (A * ex * Omega + B * ez * omega) / (K * gamma2_m) * mega;
1019 
1020  // formula (30), [sigma(5,5)] = rad * 10^{6} = mrad (and promille)
1021  sigma(5,5) = invAB * (ex / (B * Omega) + ez / (A * omega)) * mega;
1022 
1023  // fill in initial guess of the sigma matrix (for each angle the same guess)
1024  sigmas_m.resize(nSteps_m);
1025  for (typename std::vector<matrix_type>::iterator it = sigmas_m.begin(); it != sigmas_m.end(); ++it) {
1026  *it = sigma;
1027  }
1028 
1029  if (write_m) {
1030  std::string energy = float2string(E_m);
1031  std::ofstream writeInit("data/maps/InitialSigmaPerAngleForEnergy" +
1032  energy + "MeV.dat", std::ios::app);
1033  writeInit << sigma << std::endl;
1034  writeInit.close();
1035  }
1036 }
1037 
1038 
1039 template<typename Value_type, typename Size_type>
1043  sparse_matrix_type& invR)
1044 {
1045  /*
1046  * Function input:
1047  * - M: one turn transfer matrix
1048  * - R: transformation matrix (in paper: E)
1049  * - invR: inverse transformation matrix (in paper: E^{-1}
1050  */
1051 
1052  /* formula (18):
1053  * sigma = -E*D*E^{-1}*S
1054  * with diagonal matrix D (stores eigenvalues of sigma*S (emittances apart from +- i),
1055  * skew-symmetric matrix (formula (13)), and tranformation matrices E, E^{-1}
1056  */
1057 
1058  cmatrix_type S = boost::numeric::ublas::zero_matrix<complex_t>(6,6);
1059  S(0,1) = S(2,3) = S(4,5) = 1;
1060  S(1,0) = S(3,2) = S(5,4) = -1;
1061 
1062  // Build new D-Matrix
1063  // Section 2.4 Eq. 18 in M. Frey Semester thesis
1064  // D = diag(i*emx,-i*emx,i*emy,-i*emy,i*emz, -i*emz)
1065 
1066 
1067  cmatrix_type D = boost::numeric::ublas::zero_matrix<complex_t>(6,6);
1068  value_type invbg = 1.0 / (beta_m * gamma_m);
1069  complex_t im(0,1);
1070  for(size_type i = 0; i < 3; ++i){
1071  D(2*i, 2*i) = emittance_m[i] * invbg * im;
1072  D(2*i+1, 2*i+1) = -emittance_m[i] * invbg * im;
1073  }
1074 
1075  // Computing of new Sigma
1076  // sigma = -R*D*R^{-1}*S
1077  cmatrix_type csigma(6, 6);
1078  csigma = boost::numeric::ublas::prod(invR, S);
1079  csigma = boost::numeric::ublas::prod(D, csigma);
1080  csigma = boost::numeric::ublas::prod(-R, csigma);
1081 
1082  matrix_type sigma(6,6);
1083  for (size_type i = 0; i < 6; ++i){
1084  for (size_type j = 0; j < 6; ++j){
1085  sigma(i,j) = csigma(i,j).real();
1086  }
1087  }
1088 
1089  for (size_type i = 0; i < 6; ++i) {
1090  if(sigma(i,i) < 0.)
1091  sigma(i,i) *= -1.0;
1092  }
1093 
1094  if (write_m) {
1095  std::string energy = float2string(E_m);
1096  std::ofstream writeSigma("data/maps/SigmaPerAngleForEnergy" +
1097  energy + "MeV.dat", std::ios::app);
1098 
1099  writeSigma << "--------------------------------" << std::endl;
1100  writeSigma << "Iteration: " << niterations_m + 1 << std::endl;
1101  writeSigma << "--------------------------------" << std::endl;
1102 
1103  writeSigma << sigma << std::endl;
1104  writeSigma.close();
1105  }
1106 
1107  return sigma;
1108 }
1109 
1110 template<typename Value_type, typename Size_type>
1111 void SigmaGenerator<Value_type, Size_type>::updateSigma(const std::vector<matrix_type>& Mscs,
1112  const std::vector<matrix_type>& Mcycs)
1113 {
1114  matrix_type M = boost::numeric::ublas::matrix<value_type>(6,6);
1115 
1116  std::ofstream writeSigma;
1117 
1118  if (write_m) {
1119  std::string energy = float2string(E_m);
1120  writeSigma.open("data/maps/SigmaPerAngleForEnergy"+energy+"MeV.dat",std::ios::app);
1121  }
1122 
1123  // initial sigma is already computed
1124  for (size_type i = 1; i < nSteps_m; ++i) {
1125  // transfer matrix for one angle
1126  M = boost::numeric::ublas::prod(Mscs[i - 1],Mcycs[i - 1]);
1127  // transfer the matrix sigma
1128  sigmas_m[i] = matt_boost::gemmm<matrix_type>(M,sigmas_m[i - 1],
1129  boost::numeric::ublas::trans(M));
1130 
1131  if (write_m)
1132  writeSigma << sigmas_m[i] << std::endl;
1133  }
1134 
1135  if (write_m) {
1136  writeSigma << std::endl;
1137  writeSigma.close();
1138  }
1139 }
1140 
1141 template<typename Value_type, typename Size_type>
1144  const matrix_type& newS)
1145 {
1146  // compute difference
1147  matrix_type diff = newS - oldS;
1148 
1149  // sum squared error up and take square root
1150  return std::sqrt(std::inner_product(diff.data().begin(),
1151  diff.data().end(),
1152  diff.data().begin(),
1153  0.0));
1154 }
1155 
1156 template<typename Value_type, typename Size_type>
1159  const matrix_type& newS)
1160 {
1161  // compute difference
1162  matrix_type diff = newS - oldS;
1163 
1164  std::for_each(diff.data().begin(), diff.data().end(),
1165  [](value_type& val) {
1166  return std::abs(val);
1167  });
1168 
1169  // sum squared error up and take square root
1170  return std::accumulate(diff.data().begin(), diff.data().end(), 0.0);
1171 }
1172 
1173 
1174 template<typename Value_type, typename Size_type>
1176  std::ostringstream out;
1177  out << std::setprecision(6) << val;
1178  return out.str();
1179 }
1180 
1181 
1182 template<typename Value_type, typename Size_type>
1184  const std::pair<value_type,value_type>& tunes,
1185  const value_type& ravg,
1186  const value_type& freqError,
1187  const container_type& r_turn,
1188  const container_type& peo,
1189  const container_type& h_turn,
1190  const container_type& fidx_turn,
1191  const container_type& ds_turn)
1192 {
1193  // write tunes
1194  std::ofstream writeTunes("data/Tunes.dat", std::ios::app);
1195 
1196  if(writeTunes.tellp() == 0) // if nothing yet written --> write description
1197  writeTunes << "energy [MeV]" << std::setw(15)
1198  << "nur" << std::setw(25)
1199  << "nuz" << std::endl;
1200 
1201  writeTunes << E_m << std::setw(30) << std::setprecision(10)
1202  << tunes.first << std::setw(25)
1203  << tunes.second << std::endl;
1204 
1205  // write average radius
1206  std::ofstream writeAvgRadius("data/AverageValues.dat", std::ios::app);
1207 
1208  if (writeAvgRadius.tellp() == 0) // if nothing yet written --> write description
1209  writeAvgRadius << "energy [MeV]" << std::setw(15)
1210  << "avg. radius [m]" << std::setw(15)
1211  << "r [m]" << std::setw(15)
1212  << "pr [m]" << std::endl;
1213 
1214  writeAvgRadius << E_m << std::setw(25) << std::setprecision(10)
1215  << ravg << std::setw(25) << std::setprecision(10)
1216  << r_turn[0] << std::setw(25) << std::setprecision(10)
1217  << peo[0] << std::endl;
1218 
1219  // write frequency error
1220  std::ofstream writePhase("data/FrequencyError.dat",std::ios::app);
1221 
1222  if(writePhase.tellp() == 0) // if nothing yet written --> write description
1223  writePhase << "energy [MeV]" << std::setw(15)
1224  << "freq. error" << std::endl;
1225 
1226  writePhase << E_m << std::setw(30) << std::setprecision(10)
1227  << freqError << std::endl;
1228 
1229  // write other properties
1230  std::string energy = float2string(E_m);
1231  std::ofstream writeProperties("data/PropertiesForEnergy"+energy+"MeV.dat", std::ios::out);
1232  writeProperties << std::left
1233  << std::setw(25) << "orbit radius"
1234  << std::setw(25) << "orbit momentum"
1235  << std::setw(25) << "inverse bending radius"
1236  << std::setw(25) << "field index"
1237  << std::setw(25) << "path length" << std::endl;
1238 
1239  for (size_type i = 0; i < r_turn.size(); ++i) {
1240  writeProperties << std::setprecision(10) << std::left
1241  << std::setw(25) << r_turn[i]
1242  << std::setw(25) << peo[i]
1243  << std::setw(25) << h_turn[i]
1244  << std::setw(25) << fidx_turn[i]
1245  << std::setw(25) << ds_turn[i] << std::endl;
1246  }
1247 
1248  // close all files within this if-statement
1249  writeTunes.close();
1250  writeAvgRadius.close();
1251  writePhase.close();
1252  writeProperties.close();
1253 }
1254 
1255 #endif
value_type Emin_m
Minimum energy needed in cyclotron, .
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
bool converged_m
Is true if converged, false otherwise.
void decouple(const matrix_type &Mturn, sparse_matrix_type &R, sparse_matrix_type &invR)
Block diagonalizes the symplex part of the one turn transfer matrix.
boost::numeric::ublas::matrix< value_type > matrix_type
Type for storing maps.
size_type nSteps_m
Number of integration steps in total.
constexpr double e
The value of .
Definition: Physics.h:40
std::array< value_type, 3 > getEmittances() const
Returns the emittances (ex,ey,ez) in for which the code converged (since the whole simulation is don...
std::complex< value_type > complex_t
Finds a closed orbit of a cyclotron for a given energy.
bool match(value_type accuracy, size_type maxit, size_type maxitOrbit, Cyclotron *cycl, value_type denergy, value_type rguess, bool harmonic, bool full)
Searches for a matched distribution.
Hamiltonian H_m
Stores the Hamiltonian of the cyclotron.
Interface for a Cyclotron.
Definition: Cyclotron.h:91
value_type getError() const
Returns the error (if the program didn&#39;t converged, one can call this function to check the error) ...
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
virtual double getPHIinit() const
Definition: Cyclotron.cpp:128
The base class for all OPAL exceptions.
Definition: OpalException.h:28
void initialize(value_type, value_type)
Inform * gmsg
Definition: Main.cpp:21
size_type N_m
Number of integration steps for closed orbit computation.
size_type nSectors_m
Number of (symmetric) sectors the field is averaged over.
std::array< value_type, 3 > emittance_m
Stores the desired emittances, .
value_type L2ErrorNorm(const matrix_type &, const matrix_type &)
Returns the L2-error norm between the old and new sigma-matrix.
matrix_type updateInitialSigma(const matrix_type &, sparse_matrix_type &, sparse_matrix_type &)
Computes the new initial sigma matrix.
value_type E_m
Stores the user-define energy, .
std::function< Series(value_type, value_type, value_type)> Hamiltonian
Type of the Hamiltonian for the cyclotron.
matrix_type sigma_m
Stores the stationary distribution (sigma matrix)
Value_type value_type
Type of variables.
This class generates the matrices for the one turn matrix of a cyclotron.
Definition: MapGenerator.h:30
FTps< double, 6 > Series
Definition: LieMapper.cpp:60
const double & getInjectionRadius() const
constexpr double alpha
The fine structure constant, no dimension.
Definition: Physics.h:79
Size_type size_type
Type for specifying sizes.
value_type gamma_m
Relativistic factor (which can be computed out ot the kinetic energy and rest mass (potential energy)...
void updateSigma(const std::vector< matrix_type > &, const std::vector< matrix_type > &)
Computes new sigma matrices (one for each angle)
size_type niterations_m
Is the number of iterations needed for convergence.
constexpr double pi
The value of .
Definition: Physics.h:31
value_type I_m
Stores the value of the current, .
boost::numeric::ublas::matrix< complex_t > cmatrix_type
Type for storing complex matrices.
boost::numeric::ublas::compressed_matrix< complex_t, boost::numeric::ublas::row_major > sparse_matrix_type
Type for storing the sparse maps.
constexpr double mu_0
The permeability of vacuum in Vs/Am.
Definition: Physics.h:55
value_type isEigenEllipse(const matrix_type &Mturn, const matrix_type &sigma)
Checks if the sigma-matrix is an eigenellipse and returns the L2 error.
value_type m_m
Is the mass of the particles, .
const double pi
Definition: fftpack.cpp:894
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:52
const double & getInjectionMomentum() const
SigmaGenerator(value_type I, value_type ex, value_type ey, value_type ez, value_type E, value_type m, const Cyclotron *cycl, size_type N, size_type Nsectors, size_type truncOrder, bool write=true)
Constructs an object of type SigmaGenerator.
value_type L1ErrorNorm(const matrix_type &, const matrix_type &)
Returns the Lp-error norm between the old and new sigma-matrix.
void writeOrbitOutput_m(const std::pair< value_type, value_type > &tunes, const value_type &ravg, const value_type &freqError, const container_type &r_turn, const container_type &peo, const container_type &h_turn, const container_type &fidx_turn, const container_type &ds_turn)
Called within SigmaGenerator::match().
value_type error_m
Error of computation.
std::function< Series(value_type, value_type, value_type)> SpaceCharge
Type of the Hamiltonian for the space charge.
MMatrix< double > im(MMatrix< m_complex > mc)
Definition: MMatrix.cpp:398
value_type wo_m
Is the orbital frequency, .
T * value_type(const SliceIterator< T > &)
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
boost::numeric::ublas::vector< value_type > vector_type
Type for storing vectors.
std::vector< value_type > container_type
Container for storing the properties for each angle.
bool invertMatrix_m(const sparse_matrix_type &R, sparse_matrix_type &invR)
const value_type const_value_type
Type of constant variables.
void eigsolve_m(const matrix_type &Mturn, sparse_matrix_type &R)
SpaceCharge Hsc_m
Stores the Hamiltonian for the space charge.
FVps< double, 6 > Map
Definition: ThickMapper.cpp:67
static void setGlobalTruncOrder(int order)
Set the global truncation order.
size_type getIterations() const
Returns the number of iterations needed for convergence (if not converged, it returns zero) ...
T::PETE_Expr_t::PETE_Return_t prod(const PETE_Expr< T > &expr)
Definition: PETE.h:1243
value_type beta_m
Velocity (c/v), .
FTps< value_type, 2 *3 > Series
Type of the truncated powere series.
Series x_m
All variables x, px, y, py, z, delta.
constexpr double epsilon_0
The permittivity of vacuum in As/Vm.
Definition: Physics.h:58
size_type truncOrder_m
Truncation order of the power series for the Hamiltonian (cyclotron and space charge) ...
std::string float2string(value_type val)
Transforms a floating point value to a string.
value_type gamma2_m
Relativistic factor squared.
std::string::iterator iterator
Definition: MSLang.h:16
bool for_each(const BareFieldIterator< T, D > &p, SameFieldID s, C)
Definition: AssignDefs.h:30
size_type nStepsPerSector_m
Number of integration steps per sector (–&gt; also: number of maps per sector)
#define K
Definition: integrate.cpp:118
Definition: Inform.h:41
bool write_m
Decides for writing output (default: true)
std::vector< matrix_type > sigmas_m
Vector storing the second moment matrix for each angle.
matrix_type & getSigma()
Returns the converged stationary distribution.
value_type nh_m
Harmonic number, .
Vector truncated power series in n variables.
static FTps makeVariable(int var)
Make variable.
value_type Emax_m
Maximum energy reached in cyclotron, .
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
This class computes the matched distribution.
FVps< value_type, 2 *3 > Map
Type of a map.