OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
CSRIGFWakeFunction.cpp
Go to the documentation of this file.
1//
2// Class CSRIGFWakeFunction
3//
4// Copyright (c) 2008 - 2020, Paul Scherrer Institut, Villigen PSI, Switzerland
5// All rights reserved
6//
7// This file is part of OPAL.
8//
9// OPAL is free software: you can redistribute it and/or modify
10// it under the terms of the GNU General Public License as published by
11// the Free Software Foundation, either version 3 of the License, or
12// (at your option) any later version.
13//
14// You should have received a copy of the GNU General Public License
15// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
16//
18
19#include "AbsBeamline/Bend2D.h"
24#include "Algorithms/Vektor.h"
25#include "Filters/Filter.h"
27#include "Physics/Physics.h"
28#include "Physics/Units.h"
30#include "Utilities/Options.h"
31#include "Utilities/Util.h"
32
33#include "Utility/Inform.h"
34#include "Utility/IpplInfo.h"
35#include "Utility/PAssert.h"
36
37#include <cmath>
38#include <fstream>
39#include <iomanip>
40#include <sstream>
41
42CSRIGFWakeFunction::CSRIGFWakeFunction(const std::string& name, std::vector<Filter*> filters, const unsigned int& N):
44 filters_m(filters.begin(), filters.end()),
45 lineDensity_m(),
46 dlineDensitydz_m(),
47 bendRadius_m(0.0),
48 totalBendAngle_m(0.0)
49{
50 if (filters_m.size() == 0) {
51 defaultFilter_m.reset(new SavitzkyGolayFilter(7, 3, 3, 3));
52 filters_m.push_back(defaultFilter_m.get());
53 }
54
55 diffOp_m = filters_m.back();
56}
57
59 Inform msg("CSRWake ");
60
61 std::pair<double, double> meshInfo;
62 calculateLineDensity(bunch, meshInfo);
63 const double &meshOrigin = meshInfo.first;
64 const double &meshSpacing = meshInfo.second;
65 const unsigned int numOfSlices = lineDensity_m.size();
66
67 if (Ez_m.size() < numOfSlices) {
68 Ez_m.resize(numOfSlices, 0.0);
69 Chi_m.resize(numOfSlices, 0.0);
70 Grn_m.resize(numOfSlices, 0.0);
71 Psi_m.resize(numOfSlices, 0.0);
72 }
73
74 for (unsigned int i = 0; i < numOfSlices; ++i) {
75 Ez_m[i] = 0.0;
76 }
77
78 Vector_t smin, smax;
79 bunch->get_bounds(smin, smax);
80 double minPathLength = smin(2) + bunch->get_sPos() - FieldBegin_m;
81 for (unsigned int i = 1; i < numOfSlices; i++) {
82 double pathLengthOfSlice = minPathLength + i * meshSpacing;
83
84 /*
85 bendRadius_m==0.0 can happen if we just go out into a drift
86 */
87 double angleOfSlice;
88 if (bendRadius_m==0.0) {
89 angleOfSlice = 0.;
90 } else {
91 angleOfSlice = pathLengthOfSlice/bendRadius_m;
92 }
93
94 // pathLengthOfSlice<0.0 is expected while the bunch straddles the bend
95 // entrance; angleOfSlice<0.0 is handled safely downstream.
96
97 if (angleOfSlice > 0.0 && angleOfSlice <= totalBendAngle_m){
98 calculateGreenFunction(bunch, meshSpacing);
99 }
100
101 // convolute with line density
102 calculateContributionInside(i, angleOfSlice, meshSpacing);
103 calculateContributionAfter(i, angleOfSlice, meshSpacing);
105 }
106
107 // calculate the wake field seen by the particles
108 for (unsigned int i = 0; i < bunch->getLocalNum(); ++i) {
109 const Vector_t &R = bunch->R[i];
110 unsigned int indexz = (unsigned int)floor((R(2) - meshOrigin) / meshSpacing);
111 double leverz = (R(2) - meshOrigin) / meshSpacing - indexz;
112 PAssert_LT(indexz + 1, numOfSlices);
113
114 bunch->Ef[i](2) += (1. - leverz) * Ez_m[indexz] + leverz * Ez_m[indexz + 1];
115 }
116
117 if (Options::csrDump) {
118 static std::string oldBendName;
119 static unsigned long counter = 0;
120
121 if (oldBendName != bendName_m) counter = 0;
122
123 const int every = 1;
124 bool print_criterion = (counter + 1) % every == 0;
125 if (print_criterion) {
126 static unsigned int file_number = 0;
127 if (counter == 0) file_number = 0;
128 double spos = bunch->get_sPos();
129 if (Ippl::myNode() == 0) {
130 std::stringstream filename_str;
131 filename_str << bendName_m << "-CSRWake" << std::setw(5) << std::setfill('0') << file_number << ".txt";
132
133 std::string fname = Util::combineFilePath({
135 filename_str.str()
136 });
137
138 std::ofstream csr(fname);
139 csr << spos << ", " << FieldBegin_m << ", " << smin(2) << ", " << smax(2) << ", " << meshSpacing*64 << std::endl;
140 for (unsigned int i = 0; i < lineDensity_m.size(); ++ i) {
141 csr << i *meshSpacing << "\t"
142 << Ez_m[i] << "\t"
143 << lineDensity_m[i] << std::endl;
144 }
145 csr.close();
146 msg << "** wrote " << fname << endl;
147 }
148 ++ file_number;
149 }
150 ++ counter;
151 oldBendName = bendName_m;
152 }
153}
154
156 if (ref->getType() == ElementType::RBEND ||
157 ref->getType() == ElementType::SBEND) {
158
159 const Bend2D *bend = static_cast<const Bend2D *>(ref);
160 double End;
161
162 bendRadius_m = bend->getBendRadius();
163 bend->getDimensions(Begin_m, End);
165 FieldBegin_m = bend->getEffectiveCenter() - Length_m / 2.0;
166 totalBendAngle_m = std::abs(bend->getBendAngle());
167 bendName_m = bend->getName();
168 }
169}
170
172 std::pair<double, double>& meshInfo) {
173 bunch->calcLineDensity(nBins_m, lineDensity_m, meshInfo);
174
175 // the following is only needed for after dipole
176 std::vector<Filter *>::const_iterator fit;
177 for (fit = filters_m.begin(); fit != filters_m.end(); ++ fit) {
178 (*fit)->apply(lineDensity_m);
179 }
180 dlineDensitydz_m.assign(lineDensity_m.begin(), lineDensity_m.end());
181 diffOp_m->calc_derivative(dlineDensitydz_m, meshInfo.second);
182}
183
185 double meshSpacing) {
186 unsigned int numOfSlices = lineDensity_m.size();
187 double gamma = bunch->get_meanKineticEnergy() / (bunch->getM() * Units::eV2MeV)+1.0;
188 double xmu_const = 3.0 * gamma * gamma * gamma / (2.0 * bendRadius_m);
189 double chi_const = 9.0 / 16.0 * (6.0 - std::log(27.0 / 4.0));
190
191 for (unsigned int i = 0; i < numOfSlices; ++i) {
192 Chi_m[i] = 0.0;
193 double z = i * meshSpacing;
194 double xmu = xmu_const * z;
195 double b = std::sqrt(xmu * xmu + 1.0) + xmu;
196 if (xmu < 1e-3)
197 Chi_m[i] = chi_const + 0.5 * std::pow(xmu, 2) - 7.0 / 54.0 * std::pow(xmu, 4) + 140.0 / 2187.0 * std::pow(xmu, 6);
198 else
199 Chi_m[i] = 9.0 / 16.0 * (3.0 * (-2.0 * xmu * std::pow(b, 1.0/3.0) + std::pow(b, 2.0/3.0) + std::pow(b, 4.0/3.0)) +
200 std::log(std::pow((1 - std::pow(b, 2.0 / 3.0)) / xmu, 2) / (1 + std::pow(b, 2.0 / 3.0) + std::pow(b, 4.0 / 3.0))));
201 }
202 double grn_const = -16.0/(27.0 * gamma * gamma * meshSpacing);
203 Grn_m[0] = grn_const * (Chi_m[1] - Chi_m[0]);
204 Grn_m[numOfSlices - 1] = 0.0;
205 for (unsigned int i = 1; i < numOfSlices - 1; ++i) {
206 Grn_m[i] = grn_const * (Chi_m[i + 1] - 2.0 * Chi_m[i] + Chi_m[i - 1]);
207 }
208}
209
211 double angleOfSlice,
212 double /*meshSpacing*/) {
213 if (bendRadius_m == 0.0 || angleOfSlice > totalBendAngle_m || angleOfSlice < 0.0) return;
214 int startSliceNum = 0;
215 for (int j = sliceNumber; j >= startSliceNum; j--)
216 Ez_m[sliceNumber] += lineDensity_m[j] * Grn_m[sliceNumber - j];
217}
218
220 double angleOfSlice,
221 double meshSpacing) {
222 if (angleOfSlice <= totalBendAngle_m) return;
223
224 double Ds_max = bendRadius_m * std::pow(totalBendAngle_m, 3) / 24. * (4. - 3.* totalBendAngle_m / angleOfSlice);
225
226 // First do contribution from particles whose retarded position is
227 // prior to the bend.
228 double Ds_max2 = bendRadius_m * std::pow(totalBendAngle_m, 2) / 6. * (3. * angleOfSlice - 2. * totalBendAngle_m);
229 int j = 0;
230 double frac = 0.0;
231 if (Ds_max2 / meshSpacing < sliceNumber) {
232 j = sliceNumber - static_cast<int>(std::floor(Ds_max2 / meshSpacing));
233 frac = Ds_max2 / meshSpacing - (sliceNumber - j);
234 Ez_m[sliceNumber] -= (frac * lineDensity_m[j - 1] + (1. - frac) * lineDensity_m[j]) / (2. * angleOfSlice - totalBendAngle_m);
235 }
236
237 // Now do delta function contribution for particles whose retarded position
238 // is in the bend.
239 if (Ds_max / meshSpacing < sliceNumber) {
240 j = sliceNumber - static_cast<int>(std::floor(Ds_max / meshSpacing));
241 frac = Ds_max / meshSpacing - (sliceNumber - j);
242 Ez_m[sliceNumber] += (frac * lineDensity_m[j - 1] + (1.0 - frac) * lineDensity_m[j]) / (2. * angleOfSlice - totalBendAngle_m);
243 }
244
245 // Now do integral contribution for particles whose retarded position is in
246 // the bend.
247
248 double angleOverlap = angleOfSlice - totalBendAngle_m;
249 int k = sliceNumber;
250 if (Ds_max / meshSpacing < sliceNumber) {
251 k = j;
252 Psi_m[k] = calcPsi(Psi_m[k], angleOverlap, meshSpacing * (k + frac));
253 if (Psi_m[k] > 0 && Psi_m[k] < totalBendAngle_m)
254 Ez_m[sliceNumber] += 0.5 * (frac * dlineDensitydz_m[sliceNumber - k - 1] + (1.0 - frac) * dlineDensitydz_m[sliceNumber - k]) / (Psi_m[k] + 2.0 * angleOverlap);
255 } else {
256 Psi_m[0] = calcPsi(Psi_m[0], angleOverlap, meshSpacing * sliceNumber);
257 if (Psi_m[0] > 0 && Psi_m[0] < totalBendAngle_m)
258 Ez_m[sliceNumber] += 0.5 * dlineDensitydz_m[0] / (Psi_m[0] + 2.0 * angleOverlap);
259 }
260
261 // Do rest of integral.
262 for (unsigned int l = sliceNumber - k + 1; l < sliceNumber; ++ l) {
263 Psi_m[l] = calcPsi(Psi_m[l], angleOverlap, meshSpacing * (sliceNumber - l));
264 if (Psi_m[l] > 0 && Psi_m[l] < totalBendAngle_m)
265 Ez_m[sliceNumber] += dlineDensitydz_m[l] / (Psi_m[l] + 2.0 * angleOverlap);
266 }
267
268 // We don't go right to the end as there is a singularity in the numerical integral that we don't quite know
269 // how to deal with properly yet. This introduces a very slight error in the calculation (fractions of a percent).
270 Psi_m[sliceNumber] = calcPsi(Psi_m[sliceNumber], angleOverlap, meshSpacing / 4.0);
271 if (Psi_m[sliceNumber] > 0 && Psi_m[sliceNumber] < totalBendAngle_m)
272 Ez_m[sliceNumber] += 0.5 * dlineDensitydz_m[sliceNumber] / (Psi_m[sliceNumber] + 2.0 * angleOverlap);
273
274 double prefactor = -4 / bendRadius_m;
275 Ez_m[sliceNumber] *= prefactor;
276}
277
278double CSRIGFWakeFunction::calcPsi(const double& psiInitial, const double& x, const double& Ds) const {
286 const int Nmax = 100;
287 const double eps = 1e-10;
288 double psi = std::pow(24. * Ds / bendRadius_m, 1. / 3.);
289 if (psiInitial != 0.0) psi = psiInitial;
290
291 for (int i = 0; i < Nmax; ++i) {
292 double residual = bendRadius_m * psi * psi * psi * (psi + 4. * x) - 24. * Ds * psi - 24. * Ds * x;
293 if (std::abs(residual) < eps)
294 return psi;
295 psi -= residual / (4. * bendRadius_m * psi * psi * psi + 12. * x * bendRadius_m * psi * psi - 24. * Ds);
296 }
297 RootFinderForCSR rootFinder(bendRadius_m, 4 * x * bendRadius_m, -24 * Ds, -24 * Ds * x);
298 if (rootFinder.hasPositiveRealRoots()) {
299 return rootFinder.searchRoot(eps);
300 }
301
302 ERRORMSG("In CSRWakeFunction::calcPsi(): exceed maximum number of iterations!" << endl);
303 return psi;
304}
305
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
PartBunchBase< T, Dim >::ConstIterator begin(PartBunchBase< T, Dim > const &bunch)
WakeType
@ CSRIGFWakeFunction
#define PAssert_LT(a, b)
Definition PAssert.h:106
#define ERRORMSG(msg)
Definition IpplInfo.h:350
Inform & endl(Inform &inf)
Definition Inform.cpp:42
PETE_TUTree< FnFloor, typename T::PETE_Expr_t > floor(const PETE_Expr< T > &l)
Definition PETE.h:733
const std::string name
constexpr double epsilon_0
The permittivity of vacuum in As/Vm.
Definition Physics.h:51
constexpr double pi
The value of.
Definition Physics.h:30
constexpr double eV2MeV
Definition Units.h:77
bool csrDump
Definition Options.cpp:65
std::string combineFilePath(std::initializer_list< std::string > ilist)
Definition Util.cpp:204
ParticleAttrib< Vector_t > Ef
double get_meanKineticEnergy() const
ParticlePos_t & R
void get_bounds(Vector_t &rmin, Vector_t &rmax) const
size_t getLocalNum() const
void calcLineDensity(unsigned int nBins, std::vector< double > &lineDensity, std::pair< double, double > &meshInfo)
calculates the 1d line density (not normalized) and append it to a file.
double get_sPos() const
double getM() const
static OpalData * getInstance()
Definition OpalData.cpp:196
std::string getAuxiliaryOutputDirectory() const
get the name of the the additional data directory
Definition OpalData.cpp:666
virtual void getDimensions(double &sBegin, double &sEnd) const override
Definition Bend2D.h:284
double getEffectiveLength() const
Definition Bend2D.h:300
double getEffectiveCenter() const
Definition Bend2D.h:295
double getBendRadius() const
Definition Bend2D.h:290
double getBendAngle() const
Definition BendBase.h:93
virtual const std::string & getName() const
Get element name.
virtual void calc_derivative(std::vector< double > &histogram, const double &h)=0
void calculateGreenFunction(PartBunchBase< double, 3 > *bunch, double meshSpacing)
void apply(PartBunchBase< double, 3 > *bunch) override
void calculateLineDensity(PartBunchBase< double, 3 > *bunch, std::pair< double, double > &meshInfo)
std::vector< double > Chi_m
std::vector< double > Grn_m
CSRIGFWakeFunction(const std::string &name, std::vector< Filter * > filters, const unsigned int &N)
void initialize(const ElementBase *ref) override
std::vector< double > Psi_m
std::vector< double > Ez_m
void calculateContributionInside(std::size_t sliceNumber, double angleOfSlice, double meshSpacing)
std::shared_ptr< Filter > defaultFilter_m
void calculateContributionAfter(std::size_t sliceNumber, double angleOfSlice, double meshSpacing)
std::vector< Filter * > filters_m
double calcPsi(const double &psiInitial, const double &x, const double &Ds) const
virtual WakeType getType() const override
double searchRoot(const double &tol)
const unsigned int nBins_m
static int myNode()
Definition IpplInfo.cpp:691