OPAL (Object Oriented Parallel Accelerator Library) 2022.1
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
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
53extern 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]*Units::MHz2Hz/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 , mass_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, mass_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)
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 double m = mass_m * Units::MeV2eV;
120
121 // formula (57)
122 double lam = Physics::two_pi*Physics::c / (wo_m * nh_m); // wavelength, [lam] = m
123 // [K3] = m
124 double K3 = 3.0 * std::abs(q_m) * I_m * lam
125 / (20.0 * std::sqrt(5.0) * Physics::pi * Physics::epsilon_0 * m
127
128 // formula (30), (31)
129 // [sigma(0,0)] = m^{2} --> [sx] = [sy] = [sz] = m
130 // In the cyclotron community z is the vertical and y the longitudinal
131 // direction.
132 // x: horizontal
133 // y/l: longitudinal
134 // z: vertical
135 double sx = std::sqrt(std::abs(sigx));
136 double sy = std::sqrt(std::abs(sigy));
137 double sz = std::sqrt(std::abs(sigz));
138
139 double tmp = sx * sy; // [tmp] = m^{2}
140
141 double f = std::sqrt(tmp) / (3.0 * gamma_m * sz); // [f] = 1
142 double kxy = K3 * std::abs(1.0 - f) / ((sx + sy) * sz); // [kxy] = 1/m
143
144 double Kx = kxy / sx;
145 double Ky = kxy / sy;
146 double Kz = K3 * f / (tmp * sz);
147
148 return -0.5 * Kx * x_m * x_m
149 -0.5 * Kz * z_m * z_m
150 -0.5 * Ky * l_m * l_m * gamma2_m;
151 };
152}
153
154
155bool SigmaGenerator::match(double accuracy,
156 unsigned int maxit,
157 unsigned int maxitOrbit,
158 Cyclotron* cycl,
159 double denergy,
160 double rguess,
161 bool full)
162{
163 /* compute the equilibrium orbit for energy E_
164 * and get the the following properties:
165 * - inverse bending radius h
166 * - step sizes of path ds
167 * - tune nuz
168 */
169
170 try {
171 if ( !full )
173
174 // object for space charge map and cyclotron map
175 MapGenerator<double,
176 unsigned int,
177 Series,
178 Map,
180 SpaceCharge> mapgen(nSteps_m);
181
182 // compute cyclotron map and space charge map for each angle and store them into a vector
183 std::vector<matrix_t> Mcycs(nSteps_m), Mscs(nSteps_m);
184
185 ClosedOrbitFinder<double, unsigned int,
186 boost::numeric::odeint::runge_kutta4<container_t> > cof(mass_m, q_m, N_m, cycl, false, nSectors_m);
187
188 if ( !cof.findOrbit(accuracy, maxitOrbit, E_m, denergy, rguess) ) {
189 throw OpalException("SigmaGenerator::match()",
190 "Closed orbit finder didn't converge.");
191 }
192
193 cof.computeOrbitProperties(E_m);
194
195 double angle = cycl->getPHIinit();
196 container_t h = cof.getInverseBendingRadius(angle);
197 container_t r = cof.getOrbit(angle);
198 container_t peo = cof.getMomentum(angle);
199 container_t ds = cof.getPathLength(angle);
200 container_t fidx = cof.getFieldIndex(angle);
201
202 // write properties to file
203 writeOrbitOutput_m(r, peo, h, fidx, ds);
204
205 rinit_m = r[0];
206 prinit_m = peo[0];
207
208 std::string fpath = Util::combineFilePath({
210 "maps"
211 });
212 if (!boost::filesystem::exists(fpath)) {
213 boost::filesystem::create_directory(fpath);
214 }
215
216 std::pair<double,double> tunes = cof.getTunes();
217 double ravg = cof.getAverageRadius();
218
219 // write to terminal
220 *gmsg << "* ----------------------------" << endl
221 << "* Closed orbit info:" << endl
222 << "*" << endl
223 << "* average radius: " << ravg << " [m]" << endl
224 << "* initial radius: " << r[0] << " [m]" << endl
225 << "* initial momentum: " << peo[0] << " [Beta Gamma]" << endl
226 << "* frequency error: " << cof.getFrequencyError()*100 <<" [ % ] "<< endl
227 << "* horizontal tune: " << tunes.first << endl
228 << "* vertical tune: " << tunes.second << endl
229 << "* ----------------------------" << endl << endl;
230
231 // initialize sigma matrices (for each angle one) (first guess)
232 initialize(tunes.second,ravg);
233
234 // for writing
235 std::ofstream writeMturn, writeMcyc, writeMsc;
236
237 if (write_m) {
238
239 std::string energy = float2string(E_m);
240
241 std::string fname = Util::combineFilePath({
243 "maps",
244 "OneTurnMapsForEnergy" + energy + "MeV.dat"
245 });
246
247 writeMturn.open(fname, std::ios::out);
248
249 fname = Util::combineFilePath({
251 "maps",
252 "SpaceChargeMapPerAngleForEnergy" + energy + "MeV_iteration_0.dat"
253 });
254
255 writeMsc.open(fname, std::ios::out);
256
257 fname = Util::combineFilePath({
259 "maps",
260 "CyclotronMapPerAngleForEnergy" + energy + "MeV.dat"
261 });
262
263 writeMcyc.open(fname, std::ios::out);
264 }
265
266 // calculate only for a single sector (a nSector_-th) of the whole cyclotron
267 for (unsigned int i = 0; i < nSteps_m; ++i) {
268 Mcycs[i] = mapgen.generateMap(H_m(h[i],
269 h[i]*h[i]+fidx[i],
270 -fidx[i]),
271 ds[i],truncOrder_m);
272
273
274 Mscs[i] = mapgen.generateMap(Hsc_m(sigmas_m[i](0,0),
275 sigmas_m[i](2,2),
276 sigmas_m[i](4,4)),
277 ds[i],truncOrder_m);
278
279 writeMatrix(writeMcyc, Mcycs[i]);
280 writeMatrix(writeMsc, Mscs[i]);
281 }
282
283 if (write_m)
284 writeMsc.close();
285
286 // one turn matrix
287 mapgen.combine(Mscs,Mcycs);
288 matrix_t Mturn = mapgen.getMap();
289
290 writeMatrix(writeMturn, Mturn);
291
292 // (inverse) transformation matrix
293 sparse_matrix_t R(6, 6), invR(6, 6);
294
295 // new initial sigma matrix
296 matrix_t newSigma(6,6);
297
298 // for exiting loop
299 bool stop = false;
300
301 double weight = 0.5;
302
303 while (error_m > accuracy && !stop) {
304 // decouple transfer matrix and compute (inverse) tranformation matrix
305 vector_t eigen = decouple(Mturn, R,invR);
306
307 // construct new initial sigma-matrix
308 newSigma = updateInitialSigma(Mturn, eigen, R, invR);
309
310 // compute new sigma matrices for all angles (except for initial sigma)
311 updateSigma(Mscs,Mcycs);
312
313 // compute error with mm^2 and (mrad)^2
314 error_m = L2ErrorNorm(sigmas_m[0] * 1e6, newSigma * 1e6);
315
316 // write new initial sigma-matrix into vector
317 sigmas_m[0] = weight*newSigma + (1.0-weight)*sigmas_m[0];
318
319 if (write_m) {
320
321 std::string energy = float2string(E_m);
322
323 std::string fname = Util::combineFilePath({
325 "maps",
326 "SpaceChargeMapPerAngleForEnergy" + energy + "MeV_iteration_"
327 + std::to_string(niterations_m + 1)
328 + ".dat"
329 });
330
331 writeMsc.open(fname, std::ios::out);
332 }
333
334 // compute new space charge maps
335 for (unsigned int i = 0; i < nSteps_m; ++i) {
336 Mscs[i] = mapgen.generateMap(Hsc_m(sigmas_m[i](0,0),
337 sigmas_m[i](2,2),
338 sigmas_m[i](4,4)),
339 ds[i],truncOrder_m);
340
341 writeMatrix(writeMsc, Mscs[i]);
342 }
343
344 if (write_m) {
345 writeMsc.close();
346 }
347
348 // construct new one turn transfer matrix M
349 mapgen.combine(Mscs,Mcycs);
350 Mturn = mapgen.getMap();
351
352 writeMatrix(writeMturn, Mturn);
353
354 // check if number of iterations has maxit exceeded.
355 stop = (niterations_m++ > maxit);
356 }
357
358 // store converged sigma-matrix
359 sigma_m.resize(6,6,false);
360 sigma_m.swap(newSigma);
361
362 // returns if the sigma matrix has converged
363 converged_m = error_m < accuracy;
364
365 // Close files
366 if (write_m) {
367 writeMturn.close();
368 writeMcyc.close();
369 }
370
371 } catch(const std::exception& e) {
372 throw OpalException("SigmaGenerator::match()", e.what());
373 }
374
375 if ( converged_m && write_m ) {
376 // write tunes
377 std::string fname = Util::combineFilePath({
379 "MatchedDistributions.dat"
380 });
381
382 std::ofstream writeSigmaMatched(fname, std::ios::out);
383
384 std::array<double,3> emit = this->getEmittances();
385
386 writeSigmaMatched << "* Converged (Ex, Ey, Ez) = (" << emit[0]
387 << ", " << emit[1] << ", " << emit[2]
388 << ") pi mm mrad for E= " << E_m << " (MeV)"
389 << std::endl << "* Sigma-Matrix " << std::endl;
390
391 for(unsigned int i = 0; i < sigma_m.size1(); ++ i) {
392 writeSigmaMatched << std::setprecision(4) << std::setw(11)
393 << sigma_m(i,0);
394 for(unsigned int j = 1; j < sigma_m.size2(); ++ j) {
395 writeSigmaMatched << " & " << std::setprecision(4)
396 << std::setw(11) << sigma_m(i,j);
397 }
398 writeSigmaMatched << " \\\\" << std::endl;
399 }
400
401 writeSigmaMatched << std::endl;
402 writeSigmaMatched.close();
403 }
404
405 return converged_m;
406}
407
408
412 sparse_matrix_t& invR)
413{
414 // copy one turn matrix
415 matrix_t M(Mturn);
416
417 // reduce 6x6 matrix to 4x4 matrix
418 reduce<matrix_t>(M);
419
420 // compute symplex part
421 matrix_t Ms = rdm_m.symplex(M);
422
423 // diagonalize and compute transformation matrices
424 rdm_m.diagonalize(Ms,R,invR);
425
426 /*
427 * formula (38) in paper of Dr. Christian Baumgarten:
428 * Geometrical method of decoupling
429 *
430 * [0, alpha, 0, 0;
431 * F_{d} = -beta, 0, 0, 0;
432 * 0, 0, 0, gamma;
433 * 0, 0, -delta, 0]
434 *
435 *
436 */
437 vector_t eigen(4);
438 eigen(0) = Ms(0,1); // alpha
439 eigen(1) = - Ms(1,0); // beta
440 eigen(2) = Ms(2,3); // gamma
441 eigen(3) = - Ms(3,2); // delta
442 return eigen;
443}
444
445
447 const matrix_t& sigma)
448{
449 // compute sigma matrix after one turn
450 matrix_t newSigma = matt_boost::gemmm<matrix_t>(Mturn,
451 sigma,
452 boost::numeric::ublas::trans(Mturn));
453
454 // return L2 error
455 return L2ErrorNorm(sigma,newSigma);
456}
457
458
459void SigmaGenerator::initialize(double nuz, double ravg)
460{
461 /*
462 * The initialization is based on the analytical solution of
463 * using a spherical symmetric beam in the paper:
464 * Transverse-longitudinal coupling by space charge in cyclotrons
465 * by Dr. Christian Baumgarten
466 * (formulas: (46), (56), (57))
467 */
468
469
470 /* Units:
471 * ----------------------------------------------
472 * [wo] = 1/s
473 * [nh] = 1
474 * [q0] = 1 e
475 * [I] = A
476 * [eps0] = (A*s)^{2}/(N*m^{2})
477 * [E0] = MeV/(c^{2}) (with speed of light c)
478 * [beta] = 1
479 * [gamma] = 1
480 * [m] = eV/c^2
481 *
482 * [lam] = m
483 * [K3] = m
484 * [alpha] = 1
485 * ----------------------------------------------
486 */
487
488 // helper constants
489 double invbg = 1.0 / (beta_m * gamma_m);
490
491 double mass = mass_m * Units::MeV2eV;
492
493 // emittance [ex] = [ey] = [ez] = m rad
494 double ex = emittance_m[0] * invbg; // [ex] = m rad
495 double ey = emittance_m[1] * invbg; // [ey] = m rad
496 double ez = emittance_m[2] * invbg; // [ez] = m rad
497
498 // initial guess of emittance, [e] = m rad
499 double guessedEmittance = std::cbrt(ex * ey * ez); // cbrt computes cubic root (C++11) <cmath>
500
501 // cyclotron radius [rcyc] = m
502 double rcyc = ravg / beta_m;
503
504 // "average" inverse bending radius
505 double avgInverseBendingRadius = 1.0 / ravg;
506
507 // formula (57)
508 double lam = Physics::two_pi * Physics::c / (wo_m * nh_m); // wavelength, [lam] = m
509
510 // m * c^3 --> c^2 in [m] = eV / c^2 cancel --> m * c in denominator
511 double K3 = 3.0 * std::abs(q_m) * I_m * lam
512 / (20.0 * std::sqrt(5.0) * Physics::pi * Physics::epsilon_0 * mass
513 * Physics::c * beta_m * beta_m * gamma2_m * gamma_m); // [K3] = m
514
515 // c in denominator cancels with unit of [m] = eV / c^2 --> we need to multiply
516 // with c in order to get dimensionless quantity
518 / (5.0 * std::sqrt(10.0) * mass * gamma_m * nh_m)
519 * std::sqrt(rcyc * rcyc * rcyc / (std::pow(guessedEmittance, 3)))); // [alpha] = 1/rad --> [alpha] = 1
520
521 double sig0 = std::sqrt(2.0 * rcyc * guessedEmittance) / gamma_m; // [sig0] = m*sqrt(rad) --> [sig0] = m
522
523 // formula (56)
524 double sig; // [sig] = m
525 if (alpha >= 2.5) {
526 sig = sig0 * std::cbrt(1.0 + alpha); // cbrt computes cubic root (C++11) <cmath>
527 } else if (alpha >= 0) {
528 sig = sig0 * (1 + alpha * (0.25 - 0.03125 * alpha));
529 } else {
530 throw OpalException("SigmaGenerator::initialize()",
531 "Negative alpha value: " + std::to_string(alpha) + " < 0");
532 }
533
534 // K = Kx = Ky = Kz
535 double K = K3 * gamma_m / (3.0 * sig * sig * sig); // formula (46), [K] = 1/m^{2}
536 double kx = std::pow(avgInverseBendingRadius, 2) * gamma2_m;// formula (46) (assumption of an isochronous cyclotron), [kx] = 1/m^{2}
537
538 double a = 0.5 * kx - K; // formula (22) (with K = Kx = Kz), [a] = 1/m^{2}
539 double b = K * K; // formula (22) (with K = Kx = Kz and kx = h^2*gamma^2), [b] = 1/m^{4}
540
541
542 // b must be positive, otherwise no real-valued frequency
543 if (b < 0)
544 throw OpalException("SigmaGenerator::initialize()",
545 "Negative value --> No real-valued frequency.");
546
547 double tmp = a * a - b; // [tmp] = 1/m^{4}
548 if (tmp < 0)
549 throw OpalException("SigmaGenerator::initialize()",
550 "Square root of negative number.");
551
552 tmp = std::sqrt(tmp); // [tmp] = 1/m^{2}
553
554 if (a < tmp)
555 throw OpalException("SigmaGenerator::initialize()",
556 "Square root of negative number.");
557
558 if (std::pow(avgInverseBendingRadius, 2) * nuz * nuz <= K)
559 throw OpalException("SigmaGenerator::initialize()",
560 "h^{2} * nu_{z}^{2} <= K (i.e. square root of negative number)");
561
562 double Omega = std::sqrt(a + tmp); // formula (22), [Omega] = 1/m
563 double omega = std::sqrt(a - tmp); // formula (22), [omega] = 1/m
564
565 double A = avgInverseBendingRadius / (Omega * Omega + K); // formula (26), [A] = m
566 double B = avgInverseBendingRadius / (omega * omega + K); // formula (26), [B] = m
567 double invAB = 1.0 / (B - A); // [invAB] = 1/m
568
569 // construct initial sigma-matrix (formula (29, 30, 31)
570 matrix_t sigma = boost::numeric::ublas::zero_matrix<double>(6);
571
572 // formula (30), [sigma(0,0)] = m^2 rad
573 sigma(0,0) = invAB * (B * ex / Omega + A * ez / omega);
574
575 // [sigma(0,5)] = [sigma(5,0)] = m rad
576 sigma(0,5) = sigma(5,0) = invAB * (ex / Omega + ez / omega);
577
578 // [sigma(1,1)] = rad
579 sigma(1,1) = invAB * (B * ex * Omega + A * ez * omega);
580
581 // [sigma(1,4)] = [sigma(4,1)] = m rad
582 sigma(1,4) = sigma(4,1) = invAB * (ex * Omega+ez * omega) / (K * gamma2_m);
583
584 // formula (31), [sigma(2,2)] = m rad
585 sigma(2,2) = ey / (std::sqrt(std::pow(avgInverseBendingRadius,2) * nuz * nuz - K));
586
587 sigma(3,3) = (ey * ey) / sigma(2,2);
588
589 // [sigma(4,4)] = m^{2} rad
590 sigma(4,4) = invAB * (A * ex * Omega + B * ez * omega) / (K * gamma2_m);
591
592 // formula (30), [sigma(5,5)] = rad
593 sigma(5,5) = invAB * (ex / (B * Omega) + ez / (A * omega));
594
595 // fill in initial guess of the sigma matrix (for each angle the same guess)
596 sigmas_m.resize(nSteps_m);
597 for (typename std::vector<matrix_t>::iterator it = sigmas_m.begin(); it != sigmas_m.end(); ++it) {
598 *it = sigma;
599 }
600
601 if (write_m) {
602 std::string energy = float2string(E_m);
603
604 std::string fname = Util::combineFilePath({
606 "maps",
607 "InitialSigmaPerAngleForEnergy" + energy + "MeV.dat"
608 });
609
610 std::ofstream writeInit(fname, std::ios::out);
611 writeMatrix(writeInit, sigma);
612 writeInit.close();
613 }
614}
615
616
619 const vector_t& eigen,
621 sparse_matrix_t& invR)
622{
623 /*
624 * This function is based on the paper of Dr. Christian Baumgarten:
625 * Transverse-Longitudinal Coupling by Space Charge in Cyclotrons (2012)
626 */
627
628 /*
629 * Function input:
630 * - M: one turn transfer matrix
631 * - eigen = {alpha, beta, gamma, delta}
632 * - R: transformation matrix (in paper: E)
633 * - invR: inverse transformation matrix (in paper: E^{-1}
634 */
635
636 // normalize emittances
637 double invbg = 1.0 / (beta_m * gamma_m);
638 double ex = emittance_m[0] * invbg;
639 double ey = emittance_m[1] * invbg;
640 double ez = emittance_m[2] * invbg;
641
642 // alpha^2-beta*gamma = 1
643
644 /* 0 eigen(0) 0 0
645 * eigen(1) 0 0 0
646 * 0 0 0 eigen(2)
647 * 0 0 eigen(3) 0
648 *
649 * M = cos(mux)*[1, 0; 0, 1] + sin(mux)*[alpha, beta; -gamma, -alpha], Book, p. 242
650 *
651 * -----------------------------------------------------------------------------------
652 * X-DIRECTION and L-DIRECTION
653 * -----------------------------------------------------------------------------------
654 * --> eigen(0) = sin(mux)*betax
655 * --> eigen(1) = -gammax*sin(mux)
656 *
657 * What is sin(mux)? --> alphax = 0 --> -alphax^2+betax*gammax = betax*gammax = 1
658 *
659 * Convention: betax > 0
660 *
661 * 1) betax = 1/gammax
662 * 2) eig0 = sin(mux)*betax
663 * 3) eig1 = -gammax*sin(mux)
664 *
665 * eig0 = sin(mux)/gammax
666 * eig1 = -gammax*sin(mux) <--> 1/gammax = -sin(mux)/eig1
667 *
668 * eig0 = -sin(mux)^2/eig1 --> -sin(mux)^2 = eig0*eig1 --> sin(mux) = sqrt(-eig0*eig1)
669 * --> gammax = -eig1/sin(mux)
670 * --> betax = eig0/sin(mux)
671 */
672
673
674 // x-direction
675 //double alphax = 0.0;
676 double betax = std::sqrt(std::fabs(eigen(0) / eigen(1)));
677 double gammax = 1.0 / betax;
678
679 // l-direction
680 //double alphal = 0.0;
681 double betal = std::sqrt(std::fabs(eigen(2) / eigen(3)));
682 double gammal = 1.0 / betal;
683
684 /*
685 * -----------------------------------------------------------------------------------
686 * Z-DIRECTION
687 * -----------------------------------------------------------------------------------
688 *
689 * m22 m23
690 * m32 m33
691 *
692 * m22 = cos(muz) + alpha*sin(muz)
693 * m33 = cos(muz) - alpha*sin(muz)
694 *
695 * --> cos(muz) = 0.5*(m22 + m33)
696 * sin(muz) = sign(m32)*sqrt(1-cos(muz)^2)
697 *
698 * m22-m33 = 2*alpha*sin(muz) --> alpha = 0.5*(m22-m33)/sin(muz)
699 *
700 * m23 = betaz*sin(muz) --> betaz = m23/sin(muz)
701 * m32 = -gammaz*sin(muz) --> gammaz = -m32/sin(muz)
702 */
703
704 double cosz = 0.5 * (M(2,2) + M(3,3));
705
706 double sign = (std::signbit(M(2,3))) ? double(-1) : double(1);
707
708 double invsinz = sign / std::sqrt(std::fabs( 1.0 - cosz * cosz));
709
710 double alphaz = 0.5 * (M(2,2) - M(3,3)) * invsinz;
711 double betaz = M(2,3) * invsinz;
712 double gammaz = - M(3,2) * invsinz;
713
714 // Convention beta>0
715 if (std::signbit(betaz)) // singbit = true if beta<0, else false
716 betaz *= -1.0;
717
718 // diagonal matrix with eigenvalues
719 matrix_t D = boost::numeric::ublas::zero_matrix<double>(6,6);
720 // x-direction
721 D(0,1) = betax * ex;
722 D(1,0) = - gammax * ex;
723 // z-direction
724 D(2,2) = alphaz * ey;
725 D(3,3) = - alphaz * ey;
726 D(2,3) = betaz * ey;
727 D(3,2) = - gammaz * ey;
728 // l-direction
729 D(4,5) = betal * ez;
730 D(5,4) = - gammal * ez;
731
732 // expand 4x4 transformation matrices to 6x6
733 expand<sparse_matrix_t>(R);
734 expand<sparse_matrix_t>(invR);
735
736 // symplectic matrix
737 sparse_matrix_t S(6,6,6);
738 S(0,1) = S(2,3) = S(4,5) = 1;
739 S(1,0) = S(3,2) = S(5,4) = -1;
740
741 matrix_t sigma = matt_boost::gemmm<matrix_t>(-invR,D,R);
742 sigma = boost::numeric::ublas::prod(sigma,S);
743
744 if (write_m) {
745 std::string energy = float2string(E_m);
746
747 std::string fname = Util::combineFilePath({
749 "maps",
750 "InitialSigmaPerAngleForEnergy" + energy + "MeV.dat"
751 });
752
753 std::ofstream writeInit(fname, std::ios::app);
754 writeMatrix(writeInit, sigma);
755 writeInit.close();
756 }
757
758 return sigma;
759}
760
761
762void SigmaGenerator::updateSigma(const std::vector<matrix_t>& Mscs,
763 const std::vector<matrix_t>& Mcycs)
764{
765 std::ofstream writeSigma;
766
767 if (write_m) {
768 std::string energy = float2string(E_m);
769
770 std::string fname = Util::combineFilePath({
772 "maps",
773 "SigmaPerAngleForEnergy" + energy + "MeV_iteration_"
774 + std::to_string(niterations_m + 1)
775 + ".dat"
776 });
777
778 writeSigma.open(fname,std::ios::out);
779 }
780
781 // initial sigma is already computed
782 writeMatrix(writeSigma, sigmas_m[0]);
783
784 for (unsigned int i = 1; i < nSteps_m; ++i) {
785 // transfer matrix for one angle
786 matrix_t M = boost::numeric::ublas::prod(Mscs[i - 1],Mcycs[i - 1]);
787 // transfer the matrix sigma
788 sigmas_m[i] = matt_boost::gemmm<matrix_t>(M,sigmas_m[i - 1],
789 boost::numeric::ublas::trans(M));
790
791 writeMatrix(writeSigma, sigmas_m[i]);
792 }
793
794 if (write_m) {
795 writeSigma.close();
796 }
797}
798
799
801 const matrix_t& newS)
802{
803 // compute difference
804 matrix_t diff = newS - oldS;
805
806 // sum squared error up and take square root
807 return std::sqrt(std::inner_product(diff.data().begin(),
808 diff.data().end(),
809 diff.data().begin(),
810 0.0));
811}
812
813
814std::string SigmaGenerator::float2string(double val) {
815 std::ostringstream out;
816 out << std::setprecision(6) << val;
817 return out.str();
818}
819
820
822 const container_t& r,
823 const container_t& peo,
824 const container_t& h,
825 const container_t& fidx,
826 const container_t& ds)
827{
828 if (!write_m)
829 return;
830
831 std::string energy = float2string(E_m);
832 std::string fname = Util::combineFilePath({
834 "PropertiesForEnergy" + energy + "MeV.dat"
835 });
836
837 std::ofstream writeProperties(fname, std::ios::out);
838
839 writeProperties << std::left
840 << std::setw(25) << "orbit radius"
841 << std::setw(25) << "orbit momentum"
842 << std::setw(25) << "inverse bending radius"
843 << std::setw(25) << "field index"
844 << std::setw(25) << "path length" << std::endl;
845
846 for (unsigned int i = 0; i < r.size(); ++i) {
847 writeProperties << std::setprecision(10) << std::left
848 << std::setw(25) << r[i]
849 << std::setw(25) << peo[i]
850 << std::setw(25) << h[i]
851 << std::setw(25) << fidx[i]
852 << std::setw(25) << ds[i] << std::endl;
853 }
854 writeProperties.close();
855}
856
857
858void SigmaGenerator::writeMatrix(std::ofstream& os, const matrix_t& m) {
859 if (!write_m)
860 return;
861
862 for (unsigned int i = 0; i < 6; ++i) {
863 for (unsigned int j = 0; j < 6; ++j)
864 os << m(i, j) << " ";
865 }
866 os << std::endl;
867}
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
Inform * gmsg
Definition: Main.cpp:61
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:84
const double pi
Definition: fftpack.cpp:894
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
std::complex< double > a
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
FnArg FnReal sign(a))
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:51
constexpr double alpha
The fine structure constant, no dimension.
Definition: Physics.h:72
constexpr double e
The value of.
Definition: Physics.h:39
constexpr double mu_0
The permeability of vacuum in Vs/Am.
Definition: Physics.h:48
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:45
constexpr double pi
The value of.
Definition: Physics.h:30
Definition: Units.h:23
constexpr double mrad2rad
Definition: Units.h:140
constexpr double mm2m
Definition: Units.h:29
constexpr double MHz2Hz
Definition: Units.h:113
constexpr double MeV2eV
Definition: Units.h:74
std::string::iterator iterator
Definition: MSLang.h:16
std::string combineFilePath(std::initializer_list< std::string > ilist)
Definition: Util.cpp:196
double getBetaGamma(double Ekin, double mass)
Definition: Util.h:60
static OpalData * getInstance()
Definition: OpalData.cpp:196
std::string getAuxiliaryOutputDirectory() const
get the name of the the additional data directory
Definition: OpalData.cpp:661
virtual double getPHIinit() const
Definition: Cyclotron.cpp:143
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 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.
double mass_m
Is the mass of the particles, .
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