OPAL (Object Oriented Parallel Accelerator Library) 2024.2
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//
32
38#include "Physics/Physics.h"
39#include "Physics/Units.h"
41#include "Utilities/Util.h"
42
43#include <cmath>
44#include <filesystem>
45#include <iomanip>
46#include <limits>
47#include <numeric>
48#include <sstream>
49#include <utility>
50#include <vector>
51
52#include <boost/numeric/odeint/stepper/runge_kutta4.hpp>
53
54extern Inform *gmsg;
55
57 double ex,
58 double ey,
59 double ez,
60 double E,
61 double m,
62 double q,
63 const Cyclotron* cycl,
64 unsigned int N,
65 unsigned int Nsectors,
66 unsigned int truncOrder,
67 bool write)
68 : I_m(I)
69 , wo_m(cycl->getRfFrequ()[0]*Units::MHz2Hz/cycl->getCyclHarm()*2.0*Physics::pi)
70 , E_m(E)
71 , gamma_m(E/m+1.0)
72 , gamma2_m(gamma_m*gamma_m)
73 , nh_m(cycl->getCyclHarm())
74 , beta_m(std::sqrt(1.0-1.0/gamma2_m))
75 , mass_m(m)
76 , q_m(q)
77 , niterations_m(0)
78 , converged_m(false)
79 , Emin_m(cycl->getFMLowE())
80 , Emax_m(cycl->getFMHighE())
81 , N_m(N)
82 , nSectors_m(Nsectors)
83 , nStepsPerSector_m(N/cycl->getSymmetry())
84 , nSteps_m(N)
85 , error_m(std::numeric_limits<double>::max())
86 , truncOrder_m(truncOrder)
87 , write_m(write)
88 , sigmas_m(nStepsPerSector_m)
89 , rinit_m(0.0)
90 , prinit_m(0.0)
91{
92 // minimum beta*gamma
93 double bgam = Util::getBetaGamma(Emin_m, mass_m);
94
95 // set emittances (initialization like that due to old compiler version)
96 // [ex] = [ey] = [ez] = pi*mm*mrad --> [emittance] = m rad
97 // normalized emittance (--> multiply with beta*gamma)
101
102 // Define the Hamiltonian
104
105 // infinitesimal elements
112
113 H_m = [&](double h, double kx, double ky) {
114 return 0.5*px_m*px_m + 0.5*kx*x_m*x_m - h*x_m*delta_m +
115 0.5*pz_m*pz_m + 0.5*ky*z_m*z_m +
117 };
118
119 Hsc_m = [&](double sigx, double sigy, double sigz) {
120 double m = mass_m * Units::MeV2eV;
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
156bool 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,
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(mass_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 (!std::filesystem::exists(fpath)) {
214 std::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
460void 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 double mass = mass_m * Units::MeV2eV;
493
494 // emittance [ex] = [ey] = [ez] = m rad
495 double ex = emittance_m[0] * invbg; // [ex] = m rad
496 double ey = emittance_m[1] * invbg; // [ey] = m rad
497 double ez = emittance_m[2] * invbg; // [ez] = m rad
498
499 // initial guess of emittance, [e] = m rad
500 double guessedEmittance = std::cbrt(ex * ey * ez); // cbrt computes cubic root (C++11) <cmath>
501
502 // cyclotron radius [rcyc] = m
503 double rcyc = ravg / beta_m;
504
505 // "average" inverse bending radius
506 double avgInverseBendingRadius = 1.0 / ravg;
507
508 // formula (57)
509 double lam = Physics::two_pi * Physics::c / (wo_m * nh_m); // wavelength, [lam] = m
510
511 // m * c^3 --> c^2 in [m] = eV / c^2 cancel --> m * c in denominator
512 double K3 = 3.0 * std::abs(q_m) * I_m * lam
513 / (20.0 * std::sqrt(5.0) * Physics::pi * Physics::epsilon_0 * mass
514 * Physics::c * beta_m * beta_m * gamma2_m * gamma_m); // [K3] = m
515
516 // c in denominator cancels with unit of [m] = eV / c^2 --> we need to multiply
517 // with c in order to get dimensionless quantity
518 double alpha = (std::abs(q_m) * Physics::mu_0 * I_m * Physics::c
519 / (5.0 * std::sqrt(10.0) * mass * gamma_m * nh_m)
520 * std::sqrt(rcyc * rcyc * rcyc / (std::pow(guessedEmittance, 3)))); // [alpha] = 1/rad --> [alpha] = 1
521
522 double sig0 = std::sqrt(2.0 * rcyc * guessedEmittance) / 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 = std::pow(avgInverseBendingRadius, 2) * 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 (std::pow(avgInverseBendingRadius, 2) * 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 = avgInverseBendingRadius / (Omega * Omega + K); // formula (26), [A] = m
567 double B = avgInverseBendingRadius / (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(std::pow(avgInverseBendingRadius,2) * 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::abs(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::abs(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::abs( 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
763void 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
815std::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
859void 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:69
Inform * gmsg
Definition Main.cpp:69
Inform & endl(Inform &inf)
Definition Inform.cpp:42
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
const double pi
Definition fftpack.cpp:893
std::complex< double > a
FnArg FnReal sign(a))
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 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 MeV2eV
Definition Units.h:74
std::string combineFilePath(std::initializer_list< std::string > ilist)
Definition Util.cpp:204
double getBetaGamma(double Ekin, double mass)
Definition Util.h:64
static OpalData * getInstance()
Definition OpalData.cpp:196
std::string getAuxiliaryOutputDirectory() const
get the name of the the additional data directory
Definition OpalData.cpp:666
virtual double getPHIinit() const
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.
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.