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