diff --git a/src/Distribution/CMakeLists.txt b/src/Distribution/CMakeLists.txt index 9f6797cf9171a261fd0be89694a3036cdd43a31d..74eadb79ff74f20ed5fbda63318355db0b450050 100644 --- a/src/Distribution/CMakeLists.txt +++ b/src/Distribution/CMakeLists.txt @@ -16,6 +16,7 @@ set (HDRS LaserProfile.h MapGenerator.h matrix_vector_operation.h + rdm.h SigmaGenerator.h ) diff --git a/src/Distribution/SigmaGenerator.cpp b/src/Distribution/SigmaGenerator.cpp index 372546937a26cda5137c59ea699796c6f987e206..4a7e551b598d246bd60c88f388ff942d06f45d6d 100644 --- a/src/Distribution/SigmaGenerator.cpp +++ b/src/Distribution/SigmaGenerator.cpp @@ -323,10 +323,10 @@ bool SigmaGenerator::match(double accuracy, while (error_m > accuracy && !stop) { // decouple transfer matrix and compute (inverse) tranformation matrix - decouple(Mturn,R,invR); + vector_type eigen = decouple(Mturn, R,invR); // construct new initial sigma-matrix - newSigma = updateInitialSigma(Mturn, R, invR); + newSigma = updateInitialSigma(Mturn, eigen, R, invR); // compute new sigma matrices for all angles (except for initial sigma) updateSigma(Mscs,Mcycs); @@ -334,6 +334,15 @@ bool SigmaGenerator::match(double accuracy, // compute error error_m = L2ErrorNorm(sigmas_m[0],newSigma); + //*** + std::cout << "Error: " << error_m << std::endl; + std::cout << std::abs(sigmas_m[0](0, 0) - sigmas_m[nSteps_m - 1](0, 0)) << std::endl; + std::cout << "L1: " << L1ErrorNorm(sigmas_m[0], sigmas_m[nSteps_m - 1]) << std::endl; + std::cout << "L2: " << L2ErrorNorm(sigmas_m[0], sigmas_m[nSteps_m - 1]) << std::endl; + std::cout << "LInf: " << LInfErrorNorm(sigmas_m[0], sigmas_m[nSteps_m - 1]) << std::endl; + std::cin.get(); + //*** + // write new initial sigma-matrix into vector sigmas_m[0] = weight*newSigma + (1.0-weight)*sigmas_m[0]; @@ -427,148 +436,181 @@ bool SigmaGenerator::match(double accuracy, } -void SigmaGenerator::eigsolve_m(const matrix_type& Mturn, - sparse_matrix_type& R) -{ - typedef gsl_matrix* gsl_matrix_t; - typedef gsl_vector_complex* gsl_cvector_t; - typedef gsl_matrix_complex* gsl_cmatrix_t; - typedef gsl_eigen_nonsymmv_workspace* gsl_wspace_t; - typedef boost::numeric::ublas::vector<complex_t> complex_vector_type; - - gsl_cvector_t evals = gsl_vector_complex_alloc(6); - gsl_cmatrix_t evecs = gsl_matrix_complex_alloc(6, 6); - gsl_wspace_t wspace = gsl_eigen_nonsymmv_alloc(6); - gsl_matrix_t M = gsl_matrix_alloc(6, 6); - - // go to GSL - for (unsigned int i = 0; i < 6; ++i){ - for (unsigned int j = 0; j < 6; ++j) { - gsl_matrix_set(M, i, j, Mturn(i,j)); - } - } - - /*int status = */gsl_eigen_nonsymmv(M, evals, evecs, wspace); - -// if ( !status ) -// throw OpalException("SigmaGenerator::eigsolve_m()", -// "Couldn't perform eigendecomposition!"); - - /*status = *///gsl_eigen_nonsymmv_sort(evals, evecs, GSL_EIGEN_SORT_ABS_ASC); - -// if ( !status ) +// void SigmaGenerator::eigsolve_m(const matrix_type& Mturn, +// sparse_matrix_type& R) +// { +// typedef gsl_matrix* gsl_matrix_t; +// typedef gsl_vector_complex* gsl_cvector_t; +// typedef gsl_matrix_complex* gsl_cmatrix_t; +// typedef gsl_eigen_nonsymmv_workspace* gsl_wspace_t; +// typedef boost::numeric::ublas::vector<complex_t> complex_vector_type; +// +// gsl_cvector_t evals = gsl_vector_complex_alloc(6); +// gsl_cmatrix_t evecs = gsl_matrix_complex_alloc(6, 6); +// gsl_wspace_t wspace = gsl_eigen_nonsymmv_alloc(6); +// gsl_matrix_t M = gsl_matrix_alloc(6, 6); +// +// // go to GSL +// for (unsigned int i = 0; i < 6; ++i){ +// for (unsigned int j = 0; j < 6; ++j) { +// gsl_matrix_set(M, i, j, Mturn(i,j)); +// } +// } +// +// /*int status = */gsl_eigen_nonsymmv(M, evals, evecs, wspace); +// +// // if ( !status ) +// // throw OpalException("SigmaGenerator::eigsolve_m()", +// // "Couldn't perform eigendecomposition!"); +// +// /*status = *///gsl_eigen_nonsymmv_sort(evals, evecs, GSL_EIGEN_SORT_ABS_ASC); +// +// // if ( !status ) +// // throw OpalException("SigmaGenerator::eigsolve_m()", +// // "Couldn't sort eigenvalues and eigenvectors!"); +// +// // go to UBLAS +// for( unsigned int i = 0; i < 6; i++){ +// gsl_vector_complex_view evec_i = gsl_matrix_complex_column(evecs, i); +// +// for(unsigned int j = 0;j < 6; j++){ +// gsl_complex zgsl = gsl_vector_complex_get(&evec_i.vector, j); +// complex_t z(GSL_REAL(zgsl), GSL_IMAG(zgsl)); +// R(i,j) = z; +// } +// } +// +// // Sorting the Eigenvectors +// // This is an arbitrary threshold that has worked for me. (We should fix this) +// double threshold = 10e-12; +// bool isZdirection = false; +// std::vector<complex_vector_type> zVectors{}; +// std::vector<complex_vector_type> xyVectors{}; +// +// for(unsigned int i = 0; i < 6; i++){ +// complex_t z = R(i,0); +// if(std::abs(z) < threshold) z = 0.; +// if(z == 0.) isZdirection = true; +// complex_vector_type v(6); +// if(isZdirection){ +// for(unsigned int j = 0;j < 6; j++){ +// complex_t z = R(i,j); +// v(j) = z; +// } +// zVectors.push_back(v); +// } +// else{ +// for(unsigned int j = 0; j < 6; j++){ +// complex_t z = R(i,j); +// v(j) = z; +// } +// xyVectors.push_back(v); +// } +// isZdirection = false; +// } +// +// //if z-direction not found, then the system does not behave as expected +// if(zVectors.size() != 2) // throw OpalException("SigmaGenerator::eigsolve_m()", -// "Couldn't sort eigenvalues and eigenvectors!"); - - // go to UBLAS - for( unsigned int i = 0; i < 6; i++){ - gsl_vector_complex_view evec_i = gsl_matrix_complex_column(evecs, i); - - for(unsigned int j = 0;j < 6; j++){ - gsl_complex zgsl = gsl_vector_complex_get(&evec_i.vector, j); - complex_t z(GSL_REAL(zgsl), GSL_IMAG(zgsl)); - R(i,j) = z; - } - } - - // Sorting the Eigenvectors - // This is an arbitrary threshold that has worked for me. (We should fix this) - double threshold = 10e-12; - bool isZdirection = false; - std::vector<complex_vector_type> zVectors{}; - std::vector<complex_vector_type> xyVectors{}; - - for(unsigned int i = 0; i < 6; i++){ - complex_t z = R(i,0); - if(std::abs(z) < threshold) z = 0.; - if(z == 0.) isZdirection = true; - complex_vector_type v(6); - if(isZdirection){ - for(unsigned int j = 0;j < 6; j++){ - complex_t z = R(i,j); - v(j) = z; - } - zVectors.push_back(v); - } - else{ - for(unsigned int j = 0; j < 6; j++){ - complex_t z = R(i,j); - v(j) = z; - } - xyVectors.push_back(v); - } - isZdirection = false; - } - - //if z-direction not found, then the system does not behave as expected - if(zVectors.size() != 2) - throw OpalException("SigmaGenerator::eigsolve_m()", - "Couldn't find the vertical Eigenvectors."); +// "Couldn't find the vertical Eigenvectors."); +// +// // Norms the Eigenvectors +// for(unsigned int i = 0; i < 4; i++){ +// double norm{0}; +// for(unsigned int j = 0; j < 6; j++) norm += std::norm(xyVectors[i](j)); +// for(unsigned int j = 0; j < 6; j++) xyVectors[i](j) /= std::sqrt(norm); +// } +// for(unsigned int i = 0; i < 2; i++){ +// double norm{0}; +// for(unsigned int j = 0; j < 6; j++) norm += std::norm(zVectors[i](j)); +// for(unsigned int j = 0; j < 6; j++) zVectors[i](j) /= std::sqrt(norm); +// } +// +// for(double i = 0; i < 6; i++){ +// R(i,0) = xyVectors[0](i); +// R(i,1) = xyVectors[1](i); +// R(i,2) = zVectors[0](i); +// R(i,3) = zVectors[1](i); +// R(i,4) = xyVectors[2](i); +// R(i,5) = xyVectors[3](i); +// } +// +// gsl_vector_complex_free(evals); +// gsl_matrix_complex_free(evecs); +// gsl_eigen_nonsymmv_free(wspace); +// gsl_matrix_free(M); +// } - // Norms the Eigenvectors - for(unsigned int i = 0; i < 4; i++){ - double norm{0}; - for(unsigned int j = 0; j < 6; j++) norm += std::norm(xyVectors[i](j)); - for(unsigned int j = 0; j < 6; j++) xyVectors[i](j) /= std::sqrt(norm); - } - for(unsigned int i = 0; i < 2; i++){ - double norm{0}; - for(unsigned int j = 0; j < 6; j++) norm += std::norm(zVectors[i](j)); - for(unsigned int j = 0; j < 6; j++) zVectors[i](j) /= std::sqrt(norm); - } - for(double i = 0; i < 6; i++){ - R(i,0) = xyVectors[0](i); - R(i,1) = xyVectors[1](i); - R(i,2) = zVectors[0](i); - R(i,3) = zVectors[1](i); - R(i,4) = xyVectors[2](i); - R(i,5) = xyVectors[3](i); - } - - gsl_vector_complex_free(evals); - gsl_matrix_complex_free(evecs); - gsl_eigen_nonsymmv_free(wspace); - gsl_matrix_free(M); -} +// bool SigmaGenerator::invertMatrix_m(const sparse_matrix_type& R, +// sparse_matrix_type& invR) +// { +// typedef boost::numeric::ublas::permutation_matrix<unsigned int> pmatrix_t; +// +// //creates a working copy of R +// cmatrix_type A(R); +// +// //permutation matrix for the LU-factorization +// pmatrix_t pm(A.size1()); +// +// //LU-factorization +// int res = lu_factorize(A,pm); +// +// if( res != 0) +// return false; +// +// // create identity matrix of invR +// invR.assign(boost::numeric::ublas::identity_matrix<complex_t>(A.size1())); +// +// // backsubstitute to get the inverse +// boost::numeric::ublas::lu_substitute(A, pm, invR); +// +// return true; +// } -bool SigmaGenerator::invertMatrix_m(const sparse_matrix_type& R, - sparse_matrix_type& invR) +/*void*/ +typename SigmaGenerator::vector_type +SigmaGenerator::decouple(const matrix_type& Mturn, + sparse_matrix_type& R, + sparse_matrix_type& invR) { - typedef boost::numeric::ublas::permutation_matrix<unsigned int> pmatrix_t; - - //creates a working copy of R - cmatrix_type A(R); - - //permutation matrix for the LU-factorization - pmatrix_t pm(A.size1()); + // copy one turn matrix + matrix_type M(Mturn); - //LU-factorization - int res = lu_factorize(A,pm); + // reduce 6x6 matrix to 4x4 matrix + reduce<matrix_type>(M); - if( res != 0) - return false; + // compute symplex part + matrix_type Ms = rdm_m.symplex(M); - // create identity matrix of invR - invR.assign(boost::numeric::ublas::identity_matrix<complex_t>(A.size1())); + // diagonalize and compute transformation matrices + rdm_m.diagonalize(Ms,R,invR); - // backsubstitute to get the inverse - boost::numeric::ublas::lu_substitute(A, pm, invR); - - return true; -} + /* + * formula (38) in paper of Dr. Christian Baumgarten: + * Geometrical method of decoupling + * + * [0, alpha, 0, 0; + * F_{d} = -beta, 0, 0, 0; + * 0, 0, 0, gamma; + * 0, 0, -delta, 0] + * + * + */ + vector_type eigen(4); + eigen(0) = Ms(0,1); // alpha + eigen(1) = - Ms(1,0); // beta + eigen(2) = Ms(2,3); // gamma + eigen(3) = - Ms(3,2); // delta + return eigen; -void SigmaGenerator::decouple(const matrix_type& Mturn, - sparse_matrix_type& R, - sparse_matrix_type& invR) -{ - this->eigsolve_m(Mturn, R); +// this->eigsolve_m(Mturn, R); - if ( !this->invertMatrix_m(R, invR) ) - throw OpalException("SigmaGenerator::decouple()", - "Couldn't compute inverse matrix!"); +// if ( !this->invertMatrix_m(R, invR) ) +// throw OpalException("SigmaGenerator::decouple()", +// "Couldn't compute inverse matrix!"); } @@ -744,61 +786,211 @@ void SigmaGenerator::initialize(double nuz, double ravg) +// typename SigmaGenerator::matrix_type +// SigmaGenerator::updateInitialSigma(const matrix_type& /*M*/, +// sparse_matrix_type& R, +// sparse_matrix_type& invR) +// { typename SigmaGenerator::matrix_type -SigmaGenerator::updateInitialSigma(const matrix_type& /*M*/, +SigmaGenerator::updateInitialSigma(const matrix_type& M, + const vector_type& eigen, sparse_matrix_type& R, sparse_matrix_type& invR) { + /* + * This function is based on the paper of Dr. Christian Baumgarten: + * Transverse-Longitudinal Coupling by Space Charge in Cyclotrons (2012) + */ + /* * Function input: * - M: one turn transfer matrix + * - eigen = {alpha, beta, gamma, delta} * - R: transformation matrix (in paper: E) * - invR: inverse transformation matrix (in paper: E^{-1} */ +// * with diagonal matrix D (stores eigenvalues of sigma*S (emittances apart from +- i), +// * skew-symmetric matrix (formula (13)), and tranformation matrices E, E^{-1} +// */ + + // normalize emittances + double invbg = 1.0 / (beta_m * gamma_m); + double ex = emittance_m[0] * invbg; + double ey = emittance_m[1] * invbg; + double ez = emittance_m[2] * invbg; + + std::cout << "Eigen: " << eigen << std::endl; std::cin.get(); - /* formula (18): - * sigma = -E*D*E^{-1}*S - * with diagonal matrix D (stores eigenvalues of sigma*S (emittances apart from +- i), - * skew-symmetric matrix (formula (13)), and tranformation matrices E, E^{-1} + + // alpha^2-beta*gamma = 1 + + /* 0 eigen(0) 0 0 + * eigen(1) 0 0 0 + * 0 0 0 eigen(2) + * 0 0 eigen(3) 0 + * + * M = cos(mux)*[1, 0; 0, 1] + sin(mux)*[alpha, beta; -gamma, -alpha], Book, p. 242 + * + * ----------------------------------------------------------------------------------- + * X-DIRECTION and L-DIRECTION + * ----------------------------------------------------------------------------------- + * --> eigen(0) = sin(mux)*betax + * --> eigen(1) = -gammax*sin(mux) + * + * What is sin(mux)? --> alphax = 0 --> -alphax^2+betax*gammax = betax*gammax = 1 + * + * Convention: betax > 0 + * + * 1) betax = 1/gammax + * 2) eig0 = sin(mux)*betax + * 3) eig1 = -gammax*sin(mux) + * + * eig0 = sin(mux)/gammax + * eig1 = -gammax*sin(mux) <--> 1/gammax = -sin(mux)/eig1 + * + * eig0 = -sin(mux)^2/eig1 --> -sin(mux)^2 = eig0*eig1 --> sin(mux) = sqrt(-eig0*eig1) + * --> gammax = -eig1/sin(mux) + * --> betax = eig0/sin(mux) */ - cmatrix_type S = boost::numeric::ublas::zero_matrix<complex_t>(6,6); + + // x-direction + //double alphax = 0.0; + double betax = std::sqrt(std::fabs(eigen(0) / eigen(1))); + double gammax = 1.0 / betax; + + // l-direction + //double alphal = 0.0; + double betal = std::sqrt(std::fabs(eigen(2) / eigen(3))); + double gammal = 1.0 / betal; + + /* + * ----------------------------------------------------------------------------------- + * Z-DIRECTION + * ----------------------------------------------------------------------------------- + * + * m22 m23 + * m32 m33 + * + * m22 = cos(muz) + alpha*sin(muz) + * m33 = cos(muz) - alpha*sin(muz) + * + * --> cos(muz) = 0.5*(m22 + m33) + * sin(muz) = sign(m32)*sqrt(1-cos(muz)^2) + * + * m22-m33 = 2*alpha*sin(muz) --> alpha = 0.5*(m22-m33)/sin(muz) + * + * m23 = betaz*sin(muz) --> betaz = m23/sin(muz) + * m32 = -gammaz*sin(muz) --> gammaz = -m32/sin(muz) + */ + + double cosz = 0.5 * (M(2,2) + M(3,3)); + + double sign = (std::signbit(M(2,3))) ? double(-1) : double(1); + + double invsinz = sign / std::sqrt(std::fabs( 1.0 - cosz * cosz)); + + double alphaz = 0.5 * (M(2,2) - M(3,3)) * invsinz; + double betaz = M(2,3) * invsinz; + double gammaz = - M(3,2) * invsinz; + + // Convention beta>0 + if (std::signbit(betaz)) // singbit = true if beta<0, else false + betaz *= -1.0; + + // diagonal matrix with eigenvalues + matrix_type D = boost::numeric::ublas::zero_matrix<double>(6,6); + // x-direction + D(0,1) = betax * ex; + D(1,0) = - gammax * ex; + // z-direction + D(2,2) = alphaz * ey; + D(3,3) = - alphaz * ey; + D(2,3) = betaz * ey; + D(3,2) = - gammaz * ey; + // l-direction + D(4,5) = betal * ez; + D(5,4) = - gammal * ez; + + // expand 4x4 transformation matrices to 6x6 + expand<sparse_matrix_type>(R); + expand<sparse_matrix_type>(invR); + + // symplectic matrix + sparse_matrix_type S(6,6,6); S(0,1) = S(2,3) = S(4,5) = 1; S(1,0) = S(3,2) = S(5,4) = -1; - // Build new D-Matrix - // Section 2.4 Eq. 18 in M. Frey Semester thesis - // D = diag(i*emx,-i*emx,i*emy,-i*emy,i*emz, -i*emz) + matrix_type sigma = matt_boost::gemmm<matrix_type>(-invR,D,R); + sigma = boost::numeric::ublas::prod(sigma,S); + if (write_m) { + std::string energy = float2string(E_m); - cmatrix_type D = boost::numeric::ublas::zero_matrix<complex_t>(6,6); - double invbg = 1.0 / (beta_m * gamma_m); - complex_t im(0,1); - for(unsigned int i = 0; i < 3; ++i){ - D(2*i, 2*i) = emittance_m[i] * invbg * im; - D(2*i+1, 2*i+1) = -emittance_m[i] * invbg * im; - } - - // Computing of new Sigma - // sigma = -R*D*R^{-1}*S - cmatrix_type csigma(6, 6); - csigma = boost::numeric::ublas::prod(invR, S); - csigma = boost::numeric::ublas::prod(D, csigma); - csigma = boost::numeric::ublas::prod(-R, csigma); - - matrix_type sigma(6,6); - for (unsigned int i = 0; i < 6; ++i){ - for (unsigned int j = 0; j < 6; ++j){ - sigma(i,j) = csigma(i,j).real(); - } - } + std::string fname = Util::combineFilePath({ + OpalData::getInstance()->getAuxiliaryOutputDirectory(), + "maps", + "InitialSigmaPerAngleForEnergy" + energy + "MeV.dat" + }); - for (unsigned int i = 0; i < 6; ++i) { - if(sigma(i,i) < 0.) - sigma(i,i) *= -1.0; + std::ofstream writeInit(fname, std::ios::app); + writeMatrix(writeInit, sigma); + writeInit.close(); } return sigma; + + +// /* +// * Function input: +// * - M: one turn transfer matrix +// * - R: transformation matrix (in paper: E) +// * - invR: inverse transformation matrix (in paper: E^{-1} +// */ +// +// /* formula (18): +// * sigma = -E*D*E^{-1}*S +// * with diagonal matrix D (stores eigenvalues of sigma*S (emittances apart from +- i), +// * skew-symmetric matrix (formula (13)), and tranformation matrices E, E^{-1} +// */ +// +// cmatrix_type S = boost::numeric::ublas::zero_matrix<complex_t>(6,6); +// S(0,1) = S(2,3) = S(4,5) = 1; +// S(1,0) = S(3,2) = S(5,4) = -1; +// +// // Build new D-Matrix +// // Section 2.4 Eq. 18 in M. Frey Semester thesis +// // D = diag(i*emx,-i*emx,i*emy,-i*emy,i*emz, -i*emz) +// +// +// cmatrix_type D = boost::numeric::ublas::zero_matrix<complex_t>(6,6); +// double invbg = 1.0 / (beta_m * gamma_m); +// complex_t im(0,1); +// for(unsigned int i = 0; i < 3; ++i){ +// D(2*i, 2*i) = emittance_m[i] * invbg * im; +// D(2*i+1, 2*i+1) = -emittance_m[i] * invbg * im; +// } +// +// // Computing of new Sigma +// // sigma = -R*D*R^{-1}*S +// cmatrix_type csigma(6, 6); +// csigma = boost::numeric::ublas::prod(invR, S); +// csigma = boost::numeric::ublas::prod(D, csigma); +// csigma = boost::numeric::ublas::prod(-R, csigma); +// +// matrix_type sigma(6,6); +// for (unsigned int i = 0; i < 6; ++i){ +// for (unsigned int j = 0; j < 6; ++j){ +// sigma(i,j) = csigma(i,j).real(); +// } +// } +// +// for (unsigned int i = 0; i < 6; ++i) { +// if(sigma(i,i) < 0.) +// sigma(i,i) *= -1.0; +// } +// +// return sigma; } @@ -940,4 +1132,4 @@ void SigmaGenerator::writeMatrix(std::ofstream& os, const matrix_type& m) { os << m(i, j) << " "; } os << std::endl; -} \ No newline at end of file +} diff --git a/src/Distribution/SigmaGenerator.h b/src/Distribution/SigmaGenerator.h index eb9c5ba1716595937e3e370c50a823080aa7c984..b091ae87b1396ee2aa3d92fd6593cc47817d8e47 100644 --- a/src/Distribution/SigmaGenerator.h +++ b/src/Distribution/SigmaGenerator.h @@ -44,6 +44,8 @@ #include <boost/numeric/ublas/matrix_sparse.hpp> #include <boost/numeric/ublas/vector.hpp> +#include "rdm.h" + /// @brief This class computes the matched distribution class SigmaGenerator @@ -58,7 +60,7 @@ public: typedef boost::numeric::ublas::matrix<complex_t> cmatrix_type; /// Type for storing the sparse maps - typedef boost::numeric::ublas::compressed_matrix<complex_t, + typedef boost::numeric::ublas::compressed_matrix<double /*complex_t*/, boost::numeric::ublas::row_major > sparse_matrix_type; /// Type for storing vectors @@ -114,15 +116,15 @@ public: * @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); +// 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); +// 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 <b>R</b> and its inverse <b>invR</b>. @@ -131,7 +133,8 @@ public: * @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); + /*void*/ + vector_type 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. /*! @@ -240,7 +243,12 @@ private: * @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&); + matrix_type updateInitialSigma(const matrix_type&, + const vector_type&, sparse_matrix_type&, sparse_matrix_type&); @@ -290,6 +298,16 @@ private: const container_type& ds_turn); void writeMatrix(std::ofstream&, const matrix_type&); + + /// <b>RDM</b>-class member used for decoupling + RDM<double, unsigned int> rdm_m; + + + template<class matrix> + void reduce(matrix&); + + template<class matrix> + void expand(matrix&); }; @@ -326,4 +344,71 @@ std::array<double,3> SigmaGenerator::getEmittances() const }}; } + +template<class matrix> +void SigmaGenerator::reduce(matrix& M) { + /* The 6x6 matrix gets reduced to a 4x4 matrix in the following way: + * + * a11 a12 a13 a14 a15 a16 + * a21 a22 a23 a24 a25 a26 a11 a12 a15 a16 + * a31 a32 a33 a34 a35 a36 --> a21 a22 a25 a26 + * a41 a42 a43 a44 a45 a46 a51 a52 a55 a56 + * a51 a52 a53 a54 a55 a56 a61 a62 a65 a66 + * a61 a62 a63 a64 a65 a66 + */ + + // copy x- and l-direction to a 4x4 matrix_type + matrix_type M4x4(4,4); + for (unsigned int i = 0; i < 2; ++i) { + // upper left 2x2 [a11,a12;a21,a22] + M4x4(i,0) = M(i,0); + M4x4(i,1) = M(i,1); + // lower left 2x2 [a51,a52;a61,a62] + M4x4(i + 2,0) = M(i + 4,0); + M4x4(i + 2,1) = M(i + 4,1); + // upper right 2x2 [a15,a16;a25,a26] + M4x4(i,2) = M(i,4); + M4x4(i,3) = M(i,5); + // lower right 2x2 [a55,a56;a65,a66] + M4x4(i + 2,2) = M(i + 4,4); + M4x4(i + 2,3) = M(i + 4,5); + } + + M.resize(4,4,false); + M.swap(M4x4); +} + +template<class matrix> +void SigmaGenerator::expand(matrix& M) { + /* The 4x4 matrix gets expanded to a 6x6 matrix in the following way: + * + * a11 a12 0 0 a13 a14 + * a11 a12 a13 a14 a21 a22 0 0 a23 a24 + * a21 a22 a23 a24 --> 0 0 1 0 0 0 + * a31 a32 a33 a34 0 0 0 1 0 0 + * a41 a42 a43 a44 a31 a32 0 0 a33 a34 + * a41 a42 0 0 a43 a44 + */ + + matrix M6x6 = boost::numeric::ublas::identity_matrix<double>(6,6); + + for (unsigned int i = 0; i < 2; ++i) { + // upper left 2x2 [a11,a12;a21,a22] + M6x6(i,0) = M(i,0); + M6x6(i,1) = M(i,1); + // lower left 2x2 [a31,a32;a41,a42] + M6x6(i + 4,0) = M(i + 2,0); + M6x6(i + 4,1) = M(i + 2,1); + // upper right 2x2 [a13,a14;a23,a24] + M6x6(i,4) = M(i,2); + M6x6(i,5) = M(i,3); + // lower right 2x2 [a22,a34;a43,a44] + M6x6(i + 4,4) = M(i + 2,2); + M6x6(i + 4,5) = M(i + 2,3); + } + + // exchange + M.swap(M6x6); +} + #endif \ No newline at end of file diff --git a/src/Distribution/rdm.h b/src/Distribution/rdm.h new file mode 100644 index 0000000000000000000000000000000000000000..bbca9bd993dcd16f329918b9de33eda26a919b6b --- /dev/null +++ b/src/Distribution/rdm.h @@ -0,0 +1,304 @@ +/** + * @file rdm.h + * They're ordered after the paper of Dr. C. Baumgarten: "Use of real Dirac matrices in two-dimensional coupled linear optics". + * The diagonalizing method is based on the paper "Geometrical method of decoupling" (2012) of Dr. C. Baumgarten. + * The template parameter Value_type is the datatype of the matrices and variables. + * + * @author Matthias Frey + * @version 1.0 + */ +#ifndef RDM_H +#define RDM_H + +#include <cmath> +#include <iostream> +#include <stdexcept> + +#include <boost/numeric/ublas/matrix_sparse.hpp> +#include <boost/numeric/ublas/matrix.hpp> +#include <boost/numeric/ublas/vector.hpp> + +#include "matrix_vector_operation.h" + +// Remark: Comments starting with "///" are doxygen comments, comments with "//" are normal comments. + +/// @brief Real Dirac Matrix class +template<typename Value_type, typename Size_type> +class RDM +{ + public: + /// Type of variables + typedef Value_type value_type; + /// Type for specifying sizes + typedef Size_type size_type; + /// Sparse matrix type definition + typedef boost::numeric::ublas::compressed_matrix<value_type,boost::numeric::ublas::row_major> sparse_matrix_type; + /// Dense matrix type definition + typedef boost::numeric::ublas::matrix<value_type> matrix_type; + /// Dense vector type definition + typedef boost::numeric::ublas::vector<value_type> vector_type; + + /// Default constructor (sets only NumOfRDMs and DimOfRDMs) + RDM(); + + /// Returns the i-th Real Dirac matrix + /*! + * @param i specifying the matrix (has to be in the range from 0 to 15) + */ + sparse_matrix_type getRDM(short); + + /// Decomposes a real-valued 4x4 matrix into a linear combination and returns a vector containing the coefficients + /*! + * @param M an arbitrary real-valued 4x4 matrix + */ + vector_type decompose(const matrix_type&); + + /// Takes a vector of coefficients, evaluates the linear combination of RDMs with these coefficients and returns a 4x4 matrix + /*! + * @param coeffs is a vector of coefficients (at most length NumOfRDMs) + */ + matrix_type combine(const vector_type&); + + /// Brings a 4x4 symplex matrix into Hamilton form and computes the transformation matrix and its inverse + /*! + * @param Ms is a 4x4 symplex matrix + * @param R is the 4x4 transformation matrix (gets computed) + * @param invR is the 4x4 inverse transformation matrix (gets computed) + */ + void diagonalize(matrix_type&, sparse_matrix_type&, sparse_matrix_type&); + + /// Returns the symplex part of a 4x4 real-valued matrix + /*! + * @param M 4x4 real-valued matrix + */ + matrix_type symplex(const matrix_type&); + + /// Returns the cosymplex part of a 4x4 real-valued matrix + /*! + * @param M 4x4 real-valued matrix + */ + matrix_type cosymplex(const matrix_type&); + + /// The number of real Dirac matrices + short NumOfRDMs; + /// The matrix dimension (4x4) + short DimOfRDMs; + + private: + /// Applies a rotation to the matrix M by a given angle + /*! + * @param M is the matrix to be transformed + * @param i is the i-th RDM used for transformation + * @param phi is the angle of rotation + * @param Rtot is a reference to the current transformation matrix + * @param invRtot is a reference to the inverse of the current transformation matrix + */ + void transform(matrix_type&, short, value_type, sparse_matrix_type&, sparse_matrix_type&); +}; + +// ----------------------------------------------------------------------------------------------------------------------- +// PUBLIC MEMBER FUNCTIONS +// ----------------------------------------------------------------------------------------------------------------------- + +template<typename Value_type, typename Size_type> +RDM<Value_type, Size_type>::RDM() : NumOfRDMs(16), DimOfRDMs(4) {}; + +template<typename Value_type, typename Size_type> +typename RDM<Value_type, Size_type>::sparse_matrix_type RDM<Value_type, Size_type>::getRDM(short i) { + sparse_matrix_type rdm(4,4,4); // #nrows, #ncols, #non-zeros + switch(i) { + case 0: rdm(0,1) = rdm(2,3) = 1; rdm(1,0) = rdm(3,2) = -1; break; + case 1: rdm(0,1) = rdm(1,0) = -1; rdm(2,3) = rdm(3,2) = 1; break; + case 2: rdm(0,3) = rdm(1,2) = rdm(2,1) = rdm(3,0) = 1; break; + case 3: rdm(0,0) = rdm(2,2) = -1; rdm(1,1) = rdm(3,3) = 1; break; + case 4: rdm(0,0) = rdm(3,3) = -1; rdm(1,1) = rdm(2,2) = 1; break; + case 5: rdm(0,2) = rdm(2,0) = 1; rdm(1,3) = rdm(3,1) = -1; break; + case 6: rdm(0,1) = rdm(1,0) = rdm(2,3) = rdm(3,2) = 1; break; + case 7: rdm(0,3) = rdm(2,1) = 1; rdm(1,2) = rdm(3,0) = -1; break; + case 8: rdm(0,1) = rdm(3,2) = 1; rdm(1,0) = rdm(2,3) = -1; break; + case 9: rdm(0,2) = rdm(1,3) = -1; rdm(2,0) = rdm(3,1) = 1; break; + case 10: rdm(0,2) = rdm(3,1) = 1; rdm(1,3) = rdm(2,0) = -1; break; + case 11: rdm(0,2) = rdm(1,3) = rdm(2,0) = rdm(3,1) = -1; break; + case 12: rdm(0,0) = rdm(1,1) = -1; rdm(2,2) = rdm(3,3) = 1; break; + case 13: rdm(0,3) = rdm(3,0) = -1; rdm(1,2) = rdm(2,1) = 1; break; + case 14: rdm(0,3) = rdm(1,2) = -1; rdm(2,1) = rdm(3,0) = 1; break; + case 15: rdm(0,0) = rdm(1,1) = rdm(2,2) = rdm(3,3) = 1; break; + default: throw std::out_of_range("Error in RDM::generate: 0 <= i <= 15"); break; + } + return rdm; +} + +template<typename Value_type, typename Size_type> +typename RDM<Value_type, Size_type>::vector_type RDM<Value_type, Size_type>::decompose(const matrix_type& M) { + /* + * Formula (11) from paper: + * Geometrical method of decoupling + */ + if (M.size1() != 4 && M.size1() != M.size2()) + throw std::length_error("Error in RDM::decompose: Wrong matrix dimensions."); + + vector_type coeffs(16); + + matrix_type left(4,4), right(4,4), rdm2(4,4); + + value_type inv32 = 1.0 / 32.0; + + // do it with iterators + for (short i = 0; i < 16; ++i) { + sparse_matrix_type rdm = getRDM(i); + left = boost::numeric::ublas::prod(M,rdm); + right = boost::numeric::ublas::prod(rdm,M); + rdm2 = boost::numeric::ublas::prod(rdm,rdm); + left *= inv32; + right *= inv32; + left += right; + coeffs(i) = matt_boost::trace(rdm2) * matt_boost::trace(left); + } + return coeffs; +} + +template<typename Value_type, typename Size_type> +typename RDM<Value_type, Size_type>::matrix_type RDM<Value_type, Size_type>::combine(const vector_type& coeffs) { + if (coeffs.size() > 16) + throw std::length_error("Error in RDM::combine: Wrong size of coefficient vector."); + + // initialize a 4x4 zero matrix + matrix_type M = boost::numeric::ublas::zero_matrix<value_type>(4,4); + + // evaluate linear combination + for (short i = 0; i < 16; ++i) + M += coeffs(i)*getRDM(i); + + return M; +} + +template<typename Value_type, typename Size_type> +void RDM<Value_type, Size_type>::diagonalize(matrix_type& Ms, sparse_matrix_type& R, sparse_matrix_type& invR) { + + // R and invR store the total transformation + R = boost::numeric::ublas::identity_matrix<value_type>(4); + invR = R; + + vector_type P(3), E(3), B(3), b; + value_type mr, mg, mb, eps; + + // Lambda function to compute vectors E, P, B and scalar eps (it takes the current Ms as reference argument (--> [&]) + auto mult = [&](short i) { + /* + * For computing E, P, B, eps according to formula (C4) from paper: + * Geometrical method of decoupling + */ + matrix_type tmp = boost::numeric::ublas::prod(Ms,getRDM(i))+boost::numeric::ublas::prod(getRDM(i),Ms); + return 0.125*matt_boost::trace(tmp); + }; + + // 1. Transformation with \gamma_{0} + P(0) = mult(1); P(1) = mult(2); P(2) = mult(3); + E(0) = mult(4); E(1) = mult(5); E(2) = mult(6); + B(0) = -mult(7); B(1) = -mult(8); B(2) = -mult(9); + mr = boost::numeric::ublas::inner_prod(E,B); // formula (31), paper: Geometrical method of decoupling + mg = boost::numeric::ublas::inner_prod(B,P); // formula (31), paper: Geometrical method of decoupling + + transform(Ms,short(0),0.5 * std::atan2(mg,mr),R,invR); + + // 2. Transformation with \gamma_{7} + eps = -mult(0); + P(0) = mult(1); P(1) = mult(2); P(2) = mult(3); + E(0) = mult(4); E(1) = mult(5); E(2) = mult(6); + B(0) = -mult(7); B(1) = -mult(8); B(2) = -mult(9); + b = eps*B+matt_boost::cross_prod(E,P); // formula (32), paper: Geometrical method of decoupling + + transform(Ms,short(7),0.5 * std::atan2(b(2),b(1)),R,invR); + + // 3. Transformation with \gamma_{9} + eps = -mult(0); + P(0) = mult(1); P(1) = mult(2); P(2) = mult(3); + E(0) = mult(4); E(1) = mult(5); E(2) = mult(6); + B(0) = -mult(7); B(1) = -mult(8); B(2) = -mult(9); + b = eps*B+matt_boost::cross_prod(E,P); + + transform(Ms,short(9),-0.5 * std::atan2(b(0),b(1)),R,invR); + + // 4. Transformation with \gamma_{2} + eps = -mult(0); + P(0) = mult(1); P(1) = mult(2); P(2) = mult(3); + E(0) = mult(4); E(1) = mult(5); E(2) = mult(6); + B(0) = -mult(7); B(1) = -mult(8); B(2) = -mult(9); + mr = boost::numeric::ublas::inner_prod(E,B); + b = eps*B+matt_boost::cross_prod(E,P); + + // Transformation distinction made according to function "rdm_Decouple_F" in rdm.c of Dr. Christian Baumgarten + + if (std::fabs(mr) < std::fabs(b(1))) { + transform(Ms,short(2),0.5 * std::atanh(mr / b(1)),R,invR); + } else { + transform(Ms,short(2),0.5 * std::atanh(b(1) / mr),R,invR); + } + + eps = -mult(0); + P(0) = mult(1); P(1) = mult(2); P(2) = mult(3); + E(0) = mult(4); E(1) = mult(5); E(2) = mult(6); + B(0) = -mult(7); B(1) = -mult(8); B(2) = -mult(9); + + // formula (31), paper: Geometrical method of decoupling + mr = boost::numeric::ublas::inner_prod(E,B); + mg = boost::numeric::ublas::inner_prod(B,P); + mb = boost::numeric::ublas::inner_prod(E,P); + + value_type P2 = boost::numeric::ublas::inner_prod(P,P); + value_type E2 = boost::numeric::ublas::inner_prod(E,E); + + // 5. Transform with \gamma_{0} + transform(Ms,short(0),0.25 * std::atan2(mb,0.5 * (E2 - P2)),R,invR); + + // 6. Transformation with \gamma_{8} + P(0) = mult(1); P(2) = mult(3); + + transform(Ms,short(8),-0.5 * std::atan2(P(2),P(0)),R,invR); +} + +template<typename Value_type, typename Size_type> +typename RDM<Value_type, Size_type>::matrix_type RDM<Value_type, Size_type>::symplex(const matrix_type& M) { + /* + * formula(16), p. 3 + */ + sparse_matrix_type rdm0 = getRDM(0); + return 0.5 * (M + matt_boost::gemmm<matrix_type>(rdm0,boost::numeric::ublas::trans(M),rdm0)); +} + + +template<typename Value_type, typename Size_type> +typename RDM<Value_type, Size_type>::matrix_type RDM<Value_type, Size_type>::cosymplex(const matrix_type& M) { + /* + * formula (16), p, 3 + */ + sparse_matrix_type rdm0 = getRDM(0); + return 0.5 * (M - matt_boost::gemmm<matrix_type>(rdm0,boost::numeric::ublas::trans(M),rdm0)); +} + +// ----------------------------------------------------------------------------------------------------------------------- +// PRIVATE MEMBER FUNCTIONS +// ----------------------------------------------------------------------------------------------------------------------- + +template<typename Value_type, typename Size_type> +void RDM<Value_type, Size_type>::transform(matrix_type& M, short i, value_type phi, sparse_matrix_type& Rtot, sparse_matrix_type& invRtot) { + + if (phi) { // if phi == 0 --> nothing happens, since R and invR would be identity_matrix matrix + sparse_matrix_type R(4,4), invR(4,4); + sparse_matrix_type I = boost::numeric::ublas::identity_matrix<value_type>(4); + + if (i < 7 && i != 0 && i < 11 && i != 14) { + R = I * std::cosh(phi) + getRDM(i) * std::sinh(phi); + invR = I * std::cosh(phi) - getRDM(i) * std::sinh(phi); + } else { + R = I * std::cos(phi) + getRDM(i) * std::sin(phi); + invR = I * std::cos(phi) - getRDM(i) * std::sin(phi); + } + // update matrices + M = matt_boost::gemmm<matrix_type>(R,M,invR); + Rtot = boost::numeric::ublas::prod(R,Rtot); + invRtot = boost::numeric::ublas::prod(invRtot,invR); + } +} + +#endif \ No newline at end of file