From 71fe312b76f50cf3389162733c182959f185be61 Mon Sep 17 00:00:00 2001 From: Matthias Frey <matthias.frey@psi.ch> Date: Wed, 8 Jul 2020 11:36:04 +0200 Subject: [PATCH] SigmaGenerator: no template class, split into *.h and *.cpp file --- src/Distribution/CMakeLists.txt | 1 + src/Distribution/ClosedOrbitFinder.h | 1 + src/Distribution/Distribution.cpp | 39 +- src/Distribution/Distribution.h | 1 - src/Distribution/SigmaGenerator.cpp | 1006 +++++++++++++++++++++++ src/Distribution/SigmaGenerator.h | 1119 ++------------------------ 6 files changed, 1083 insertions(+), 1084 deletions(-) create mode 100644 src/Distribution/SigmaGenerator.cpp diff --git a/src/Distribution/CMakeLists.txt b/src/Distribution/CMakeLists.txt index 15a96fc25..9f6797cf9 100644 --- a/src/Distribution/CMakeLists.txt +++ b/src/Distribution/CMakeLists.txt @@ -1,6 +1,7 @@ set (_SRCS Distribution.cpp LaserProfile.cpp + SigmaGenerator.cpp ) include_directories ( diff --git a/src/Distribution/ClosedOrbitFinder.h b/src/Distribution/ClosedOrbitFinder.h index 8c6ec78a5..7a351afe9 100644 --- a/src/Distribution/ClosedOrbitFinder.h +++ b/src/Distribution/ClosedOrbitFinder.h @@ -40,6 +40,7 @@ #include "Utilities/Options.h" #include "Utilities/Options.h" #include "Utilities/OpalException.h" +#include "Physics/Physics.h" #include "AbstractObjects/OpalData.h" diff --git a/src/Distribution/Distribution.cpp b/src/Distribution/Distribution.cpp index 2778e4631..f2416be44 100644 --- a/src/Distribution/Distribution.cpp +++ b/src/Distribution/Distribution.cpp @@ -8,6 +8,7 @@ #include "Distribution/Distribution.h" #include "Distribution/SigmaGenerator.h" +#include "Distribution/ClosedOrbitFinder.h" #include "AbsBeamline/SpecificElementVisitor.h" #include <cmath> @@ -212,16 +213,6 @@ Distribution::~Distribution() { delete laserProfile_m; } -/* - void Distribution::printSigma(SigmaGenerator<double,unsigned int>::matrix_type& M, Inform& out) { - for (int i=0; i<M.size1(); ++i) { - for (int j=0; j<M.size2(); ++j) { - *gmsg << M(i,j) << " "; - } - *gmsg << endl; - } - } -*/ /** * Calculate the local number of particles evenly and adjust node 0 @@ -1278,18 +1269,18 @@ void Distribution::createMatchedGaussDistribution(size_t numberOfParticles, doub bool writeMap = true; - typedef SigmaGenerator<double, unsigned int> sGenerator_t; - sGenerator_t *siggen = new sGenerator_t(I_m, - Attributes::getReal(itsAttr[Attrib::Distribution::EX])*1E6, - Attributes::getReal(itsAttr[Attrib::Distribution::EY])*1E6, - Attributes::getReal(itsAttr[Attrib::Distribution::ET])*1E6, - E_m*1E-6, - massIneV*1E-6, - CyclotronElement, - Nint, - Nsectors, - Attributes::getReal(itsAttr[Attrib::Distribution::ORDERMAPS]), - writeMap); + std::unique_ptr<SigmaGenerator> siggen = std::unique_ptr<SigmaGenerator>( + new SigmaGenerator(I_m, + Attributes::getReal(itsAttr[Attrib::Distribution::EX])*1E6, + Attributes::getReal(itsAttr[Attrib::Distribution::EY])*1E6, + Attributes::getReal(itsAttr[Attrib::Distribution::ET])*1E6, + E_m*1E-6, + massIneV*1E-6, + CyclotronElement, + Nint, + Nsectors, + Attributes::getReal(itsAttr[Attrib::Distribution::ORDERMAPS]), + writeMap)); if (siggen->match(accuracy, Attributes::getReal(itsAttr[Attrib::Distribution::MAXSTEPSSI]), @@ -1375,13 +1366,9 @@ void Distribution::createMatchedGaussDistribution(size_t numberOfParticles, doub else { *gmsg << "* Not converged for " << E_m*1E-6 << " MeV" << endl; - delete siggen; - throw OpalException("Distribution::CreateMatchedGaussDistribution", "didn't find any matched distribution."); } - - delete siggen; } void Distribution::createDistributionGauss(size_t numberOfParticles, double massIneV) { diff --git a/src/Distribution/Distribution.h b/src/Distribution/Distribution.h index 159669467..fa6bfb4e6 100644 --- a/src/Distribution/Distribution.h +++ b/src/Distribution/Distribution.h @@ -279,7 +279,6 @@ private: void eraseTOrZDist(); void eraseBGzDist(); - // void printSigma(SigmaGenerator<double,unsigned int>::matrix_type& M, Inform& out); void addDistributions(); void applyEmissionModel(double lowEnergyLimit, double &px, double &py, double &pz, std::vector<double> &additionalRNs); void applyEmissModelAstra(double &px, double &py, double &pz, std::vector<double> &additionalRNs); diff --git a/src/Distribution/SigmaGenerator.cpp b/src/Distribution/SigmaGenerator.cpp new file mode 100644 index 000000000..077e490e3 --- /dev/null +++ b/src/Distribution/SigmaGenerator.cpp @@ -0,0 +1,1006 @@ +// +// Class: SigmaGenerator.h +// The SigmaGenerator class uses the class <b>ClosedOrbitFinder</b> 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 <b>match(double, unsigned int)</b>, 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 <https://www.gnu.org/licenses/>. +// +#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 <cmath> +#include <fstream> +#include <functional> +#include <iomanip> +#include <iterator> +#include <limits> +#include <list> +#include <numeric> +#include <sstream> + +#include <boost/numeric/odeint/stepper/runge_kutta4.hpp> + +#include <boost/numeric/ublas/vector_proxy.hpp> +#include <boost/numeric/ublas/triangular.hpp> +#include <boost/numeric/ublas/lu.hpp> +#include <boost/numeric/ublas/io.hpp> + +#include <gsl/gsl_matrix.h> +#include <gsl/gsl_math.h> +#include <gsl/gsl_eigen.h> + +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<double>::max()) + , truncOrder_m(truncOrder) + , write_m(write) + , sigmas_m(nStepsPerSector_m) + , rinit_m(0.0) + , prinit_m(0.0) +{ + // set emittances (initialization like that due to old compiler version) + // [ex] = [ey] = [ez] = pi*mm*mrad --> [emittance] = mm mrad + emittance_m[0] = ex * Physics::pi; + emittance_m[1] = ey * Physics::pi; + emittance_m[2] = ez * Physics::pi; + + // minimum beta*gamma + double minGamma = Emin_m / m_m + 1.0; + double bgam = std::sqrt(minGamma * minGamma - 1.0); + + // normalized emittance (--> multiply with beta*gamma) + // [emittance] = mm mrad + emittance_m[0] *= bgam; + emittance_m[1] *= bgam; + emittance_m[2] *= 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 + + double milli = 1.0e-3; + + // formula (30), (31) + // [sigma(0,0)] = mm^{2} --> [sx] = [sy] = [sz] = mm + // multiply with 0.001 to get meter --> [sx] = [sy] = [sz] = m + double sx = std::sqrt(std::abs(sigx)) * milli; + double sy = std::sqrt(std::abs(sigy)) * milli; + double sz = std::sqrt(std::abs(sigz)) * milli; + + 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<double, + unsigned int, + Series, + Map, + Hamiltonian, + SpaceCharge> mapgen(nSteps_m); + + // compute cyclotron map and space charge map for each angle and store them into a vector + std::vector<matrix_type> Mcycs(nSteps_m), Mscs(nSteps_m); + + container_type h(nSteps_m), r(nSteps_m), ds(nSteps_m), fidx(nSteps_m); + + ClosedOrbitFinder<double, unsigned int, + boost::numeric::odeint::runge_kutta4<container_type> > 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<double,double> 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<double,3> 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_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 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<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; +} + + +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<matrix_type>(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 micro = 1.0e-6; + double mega = 1.0e6; + //double kilo = 1.0e3; + + // convert mass m_m from MeV/c^2 to eV*s^{2}/m^{2} + double m = m_m * mega/(Physics::c * Physics::c); // [m] = eV*s^{2}/m^{2}, [m_m] = MeV/c^2 + + /* Emittance [ex] = [ey] = [ez] = mm mrad (emittance_m are normalized emittances + * (i.e. emittance multiplied with beta*gamma) + */ + double ex = emittance_m[0] * invbg; // [ex] = mm mrad + double ey = emittance_m[1] * invbg; // [ey] = mm mrad + double ez = emittance_m[2] * invbg; // [ez] = mm mrad + + // convert normalized emittances: mm mrad --> m rad (mm mrad: millimeter milliradian) + ex *= micro; + ey *= micro; + ez *= micro; + + // initial guess of emittance, [e] = m rad + double e = std::cbrt(ex * ey * ez); // cbrt computes cubic root (C++11) <cmath> + + // 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 * /* 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 * gamma2_m * gamma_m); // [K3] = m + + double alpha = /* physics::q0 */ 1.0 * 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) <cmath> + } 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) + // Remark: We multiply with 10^{6} (= mega) to convert emittances back. + // 1 m^{2} = 10^{6} mm^{2} + matrix_type sigma = boost::numeric::ublas::zero_matrix<double>(6); + + // formula (30), [sigma(0,0)] = m^2 rad * 10^{6} = mm^{2} rad = mm mrad + sigma(0,0) = invAB * (B * ex / Omega + A * ez / omega) * mega; + + // [sigma(0,5)] = [sigma(5,0)] = m rad * 10^{6} = mm mrad // 1000: m --> mm and 1000 to promille + sigma(0,5) = sigma(5,0) = invAB * (ex / Omega + ez / omega) * mega; + + // [sigma(1,1)] = rad * 10^{6} = mrad (and promille) + sigma(1,1) = invAB * (B * ex * Omega + A * ez * omega) * mega; + + // [sigma(1,4)] = [sigma(4,1)] = m rad * 10^{6} = mm mrad + sigma(1,4) = sigma(4,1) = invAB * (ex * Omega+ez * omega) / (K * gamma2_m) * mega; + + // formula (31), [sigma(2,2)] = m rad * 10^{6} = mm mrad + sigma(2,2) = ey / (std::sqrt(h * h * nuz * nuz - K)) * mega; + + sigma(3,3) = (ey * mega) * (ey * mega) / sigma(2,2); + + // [sigma(4,4)] = m^{2} rad * 10^{6} = mm^{2} rad = mm mrad + sigma(4,4) = invAB * (A * ex * Omega + B * ez * omega) / (K * gamma2_m) * mega; + + // formula (30), [sigma(5,5)] = rad * 10^{6} = mrad (and promille) + sigma(5,5) = invAB * (ex / (B * Omega) + ez / (A * omega)) * mega; + + // fill in initial guess of the sigma matrix (for each angle the same guess) + sigmas_m.resize(nSteps_m); + for (typename std::vector<matrix_type>::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<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; +} + + +void SigmaGenerator::updateSigma(const std::vector<matrix_type>& Mscs, + const std::vector<matrix_type>& Mcycs) +{ + matrix_type M = boost::numeric::ublas::matrix<double>(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<matrix_type>(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<double,double>& 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; +} \ No newline at end of file diff --git a/src/Distribution/SigmaGenerator.h b/src/Distribution/SigmaGenerator.h index c3b43acc1..f7c248a97 100644 --- a/src/Distribution/SigmaGenerator.h +++ b/src/Distribution/SigmaGenerator.h @@ -1,8 +1,9 @@ // // Class: SigmaGenerator.h -// The SigmaGenerator class uses the class <b>ClosedOrbitFinder</b> to get the parameters (inverse bending radius, path length +// The SigmaGenerator class uses the class <b>ClosedOrbitFinder</b> 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 <b>match(value_type, size_type)</b>, where it iteratively tries to find a matched distribution for given +// The main function of this class is <b>match(double, unsigned int)</b>, 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 @@ -32,62 +33,27 @@ #define SIGMAGENERATOR_H #include <array> -#include <cmath> -#include <fstream> -#include <functional> -#include <iomanip> -#include <iterator> -#include <limits> -#include <list> -#include <numeric> -#include <sstream> #include <string> #include <utility> #include <vector> -#include "AbstractObjects/OpalData.h" +#include "AbsBeamline/Cyclotron.h" +#include "FixedAlgebra/FTps.h" #include "Physics/Physics.h" -#include "Utilities/Options.h" -#include "Utilities/Options.h" -#include "Utilities/OpalException.h" -#include "Utilities/Util.h" - -#include <boost/numeric/odeint/stepper/runge_kutta4.hpp> #include <boost/numeric/ublas/matrix.hpp> #include <boost/numeric/ublas/matrix_sparse.hpp> #include <boost/numeric/ublas/vector.hpp> -#include <boost/numeric/ublas/vector_proxy.hpp> -#include <boost/numeric/ublas/triangular.hpp> -#include <boost/numeric/ublas/lu.hpp> -#include <boost/numeric/ublas/io.hpp> - -#include <gsl/gsl_matrix.h> -#include <gsl/gsl_math.h> -#include <gsl/gsl_eigen.h> - -#include "matrix_vector_operation.h" -#include "ClosedOrbitFinder.h" -#include "MapGenerator.h" - -extern Inform *gmsg; /// @brief This class computes the matched distribution -template<typename Value_type, typename Size_type> class SigmaGenerator { public: - /// Type of variables - typedef Value_type value_type; - /// Type of constant variables - typedef const value_type const_value_type; - /// Type for specifying sizes - typedef Size_type size_type; /// Type for storing maps - typedef boost::numeric::ublas::matrix<value_type> matrix_type; + typedef boost::numeric::ublas::matrix<double> matrix_type; - typedef std::complex<value_type> complex_t; + typedef std::complex<double> complex_t; /// Type for storing complex matrices typedef boost::numeric::ublas::matrix<complex_t> cmatrix_type; @@ -97,17 +63,17 @@ public: boost::numeric::ublas::row_major > sparse_matrix_type; /// Type for storing vectors - typedef boost::numeric::ublas::vector<value_type> vector_type; + typedef boost::numeric::ublas::vector<double> vector_type; /// Container for storing the properties for each angle - typedef std::vector<value_type> container_type; + typedef std::vector<double> container_type; /// Type of the truncated powere series - typedef FTps<value_type,2*3> Series; + typedef FTps<double,2*3> Series; /// Type of a map - typedef FVps<value_type,2*3> Map; + typedef FVps<double,2*3> Map; /// Type of the Hamiltonian for the cyclotron - typedef std::function<Series(value_type,value_type,value_type)> Hamiltonian; + typedef std::function<Series(double,double,double)> Hamiltonian; /// Type of the Hamiltonian for the space charge - typedef std::function<Series(value_type,value_type,value_type)> SpaceCharge; + typedef std::function<Series(double,double,double)> SpaceCharge; /// Constructs an object of type SigmaGenerator /*! @@ -124,9 +90,9 @@ public: * @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(value_type I, value_type ex, value_type ey, value_type ez, - value_type E, value_type m, const Cyclotron* cycl, - size_type N, size_type Nsectors, size_type truncOrder, bool write = true); + 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. /*! @@ -141,8 +107,8 @@ public: * @param type specifies the magnetic field format (e.g. CARBONCYCL) * @param full match over full turn not just single sector */ - bool match(value_type accuracy, size_type maxit, size_type maxitOrbit, - Cyclotron* cycl, value_type denergy, value_type rguess, bool full); + bool match(double accuracy, unsigned int maxit, unsigned int maxitOrbit, + Cyclotron* cycl, double denergy, double rguess, bool full); /*! * Eigenvalue / eigenvector solver @@ -174,19 +140,19 @@ public: * @param Mturn is the one turn transfer matrix * @param sigma is the sigma matrix to be tested */ - value_type isEigenEllipse(const matrix_type& Mturn, const matrix_type& sigma); + 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) - size_type getIterations() const; + unsigned int getIterations() const; /// Returns the error (if the program didn't converged, one can call this function to check the error) - value_type getError() const; + 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<value_type,3> getEmittances() const; + std::array<double,3> getEmittances() const; const double& getInjectionRadius() const { return rinit_m; @@ -198,46 +164,46 @@ public: private: /// Stores the value of the current, \f$ \left[I\right] = A \f$ - value_type I_m; + double I_m; /// Stores the desired emittances, \f$ \left[\varepsilon_{x}\right] = \left[\varepsilon_{y}\right] = \left[\varepsilon_{z}\right] = mm \ mrad \f$ - std::array<value_type,3> emittance_m; + std::array<double,3> emittance_m; /// Is the orbital frequency, \f$ \left[\omega_{o}\right] = \frac{1}{s} \f$ - value_type wo_m; + double wo_m; /// Stores the user-define energy, \f$ \left[E\right] = MeV \f$ - value_type E_m; + 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$ - value_type gamma_m; + double gamma_m; /// Relativistic factor squared - value_type gamma2_m; + double gamma2_m; /// Harmonic number, \f$ \left[N_{h}\right] = 1 \f$ - value_type nh_m; + double nh_m; /// Velocity (c/v), \f$ \left[\beta\right] = 1 \f$ - value_type beta_m; + double beta_m; /// Is the mass of the particles, \f$ \left[m\right] = \frac{MeV}{c^{2}} \f$ - value_type m_m; + double m_m; /// Is the number of iterations needed for convergence - size_type niterations_m; + 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$ - value_type Emin_m; + double Emin_m; /// Maximum energy reached in cyclotron, \f$ \left[E_{max}\right] = MeV \f$ - value_type Emax_m; + double Emax_m; /// Number of integration steps for closed orbit computation - size_type N_m; + unsigned int N_m; /// Number of (symmetric) sectors the field is averaged over - size_type nSectors_m; + unsigned int nSectors_m; /// Number of integration steps per sector (--> also: number of maps per sector) - size_type nStepsPerSector_m; + unsigned int nStepsPerSector_m; /// Number of integration steps in total - size_type nSteps_m; + unsigned int nSteps_m; /// Error of computation - value_type error_m; + double error_m; /// Truncation order of the power series for the Hamiltonian (cyclotron and space charge) - size_type truncOrder_m; + unsigned int truncOrder_m; /// Decides for writing output (default: true) bool write_m; @@ -267,7 +233,7 @@ private: * @param nuz is the vertical tune * @param ravg is the average radius of the closed orbit */ - void initialize(value_type, value_type); + void initialize(double, double); /// Computes the new initial sigma matrix /*! @@ -292,7 +258,7 @@ private: * @param oldS is the old sigma matrix * @param newS is the new sigma matrix */ - value_type L2ErrorNorm(const matrix_type&, const matrix_type&); + double L2ErrorNorm(const matrix_type&, const matrix_type&); /// Returns the Lp-error norm between the old and new sigma-matrix @@ -300,15 +266,15 @@ private: * @param oldS is the old sigma matrix * @param newS is the new sigma matrix */ - value_type L1ErrorNorm(const matrix_type&, const matrix_type&); + double L1ErrorNorm(const matrix_type&, const matrix_type&); - value_type LInfErrorNorm(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(value_type val); + std::string float2string(double val); /// Called within SigmaGenerator::match(). /*! @@ -320,9 +286,9 @@ private: * @param fidx_turn is the field index * @param ds_turn is the path length element */ - void writeOrbitOutput_m(const std::pair<value_type,value_type>& tunes, - const value_type& ravg, - const value_type& freqError, + void writeOrbitOutput_m(const std::pair<double,double>& tunes, + const double& ravg, + const double& freqError, const container_type& r_turn, const container_type& peo, const container_type& h_turn, @@ -332,999 +298,38 @@ private: void writeMatrix(std::ofstream&, const matrix_type&); }; -// ----------------------------------------------------------------------------------------------------------------------- -// PUBLIC MEMBER FUNCTIONS -// ----------------------------------------------------------------------------------------------------------------------- - -template<typename Value_type, typename Size_type> -SigmaGenerator<Value_type, Size_type>::SigmaGenerator(value_type I, - value_type ex, - value_type ey, - value_type ez, - value_type E, - value_type m, - const Cyclotron* cycl, - size_type N, - size_type Nsectors, - size_type 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<value_type>::max()) - , truncOrder_m(truncOrder) - , write_m(write) - , sigmas_m(nStepsPerSector_m) - , rinit_m(0.0) - , prinit_m(0.0) -{ - // set emittances (initialization like that due to old compiler version) - // [ex] = [ey] = [ez] = pi*mm*mrad --> [emittance] = mm mrad - emittance_m[0] = ex * Physics::pi; - emittance_m[1] = ey * Physics::pi; - emittance_m[2] = ez * Physics::pi; - - // minimum beta*gamma - value_type minGamma = Emin_m / m_m + 1.0; - value_type bgam = std::sqrt(minGamma * minGamma - 1.0); - - // normalized emittance (--> multiply with beta*gamma) - // [emittance] = mm mrad - emittance_m[0] *= bgam; - emittance_m[1] *= bgam; - emittance_m[2] *= 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 = [&](value_type h, value_type kx, value_type 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 = [&](value_type sigx, value_type sigy, value_type sigz) { - // convert m from MeV/c^2 to eV*s^{2}/m^{2} - value_type m = m_m * 1.0e6 / (Physics::c * Physics::c); - - // formula (57) - value_type lam = Physics::two_pi*Physics::c / (wo_m * nh_m); // wavelength, [lam] = m - value_type 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 - - value_type milli = 1.0e-3; - - // formula (30), (31) - // [sigma(0,0)] = mm^{2} --> [sx] = [sy] = [sz] = mm - // multiply with 0.001 to get meter --> [sx] = [sy] = [sz] = m - value_type sx = std::sqrt(std::abs(sigx)) * milli; - value_type sy = std::sqrt(std::abs(sigy)) * milli; - value_type sz = std::sqrt(std::abs(sigz)) * milli; - - value_type tmp = sx * sy; // [tmp] = m^{2} - - value_type f = std::sqrt(tmp) / (3.0 * gamma_m * sz); // [f] = 1 - value_type kxy = K3 * std::abs(1.0 - f) / ((sx + sy) * sz); // [kxy] = 1/m - - value_type Kx = kxy / sx; - value_type Ky = kxy / sy; - value_type 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; - }; -} - -template<typename Value_type, typename Size_type> - bool SigmaGenerator<Value_type, Size_type>::match(value_type accuracy, - size_type maxit, - size_type maxitOrbit, - Cyclotron* cycl, - value_type denergy, - value_type 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<value_type, - size_type, - Series, - Map, - Hamiltonian, - SpaceCharge> mapgen(nSteps_m); - - // compute cyclotron map and space charge map for each angle and store them into a vector - std::vector<matrix_type> Mcycs(nSteps_m), Mscs(nSteps_m); - - container_type h(nSteps_m), r(nSteps_m), ds(nSteps_m), fidx(nSteps_m); - - ClosedOrbitFinder<value_type, size_type, - boost::numeric::odeint::runge_kutta4<container_type> > 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<value_type,value_type> tunes = cof.getTunes(); - value_type ravg = cof.getAverageRadius(); // average radius - - value_type 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 (size_type 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; - - value_type 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 (size_type 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<double,3> 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; -} - - -template<typename Value_type, typename Size_type> -void SigmaGenerator<Value_type, Size_type>::eigsolve_m(const matrix_type& Mturn, - sparse_matrix_type& R) +inline +typename SigmaGenerator::matrix_type& +SigmaGenerator::getSigma() { - 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 (size_type i = 0; i < 6; ++i){ - for (size_type 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( size_type i = 0; i < 6; i++){ - gsl_vector_complex_view evec_i = gsl_matrix_complex_column(evecs, i); - - for(size_type 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) - value_type threshold = 10e-12; - bool isZdirection = false; - std::vector<complex_vector_type> zVectors{}; - std::vector<complex_vector_type> xyVectors{}; - - for(size_type 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(size_type j = 0;j < 6; j++){ - complex_t z = R(i,j); - v(j) = z; - } - zVectors.push_back(v); - } - else{ - for(size_type 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(size_type i = 0; i < 4; i++){ - value_type norm{0}; - for(size_type j = 0; j < 6; j++) norm += std::norm(xyVectors[i](j)); - for(size_type j = 0; j < 6; j++) xyVectors[i](j) /= std::sqrt(norm); - } - for(size_type i = 0; i < 2; i++){ - value_type norm{0}; - for(size_type j = 0; j < 6; j++) norm += std::norm(zVectors[i](j)); - for(size_type j = 0; j < 6; j++) zVectors[i](j) /= std::sqrt(norm); - } - - for(value_type 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); -} - - -template<typename Value_type, typename Size_type> -bool SigmaGenerator<Value_type, Size_type>::invertMatrix_m(const sparse_matrix_type& R, - sparse_matrix_type& invR) -{ - typedef boost::numeric::ublas::permutation_matrix<size_type> 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; -} - - -template<typename Value_type, typename Size_type> -void SigmaGenerator<Value_type, Size_type>::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!"); + return sigma_m; } -template<typename Value_type, typename Size_type> -typename SigmaGenerator<Value_type, Size_type>::value_type -SigmaGenerator<Value_type, Size_type>::isEigenEllipse(const matrix_type& Mturn, - const matrix_type& sigma) -{ - // compute sigma matrix after one turn - matrix_type newSigma = matt_boost::gemmm<matrix_type>(Mturn, - sigma, - boost::numeric::ublas::trans(Mturn)); - - // return L2 error - return L2ErrorNorm(sigma,newSigma); -} -template<typename Value_type, typename Size_type> -inline typename SigmaGenerator<Value_type, Size_type>::matrix_type& -SigmaGenerator<Value_type, Size_type>::getSigma() +inline +unsigned int SigmaGenerator::getIterations() const { - return sigma_m; + return (converged_m) ? niterations_m : 0; } -template<typename Value_type, typename Size_type> -inline typename SigmaGenerator<Value_type, Size_type>::size_type -SigmaGenerator<Value_type, Size_type>::getIterations() const -{ - return (converged_m) ? niterations_m : size_type(0); -} -template<typename Value_type, typename Size_type> -inline typename SigmaGenerator<Value_type, Size_type>::value_type -SigmaGenerator<Value_type, Size_type>::getError() const +inline +double SigmaGenerator::getError() const { return error_m; } -template<typename Value_type, typename Size_type> -inline std::array<Value_type,3> -SigmaGenerator<Value_type, Size_type>::getEmittances() const + +inline +std::array<double,3> SigmaGenerator::getEmittances() const { - value_type bgam = gamma_m*beta_m; - return std::array<value_type,3>{{ + double bgam = gamma_m*beta_m; + return std::array<double,3>{{ emittance_m[0]/Physics::pi/bgam, emittance_m[1]/Physics::pi/bgam, emittance_m[2]/Physics::pi/bgam }}; } -// ----------------------------------------------------------------------------------------------------------------------- -// PRIVATE MEMBER FUNCTIONS -// ----------------------------------------------------------------------------------------------------------------------- - -template<typename Value_type, typename Size_type> -void SigmaGenerator<Value_type, Size_type>::initialize(value_type nuz, - value_type 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 - value_type invbg = 1.0 / (beta_m * gamma_m); - value_type micro = 1.0e-6; - value_type mega = 1.0e6; - //value_type kilo = 1.0e3; - - // convert mass m_m from MeV/c^2 to eV*s^{2}/m^{2} - value_type m = m_m * mega/(Physics::c * Physics::c); // [m] = eV*s^{2}/m^{2}, [m_m] = MeV/c^2 - - /* Emittance [ex] = [ey] = [ez] = mm mrad (emittance_m are normalized emittances - * (i.e. emittance multiplied with beta*gamma) - */ - value_type ex = emittance_m[0] * invbg; // [ex] = mm mrad - value_type ey = emittance_m[1] * invbg; // [ey] = mm mrad - value_type ez = emittance_m[2] * invbg; // [ez] = mm mrad - - // convert normalized emittances: mm mrad --> m rad (mm mrad: millimeter milliradian) - ex *= micro; - ey *= micro; - ez *= micro; - - // initial guess of emittance, [e] = m rad - value_type e = std::cbrt(ex * ey * ez); // cbrt computes cubic root (C++11) <cmath> - - // cyclotron radius [rcyc] = m - value_type rcyc = ravg / beta_m; - - // "average" inverse bending radius - value_type h = 1.0 / ravg; // [h] = 1/m - - // formula (57) - value_type lam = Physics::two_pi * Physics::c / (wo_m * nh_m); // wavelength, [lam] = m - value_type 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 * gamma2_m * gamma_m); // [K3] = m - - value_type alpha = /* physics::q0 */ 1.0 * 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 - - value_type sig0 = std::sqrt(2.0 * rcyc * e) / gamma_m; // [sig0] = m*sqrt(rad) --> [sig0] = m - - // formula (56) - value_type sig; // [sig] = m - if (alpha >= 2.5) { - sig = sig0 * std::cbrt(1.0 + alpha); // cbrt computes cubic root (C++11) <cmath> - } 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 - value_type K = K3 * gamma_m / (3.0 * sig * sig * sig); // formula (46), [K] = 1/m^{2} - value_type kx = h * h * gamma2_m; // formula (46) (assumption of an isochronous cyclotron), [kx] = 1/m^{2} - - value_type a = 0.5 * kx - K; // formula (22) (with K = Kx = Kz), [a] = 1/m^{2} - value_type 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."); - - value_type 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)"); - - value_type Omega = std::sqrt(a + tmp); // formula (22), [Omega] = 1/m - value_type omega = std::sqrt(a - tmp); // formula (22), [omega] = 1/m - - value_type A = h / (Omega * Omega + K); // formula (26), [A] = m - value_type B = h / (omega * omega + K); // formula (26), [B] = m - value_type invAB = 1.0 / (B - A); // [invAB] = 1/m - - // construct initial sigma-matrix (formula (29, 30, 31) - // Remark: We multiply with 10^{6} (= mega) to convert emittances back. - // 1 m^{2} = 10^{6} mm^{2} - matrix_type sigma = boost::numeric::ublas::zero_matrix<value_type>(6); - - // formula (30), [sigma(0,0)] = m^2 rad * 10^{6} = mm^{2} rad = mm mrad - sigma(0,0) = invAB * (B * ex / Omega + A * ez / omega) * mega; - - // [sigma(0,5)] = [sigma(5,0)] = m rad * 10^{6} = mm mrad // 1000: m --> mm and 1000 to promille - sigma(0,5) = sigma(5,0) = invAB * (ex / Omega + ez / omega) * mega; - - // [sigma(1,1)] = rad * 10^{6} = mrad (and promille) - sigma(1,1) = invAB * (B * ex * Omega + A * ez * omega) * mega; - - // [sigma(1,4)] = [sigma(4,1)] = m rad * 10^{6} = mm mrad - sigma(1,4) = sigma(4,1) = invAB * (ex * Omega+ez * omega) / (K * gamma2_m) * mega; - - // formula (31), [sigma(2,2)] = m rad * 10^{6} = mm mrad - sigma(2,2) = ey / (std::sqrt(h * h * nuz * nuz - K)) * mega; - - sigma(3,3) = (ey * mega) * (ey * mega) / sigma(2,2); - - // [sigma(4,4)] = m^{2} rad * 10^{6} = mm^{2} rad = mm mrad - sigma(4,4) = invAB * (A * ex * Omega + B * ez * omega) / (K * gamma2_m) * mega; - - // formula (30), [sigma(5,5)] = rad * 10^{6} = mrad (and promille) - sigma(5,5) = invAB * (ex / (B * Omega) + ez / (A * omega)) * mega; - - // fill in initial guess of the sigma matrix (for each angle the same guess) - sigmas_m.resize(nSteps_m); - for (typename std::vector<matrix_type>::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(); - } -} - - -template<typename Value_type, typename Size_type> -typename SigmaGenerator<Value_type, Size_type>::matrix_type -SigmaGenerator<Value_type, Size_type>::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<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); - value_type invbg = 1.0 / (beta_m * gamma_m); - complex_t im(0,1); - for(size_type 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 (size_type i = 0; i < 6; ++i){ - for (size_type j = 0; j < 6; ++j){ - sigma(i,j) = csigma(i,j).real(); - } - } - - for (size_type i = 0; i < 6; ++i) { - if(sigma(i,i) < 0.) - sigma(i,i) *= -1.0; - } - - return sigma; -} - -template<typename Value_type, typename Size_type> -void SigmaGenerator<Value_type, Size_type>::updateSigma(const std::vector<matrix_type>& Mscs, - const std::vector<matrix_type>& Mcycs) -{ - matrix_type M = boost::numeric::ublas::matrix<value_type>(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 (size_type 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<matrix_type>(M,sigmas_m[i - 1], - boost::numeric::ublas::trans(M)); - - writeMatrix(writeSigma, sigmas_m[i]); - } - - if (write_m) { - writeSigma.close(); - } -} - -template<typename Value_type, typename Size_type> -typename SigmaGenerator<Value_type, Size_type>::value_type -SigmaGenerator<Value_type, Size_type>::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)); -} - -template<typename Value_type, typename Size_type> -typename SigmaGenerator<Value_type, Size_type>::value_type - SigmaGenerator<Value_type, Size_type>::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(), - [](value_type& val) { - return std::abs(val); - }); - - return std::accumulate(diff.data().begin(), diff.data().end(), 0.0); -} - - -template<typename Value_type, typename Size_type> -typename SigmaGenerator<Value_type, Size_type>::value_type - SigmaGenerator<Value_type, Size_type>::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(), - [](value_type& val) { - return std::abs(val); - }); - - return *std::max_element(diff.data().begin(), diff.data().end()); -} - - -template<typename Value_type, typename Size_type> -std::string SigmaGenerator<Value_type, Size_type>::float2string(value_type val) { - std::ostringstream out; - out << std::setprecision(6) << val; - return out.str(); -} - - -template<typename Value_type, typename Size_type> -void SigmaGenerator<Value_type, Size_type>::writeOrbitOutput_m( - const std::pair<value_type,value_type>& tunes, - const value_type& ravg, - const value_type& 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 (size_type 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(); -} - - -template<typename Value_type, typename Size_type> -void SigmaGenerator<Value_type, Size_type>::writeMatrix(std::ofstream& os, const matrix_type& m) { - if (!write_m) - return; - - for (size_type i = 0; i < 6; ++i) { - for (size_type j = 0; j < 6; ++j) - os << m(i, j) << " "; - } - os << std::endl; -} - #endif \ No newline at end of file -- GitLab