OPAL (Object Oriented Parallel Accelerator Library)  2024.1
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/RBend.h"
20 #include "AbsBeamline/SBend.h"
23 #include "Filters/Filter.h"
24 #include "Filters/SavitzkyGolay.h"
25 #include "Physics/Physics.h"
26 #include "Physics/Units.h"
28 #include "Utilities/Options.h"
29 #include "Utilities/Util.h"
30 
31 #include <cmath>
32 #include <fstream>
33 #include <iostream>
34 
35 CSRIGFWakeFunction::CSRIGFWakeFunction(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  std::pair<double, double> meshInfo;
55  calculateLineDensity(bunch, meshInfo);
56  const double &meshOrigin = meshInfo.first;
57  const double &meshSpacing = meshInfo.second;
58  const unsigned int numOfSlices = lineDensity_m.size();
59 
60  if (Ez_m.size() < numOfSlices) {
61  Ez_m.resize(numOfSlices, 0.0);
62  Chi_m.resize(numOfSlices, 0.0);
63  Grn_m.resize(numOfSlices, 0.0);
64  Psi_m.resize(numOfSlices, 0.0);
65  }
66 
67  for (unsigned int i = 0; i < numOfSlices; ++i) {
68  Ez_m[i] = 0.0;
69  }
70 
71  Vector_t smin, smax;
72  bunch->get_bounds(smin, smax);
73  double minPathLength = smin(2) + bunch->get_sPos() - FieldBegin_m;
74  for (unsigned int i = 1; i < numOfSlices; i++) {
75  double pathLengthOfSlice = minPathLength + i * meshSpacing;
76  double angleOfSlice = pathLengthOfSlice/bendRadius_m;
77  if (angleOfSlice > 0.0 && angleOfSlice <= totalBendAngle_m){
78  calculateGreenFunction(bunch, meshSpacing);
79  }
80  // convolute with line density
81  calculateContributionInside(i, angleOfSlice, meshSpacing);
82  calculateContributionAfter(i, angleOfSlice, meshSpacing);
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  unsigned int indexz = (unsigned int)floor((R(2) - meshOrigin) / meshSpacing);
90  double leverz = (R(2) - meshOrigin) / meshSpacing - indexz;
91  PAssert_LT(indexz + 1, numOfSlices);
92 
93  bunch->Ef[i](2) += (1. - leverz) * Ez_m[indexz] + leverz * Ez_m[indexz + 1];
94  }
95 
96  if (Options::csrDump) {
97  static std::string oldBendName;
98  static unsigned long counter = 0;
99 
100  if (oldBendName != bendName_m) counter = 0;
101 
102  const int every = 1;
103  bool print_criterion = (counter + 1) % every == 0;
104  if (print_criterion) {
105  static unsigned int file_number = 0;
106  if (counter == 0) file_number = 0;
107  double spos = bunch->get_sPos();
108  if (Ippl::myNode() == 0) {
109  std::stringstream filename_str;
110  filename_str << bendName_m << "-CSRWake" << std::setw(5) << std::setfill('0') << file_number << ".txt";
111 
112  std::string fname = Util::combineFilePath({
114  filename_str.str()
115  });
116 
117  std::ofstream csr(fname);
118  csr << spos << ", " << FieldBegin_m << ", " << smin(2) << ", " << smax(2) << ", " << meshSpacing*64 << std::endl;
119  for (unsigned int i = 0; i < lineDensity_m.size(); ++ i) {
120  csr << i *meshSpacing << "\t"
121  << Ez_m[i] << "\t"
122  << lineDensity_m[i] << std::endl;
123  }
124  csr.close();
125  msg << "** wrote " << fname << endl;
126  }
127  ++ file_number;
128  }
129  ++ counter;
130  oldBendName = bendName_m;
131  }
132 }
133 
135  if (ref->getType() == ElementType::RBEND ||
136  ref->getType() == ElementType::SBEND) {
137 
138  const Bend2D *bend = static_cast<const Bend2D *>(ref);
139  double End;
140 
141  bendRadius_m = bend->getBendRadius();
142  bend->getDimensions(Begin_m, End);
143  Length_m = bend->getEffectiveLength();
144  FieldBegin_m = bend->getEffectiveCenter() - Length_m / 2.0;
146  bendName_m = bend->getName();
147  }
148 }
149 
151  std::pair<double, double>& meshInfo) {
152  bunch->calcLineDensity(nBins_m, lineDensity_m, meshInfo);
153 
154  // the following is only needed for after dipole
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  dlineDensitydz_m.assign(lineDensity_m.begin(), lineDensity_m.end());
160  diffOp_m->calc_derivative(dlineDensitydz_m, meshInfo.second);
161 }
162 
164  double meshSpacing) {
165  unsigned int numOfSlices = lineDensity_m.size();
166  double gamma = bunch->get_meanKineticEnergy() / (bunch->getM() * Units::eV2MeV)+1.0;
167  double xmu_const = 3.0 * gamma * gamma * gamma / (2.0 * bendRadius_m);
168  double chi_const = 9.0 / 16.0 * (6.0 - std::log(27.0 / 4.0));
169 
170  for (unsigned int i = 0; i < numOfSlices; ++i) {
171  Chi_m[i] = 0.0;
172  double z = i * meshSpacing;
173  double xmu = xmu_const * z;
174  double b = std::sqrt(xmu * xmu + 1.0) + xmu;
175  if (xmu < 1e-3)
176  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);
177  else
178  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)) +
179  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))));
180  }
181  double grn_const = -16.0/(27.0 * gamma * gamma * meshSpacing);
182  Grn_m[0] = grn_const * (Chi_m[1] - Chi_m[0]);
183  Grn_m[numOfSlices - 1] = 0.0;
184  for (unsigned int i = 1; i < numOfSlices - 1; ++i) {
185  Grn_m[i] = grn_const * (Chi_m[i + 1] - 2.0 * Chi_m[i] + Chi_m[i - 1]);
186  }
187 }
188 
190  double angleOfSlice,
191  double /*meshSpacing*/) {
192  if (angleOfSlice > totalBendAngle_m || angleOfSlice < 0.0) return;
193  int startSliceNum = 0;
194  for (int j = sliceNumber; j >= startSliceNum; j--)
195  Ez_m[sliceNumber] += lineDensity_m[j] * Grn_m[sliceNumber - j];
196 }
197 
199  double angleOfSlice,
200  double meshSpacing) {
201  if (angleOfSlice <= totalBendAngle_m) return;
202 
203  double Ds_max = bendRadius_m * std::pow(totalBendAngle_m, 3) / 24. * (4. - 3.* totalBendAngle_m / angleOfSlice);
204 
205  // First do contribution from particles whose retarded position is
206  // prior to the bend.
207  double Ds_max2 = bendRadius_m * std::pow(totalBendAngle_m, 2) / 6. * (3. * angleOfSlice - 2. * totalBendAngle_m);
208  int j = 0;
209  double frac = 0.0;
210  if (Ds_max2 / meshSpacing < sliceNumber) {
211  j = sliceNumber - static_cast<int>(std::floor(Ds_max2 / meshSpacing));
212  frac = Ds_max2 / meshSpacing - (sliceNumber - j);
213  Ez_m[sliceNumber] -= (frac * lineDensity_m[j - 1] + (1. - frac) * lineDensity_m[j]) / (2. * angleOfSlice - totalBendAngle_m);
214  }
215 
216  // Now do delta function contribution for particles whose retarded position
217  // is in the bend.
218  if (Ds_max / meshSpacing < sliceNumber) {
219  j = sliceNumber - static_cast<int>(std::floor(Ds_max / meshSpacing));
220  frac = Ds_max / meshSpacing - (sliceNumber - j);
221  Ez_m[sliceNumber] += (frac * lineDensity_m[j - 1] + (1.0 - frac) * lineDensity_m[j]) / (2. * angleOfSlice - totalBendAngle_m);
222  }
223 
224  // Now do integral contribution for particles whose retarded position is in
225  // the bend.
226 
227  double angleOverlap = angleOfSlice - totalBendAngle_m;
228  int k = sliceNumber;
229  if (Ds_max / meshSpacing < sliceNumber) {
230  k = j;
231  Psi_m[k] = calcPsi(Psi_m[k], angleOverlap, meshSpacing * (k + frac));
232  if (Psi_m[k] > 0 && Psi_m[k] < totalBendAngle_m)
233  Ez_m[sliceNumber] += 0.5 * (frac * dlineDensitydz_m[sliceNumber - k - 1] + (1.0 - frac) * dlineDensitydz_m[sliceNumber - k]) / (Psi_m[k] + 2.0 * angleOverlap);
234  } else {
235  Psi_m[0] = calcPsi(Psi_m[0], angleOverlap, meshSpacing * sliceNumber);
236  if (Psi_m[0] > 0 && Psi_m[0] < totalBendAngle_m)
237  Ez_m[sliceNumber] += 0.5 * dlineDensitydz_m[0] / (Psi_m[0] + 2.0 * angleOverlap);
238  }
239 
240  // Do rest of integral.
241  for (unsigned int l = sliceNumber - k + 1; l < sliceNumber; ++ l) {
242  Psi_m[l] = calcPsi(Psi_m[l], angleOverlap, meshSpacing * (sliceNumber - l));
243  if (Psi_m[l] > 0 && Psi_m[l] < totalBendAngle_m)
244  Ez_m[sliceNumber] += dlineDensitydz_m[l] / (Psi_m[l] + 2.0 * angleOverlap);
245  }
246 
247  // We don't go right to the end as there is a singularity in the numerical integral that we don't quite know
248  // how to deal with properly yet. This introduces a very slight error in the calculation (fractions of a percent).
249  Psi_m[sliceNumber] = calcPsi(Psi_m[sliceNumber], angleOverlap, meshSpacing / 4.0);
250  if (Psi_m[sliceNumber] > 0 && Psi_m[sliceNumber] < totalBendAngle_m)
251  Ez_m[sliceNumber] += 0.5 * dlineDensitydz_m[sliceNumber] / (Psi_m[sliceNumber] + 2.0 * angleOverlap);
252 
253  double prefactor = -4 / bendRadius_m;
254  Ez_m[sliceNumber] *= prefactor;
255 }
256 
257 double CSRIGFWakeFunction::calcPsi(const double& psiInitial, const double& x, const double& Ds) const {
265  const int Nmax = 100;
266  const double eps = 1e-10;
267  double psi = std::pow(24. * Ds / bendRadius_m, 1. / 3.);
268  if (psiInitial != 0.0) psi = psiInitial;
269 
270  for (int i = 0; i < Nmax; ++i) {
271  double residual = bendRadius_m * psi * psi * psi * (psi + 4. * x) - 24. * Ds * psi - 24. * Ds * x;
272  if (std::abs(residual) < eps)
273  return psi;
274  psi -= residual / (4. * bendRadius_m * psi * psi * psi + 12. * x * bendRadius_m * psi * psi - 24. * Ds);
275  }
276  RootFinderForCSR rootFinder(bendRadius_m, 4 * x * bendRadius_m, -24 * Ds, -24 * Ds * x);
277  if (rootFinder.hasPositiveRealRoots()) {
278  return rootFinder.searchRoot(eps);
279  }
280 
281  ERRORMSG("In CSRWakeFunction::calcPsi(): exceed maximum number of iterations!" << endl);
282  return psi;
283 }
284 
287 }
const unsigned int nBins_m
Definition: WakeFunction.h:52
static OpalData * getInstance()
Definition: OpalData.cpp:196
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
CSRIGFWakeFunction(const std::string &name, std::vector< Filter * > filters, const unsigned int &N)
void calculateContributionInside(size_t sliceNumber, double angleOfSlice, double meshSpacing)
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
constexpr double epsilon_0
The permittivity of vacuum in As/Vm.
Definition: Physics.h:51
static int myNode()
Definition: IpplInfo.cpp:691
void apply(PartBunchBase< double, 3 > *bunch) override
std::vector< double > Chi_m
Definition: Bend2D.h:51
ParticleAttrib< Vector_t > Ef
#define ERRORMSG(msg)
Definition: IpplInfo.h:350
void get_bounds(Vector_t &rmin, Vector_t &rmax) const
void calculateContributionAfter(size_t sliceNumber, double angleOfSlice, double meshSpacing)
double get_sPos() const
double searchRoot(const double &tol)
virtual const std::string & getName() const
Get element name.
clearpage the user may choose between constant or variable radius This model includes fringe fields begin
Definition: multipole_t.tex:6
constexpr double pi
The value of .
Definition: Physics.h:30
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
std::vector< double > Ez_m
double getM() const
double getBendAngle() const
Definition: BendBase.h:92
#define PAssert_LT(a, b)
Definition: PAssert.h:106
bool csrDump
Definition: Options.cpp:67
std::string getAuxiliaryOutputDirectory() const
get the name of the the additional data directory
Definition: OpalData.cpp:666
double getEffectiveCenter() const
Definition: Bend2D.h:295
size_t getLocalNum() const
double calcPsi(const double &psiInitial, const double &x, const double &Ds) const
Definition: Inform.h:42
std::vector< double > Psi_m
virtual ElementType getType() const =0
Get element type std::string.
std::shared_ptr< Filter > defaultFilter_m
LineDensity dlineDensitydz_m
Tps< T > log(const Tps< T > &x)
Natural logarithm.
Definition: TpsMath.h:182
const std::string name
ParticlePos_t & R
std::string combineFilePath(std::initializer_list< std::string > ilist)
Definition: Util.cpp:197
WakeType
Definition: WakeFunction.h:28
virtual void calc_derivative(std::vector< double > &histogram, const double &h)=0
double getBendRadius() const
Definition: Bend2D.h:290
virtual void getDimensions(double &sBegin, double &sEnd) const override
Definition: Bend2D.h:284
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.
constexpr double e
The value of .
Definition: Physics.h:39
std::vector< Filter * > filters_m
double get_meanKineticEnergy() const
void calculateLineDensity(PartBunchBase< double, 3 > *bunch, std::pair< double, double > &meshInfo)
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
PETE_TUTree< FnFloor, typename T::PETE_Expr_t > floor(const PETE_Expr< T > &l)
Definition: PETE.h:733
std::vector< double > Grn_m
void calculateGreenFunction(PartBunchBase< double, 3 > *bunch, double meshSpacing)
end
Definition: multipole_t.tex:9
virtual WakeType getType() const override
constexpr double eV2MeV
Definition: Units.h:77
double getEffectiveLength() const
Definition: Bend2D.h:300
void initialize(const ElementBase *ref) override