// // 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 . // #include "SigmaGenerator.h" #include "AbstractObjects/OpalData.h" #include "Utilities/Options.h" #include "Utilities/Options.h" #include "Utilities/OpalException.h" #include "Utilities/Util.h" #include "matrix_vector_operation.h" #include "ClosedOrbitFinder.h" #include "MapGenerator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern Inform *gmsg; SigmaGenerator::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) : I_m(I) , wo_m(cycl->getRfFrequ(0)*1E6/cycl->getCyclHarm()*2.0*Physics::pi) , E_m(E) , gamma_m(E/m+1.0) , gamma2_m(gamma_m*gamma_m) , nh_m(cycl->getCyclHarm()) , beta_m(std::sqrt(1.0-1.0/gamma2_m)) , m_m(m) , niterations_m(0) , converged_m(false) , Emin_m(cycl->getFMLowE()) , Emax_m(cycl->getFMHighE()) , N_m(N) , nSectors_m(Nsectors) , nStepsPerSector_m(N/cycl->getSymmetry()) , nSteps_m(N) , error_m(std::numeric_limits::max()) , truncOrder_m(truncOrder) , write_m(write) , sigmas_m(nStepsPerSector_m) , rinit_m(0.0) , prinit_m(0.0) { // minimum beta*gamma double minGamma = Emin_m / m_m + 1.0; double bgam = std::sqrt(minGamma * minGamma - 1.0); // set emittances (initialization like that due to old compiler version) // [ex] = [ey] = [ez] = pi*mm*mrad --> [emittance] = m rad // normalized emittance (--> multiply with beta*gamma) emittance_m[0] = ex * Physics::pi * 1.0e-6 * bgam; emittance_m[1] = ey * Physics::pi * 1.0e-6 * bgam; emittance_m[2] = ez * Physics::pi * 1.0e-6 * bgam; // Define the Hamiltonian Series::setGlobalTruncOrder(truncOrder_m); // infinitesimal elements x_m = Series::makeVariable(0); px_m = Series::makeVariable(1); y_m = Series::makeVariable(2); py_m = Series::makeVariable(3); z_m = Series::makeVariable(4); delta_m = Series::makeVariable(5); H_m = [&](double h, double kx, double ky) { return 0.5*px_m*px_m + 0.5*kx*x_m*x_m - h*x_m*delta_m + 0.5*py_m*py_m + 0.5*ky*y_m*y_m + 0.5*delta_m*delta_m/gamma2_m; }; Hsc_m = [&](double sigx, double sigy, double sigz) { // convert m from MeV/c^2 to eV*s^{2}/m^{2} double m = m_m * 1.0e6 / (Physics::c * Physics::c); // formula (57) double lam = Physics::two_pi*Physics::c / (wo_m * nh_m); // wavelength, [lam] = m double K3 = 3.0 * /* physics::q0 */ 1.0 * I_m * lam / (20.0 * std::sqrt(5.0) * Physics::pi * Physics::epsilon_0 * m * Physics::c * Physics::c * Physics::c * beta_m * beta_m * gamma_m * gamma2_m); // [K3] = m // formula (30), (31) // [sigma(0,0)] = m^{2} --> [sx] = [sy] = [sz] = m // multiply with 0.001 to get meter --> [sx] = [sy] = [sz] = m double sx = std::sqrt(std::abs(sigx)); double sy = std::sqrt(std::abs(sigy)); double sz = std::sqrt(std::abs(sigz)); double tmp = sx * sy; // [tmp] = m^{2} double f = std::sqrt(tmp) / (3.0 * gamma_m * sz); // [f] = 1 double kxy = K3 * std::abs(1.0 - f) / ((sx + sy) * sz); // [kxy] = 1/m double Kx = kxy / sx; double Ky = kxy / sy; double Kz = K3 * f / (tmp * sz); return -0.5 * Kx * x_m * x_m -0.5 * Ky * y_m * y_m -0.5 * Kz * z_m * z_m * gamma2_m; }; } bool SigmaGenerator::match(double accuracy, unsigned int maxit, unsigned int maxitOrbit, Cyclotron* cycl, double denergy, double rguess, bool full) { /* compute the equilibrium orbit for energy E_ * and get the the following properties: * - inverse bending radius h * - step sizes of path ds * - tune nuz */ try { if ( !full ) nSteps_m = nStepsPerSector_m; // object for space charge map and cyclotron map MapGenerator mapgen(nSteps_m); // compute cyclotron map and space charge map for each angle and store them into a vector std::vector Mcycs(nSteps_m), Mscs(nSteps_m); container_type h(nSteps_m), r(nSteps_m), ds(nSteps_m), fidx(nSteps_m); ClosedOrbitFinder > cof(m_m, N_m, cycl, false, nSectors_m); if ( !cof.findOrbit(accuracy, maxitOrbit, E_m, denergy, rguess) ) { throw OpalException("SigmaGenerator::match()", "Closed orbit finder didn't converge."); } cof.computeOrbitProperties(E_m); // properties of one turn std::pair tunes = cof.getTunes(); double ravg = cof.getAverageRadius(); // average radius double angle = cycl->getPHIinit(); container_type h_turn = cof.getInverseBendingRadius(angle); container_type r_turn = cof.getOrbit(angle); container_type ds_turn = cof.getPathLength(angle); container_type fidx_turn = cof.getFieldIndex(angle); container_type peo = cof.getMomentum(angle); // write properties to file if (write_m) writeOrbitOutput_m(tunes, ravg, cof.getFrequencyError(), r_turn, peo, h_turn, fidx_turn, ds_turn); // write to terminal *gmsg << "* ----------------------------" << endl << "* Closed orbit info:" << endl << "*" << endl << "* average radius: " << ravg << " [m]" << endl << "* initial radius: " << r_turn[0] << " [m]" << endl << "* initial momentum: " << peo[0] << " [Beta Gamma]" << endl << "* frequency error: " << cof.getFrequencyError()*100 <<" [ % ] "<< endl << "* horizontal tune: " << tunes.first << endl << "* vertical tune: " << tunes.second << endl << "* ----------------------------" << endl << endl; // copy properties std::copy_n(r_turn.begin(), nSteps_m, r.begin()); std::copy_n(h_turn.begin(), nSteps_m, h.begin()); std::copy_n(fidx_turn.begin(), nSteps_m, fidx.begin()); std::copy_n(ds_turn.begin(), nSteps_m, ds.begin()); rinit_m = r[0]; prinit_m = peo[0]; std::string fpath = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "maps" }); if (!boost::filesystem::exists(fpath)) { boost::filesystem::create_directory(fpath); } // initialize sigma matrices (for each angle one) (first guess) initialize(tunes.second,ravg); // for writing std::ofstream writeMturn, writeMcyc, writeMsc; if (write_m) { std::string energy = float2string(E_m); std::string fname = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "maps", "OneTurnMapsForEnergy" + energy + "MeV.dat" }); writeMturn.open(fname, std::ios::app); fname = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "maps", "SpaceChargeMapPerAngleForEnergy" + energy + "MeV_iteration_0.dat" }); writeMsc.open(fname, std::ios::app); fname = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "maps", "CyclotronMapPerAngleForEnergy" + energy + "MeV.dat" }); writeMcyc.open(fname, std::ios::app); } // calculate only for a single sector (a nSector_-th) of the whole cyclotron for (unsigned int i = 0; i < nSteps_m; ++i) { Mcycs[i] = mapgen.generateMap(H_m(h[i], h[i]*h[i]+fidx[i], -fidx[i]), ds[i],truncOrder_m); Mscs[i] = mapgen.generateMap(Hsc_m(sigmas_m[i](0,0), sigmas_m[i](2,2), sigmas_m[i](4,4)), ds[i],truncOrder_m); writeMatrix(writeMcyc, Mcycs[i]); writeMatrix(writeMsc, Mscs[i]); } if (write_m) writeMsc.close(); // one turn matrix mapgen.combine(Mscs,Mcycs); matrix_type Mturn = mapgen.getMap(); writeMatrix(writeMturn, Mturn); // (inverse) transformation matrix sparse_matrix_type R(6, 6), invR(6, 6); // new initial sigma matrix matrix_type newSigma(6,6); // for exiting loop bool stop = false; double weight = 0.5; while (error_m > accuracy && !stop) { // decouple transfer matrix and compute (inverse) tranformation matrix decouple(Mturn,R,invR); // construct new initial sigma-matrix newSigma = updateInitialSigma(Mturn, R, invR); // compute new sigma matrices for all angles (except for initial sigma) updateSigma(Mscs,Mcycs); // compute error error_m = L2ErrorNorm(sigmas_m[0],newSigma); // write new initial sigma-matrix into vector sigmas_m[0] = weight*newSigma + (1.0-weight)*sigmas_m[0]; // compute new space charge maps for (unsigned int i = 0; i < nSteps_m; ++i) { Mscs[i] = mapgen.generateMap(Hsc_m(sigmas_m[i](0,0), sigmas_m[i](2,2), sigmas_m[i](4,4)), ds[i],truncOrder_m); if (write_m) { std::string energy = float2string(E_m); std::string fname = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "maps", "SpaceChargeMapPerAngleForEnergy" + energy + "MeV_iteration_" + std::to_string(niterations_m + 1) + ".dat" }); writeMsc.open(fname, std::ios::out); } writeMatrix(writeMsc, Mscs[i]); if (write_m) { writeMsc.close(); } } // construct new one turn transfer matrix M mapgen.combine(Mscs,Mcycs); Mturn = mapgen.getMap(); writeMatrix(writeMturn, Mturn); // check if number of iterations has maxit exceeded. stop = (niterations_m++ > maxit); } // store converged sigma-matrix sigma_m.resize(6,6,false); sigma_m.swap(newSigma); // returns if the sigma matrix has converged converged_m = error_m < accuracy; // Close files if (write_m) { writeMturn.close(); writeMcyc.close(); } } catch(const std::exception& e) { std::cerr << e.what() << std::endl; } if ( converged_m && write_m ) { // write tunes std::string fname = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "MatchedDistributions.dat" }); std::ofstream writeSigmaMatched(fname, std::ios::app); std::array emit = this->getEmittances(); writeSigmaMatched << "* Converged (Ex, Ey, Ez) = (" << emit[0] << ", " << emit[1] << ", " << emit[2] << ") pi mm mrad for E= " << E_m << " (MeV)" << std::endl << "* Sigma-Matrix " << std::endl; for(unsigned int i = 0; i < sigma_m.size1(); ++ i) { writeSigmaMatched << std::setprecision(4) << std::setw(11) << sigma_m(i,0); for(unsigned int j = 1; j < sigma_m.size2(); ++ j) { writeSigmaMatched << " & " << std::setprecision(4) << std::setw(11) << sigma_m(i,j); } writeSigmaMatched << " \\\\" << std::endl; } writeSigmaMatched << std::endl; writeSigmaMatched.close(); } return converged_m; } 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_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 zVectors{}; std::vector 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."); // 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 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(A.size1())); // backsubstitute to get the inverse boost::numeric::ublas::lu_substitute(A, pm, invR); return true; } void SigmaGenerator::decouple(const matrix_type& Mturn, sparse_matrix_type& R, sparse_matrix_type& invR) { this->eigsolve_m(Mturn, R); if ( !this->invertMatrix_m(R, invR) ) throw OpalException("SigmaGenerator::decouple()", "Couldn't compute inverse matrix!"); } double SigmaGenerator::isEigenEllipse(const matrix_type& Mturn, const matrix_type& sigma) { // compute sigma matrix after one turn matrix_type newSigma = matt_boost::gemmm(Mturn, sigma, boost::numeric::ublas::trans(Mturn)); // return L2 error return L2ErrorNorm(sigma,newSigma); } void SigmaGenerator::initialize(double nuz, double ravg) { /* * The initialization is based on the analytical solution of * using a spherical symmetric beam in the paper: * Transverse-longitudinal coupling by space charge in cyclotrons * by Dr. Christian Baumgarten * (formulas: (46), (56), (57)) */ /* Units: * ---------------------------------------------- * [wo] = 1/s * [nh] = 1 * [q0] = e * [I] = A * [eps0] = (A*s)^{2}/(N*m^{2}) * [E0] = MeV/(c^{2}) (with speed of light c) * [beta] = 1 * [gamma] = 1 * [m] = kg * * [lam] = m * [K3] = m * [alpha] = 10^{3}/(pi*mrad) * ---------------------------------------------- */ // helper constants double invbg = 1.0 / (beta_m * gamma_m); double c2 = Physics::c * Physics::c; // convert mass m_m from MeV/c^2 to eV*s^{2}/m^{2} double m = m_m * 1.0e6 / c2; // [m] = eV*s^{2}/m^{2}, [m_m] = MeV/c^2 // emittance [ex] = [ey] = [ez] = m rad double ex = emittance_m[0] * invbg; // [ex] = m rad double ey = emittance_m[1] * invbg; // [ey] = m rad double ez = emittance_m[2] * invbg; // [ez] = m rad // initial guess of emittance, [e] = m rad double e = std::cbrt(ex * ey * ez); // cbrt computes cubic root (C++11) // cyclotron radius [rcyc] = m double rcyc = ravg / beta_m; // "average" inverse bending radius double h = 1.0 / ravg; // [h] = 1/m // formula (57) double lam = Physics::two_pi * Physics::c / (wo_m * nh_m); // wavelength, [lam] = m double K3 = 3.0 * I_m * lam / (20.0 * std::sqrt(5.0) * Physics::pi * Physics::epsilon_0 * m * c2 * Physics::c * beta_m * beta_m * gamma2_m * gamma_m); // [K3] = m double alpha = Physics::mu_0 * I_m / (5.0 * std::sqrt(10.0) * m * Physics::c * gamma_m * nh_m) * std::sqrt(rcyc * rcyc * rcyc / (e * e * e)); // [alpha] = 1/rad --> [alpha] = 1 double sig0 = std::sqrt(2.0 * rcyc * e) / gamma_m; // [sig0] = m*sqrt(rad) --> [sig0] = m // formula (56) double sig; // [sig] = m if (alpha >= 2.5) { sig = sig0 * std::cbrt(1.0 + alpha); // cbrt computes cubic root (C++11) } else if (alpha >= 0) { sig = sig0 * (1 + alpha * (0.25 - 0.03125 * alpha)); } else { throw OpalException("SigmaGenerator::initialize()", "Negative alpha value: " + std::to_string(alpha) + " < 0"); } // K = Kx = Ky = Kz double K = K3 * gamma_m / (3.0 * sig * sig * sig); // formula (46), [K] = 1/m^{2} double kx = h * h * gamma2_m; // formula (46) (assumption of an isochronous cyclotron), [kx] = 1/m^{2} double a = 0.5 * kx - K; // formula (22) (with K = Kx = Kz), [a] = 1/m^{2} double b = K * K; // formula (22) (with K = Kx = Kz and kx = h^2*gamma^2), [b] = 1/m^{4} // b must be positive, otherwise no real-valued frequency if (b < 0) throw OpalException("SigmaGenerator::initialize()", "Negative value --> No real-valued frequency."); double tmp = a * a - b; // [tmp] = 1/m^{4} if (tmp < 0) throw OpalException("SigmaGenerator::initialize()", "Square root of negative number."); tmp = std::sqrt(tmp); // [tmp] = 1/m^{2} if (a < tmp) throw OpalException("Error in SigmaGenerator::initialize()", "Square root of negative number."); if (h * h * nuz * nuz <= K) throw OpalException("SigmaGenerator::initialize()", "h^{2} * nu_{z}^{2} <= K (i.e. square root of negative number)"); double Omega = std::sqrt(a + tmp); // formula (22), [Omega] = 1/m double omega = std::sqrt(a - tmp); // formula (22), [omega] = 1/m double A = h / (Omega * Omega + K); // formula (26), [A] = m double B = h / (omega * omega + K); // formula (26), [B] = m double invAB = 1.0 / (B - A); // [invAB] = 1/m // construct initial sigma-matrix (formula (29, 30, 31) matrix_type sigma = boost::numeric::ublas::zero_matrix(6); // formula (30), [sigma(0,0)] = m^2 rad sigma(0,0) = invAB * (B * ex / Omega + A * ez / omega); // [sigma(0,5)] = [sigma(5,0)] = m rad sigma(0,5) = sigma(5,0) = invAB * (ex / Omega + ez / omega); // [sigma(1,1)] = rad sigma(1,1) = invAB * (B * ex * Omega + A * ez * omega); // [sigma(1,4)] = [sigma(4,1)] = m rad sigma(1,4) = sigma(4,1) = invAB * (ex * Omega+ez * omega) / (K * gamma2_m); // formula (31), [sigma(2,2)] = m rad sigma(2,2) = ey / (std::sqrt(h * h * nuz * nuz - K)); sigma(3,3) = (ey * ey) / sigma(2,2); // [sigma(4,4)] = m^{2} rad sigma(4,4) = invAB * (A * ex * Omega + B * ez * omega) / (K * gamma2_m); // formula (30), [sigma(5,5)] = rad sigma(5,5) = invAB * (ex / (B * Omega) + ez / (A * omega)); // fill in initial guess of the sigma matrix (for each angle the same guess) sigmas_m.resize(nSteps_m); for (typename std::vector::iterator it = sigmas_m.begin(); it != sigmas_m.end(); ++it) { *it = sigma; } if (write_m) { std::string energy = float2string(E_m); std::string fname = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "maps", "InitialSigmaPerAngleForEnergy" + energy + "MeV.dat" }); std::ofstream writeInit(fname, std::ios::app); writeInit << sigma << std::endl; writeInit.close(); } } typename SigmaGenerator::matrix_type SigmaGenerator::updateInitialSigma(const matrix_type& /*M*/, sparse_matrix_type& R, sparse_matrix_type& invR) { /* * 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(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(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; } void SigmaGenerator::updateSigma(const std::vector& Mscs, const std::vector& Mcycs) { matrix_type M = boost::numeric::ublas::matrix(6,6); std::ofstream writeSigma; if (write_m) { std::string energy = float2string(E_m); std::string fname = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "maps", "SigmaPerAngleForEnergy" + energy + "MeV_iteration_" + std::to_string(niterations_m + 1) + ".dat" }); writeSigma.open(fname,std::ios::app); } // initial sigma is already computed writeMatrix(writeSigma, sigmas_m[0]); for (unsigned int i = 1; i < nSteps_m; ++i) { // transfer matrix for one angle M = boost::numeric::ublas::prod(Mscs[i - 1],Mcycs[i - 1]); // transfer the matrix sigma sigmas_m[i] = matt_boost::gemmm(M,sigmas_m[i - 1], boost::numeric::ublas::trans(M)); writeMatrix(writeSigma, sigmas_m[i]); } if (write_m) { writeSigma.close(); } } double SigmaGenerator::L2ErrorNorm(const matrix_type& oldS, const matrix_type& newS) { // compute difference matrix_type diff = newS - oldS; // sum squared error up and take square root return std::sqrt(std::inner_product(diff.data().begin(), diff.data().end(), diff.data().begin(), 0.0)); } double SigmaGenerator::L1ErrorNorm(const matrix_type& oldS, const matrix_type& newS) { // compute difference matrix_type diff = newS - oldS; std::transform(diff.data().begin(), diff.data().end(), diff.data().begin(), [](double& val) { return std::abs(val); }); return std::accumulate(diff.data().begin(), diff.data().end(), 0.0); } double SigmaGenerator::LInfErrorNorm(const matrix_type& oldS, const matrix_type& newS) { // compute difference matrix_type diff = newS - oldS; std::transform(diff.data().begin(), diff.data().end(), diff.data().begin(), [](double& val) { return std::abs(val); }); return *std::max_element(diff.data().begin(), diff.data().end()); } std::string SigmaGenerator::float2string(double val) { std::ostringstream out; out << std::setprecision(6) << val; return out.str(); } void SigmaGenerator::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) { std::string fname = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "Tunes.dat" }); // write tunes std::ofstream writeTunes(fname, std::ios::app); if(writeTunes.tellp() == 0) // if nothing yet written --> write description writeTunes << "energy [MeV]" << std::setw(15) << "nur" << std::setw(25) << "nuz" << std::endl; writeTunes << E_m << std::setw(30) << std::setprecision(10) << tunes.first << std::setw(25) << tunes.second << std::endl; // write average radius fname = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "AverageValues.dat" }); std::ofstream writeAvgRadius(fname, std::ios::app); if (writeAvgRadius.tellp() == 0) // if nothing yet written --> write description writeAvgRadius << "energy [MeV]" << std::setw(15) << "avg. radius [m]" << std::setw(15) << "r [m]" << std::setw(15) << "pr [m]" << std::endl; writeAvgRadius << E_m << std::setw(25) << std::setprecision(10) << ravg << std::setw(25) << std::setprecision(10) << r_turn[0] << std::setw(25) << std::setprecision(10) << peo[0] << std::endl; // write frequency error fname = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "FrequencyError.dat" }); std::ofstream writePhase(fname, std::ios::app); if(writePhase.tellp() == 0) // if nothing yet written --> write description writePhase << "energy [MeV]" << std::setw(15) << "freq. error" << std::endl; writePhase << E_m << std::setw(30) << std::setprecision(10) << freqError << std::endl; // write other properties std::string energy = float2string(E_m); fname = Util::combineFilePath({ OpalData::getInstance()->getAuxiliaryOutputDirectory(), "PropertiesForEnergy" + energy + "MeV.dat" }); std::ofstream writeProperties(fname, std::ios::out); writeProperties << std::left << std::setw(25) << "orbit radius" << std::setw(25) << "orbit momentum" << std::setw(25) << "inverse bending radius" << std::setw(25) << "field index" << std::setw(25) << "path length" << std::endl; for (unsigned int i = 0; i < r_turn.size(); ++i) { writeProperties << std::setprecision(10) << std::left << std::setw(25) << r_turn[i] << std::setw(25) << peo[i] << std::setw(25) << h_turn[i] << std::setw(25) << fidx_turn[i] << std::setw(25) << ds_turn[i] << std::endl; } // close all files within this if-statement writeTunes.close(); writeAvgRadius.close(); writePhase.close(); writeProperties.close(); } void SigmaGenerator::writeMatrix(std::ofstream& os, const matrix_type& m) { if (!write_m) return; for (unsigned int i = 0; i < 6; ++i) { for (unsigned int j = 0; j < 6; ++j) os << m(i, j) << " "; } os << std::endl; }