// // Class: SigmaGenerator.h // The SigmaGenerator class uses the class ClosedOrbitFinder to get the parameters(inverse bending radius, path length // field index and tunes) to initialize the sigma matrix. // The main function of this class is match(double, unsigned int), where it iteratively tries to find a matched // distribution for given // emittances, energy and current. The computation stops when the L2-norm is smaller than a user-defined tolerance. \n // In default mode it prints all space charge maps, cyclotron maps and second moment matrices. The orbit properties, i.e. // tunes, average radius, orbit radius, inverse bending radius, path length, field index and frequency error, are printed // as well. // // Copyright (c) 2014, 2018, Matthias Frey, Cristopher Cortes, ETH Zürich // All rights reserved // // Implemented as part of the Semester thesis by Matthias Frey // "Matched Distributions in Cyclotrons" // // Some adaptations done as part of the Bachelor thesis by Cristopher Cortes // "Limitations of a linear transfer map method for finding matched distributions in high intensity cyclotrons" // // This file is part of OPAL. // // OPAL is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // You should have received a copy of the GNU General Public License // along with OPAL. If not, see . // #ifndef SIGMAGENERATOR_H #define SIGMAGENERATOR_H #include #include #include #include #include "AbsBeamline/Cyclotron.h" #include "FixedAlgebra/FTps.h" #include "Physics/Physics.h" #include #include #include /// @brief This class computes the matched distribution class SigmaGenerator { public: /// Type for storing maps typedef boost::numeric::ublas::matrix matrix_type; typedef std::complex complex_t; /// Type for storing complex matrices typedef boost::numeric::ublas::matrix cmatrix_type; /// Type for storing the sparse maps typedef boost::numeric::ublas::compressed_matrix sparse_matrix_type; /// Type for storing vectors typedef boost::numeric::ublas::vector vector_type; /// Container for storing the properties for each angle typedef std::vector container_type; /// Type of the truncated powere series typedef FTps Series; /// Type of a map typedef FVps Map; /// Type of the Hamiltonian for the cyclotron typedef std::function Hamiltonian; /// Type of the Hamiltonian for the space charge typedef std::function SpaceCharge; /// Constructs an object of type SigmaGenerator /*! * @param I specifies the current for which a matched distribution should be found, \f$ [I] = A \f$ * @param ex is the emittance in x-direction (horizontal), \f$ \left[\varepsilon_{x}\right] = \pi\ mm\ mrad \f$ * @param ey is the emittance in y-direction (longitudinal), \f$ \left[\varepsilon_{y}\right] = \pi\ mm\ mrad \f$ * @param ez is the emittance in z-direction (vertical), \f$ \left[\varepsilon_{z}\right] = \pi\ mm\ mrad \f$ * @param E is the energy, \f$ \left[E\right] = MeV \f$ * @param m is the mass of the particles \f$ \left[m\right] = \frac{MeV}{c^{2}} \f$ * @param cycl is the cyclotron element * @param N is the number of integration steps (closed orbit computation). That's why its also the number * of maps (for each integration step a map) * @param Nsectors is the number of sectors that the field map is averaged over (closed orbit computation) * @param truncOrder is the truncation order for power series of the Hamiltonian * @param write is a boolean (default: true). If true all maps of all iterations are stored, otherwise not. */ SigmaGenerator(double I, double ex, double ey, double ez, double E, double m, const Cyclotron* cycl, unsigned int N, unsigned int Nsectors, unsigned int truncOrder, bool write = true); /// Searches for a matched distribution. /*! * Returns "true" if a matched distribution within the accuracy could be found, returns "false" otherwise. * @param accuracy is used for computing the equilibrium orbit (to a certain accuracy) * @param maxit is the maximum number of iterations (the program stops if within this value no stationary * distribution was found) * @param maxitOrbit is the maximum number of iterations for finding closed orbit * @param angle defines the start of the sector (one can choose any angle between 0° and 360°) * @param denergy energy step size for closed orbit finder [MeV] * @param rguess value of radius for closed orbit finder * @param type specifies the magnetic field format (e.g. CARBONCYCL) * @param full match over full turn not just single sector */ bool match(double accuracy, unsigned int maxit, unsigned int maxitOrbit, Cyclotron* cycl, double denergy, double rguess, bool full); /*! * Eigenvalue / eigenvector solver * @param Mturn is a 6x6 dimensional one turn transfer matrix * @param R is the 6x6 dimensional transformation matrix (gets computed) */ void eigsolve_m(const matrix_type& Mturn, sparse_matrix_type& R); /*! * @param R is the 6x6 dimensional transformation matrix * @param invR is the 6x6 dimensional inverse transformation (gets computed) * @return true if success */ bool invertMatrix_m(const sparse_matrix_type& R, sparse_matrix_type& invR); /// Block diagonalizes the symplex part of the one turn transfer matrix /*! It computes the transformation matrix R and its inverse invR. * * @param Mturn is a 6x6 dimensional one turn transfer matrix * @param R is the 6x6 dimensional transformation matrix (gets computed) * @param invR is the 6x6 dimensional inverse transformation (gets computed) */ void decouple(const matrix_type& Mturn, sparse_matrix_type& R, sparse_matrix_type& invR); /// Checks if the sigma-matrix is an eigenellipse and returns the L2 error. /*! * The idea of this function is taken from Dr. Christian Baumgarten's program. * @param Mturn is the one turn transfer matrix * @param sigma is the sigma matrix to be tested */ double isEigenEllipse(const matrix_type& Mturn, const matrix_type& sigma); /// Returns the converged stationary distribution matrix_type& getSigma(); /// Returns the number of iterations needed for convergence (if not converged, it returns zero) unsigned int getIterations() const; /// Returns the error (if the program didn't converged, one can call this function to check the error) double getError() const; /// Returns the emittances (ex,ey,ez) in \f$ \pi\ mm\ mrad \f$ for which the code converged (since the whole simulation is done on normalized emittances) std::array getEmittances() const; const double& getInjectionRadius() const { return rinit_m; } const double& getInjectionMomentum() const { return prinit_m; } private: /// Stores the value of the current, \f$ \left[I\right] = A \f$ double I_m; /// Stores the desired emittances, \f$ \left[\varepsilon_{x}\right] = \left[\varepsilon_{y}\right] = \left[\varepsilon_{z}\right] = m \ rad \f$ std::array emittance_m; /// Is the orbital frequency, \f$ \left[\omega_{o}\right] = \frac{1}{s} \f$ double wo_m; /// Stores the user-define energy, \f$ \left[E\right] = MeV \f$ double E_m; /// Relativistic factor (which can be computed out ot the kinetic energy and rest mass (potential energy)), \f$ \left[\gamma\right] = 1 \f$ double gamma_m; /// Relativistic factor squared double gamma2_m; /// Harmonic number, \f$ \left[N_{h}\right] = 1 \f$ double nh_m; /// Velocity (c/v), \f$ \left[\beta\right] = 1 \f$ double beta_m; /// Is the mass of the particles, \f$ \left[m\right] = \frac{MeV}{c^{2}} \f$ double m_m; /// Is the number of iterations needed for convergence unsigned int niterations_m; /// Is true if converged, false otherwise bool converged_m; /// Minimum energy needed in cyclotron, \f$ \left[E_{min}\right] = MeV \f$ double Emin_m; /// Maximum energy reached in cyclotron, \f$ \left[E_{max}\right] = MeV \f$ double Emax_m; /// Number of integration steps for closed orbit computation unsigned int N_m; /// Number of (symmetric) sectors the field is averaged over unsigned int nSectors_m; /// Number of integration steps per sector (--> also: number of maps per sector) unsigned int nStepsPerSector_m; /// Number of integration steps in total unsigned int nSteps_m; /// Error of computation double error_m; /// Truncation order of the power series for the Hamiltonian (cyclotron and space charge) unsigned int truncOrder_m; /// Decides for writing output (default: true) bool write_m; /// Stores the stationary distribution (sigma matrix) matrix_type sigma_m; /// Vector storing the second moment matrix for each angle std::vector sigmas_m; /// Stores the Hamiltonian of the cyclotron Hamiltonian H_m; /// Stores the Hamiltonian for the space charge SpaceCharge Hsc_m; /// All variables x, px, y, py, z, delta Series x_m, px_m, y_m, py_m, z_m, delta_m; double rinit_m; double prinit_m; /*! Initializes a first guess of the sigma matrix with the assumption of * a spherical symmetric beam (ex = ey = ez). For each angle split the * same initial guess is taken. * * @param nuz is the vertical tune * @param ravg is the average radius of the closed orbit */ void initialize(double, double); /// Computes the new initial sigma matrix /*! * @param M is the 6x6 one turn transfer matrix * @param R is the transformation matrix * @param invR is the inverse transformation matrix */ matrix_type updateInitialSigma(const matrix_type&, sparse_matrix_type&, sparse_matrix_type&); /// Computes new sigma matrices (one for each angle) /*! * Mscs is a vector of all space charge maps * Mcycs is a vector of all cyclotron maps */ void updateSigma(const std::vector&, const std::vector&); /// Returns the L2-error norm between the old and new sigma-matrix /*! * @param oldS is the old sigma matrix * @param newS is the new sigma matrix */ double L2ErrorNorm(const matrix_type&, const matrix_type&); /// Returns the Lp-error norm between the old and new sigma-matrix /*! * @param oldS is the old sigma matrix * @param newS is the new sigma matrix */ double L1ErrorNorm(const matrix_type&, const matrix_type&); double LInfErrorNorm(const matrix_type&, const matrix_type&); /// Transforms a floating point value to a string /*! * @param val is the floating point value which is transformed to a string */ std::string float2string(double val); /// Called within SigmaGenerator::match(). /*! * @param tunes * @param ravg is the average radius [m] * @param r_turn is the radius [m] * @param peo is the momentum * @param h_turn is the inverse bending radius * @param fidx_turn is the field index * @param ds_turn is the path length element */ void writeOrbitOutput_m(const std::pair& tunes, const double& ravg, const double& 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); void writeMatrix(std::ofstream&, const matrix_type&); }; inline typename SigmaGenerator::matrix_type& SigmaGenerator::getSigma() { return sigma_m; } inline unsigned int SigmaGenerator::getIterations() const { return (converged_m) ? niterations_m : 0; } inline double SigmaGenerator::getError() const { return error_m; } inline std::array SigmaGenerator::getEmittances() const { double bgam = gamma_m*beta_m; return std::array{{ emittance_m[0] / Physics::pi / bgam * 1.0e6, emittance_m[1] / Physics::pi / bgam * 1.0e6, emittance_m[2] / Physics::pi / bgam * 1.0e6 }}; } #endif