OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
CSRWakeFunction.cpp
Go to the documentation of this file.
1//
2// Class CSRWakeFunction
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"
29#include "Utilities/Options.h"
30#include "Utilities/Util.h"
31
32#include "Utility/Inform.h"
33#include "Utility/IpplInfo.h"
34#include "Utility/PAssert.h"
35
36#include <cmath>
37#include <fstream>
38#include <iomanip>
39#include <sstream>
40
42 std::vector<Filter*> filters,
43 const unsigned int& N):
45 filters_m(filters.begin(), filters.end()),
46 lineDensity_m(),
47 dlineDensitydz_m(),
48 bendRadius_m(0.0),
49 totalBendAngle_m(0.0)
50{
51 if (filters_m.size() == 0) {
52 defaultFilter_m.reset(new SavitzkyGolayFilter(7, 3, 3, 3));
53 filters_m.push_back(defaultFilter_m.get());
54 }
55
56 diffOp_m = filters_m.back();
57}
58
60 Inform msg("CSRWake ");
61
62 const double sPos = bunch->get_sPos();
63 std::pair<double, double> meshInfo;
64 calculateLineDensity(bunch, meshInfo);
65 const double &meshSpacing = meshInfo.second;
66 const double &meshOrigin = meshInfo.first + 0.5 * meshSpacing;
67
68 if (Ez_m.size() < lineDensity_m.size()) {
69 Ez_m.resize(lineDensity_m.size(), 0.0);
70 Psi_m.resize(lineDensity_m.size(), 0.0);
71 }
72
73 Vector_t smin, smax;
74 bunch->get_bounds(smin, smax);
75 double minPathLength = smin(2) + sPos - FieldBegin_m;
76 if (sPos + smax(2) < FieldBegin_m) return;
77
78 Ez_m[0] = 0.0;
79 // calculate wake field of bunch
80 for (unsigned int i = 1; i < lineDensity_m.size(); ++i) {
81 Ez_m[i] = 0.0;
82
83 double angleOfSlice = 0.0;
84 double pathLengthOfSlice = minPathLength + i * meshSpacing;
85
86 /*
87 bendRadius_m==0.0 can happen if we just go out into a drift
88 */
89 if (bendRadius_m==0.0) {
90 angleOfSlice = 0.;
91 } else {
92 angleOfSlice = pathLengthOfSlice/bendRadius_m;
93 }
94
95 // pathLengthOfSlice<0.0 is expected while the bunch straddles the bend
96 // entrance; angleOfSlice<0.0 is handled safely downstream.
97
98 calculateContributionInside(i, angleOfSlice, meshSpacing);
99 calculateContributionAfter(i, angleOfSlice, meshSpacing);
100 Ez_m[i] /= (4. * Physics::pi * Physics::epsilon_0);
101 }
102
103 // calculate the wake field seen by the particles
104 for (unsigned int i = 0; i < bunch->getLocalNum(); ++i) {
105 const Vector_t &R = bunch->R[i];
106 double distanceToOrigin = (R(2) - meshOrigin) / meshSpacing;
107
108 unsigned int indexz = (unsigned int)floor(distanceToOrigin);
109 double leverz = distanceToOrigin - indexz;
110 PAssert_LT(indexz, lineDensity_m.size() - 1);
111
112 bunch->Ef[i](2) += (1. - leverz) * Ez_m[indexz] + leverz * Ez_m[indexz + 1];
113 }
114
115 if (Options::csrDump) {
116 static std::string oldBendName;
117 static unsigned long counter = 0;
118
119 if (oldBendName != bendName_m) counter = 0;
120
121 const int every = 1;
122 bool print_criterion = (counter + 1) % every == 0;
123 if (print_criterion) {
124 static unsigned int file_number = 0;
125 if (counter == 0) file_number = 0;
126 if (Ippl::myNode() == 0) {
127 std::stringstream filename_str;
128 filename_str << bendName_m << "-CSRWake" << std::setw(5) << std::setfill('0') << file_number << ".txt";
129 std::string fname = Util::combineFilePath({
131 filename_str.str()
132 });
133
134 std::ofstream csr(fname);
135 csr << std::setprecision(8);
136 csr << "# " << sPos + smin(2) - FieldBegin_m << "\t" << sPos + smax(2) - FieldBegin_m << std::endl;
137 for (unsigned int i = 0; i < lineDensity_m.size(); ++ i) {
138 csr << i *meshSpacing << "\t"
139 << Ez_m[i] << "\t"
140 << lineDensity_m[i] << "\t"
141 << dlineDensitydz_m[i] << std::endl;
142 }
143 csr.close();
144 msg << "** wrote " << fname << endl;
145 }
146 ++ file_number;
147 }
148 ++ counter;
149 oldBendName = bendName_m;
150 }
151}
152
154 if (ref->getType() == ElementType::RBEND ||
155 ref->getType() == ElementType::SBEND) {
156
157 const Bend2D *bend = static_cast<const Bend2D *>(ref);
158 double End;
159
160 bendRadius_m = bend->getBendRadius();
161 bend->getDimensions(Begin_m, End);
163 FieldBegin_m = bend->getEffectiveCenter() - Length_m / 2.0;
164 totalBendAngle_m = std::abs(bend->getBendAngle());
165 bendName_m = bend->getName();
166 }
167}
168
170 std::pair<double, double>& meshInfo) {
171 bunch->calcLineDensity(nBins_m, lineDensity_m, meshInfo);
172
173 std::vector<Filter *>::const_iterator fit;
174 for (fit = filters_m.begin(); fit != filters_m.end(); ++ fit) {
175 (*fit)->apply(lineDensity_m);
176 }
177
178 dlineDensitydz_m.assign(lineDensity_m.begin(), lineDensity_m.end());
179 diffOp_m->calc_derivative(dlineDensitydz_m, meshInfo.second);
180}
181
182void CSRWakeFunction::calculateContributionInside(std::size_t sliceNumber, double angleOfSlice, double meshSpacing) {
183 if (bendRadius_m == 0.0 || angleOfSlice > totalBendAngle_m || angleOfSlice < 0.0) return;
184
185 const double meshSpacingsup = std::pow(meshSpacing, -1. / 3.);
186 double SlippageLength = std::pow(angleOfSlice, 3) * bendRadius_m / 24.;
187 double relativeSlippageLength = SlippageLength / meshSpacing;
188 if (relativeSlippageLength > sliceNumber) {
189
190 /*
191 Break integral into sum of integrals between grid points, then
192 use linear interpolation between each grid point.
193 */
194
195 double dx1 = std::pow(sliceNumber, 2. / 3.);
196 double dx2 = std::pow(sliceNumber, 5. / 3.);
197 double dx3 = std::pow(sliceNumber - 1., 5. / 3.);
198 Ez_m[sliceNumber] += 0.3 * meshSpacingsup * dlineDensitydz_m[0] * (5. * dx1 - 3. * dx2 + 3. * dx3);
199 for (unsigned int j = 1; j < sliceNumber; ++ j) {
200 dx1 = dx2;
201 dx2 = dx3;
202 dx3 = std::pow(sliceNumber - j - 1., 5. / 3.);
203 Ez_m[sliceNumber] += 0.9 * meshSpacingsup * dlineDensitydz_m[j] * (dx1 - 2.* dx2 + dx3);
204 }
205 Ez_m[sliceNumber] += 0.9 * meshSpacingsup * dlineDensitydz_m[sliceNumber];
206
207 } else if (relativeSlippageLength < 1) {
208
209 // First do transient term.
210 if (4.0 * relativeSlippageLength <= 1) {
211
212 Ez_m[sliceNumber] += 3.0 * std::pow(SlippageLength, 2.0 / 3.0) * (lineDensity_m[sliceNumber] - lineDensity_m[sliceNumber - 1]) / meshSpacing;
213 } else {
214
215 if (4.0 * relativeSlippageLength < sliceNumber) {
216
217 int j = sliceNumber - static_cast<int>(std::floor(4.0 * relativeSlippageLength));
218 double frac = 4.0 * relativeSlippageLength - (sliceNumber - j);
219 Ez_m[sliceNumber] -= (frac * lineDensity_m[j - 1] + (1. - frac) * lineDensity_m[j]) / std::pow(SlippageLength, 1. / 3.);
220
221 }
222
223 Ez_m[sliceNumber] += (relativeSlippageLength * lineDensity_m[sliceNumber - 1] + (1. - relativeSlippageLength) * lineDensity_m[sliceNumber]) / std::pow(SlippageLength, 1. / 3.);
224
225 }
226
227 // Now do steady state term.
228 Ez_m[sliceNumber] += (0.3 / meshSpacing) * std::pow(SlippageLength, 2. / 3.) * (5. * dlineDensitydz_m[sliceNumber] - 2. * relativeSlippageLength * (dlineDensitydz_m[sliceNumber] - dlineDensitydz_m[sliceNumber - 1]));
229
230 } else {
231
232 if (4. * relativeSlippageLength < sliceNumber) {
233
234 int j = sliceNumber - static_cast<int>(std::floor(4. * relativeSlippageLength));
235 double frac = 4. * relativeSlippageLength - (sliceNumber - j);
236 Ez_m[sliceNumber] -= (frac * lineDensity_m[j - 1] + (1. - frac) * lineDensity_m[j]) / std::pow(SlippageLength, 1. / 3.);
237
238 }
239
240 int j = sliceNumber - static_cast<int>(std::floor(SlippageLength / meshSpacing));
241 double frac = relativeSlippageLength - (sliceNumber - j);
242 Ez_m[sliceNumber] += (frac * lineDensity_m[j - 1] + (1. - frac) * lineDensity_m[j]) / std::pow(SlippageLength, 1. / 3.);
243
244 double dx1 = std::pow(sliceNumber - j + frac, 2. / 3.);
245 double dx2 = std::pow(sliceNumber - j, 2. / 3.);
246 double dx3 = std::pow(sliceNumber - j + frac, 5. / 3.);
247 double dx4 = std::pow(sliceNumber - j, 5. / 3.);
248
249 Ez_m[sliceNumber] += 1.5 * meshSpacingsup * dlineDensitydz_m[j - 1] * (dx1 - dx2);
250 Ez_m[sliceNumber] += 0.3 * meshSpacingsup * (dlineDensitydz_m[j] - dlineDensitydz_m[j - 1]) * (5.*(dx1 - dx2) + 3.*(dx3 - dx4));
251
252 dx1 = dx2;
253 dx2 = dx4;
254 dx3 = std::pow(sliceNumber - j - 1., 5. / 3.);
255 Ez_m[sliceNumber] += 0.3 * meshSpacingsup * dlineDensitydz_m[j] * (5.*dx1 - 3.*dx2 + 3.*dx3);
256 for (unsigned int k = j + 1; k < sliceNumber; ++ k) {
257 dx1 = dx2;
258 dx2 = dx3;
259 dx3 = std::pow(sliceNumber - k - 1., 5. / 3.);
260 Ez_m[sliceNumber] += 0.9 * meshSpacingsup * dlineDensitydz_m[k] * (dx1 - 2.*dx2 + dx3);
261 }
262 Ez_m[sliceNumber] += 0.9 * meshSpacingsup * dlineDensitydz_m[sliceNumber];
263 }
264 double prefactor = -2. / std::pow(3. * bendRadius_m * bendRadius_m, 1. / 3.);
265 Ez_m[sliceNumber] *= prefactor;
266}
267
268void CSRWakeFunction::calculateContributionAfter(std::size_t sliceNumber, double angleOfSlice, double meshSpacing) {
269 if (angleOfSlice <= totalBendAngle_m) return;
270
271 double Ds_max = bendRadius_m * std::pow(totalBendAngle_m, 3) / 24. * (4. - 3.* totalBendAngle_m / angleOfSlice);
272
273 // First do contribution from particles whose retarded position is
274 // prior to the bend.
275 double Ds_max2 = bendRadius_m * std::pow(totalBendAngle_m, 2) / 6. * (3. * angleOfSlice - 2. * totalBendAngle_m);
276 int j = 0;
277 double frac = 0.0;
278 if (Ds_max2 / meshSpacing < sliceNumber) {
279 j = sliceNumber - static_cast<int>(floor(Ds_max2 / meshSpacing));
280 frac = Ds_max2 / meshSpacing - (sliceNumber - j);
281 Ez_m[sliceNumber] -= (frac * lineDensity_m[j - 1] + (1. - frac) * lineDensity_m[j]) / (2. * angleOfSlice - totalBendAngle_m);
282 }
283
284 // Now do delta function contribution for particles whose retarded position
285 // is in the bend.
286 if (Ds_max / meshSpacing < sliceNumber) {
287 j = sliceNumber - static_cast<int>(floor(Ds_max / meshSpacing));
288 frac = Ds_max / meshSpacing - (sliceNumber - j);
289 Ez_m[sliceNumber] += (frac * lineDensity_m[j - 1] + (1.0 - frac) * lineDensity_m[j]) / (2. * angleOfSlice - totalBendAngle_m);
290 }
291
292 // Now do integral contribution for particles whose retarded position is in
293 // the bend.
294
295 double angleOverlap = angleOfSlice - totalBendAngle_m;
296 int k = sliceNumber;
297 if (Ds_max / meshSpacing < sliceNumber) {
298 k = j;
299 Psi_m[k] = calcPsi(Psi_m[k], angleOverlap, meshSpacing * (k + frac));
300 if (Psi_m[k] > 0 && Psi_m[k] < totalBendAngle_m)
301 Ez_m[sliceNumber] += 0.5 * (frac * dlineDensitydz_m[sliceNumber - k - 1] + (1.0 - frac) * dlineDensitydz_m[sliceNumber - k]) / (Psi_m[k] + 2.0 * angleOverlap);
302 } else {
303 Psi_m[0] = calcPsi(Psi_m[0], angleOverlap, meshSpacing * sliceNumber);
304 if (Psi_m[0] > 0 && Psi_m[0] < totalBendAngle_m)
305 Ez_m[sliceNumber] += 0.5 * dlineDensitydz_m[0] / (Psi_m[0] + 2.0 * angleOverlap);
306 }
307
308 // Do rest of integral.
309 for (unsigned int l = sliceNumber - k + 1; l < sliceNumber; ++ l) {
310 Psi_m[l] = calcPsi(Psi_m[l], angleOverlap, meshSpacing * (sliceNumber - l));
311 if (Psi_m[l] > 0 && Psi_m[l] < totalBendAngle_m)
312 Ez_m[sliceNumber] += dlineDensitydz_m[l] / (Psi_m[l] + 2.0 * angleOverlap);
313 }
314
315 // We don't go right to the end as there is a singularity in the numerical integral that we don't quite know
316 // how to deal with properly yet. This introduces a very slight error in the calculation (fractions of a percent).
317 Psi_m[sliceNumber] = calcPsi(Psi_m[sliceNumber], angleOverlap, meshSpacing / 4.0);
318 if (Psi_m[sliceNumber] > 0 && Psi_m[sliceNumber] < totalBendAngle_m)
319 Ez_m[sliceNumber] += 0.5 * dlineDensitydz_m[sliceNumber] / (Psi_m[sliceNumber] + 2.0 * angleOverlap);
320
321 double prefactor = -4 / bendRadius_m;
322 Ez_m[sliceNumber] *= prefactor;
323}
324
325double CSRWakeFunction::calcPsi(const double& psiInitial, const double& x, const double& Ds) const {
333 const int Nmax = 100;
334 const double eps = 1e-10;
335
336 double psi = std::pow(24. * Ds / bendRadius_m, 1. / 3.);
337 if (psiInitial != 0.0) psi = psiInitial;
338
339 for (int i = 0; i < Nmax; ++i) {
340 double residual = bendRadius_m * psi * psi * psi * (psi + 4. * x) - 24. * Ds * psi - 24. * Ds * x;
341 if (std::abs(residual) < eps)
342 return psi;
343
344 psi -= residual / (4. * bendRadius_m * psi * psi * psi + 12. * x * bendRadius_m * psi * psi - 24. * Ds);
345 }
346
347 RootFinderForCSR rootFinder(bendRadius_m, 4 * x * bendRadius_m, -24 * Ds, -24 * Ds * x);
348 if (rootFinder.hasPositiveRealRoots()) {
349 return rootFinder.searchRoot(eps);
350 }
351
352 ERRORMSG("In CSRWakeFunction::calcPsi(): exceed maximum number of iterations!" << endl);
353 return psi;
354}
355
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
PartBunchBase< T, Dim >::ConstIterator begin(PartBunchBase< T, Dim > const &bunch)
WakeType
@ CSRWakeFunction
#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
bool csrDump
Definition Options.cpp:65
std::string combineFilePath(std::initializer_list< std::string > ilist)
Definition Util.cpp:204
ParticleAttrib< Vector_t > Ef
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
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 calculateLineDensity(PartBunchBase< double, 3 > *bunch, std::pair< double, double > &meshInfo)
std::vector< Filter * > filters_m
std::string bendName_m
LineDensity dlineDensitydz_m
double calcPsi(const double &psiInitial, const double &x, const double &Ds) const
void apply(PartBunchBase< double, 3 > *bunch) override
void calculateContributionAfter(std::size_t sliceNumber, double angleOfSlice, double meshSpacing)
std::vector< double > Ez_m
std::vector< double > Psi_m
CSRWakeFunction(const std::string &name, std::vector< Filter * > filters, const unsigned int &N)
virtual WakeType getType() const override
LineDensity lineDensity_m
std::shared_ptr< Filter > defaultFilter_m
void initialize(const ElementBase *ref) override
void calculateContributionInside(std::size_t sliceNumber, double angleOfSlice, double meshSpacing)
double searchRoot(const double &tol)
const unsigned int nBins_m
static int myNode()
Definition IpplInfo.cpp:691