OPAL (Object Oriented Parallel Accelerator Library)  2021.1.99
OPAL
SigmaGenerator.cpp
Go to the documentation of this file.
1 //
2 // Class: SigmaGenerator
3 // The SigmaGenerator class uses the class <b>ClosedOrbitFinder</b> to get the parameters(inverse bending radius, path length
4 // field index and tunes) to initialize the sigma matrix.
5 // The main function of this class is <b>match(double, unsigned int)</b>, where it iteratively tries to find a matched
6 // distribution for given
7 // emittances, energy and current. The computation stops when the L2-norm is smaller than a user-defined tolerance. \n
8 // In default mode it prints all space charge maps, cyclotron maps and second moment matrices. The orbit properties, i.e.
9 // tunes, average radius, orbit radius, inverse bending radius, path length, field index and frequency error, are printed
10 // as well.
11 //
12 // Copyright (c) 2014, 2018, Matthias Frey, Cristopher Cortes, ETH Zürich
13 // All rights reserved
14 //
15 // Implemented as part of the Semester thesis by Matthias Frey
16 // "Matched Distributions in Cyclotrons"
17 //
18 // Some adaptations done as part of the Bachelor thesis by Cristopher Cortes
19 // "Limitations of a linear transfer map method for finding matched distributions in high intensity cyclotrons"
20 //
21 // This file is part of OPAL.
22 //
23 // OPAL is free software: you can redistribute it and/or modify
24 // it under the terms of the GNU General Public License as published by
25 // the Free Software Foundation, either version 3 of the License, or
26 // (at your option) any later version.
27 //
28 // You should have received a copy of the GNU General Public License
29 // along with OPAL. If not, see <https://www.gnu.org/licenses/>.
30 //
31 #include "SigmaGenerator.h"
32 
34 #include "AbsBeamline/Cyclotron.h"
36 #include "Utilities/Util.h"
37 
39 #include "ClosedOrbitFinder.h"
40 #include "MapGenerator.h"
41 
42 #include <cmath>
43 #include <iomanip>
44 #include <limits>
45 #include <numeric>
46 #include <sstream>
47 #include <utility>
48 
49 #include <boost/filesystem.hpp>
50 
51 #include <boost/numeric/odeint/stepper/runge_kutta4.hpp>
52 
53 extern Inform *gmsg;
54 
56  double ex,
57  double ey,
58  double ez,
59  double E,
60  double m,
61  double q,
62  const Cyclotron* cycl,
63  unsigned int N,
64  unsigned int Nsectors,
65  unsigned int truncOrder,
66  bool write)
67  : I_m(I)
68  , wo_m(cycl->getRfFrequ(0)*1E6/cycl->getCyclHarm()*2.0*Physics::pi)
69  , E_m(E)
70  , gamma_m(E/m+1.0)
71  , gamma2_m(gamma_m*gamma_m)
72  , nh_m(cycl->getCyclHarm())
73  , beta_m(std::sqrt(1.0-1.0/gamma2_m))
74  , m_m(m)
75  , q_m(q)
76  , niterations_m(0)
77  , converged_m(false)
78  , Emin_m(cycl->getFMLowE())
79  , Emax_m(cycl->getFMHighE())
80  , N_m(N)
81  , nSectors_m(Nsectors)
82  , nStepsPerSector_m(N/cycl->getSymmetry())
83  , nSteps_m(N)
84  , error_m(std::numeric_limits<double>::max())
85  , truncOrder_m(truncOrder)
86  , write_m(write)
87  , sigmas_m(nStepsPerSector_m)
88  , rinit_m(0.0)
89  , prinit_m(0.0)
90 {
91  // minimum beta*gamma
92  double bgam = Util::getBetaGamma(Emin_m, m_m);
93 
94  // set emittances (initialization like that due to old compiler version)
95  // [ex] = [ey] = [ez] = pi*mm*mrad --> [emittance] = m rad
96  // normalized emittance (--> multiply with beta*gamma)
97  emittance_m[0] = ex * Physics::pi * 1.0e-6 * bgam;
98  emittance_m[1] = ey * Physics::pi * 1.0e-6 * bgam;
99  emittance_m[2] = ez * Physics::pi * 1.0e-6 * bgam;
100 
101  // Define the Hamiltonian
103 
104  // infinitesimal elements
111 
112  H_m = [&](double h, double kx, double ky) {
113  return 0.5*px_m*px_m + 0.5*kx*x_m*x_m - h*x_m*delta_m +
114  0.5*pz_m*pz_m + 0.5*ky*z_m*z_m +
116  };
117 
118  Hsc_m = [&](double sigx, double sigy, double sigz) {
119  // convert m from MeV/c^2 to eV/c^2
120  double m = m_m * 1.0e6;
121 
122  // formula (57)
123  double lam = Physics::two_pi*Physics::c / (wo_m * nh_m); // wavelength, [lam] = m
124  // [K3] = m
125  double K3 = 3.0 * std::abs(q_m) * I_m * lam
126  / (20.0 * std::sqrt(5.0) * Physics::pi * Physics::epsilon_0 * m
128 
129  // formula (30), (31)
130  // [sigma(0,0)] = m^{2} --> [sx] = [sy] = [sz] = m
131  // In the cyclotron community z is the vertical and y the longitudinal
132  // direction.
133  // x: horizontal
134  // y/l: longitudinal
135  // z: vertical
136  double sx = std::sqrt(std::abs(sigx));
137  double sy = std::sqrt(std::abs(sigy));
138  double sz = std::sqrt(std::abs(sigz));
139 
140  double tmp = sx * sy; // [tmp] = m^{2}
141 
142  double f = std::sqrt(tmp) / (3.0 * gamma_m * sz); // [f] = 1
143  double kxy = K3 * std::abs(1.0 - f) / ((sx + sy) * sz); // [kxy] = 1/m
144 
145  double Kx = kxy / sx;
146  double Ky = kxy / sy;
147  double Kz = K3 * f / (tmp * sz);
148 
149  return -0.5 * Kx * x_m * x_m
150  -0.5 * Kz * z_m * z_m
151  -0.5 * Ky * l_m * l_m * gamma2_m;
152  };
153 }
154 
155 
156 bool SigmaGenerator::match(double accuracy,
157  unsigned int maxit,
158  unsigned int maxitOrbit,
159  Cyclotron* cycl,
160  double denergy,
161  double rguess,
162  bool full)
163 {
164  /* compute the equilibrium orbit for energy E_
165  * and get the the following properties:
166  * - inverse bending radius h
167  * - step sizes of path ds
168  * - tune nuz
169  */
170 
171  try {
172  if ( !full )
174 
175  // object for space charge map and cyclotron map
176  MapGenerator<double,
177  unsigned int,
178  Series,
179  Map,
180  Hamiltonian,
181  SpaceCharge> mapgen(nSteps_m);
182 
183  // compute cyclotron map and space charge map for each angle and store them into a vector
184  std::vector<matrix_t> Mcycs(nSteps_m), Mscs(nSteps_m);
185 
186  ClosedOrbitFinder<double, unsigned int,
187  boost::numeric::odeint::runge_kutta4<container_t> > cof(m_m, q_m, N_m, cycl, false, nSectors_m);
188 
189  if ( !cof.findOrbit(accuracy, maxitOrbit, E_m, denergy, rguess) ) {
190  throw OpalException("SigmaGenerator::match()",
191  "Closed orbit finder didn't converge.");
192  }
193 
194  cof.computeOrbitProperties(E_m);
195 
196  double angle = cycl->getPHIinit();
197  container_t h = cof.getInverseBendingRadius(angle);
198  container_t r = cof.getOrbit(angle);
199  container_t peo = cof.getMomentum(angle);
200  container_t ds = cof.getPathLength(angle);
201  container_t fidx = cof.getFieldIndex(angle);
202 
203  // write properties to file
204  writeOrbitOutput_m(r, peo, h, fidx, ds);
205 
206  rinit_m = r[0];
207  prinit_m = peo[0];
208 
209  std::string fpath = Util::combineFilePath({
211  "maps"
212  });
213  if (!boost::filesystem::exists(fpath)) {
214  boost::filesystem::create_directory(fpath);
215  }
216 
217  std::pair<double,double> tunes = cof.getTunes();
218  double ravg = cof.getAverageRadius();
219 
220  // write to terminal
221  *gmsg << "* ----------------------------" << endl
222  << "* Closed orbit info:" << endl
223  << "*" << endl
224  << "* average radius: " << ravg << " [m]" << endl
225  << "* initial radius: " << r[0] << " [m]" << endl
226  << "* initial momentum: " << peo[0] << " [Beta Gamma]" << endl
227  << "* frequency error: " << cof.getFrequencyError()*100 <<" [ % ] "<< endl
228  << "* horizontal tune: " << tunes.first << endl
229  << "* vertical tune: " << tunes.second << endl
230  << "* ----------------------------" << endl << endl;
231 
232  // initialize sigma matrices (for each angle one) (first guess)
233  initialize(tunes.second,ravg);
234 
235  // for writing
236  std::ofstream writeMturn, writeMcyc, writeMsc;
237 
238  if (write_m) {
239 
240  std::string energy = float2string(E_m);
241 
242  std::string fname = Util::combineFilePath({
244  "maps",
245  "OneTurnMapsForEnergy" + energy + "MeV.dat"
246  });
247 
248  writeMturn.open(fname, std::ios::out);
249 
250  fname = Util::combineFilePath({
252  "maps",
253  "SpaceChargeMapPerAngleForEnergy" + energy + "MeV_iteration_0.dat"
254  });
255 
256  writeMsc.open(fname, std::ios::out);
257 
258  fname = Util::combineFilePath({
260  "maps",
261  "CyclotronMapPerAngleForEnergy" + energy + "MeV.dat"
262  });
263 
264  writeMcyc.open(fname, std::ios::out);
265  }
266 
267  // calculate only for a single sector (a nSector_-th) of the whole cyclotron
268  for (unsigned int i = 0; i < nSteps_m; ++i) {
269  Mcycs[i] = mapgen.generateMap(H_m(h[i],
270  h[i]*h[i]+fidx[i],
271  -fidx[i]),
272  ds[i],truncOrder_m);
273 
274 
275  Mscs[i] = mapgen.generateMap(Hsc_m(sigmas_m[i](0,0),
276  sigmas_m[i](2,2),
277  sigmas_m[i](4,4)),
278  ds[i],truncOrder_m);
279 
280  writeMatrix(writeMcyc, Mcycs[i]);
281  writeMatrix(writeMsc, Mscs[i]);
282  }
283 
284  if (write_m)
285  writeMsc.close();
286 
287  // one turn matrix
288  mapgen.combine(Mscs,Mcycs);
289  matrix_t Mturn = mapgen.getMap();
290 
291  writeMatrix(writeMturn, Mturn);
292 
293  // (inverse) transformation matrix
294  sparse_matrix_t R(6, 6), invR(6, 6);
295 
296  // new initial sigma matrix
297  matrix_t newSigma(6,6);
298 
299  // for exiting loop
300  bool stop = false;
301 
302  double weight = 0.5;
303 
304  while (error_m > accuracy && !stop) {
305  // decouple transfer matrix and compute (inverse) tranformation matrix
306  vector_t eigen = decouple(Mturn, R,invR);
307 
308  // construct new initial sigma-matrix
309  newSigma = updateInitialSigma(Mturn, eigen, R, invR);
310 
311  // compute new sigma matrices for all angles (except for initial sigma)
312  updateSigma(Mscs,Mcycs);
313 
314  // compute error with mm^2 and (mrad)^2
315  error_m = L2ErrorNorm(sigmas_m[0] * 1e6, newSigma * 1e6);
316 
317  // write new initial sigma-matrix into vector
318  sigmas_m[0] = weight*newSigma + (1.0-weight)*sigmas_m[0];
319 
320  if (write_m) {
321 
322  std::string energy = float2string(E_m);
323 
324  std::string fname = Util::combineFilePath({
326  "maps",
327  "SpaceChargeMapPerAngleForEnergy" + energy + "MeV_iteration_"
328  + std::to_string(niterations_m + 1)
329  + ".dat"
330  });
331 
332  writeMsc.open(fname, std::ios::out);
333  }
334 
335  // compute new space charge maps
336  for (unsigned int i = 0; i < nSteps_m; ++i) {
337  Mscs[i] = mapgen.generateMap(Hsc_m(sigmas_m[i](0,0),
338  sigmas_m[i](2,2),
339  sigmas_m[i](4,4)),
340  ds[i],truncOrder_m);
341 
342  writeMatrix(writeMsc, Mscs[i]);
343  }
344 
345  if (write_m) {
346  writeMsc.close();
347  }
348 
349  // construct new one turn transfer matrix M
350  mapgen.combine(Mscs,Mcycs);
351  Mturn = mapgen.getMap();
352 
353  writeMatrix(writeMturn, Mturn);
354 
355  // check if number of iterations has maxit exceeded.
356  stop = (niterations_m++ > maxit);
357  }
358 
359  // store converged sigma-matrix
360  sigma_m.resize(6,6,false);
361  sigma_m.swap(newSigma);
362 
363  // returns if the sigma matrix has converged
364  converged_m = error_m < accuracy;
365 
366  // Close files
367  if (write_m) {
368  writeMturn.close();
369  writeMcyc.close();
370  }
371 
372  } catch(const std::exception& e) {
373  throw OpalException("SigmaGenerator::match()", e.what());
374  }
375 
376  if ( converged_m && write_m ) {
377  // write tunes
378  std::string fname = Util::combineFilePath({
380  "MatchedDistributions.dat"
381  });
382 
383  std::ofstream writeSigmaMatched(fname, std::ios::out);
384 
385  std::array<double,3> emit = this->getEmittances();
386 
387  writeSigmaMatched << "* Converged (Ex, Ey, Ez) = (" << emit[0]
388  << ", " << emit[1] << ", " << emit[2]
389  << ") pi mm mrad for E= " << E_m << " (MeV)"
390  << std::endl << "* Sigma-Matrix " << std::endl;
391 
392  for(unsigned int i = 0; i < sigma_m.size1(); ++ i) {
393  writeSigmaMatched << std::setprecision(4) << std::setw(11)
394  << sigma_m(i,0);
395  for(unsigned int j = 1; j < sigma_m.size2(); ++ j) {
396  writeSigmaMatched << " & " << std::setprecision(4)
397  << std::setw(11) << sigma_m(i,j);
398  }
399  writeSigmaMatched << " \\\\" << std::endl;
400  }
401 
402  writeSigmaMatched << std::endl;
403  writeSigmaMatched.close();
404  }
405 
406  return converged_m;
407 }
408 
409 
413  sparse_matrix_t& invR)
414 {
415  // copy one turn matrix
416  matrix_t M(Mturn);
417 
418  // reduce 6x6 matrix to 4x4 matrix
419  reduce<matrix_t>(M);
420 
421  // compute symplex part
422  matrix_t Ms = rdm_m.symplex(M);
423 
424  // diagonalize and compute transformation matrices
425  rdm_m.diagonalize(Ms,R,invR);
426 
427  /*
428  * formula (38) in paper of Dr. Christian Baumgarten:
429  * Geometrical method of decoupling
430  *
431  * [0, alpha, 0, 0;
432  * F_{d} = -beta, 0, 0, 0;
433  * 0, 0, 0, gamma;
434  * 0, 0, -delta, 0]
435  *
436  *
437  */
438  vector_t eigen(4);
439  eigen(0) = Ms(0,1); // alpha
440  eigen(1) = - Ms(1,0); // beta
441  eigen(2) = Ms(2,3); // gamma
442  eigen(3) = - Ms(3,2); // delta
443  return eigen;
444 }
445 
446 
448  const matrix_t& sigma)
449 {
450  // compute sigma matrix after one turn
451  matrix_t newSigma = matt_boost::gemmm<matrix_t>(Mturn,
452  sigma,
453  boost::numeric::ublas::trans(Mturn));
454 
455  // return L2 error
456  return L2ErrorNorm(sigma,newSigma);
457 }
458 
459 
460 void SigmaGenerator::initialize(double nuz, double ravg)
461 {
462  /*
463  * The initialization is based on the analytical solution of
464  * using a spherical symmetric beam in the paper:
465  * Transverse-longitudinal coupling by space charge in cyclotrons
466  * by Dr. Christian Baumgarten
467  * (formulas: (46), (56), (57))
468  */
469 
470 
471  /* Units:
472  * ----------------------------------------------
473  * [wo] = 1/s
474  * [nh] = 1
475  * [q0] = 1 e
476  * [I] = A
477  * [eps0] = (A*s)^{2}/(N*m^{2})
478  * [E0] = MeV/(c^{2}) (with speed of light c)
479  * [beta] = 1
480  * [gamma] = 1
481  * [m] = eV/c^2
482  *
483  * [lam] = m
484  * [K3] = m
485  * [alpha] = 1
486  * ----------------------------------------------
487  */
488 
489  // helper constants
490  double invbg = 1.0 / (beta_m * gamma_m);
491 
492  // convert mass m_m from MeV/c^2 to eV*s^{2}/m^{2}
493  double m = m_m * 1.0e6; // [m] = eV/c^2, [m_m] = MeV/c^2
494 
495  // emittance [ex] = [ey] = [ez] = m rad
496  double ex = emittance_m[0] * invbg; // [ex] = m rad
497  double ey = emittance_m[1] * invbg; // [ey] = m rad
498  double ez = emittance_m[2] * invbg; // [ez] = m rad
499 
500  // initial guess of emittance, [e] = m rad
501  double e = std::cbrt(ex * ey * ez); // cbrt computes cubic root (C++11) <cmath>
502 
503  // cyclotron radius [rcyc] = m
504  double rcyc = ravg / beta_m;
505 
506  // "average" inverse bending radius
507  double h = 1.0 / ravg; // [h] = 1/m
508 
509  // formula (57)
510  double lam = Physics::two_pi * Physics::c / (wo_m * nh_m); // wavelength, [lam] = m
511 
512  // m * c^3 --> c^2 in [m] = eV / c^2 cancel --> m * c in denominator
513  double K3 = 3.0 * std::abs(q_m) * I_m * lam
514  / (20.0 * std::sqrt(5.0) * Physics::pi * Physics::epsilon_0 * m
515  * Physics::c * beta_m * beta_m * gamma2_m * gamma_m); // [K3] = m
516 
517  // c in denominator cancels with unit of [m] = eV / c^2 --> we need to multiply
518  // with c in order to get dimensionless quantity
519  double alpha = std::abs(q_m) * Physics::mu_0 * I_m * Physics::c / (5.0 * std::sqrt(10.0) * m
520  * gamma_m * nh_m) * std::sqrt(rcyc * rcyc * rcyc / (e * e * e)); // [alpha] = 1/rad --> [alpha] = 1
521 
522  double sig0 = std::sqrt(2.0 * rcyc * e) / gamma_m; // [sig0] = m*sqrt(rad) --> [sig0] = m
523 
524  // formula (56)
525  double sig; // [sig] = m
526  if (alpha >= 2.5) {
527  sig = sig0 * std::cbrt(1.0 + alpha); // cbrt computes cubic root (C++11) <cmath>
528  } else if (alpha >= 0) {
529  sig = sig0 * (1 + alpha * (0.25 - 0.03125 * alpha));
530  } else {
531  throw OpalException("SigmaGenerator::initialize()",
532  "Negative alpha value: " + std::to_string(alpha) + " < 0");
533  }
534 
535  // K = Kx = Ky = Kz
536  double K = K3 * gamma_m / (3.0 * sig * sig * sig); // formula (46), [K] = 1/m^{2}
537  double kx = h * h * gamma2_m; // formula (46) (assumption of an isochronous cyclotron), [kx] = 1/m^{2}
538 
539  double a = 0.5 * kx - K; // formula (22) (with K = Kx = Kz), [a] = 1/m^{2}
540  double b = K * K; // formula (22) (with K = Kx = Kz and kx = h^2*gamma^2), [b] = 1/m^{4}
541 
542 
543  // b must be positive, otherwise no real-valued frequency
544  if (b < 0)
545  throw OpalException("SigmaGenerator::initialize()",
546  "Negative value --> No real-valued frequency.");
547 
548  double tmp = a * a - b; // [tmp] = 1/m^{4}
549  if (tmp < 0)
550  throw OpalException("SigmaGenerator::initialize()",
551  "Square root of negative number.");
552 
553  tmp = std::sqrt(tmp); // [tmp] = 1/m^{2}
554 
555  if (a < tmp)
556  throw OpalException("SigmaGenerator::initialize()",
557  "Square root of negative number.");
558 
559  if (h * h * nuz * nuz <= K)
560  throw OpalException("SigmaGenerator::initialize()",
561  "h^{2} * nu_{z}^{2} <= K (i.e. square root of negative number)");
562 
563  double Omega = std::sqrt(a + tmp); // formula (22), [Omega] = 1/m
564  double omega = std::sqrt(a - tmp); // formula (22), [omega] = 1/m
565 
566  double A = h / (Omega * Omega + K); // formula (26), [A] = m
567  double B = h / (omega * omega + K); // formula (26), [B] = m
568  double invAB = 1.0 / (B - A); // [invAB] = 1/m
569 
570  // construct initial sigma-matrix (formula (29, 30, 31)
571  matrix_t sigma = boost::numeric::ublas::zero_matrix<double>(6);
572 
573  // formula (30), [sigma(0,0)] = m^2 rad
574  sigma(0,0) = invAB * (B * ex / Omega + A * ez / omega);
575 
576  // [sigma(0,5)] = [sigma(5,0)] = m rad
577  sigma(0,5) = sigma(5,0) = invAB * (ex / Omega + ez / omega);
578 
579  // [sigma(1,1)] = rad
580  sigma(1,1) = invAB * (B * ex * Omega + A * ez * omega);
581 
582  // [sigma(1,4)] = [sigma(4,1)] = m rad
583  sigma(1,4) = sigma(4,1) = invAB * (ex * Omega+ez * omega) / (K * gamma2_m);
584 
585  // formula (31), [sigma(2,2)] = m rad
586  sigma(2,2) = ey / (std::sqrt(h * h * nuz * nuz - K));
587 
588  sigma(3,3) = (ey * ey) / sigma(2,2);
589 
590  // [sigma(4,4)] = m^{2} rad
591  sigma(4,4) = invAB * (A * ex * Omega + B * ez * omega) / (K * gamma2_m);
592 
593  // formula (30), [sigma(5,5)] = rad
594  sigma(5,5) = invAB * (ex / (B * Omega) + ez / (A * omega));
595 
596  // fill in initial guess of the sigma matrix (for each angle the same guess)
597  sigmas_m.resize(nSteps_m);
598  for (typename std::vector<matrix_t>::iterator it = sigmas_m.begin(); it != sigmas_m.end(); ++it) {
599  *it = sigma;
600  }
601 
602  if (write_m) {
603  std::string energy = float2string(E_m);
604 
605  std::string fname = Util::combineFilePath({
607  "maps",
608  "InitialSigmaPerAngleForEnergy" + energy + "MeV.dat"
609  });
610 
611  std::ofstream writeInit(fname, std::ios::out);
612  writeMatrix(writeInit, sigma);
613  writeInit.close();
614  }
615 }
616 
617 
620  const vector_t& eigen,
622  sparse_matrix_t& invR)
623 {
624  /*
625  * This function is based on the paper of Dr. Christian Baumgarten:
626  * Transverse-Longitudinal Coupling by Space Charge in Cyclotrons (2012)
627  */
628 
629  /*
630  * Function input:
631  * - M: one turn transfer matrix
632  * - eigen = {alpha, beta, gamma, delta}
633  * - R: transformation matrix (in paper: E)
634  * - invR: inverse transformation matrix (in paper: E^{-1}
635  */
636 
637  // normalize emittances
638  double invbg = 1.0 / (beta_m * gamma_m);
639  double ex = emittance_m[0] * invbg;
640  double ey = emittance_m[1] * invbg;
641  double ez = emittance_m[2] * invbg;
642 
643  // alpha^2-beta*gamma = 1
644 
645  /* 0 eigen(0) 0 0
646  * eigen(1) 0 0 0
647  * 0 0 0 eigen(2)
648  * 0 0 eigen(3) 0
649  *
650  * M = cos(mux)*[1, 0; 0, 1] + sin(mux)*[alpha, beta; -gamma, -alpha], Book, p. 242
651  *
652  * -----------------------------------------------------------------------------------
653  * X-DIRECTION and L-DIRECTION
654  * -----------------------------------------------------------------------------------
655  * --> eigen(0) = sin(mux)*betax
656  * --> eigen(1) = -gammax*sin(mux)
657  *
658  * What is sin(mux)? --> alphax = 0 --> -alphax^2+betax*gammax = betax*gammax = 1
659  *
660  * Convention: betax > 0
661  *
662  * 1) betax = 1/gammax
663  * 2) eig0 = sin(mux)*betax
664  * 3) eig1 = -gammax*sin(mux)
665  *
666  * eig0 = sin(mux)/gammax
667  * eig1 = -gammax*sin(mux) <--> 1/gammax = -sin(mux)/eig1
668  *
669  * eig0 = -sin(mux)^2/eig1 --> -sin(mux)^2 = eig0*eig1 --> sin(mux) = sqrt(-eig0*eig1)
670  * --> gammax = -eig1/sin(mux)
671  * --> betax = eig0/sin(mux)
672  */
673 
674 
675  // x-direction
676  //double alphax = 0.0;
677  double betax = std::sqrt(std::fabs(eigen(0) / eigen(1)));
678  double gammax = 1.0 / betax;
679 
680  // l-direction
681  //double alphal = 0.0;
682  double betal = std::sqrt(std::fabs(eigen(2) / eigen(3)));
683  double gammal = 1.0 / betal;
684 
685  /*
686  * -----------------------------------------------------------------------------------
687  * Z-DIRECTION
688  * -----------------------------------------------------------------------------------
689  *
690  * m22 m23
691  * m32 m33
692  *
693  * m22 = cos(muz) + alpha*sin(muz)
694  * m33 = cos(muz) - alpha*sin(muz)
695  *
696  * --> cos(muz) = 0.5*(m22 + m33)
697  * sin(muz) = sign(m32)*sqrt(1-cos(muz)^2)
698  *
699  * m22-m33 = 2*alpha*sin(muz) --> alpha = 0.5*(m22-m33)/sin(muz)
700  *
701  * m23 = betaz*sin(muz) --> betaz = m23/sin(muz)
702  * m32 = -gammaz*sin(muz) --> gammaz = -m32/sin(muz)
703  */
704 
705  double cosz = 0.5 * (M(2,2) + M(3,3));
706 
707  double sign = (std::signbit(M(2,3))) ? double(-1) : double(1);
708 
709  double invsinz = sign / std::sqrt(std::fabs( 1.0 - cosz * cosz));
710 
711  double alphaz = 0.5 * (M(2,2) - M(3,3)) * invsinz;
712  double betaz = M(2,3) * invsinz;
713  double gammaz = - M(3,2) * invsinz;
714 
715  // Convention beta>0
716  if (std::signbit(betaz)) // singbit = true if beta<0, else false
717  betaz *= -1.0;
718 
719  // diagonal matrix with eigenvalues
720  matrix_t D = boost::numeric::ublas::zero_matrix<double>(6,6);
721  // x-direction
722  D(0,1) = betax * ex;
723  D(1,0) = - gammax * ex;
724  // z-direction
725  D(2,2) = alphaz * ey;
726  D(3,3) = - alphaz * ey;
727  D(2,3) = betaz * ey;
728  D(3,2) = - gammaz * ey;
729  // l-direction
730  D(4,5) = betal * ez;
731  D(5,4) = - gammal * ez;
732 
733  // expand 4x4 transformation matrices to 6x6
734  expand<sparse_matrix_t>(R);
735  expand<sparse_matrix_t>(invR);
736 
737  // symplectic matrix
738  sparse_matrix_t S(6,6,6);
739  S(0,1) = S(2,3) = S(4,5) = 1;
740  S(1,0) = S(3,2) = S(5,4) = -1;
741 
742  matrix_t sigma = matt_boost::gemmm<matrix_t>(-invR,D,R);
743  sigma = boost::numeric::ublas::prod(sigma,S);
744 
745  if (write_m) {
746  std::string energy = float2string(E_m);
747 
748  std::string fname = Util::combineFilePath({
750  "maps",
751  "InitialSigmaPerAngleForEnergy" + energy + "MeV.dat"
752  });
753 
754  std::ofstream writeInit(fname, std::ios::app);
755  writeMatrix(writeInit, sigma);
756  writeInit.close();
757  }
758 
759  return sigma;
760 }
761 
762 
763 void SigmaGenerator::updateSigma(const std::vector<matrix_t>& Mscs,
764  const std::vector<matrix_t>& Mcycs)
765 {
766  std::ofstream writeSigma;
767 
768  if (write_m) {
769  std::string energy = float2string(E_m);
770 
771  std::string fname = Util::combineFilePath({
773  "maps",
774  "SigmaPerAngleForEnergy" + energy + "MeV_iteration_"
775  + std::to_string(niterations_m + 1)
776  + ".dat"
777  });
778 
779  writeSigma.open(fname,std::ios::out);
780  }
781 
782  // initial sigma is already computed
783  writeMatrix(writeSigma, sigmas_m[0]);
784 
785  for (unsigned int i = 1; i < nSteps_m; ++i) {
786  // transfer matrix for one angle
787  matrix_t M = boost::numeric::ublas::prod(Mscs[i - 1],Mcycs[i - 1]);
788  // transfer the matrix sigma
789  sigmas_m[i] = matt_boost::gemmm<matrix_t>(M,sigmas_m[i - 1],
790  boost::numeric::ublas::trans(M));
791 
792  writeMatrix(writeSigma, sigmas_m[i]);
793  }
794 
795  if (write_m) {
796  writeSigma.close();
797  }
798 }
799 
800 
802  const matrix_t& newS)
803 {
804  // compute difference
805  matrix_t diff = newS - oldS;
806 
807  // sum squared error up and take square root
808  return std::sqrt(std::inner_product(diff.data().begin(),
809  diff.data().end(),
810  diff.data().begin(),
811  0.0));
812 }
813 
814 
815 std::string SigmaGenerator::float2string(double val) {
816  std::ostringstream out;
817  out << std::setprecision(6) << val;
818  return out.str();
819 }
820 
821 
823  const container_t& r,
824  const container_t& peo,
825  const container_t& h,
826  const container_t& fidx,
827  const container_t& ds)
828 {
829  if (!write_m)
830  return;
831 
832  std::string energy = float2string(E_m);
833  std::string fname = Util::combineFilePath({
835  "PropertiesForEnergy" + energy + "MeV.dat"
836  });
837 
838  std::ofstream writeProperties(fname, std::ios::out);
839 
840  writeProperties << std::left
841  << std::setw(25) << "orbit radius"
842  << std::setw(25) << "orbit momentum"
843  << std::setw(25) << "inverse bending radius"
844  << std::setw(25) << "field index"
845  << std::setw(25) << "path length" << std::endl;
846 
847  for (unsigned int i = 0; i < r.size(); ++i) {
848  writeProperties << std::setprecision(10) << std::left
849  << std::setw(25) << r[i]
850  << std::setw(25) << peo[i]
851  << std::setw(25) << h[i]
852  << std::setw(25) << fidx[i]
853  << std::setw(25) << ds[i] << std::endl;
854  }
855  writeProperties.close();
856 }
857 
858 
859 void SigmaGenerator::writeMatrix(std::ofstream& os, const matrix_t& m) {
860  if (!write_m)
861  return;
862 
863  for (unsigned int i = 0; i < 6; ++i) {
864  for (unsigned int j = 0; j < 6; ++j)
865  os << m(i, j) << " ";
866  }
867  os << std::endl;
868 }
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
Inform * gmsg
Definition: Main.cpp:62
const double pi
Definition: fftpack.cpp:894
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:84
std::complex< double > a
FnArg FnReal sign(a))
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
T::PETE_Expr_t::PETE_Return_t prod(const PETE_Expr< T > &expr)
Definition: PETE.h:1121
PETE_TUTree< FnFabs, typename T::PETE_Expr_t > fabs(const PETE_Expr< T > &l)
Definition: PETE.h:732
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
Definition: Air.h:27
constexpr double two_pi
The value of.
Definition: Physics.h:33
constexpr double epsilon_0
The permittivity of vacuum in As/Vm.
Definition: Physics.h:57
constexpr double alpha
The fine structure constant, no dimension.
Definition: Physics.h:78
constexpr double e
The value of.
Definition: Physics.h:39
constexpr double mu_0
The permeability of vacuum in Vs/Am.
Definition: Physics.h:54
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:51
constexpr double pi
The value of.
Definition: Physics.h:30
std::string::iterator iterator
Definition: MSLang.h:16
std::string combineFilePath(std::initializer_list< std::string > ilist)
Definition: Util.cpp:139
double getBetaGamma(double Ekin, double mass)
Definition: Util.h:42
static OpalData * getInstance()
Definition: OpalData.cpp:195
std::string getAuxiliaryOutputDirectory() const
get the name of the the additional data directory
Definition: OpalData.cpp:650
virtual double getPHIinit() const
Definition: Cyclotron.cpp:142
static FTps makeVariable(int var)
Make variable.
Definition: FTps.hpp:481
static void setGlobalTruncOrder(int order)
Set the global truncation order.
Definition: FTps.hpp:419
This class generates the matrices for the one turn matrix of a cyclotron.
Definition: MapGenerator.h:31
matrix_t symplex(const matrix_t &)
void diagonalize(matrix_t &, sparse_matrix_t &, sparse_matrix_t &)
unsigned int niterations_m
Is the number of iterations needed for convergence.
double E_m
Stores the user-define energy, .
bool write_m
Decides for writing output (default: true)
void writeMatrix(std::ofstream &, const matrix_t &)
double isEigenEllipse(const matrix_t &Mturn, const matrix_t &sigma)
Checks if the sigma-matrix is an eigenellipse and returns the L2 error.
std::vector< double > container_t
Container for storing the properties for each angle.
std::array< double, 3 > getEmittances() const
double gamma2_m
Relativistic factor squared.
FTps< double, 2 *3 > Series
Type of the truncated power series.
Hamiltonian H_m
Stores the Hamiltonian of the cyclotron.
double m_m
Is the mass of the particles, .
double error_m
Error of computation.
matrix_t updateInitialSigma(const matrix_t &, const vector_t &, sparse_matrix_t &, sparse_matrix_t &)
Computes the new initial sigma matrix.
unsigned int nSteps_m
Number of integration steps in total.
RealDiracMatrix::sparse_matrix_t sparse_matrix_t
Type for storing the sparse maps.
unsigned int nSectors_m
Number of (symmetric) sectors the field is averaged over.
unsigned int nStepsPerSector_m
Number of integration steps per sector (--> also: number of maps per sector)
double I_m
Stores the value of the current, .
std::function< Series(double, double, double)> SpaceCharge
Type of the Hamiltonian for the space charge.
double Emin_m
Minimum energy needed in cyclotron, .
unsigned int truncOrder_m
Truncation order of the power series for the Hamiltonian (cyclotron and space charge)
SigmaGenerator(double I, double ex, double ey, double ez, double E, double m, double q, const Cyclotron *cycl, unsigned int N, unsigned int Nsectors, unsigned int truncOrder, bool write=true)
Constructs an object of type SigmaGenerator.
double L2ErrorNorm(const matrix_t &, const matrix_t &)
Returns the L2-error norm between the old and new sigma-matrix.
RealDiracMatrix rdm_m
RealDiracMatrix-class member used for decoupling
FVps< double, 2 *3 > Map
Type of a map.
Series x_m
All variables x, px, z, pz, l, delta.
SpaceCharge Hsc_m
Stores the Hamiltonian for the space charge.
double q_m
Is the particle charge [e].
RealDiracMatrix::matrix_t matrix_t
Type for storing maps.
std::vector< matrix_t > sigmas_m
Vector storing the second moment matrix for each angle.
matrix_t sigma_m
Stores the stationary distribution (sigma matrix)
double nh_m
Harmonic number, .
vector_t decouple(const matrix_t &Mturn, sparse_matrix_t &R, sparse_matrix_t &invR)
Block diagonalizes the symplex part of the one turn transfer matrix.
double wo_m
Is the orbital frequency, .
std::function< Series(double, double, double)> Hamiltonian
Type of the Hamiltonian for the cyclotron.
unsigned int N_m
Number of integration steps for closed orbit computation.
void initialize(double, double)
bool converged_m
Is true if converged, false otherwise.
void writeOrbitOutput_m(const container_t &r_turn, const container_t &peo, const container_t &h_turn, const container_t &fidx_turn, const container_t &ds_turn)
Called within SigmaGenerator::match().
void updateSigma(const std::vector< matrix_t > &, const std::vector< matrix_t > &)
Computes new sigma matrices (one for each angle)
std::string float2string(double val)
Transforms a floating point value to a string.
RealDiracMatrix::vector_t vector_t
Type for storing vectors.
double beta_m
Velocity (c/v), .
std::array< double, 3 > emittance_m
bool match(double accuracy, unsigned int maxit, unsigned int maxitOrbit, Cyclotron *cycl, double denergy, double rguess, bool full)
Searches for a matched distribution.
The base class for all OPAL exceptions.
Definition: OpalException.h:28
Definition: Inform.h:42