OPAL (Object Oriented Parallel Accelerator Library)  2021.1.99
OPAL
GreenWakeFunction.cpp
Go to the documentation of this file.
1 //
2 // Class GreenWakeFunction
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 //
20 
21 #include <fstream>
22 #include <string>
23 #include <vector>
24 #include <istream>
25 #include <iostream> // Needed for stream I/O
26 #include <iomanip> // Needed for I/O manipulators
27 #include "gsl/gsl_fft_real.h"
28 #include "gsl/gsl_fft_halfcomplex.h"
29 
30 
31 //IFF: TEST
32 //#define ENABLE_WAKE_DEBUG
33 //#define ENABLE_WAKE_DUMP
34 //#define ENABLE_WAKE_TESTS_FFT_OUT
35 
36 
53  std::vector<Filter *> filters,
54  int NBIN,
55  double Z0,
56  double radius,
57  double sigma,
58  int acMode,
59  double tau,
60  int direction,
61  bool constLength,
62  std::string fname):
64  lineDensity_m(),
65  //~ FftWField_m(0),
66  NBin_m(NBIN),
67  Z0_m(Z0),
68  radius_m(radius),
69  sigma_m(sigma),
70  acMode_m(acMode),
71  tau_m(tau),
72  direction_m(direction),
73  constLength_m(constLength),
74  filename_m(fname),
75  filters_m(filters.begin(), filters.end()) {
76 #ifdef ENABLE_WAKE_DEBUG
77  *gmsg << "* ************* W A K E ************************************************************ " << endl;
78  *gmsg << "* Entered GreenWakeFunction::GreenWakeFunction " << '\n';
79  *gmsg << "* ********************************************************************************** " << endl;
80 #endif
81 }
82 
84  //~ if(FftWField_m != 0) {
85  //~ delete[] FftWField_m;
86  //~ }
87 }
88 
89 
100 std::pair<int, int> GreenWakeFunction::distrIndices(int vectLen) {
101 
102  std::pair<int, int> dist;
103 
104  //IFF: properly distribute remainder
105  int rem = vectLen - (vectLen / Ippl::getNodes()) * Ippl::getNodes();
106  int tmp = (rem > Ippl::myNode()) ? 1 : 0;
107  int locBunchRange = vectLen / Ippl::getNodes() + tmp;
108 
109  dist.first = locBunchRange * Ippl::myNode() + (1 - tmp) * rem;
110  dist.second = dist.first + locBunchRange - 1;
111 
112  return dist;
113 }
114 
116 
117  Vector_t rmin, rmax;
118  double charge = bunch->getChargePerParticle();
119  // CKR: was bunch->Q[1] changed it;
120  //FIXME: why 1? bunch,getTotalCharge()
121  // or bunch->getChargePerParticle()?
122  double K = 0; // constant to normalize the lineDensity_m to 1
123  double spacing, mindist;
124  std::vector<double> OutEnergy(NBin_m);
125 
126  bunch->calcBeamParameters();
127  bunch->get_bounds(rmin, rmax);
128  //FIXME IFF: do we have unitless r's here? is that what we want?
129 
130  mindist = rmin(2);
131  switch(direction_m) {
132  case LONGITUDINAL:
133  spacing = std::abs(rmax(2) - rmin(2));
134  break; //FIXME: Kann mann das Spacing immer ändern?
135  case TRANSVERSAL:
136  spacing = rmax(0) * rmax(0) + rmax(1) * rmax(1);
137  break;
138  default:
139  throw GeneralClassicException("GreenWakeFunction::apply", "invalid direction specified");
140  }
141  PAssert(NBin_m > 0);
142  spacing /= (NBin_m - 1); //FIXME: why -1? CKR: because grid spacings = grid points - 1
143 
144  // Calculate the Wakefield if needed
145  if(FftWField_m.empty()) {
146  FftWField_m.resize(2*NBin_m-1);
147  if(filename_m != "") {
148  setWakeFromFile(NBin_m, spacing);
149  } else {
150  CalcWakeFFT(spacing);
151  }
152  } else if(!constLength_m) {
153  CalcWakeFFT(spacing);
154  }
155 
156  // Calculate the line density of the particle bunch
157  std::pair<double, double> meshInfo;
158  bunch->calcLineDensity(nBins_m, lineDensity_m, meshInfo);
159 
160 #ifdef ENABLE_WAKE_DEBUG
161  *gmsg << "* ************* W A K E ************************************************************ " << endl;
162  *gmsg << "* GreenWakeFunction::apply lineDensity_m.size() = " << lineDensity_m.size() << endl;
163  *gmsg << "* ********************************************************************************** " << endl;
164 #endif
165 
166  // smooth the line density of the particle bunch
167  for(std::vector<Filter *>::const_iterator fit = filters_m.begin(); fit != filters_m.end(); ++fit) {
168  (*fit)->apply(lineDensity_m);
169  }
170 
171  for(unsigned int i = 0; i < lineDensity_m.size(); i++) {
172  K += lineDensity_m[i];
173  }
174  K = 1 / K;
175 
176  // compute the kick due to the wakefield
177  compEnergy(K, charge, lineDensity_m, OutEnergy.data());
178 
179  // Add the right OutEnergy[i] to all the particles
180  //FIXME: can we specify LONG AND TRANS?
181  switch(direction_m) {
182  case LONGITUDINAL:
183  for(unsigned int i = 0; i < bunch->getLocalNum(); i++) {
184 
185  //FIXME: Stimmt das????????? (von den einheiten)
186  // calculate bin containing particle
187  int idx = (int)(floor((bunch->R[i](2) - mindist) / spacing));
188  //IFF: should be ok
189  if(idx == NBin_m) idx--;
190  PAssert(idx >= 0 && idx < NBin_m);
191  double dE = OutEnergy[idx];
192  bunch->Ef[i](2) += dE;
193 
194  }
195  break;
196 
197  case TRANSVERSAL:
198  for(unsigned int i = 0; i < bunch->getLocalNum(); i++) {
199 
200  // calculate bin containing particle
201  int idx = (int)(floor((bunch->R[i](2) - mindist) / spacing));
202  //IFF: should be ok
203  if(idx == NBin_m) idx--;
204  PAssert(idx >= 0 && idx < NBin_m);
205  double dE = OutEnergy[idx];
206 
207  // ACHTUNG spacing auch in transversal richtung
208  double dist = sqrt(bunch->R[i](0) * bunch->R[i](0) + bunch->R[i](1) * bunch->R[i](1));
209  PAssert(dist > 0);
210  bunch->Ef[i](0) += dE * bunch->R[i](0) / dist;
211  bunch->Ef[i](1) += dE * bunch->R[i](1) / dist;
212 
213  }
214  break;
215 
216  default:
217  throw GeneralClassicException("GreenWakeFunction::apply", "invalid direction specified");
218  }
219 
220 #ifdef ENABLE_WAKE_DUMP
221  ofstream f2("OutEnergy.dat");
222  f2 << "# Energy of the Wake calculated in Opal\n"
223  << "# Z0 = " << Z0_m << "\n"
224  << "# radius = " << radius << "\n"
225  << "# sigma = " << sigma << "\n"
226  << "# c = " << c << "\n"
227  << "# acMode = " << acMode << "\n"
228  << "# tau = " << tau << "\n"
229  << "# direction = " << direction << "\n"
230  << "# spacing = " << spacing << "\n"
231  << "# Lbunch = " << NBin_m << "\n";
232  for(int i = 0; i < NBin_m; i++) {
233  f2 << i + 1 << " " << OutEnergy[i] << "\n";
234  }
235  f2.flush();
236  f2.close();
237 #endif
238 }
239 
249 void GreenWakeFunction::compEnergy(const double K,
250  const double charge,
251  const double *lambda,
252  double *OutEnergy) {
253  int N = 2 * NBin_m - 1;
254  // Allocate Space for the zero padded lambda and its Fourier Transformed
255  std::vector<double> pLambda(N);
256 
257  gsl_fft_halfcomplex_wavetable *hc;
258  gsl_fft_real_wavetable *real = gsl_fft_real_wavetable_alloc(N);
259  gsl_fft_real_workspace *work = gsl_fft_real_workspace_alloc(N);
260 
261  // fill the arrays with data
262  for(int i = 0; i < NBin_m; i ++) {
263  pLambda[N-i-1] = 0.0;
264  pLambda[i] = lambda[i];
265  }
266 
267  //FFT of the lambda
268  gsl_fft_real_transform(pLambda.data(), 1, N, real, work);
269  gsl_fft_real_wavetable_free(real);
270 
271  // convolution -> multiplication in Fourier space
272  pLambda[0] *= FftWField_m[0];
273  for(int i = 1; i < N; i += 2) {
274  double temp = pLambda[i];
275  pLambda[i] = FftWField_m[i] * pLambda[i] - FftWField_m[i+1] * pLambda[i+1];
276  pLambda[i+1] = FftWField_m[i] * pLambda[i+1] + FftWField_m[i+1] * temp;
277  }
278 
279  // inverse transform to get c, the convolution of a and b;
280  hc = gsl_fft_halfcomplex_wavetable_alloc(N);
281 
282  gsl_fft_halfcomplex_inverse(pLambda.data(), 1, N, hc, work);
283 
284  // Write the result to the output:
285  for(int i = 0; i < NBin_m; i ++) {
286  OutEnergy[i] = -charge * K * pLambda[i] / (2.0 * NBin_m) * N; // CKR: I doubt that the multiplication with N is correct,
287  // put it here to get the same result as with FFTW
288  // My suspicion: S. Pauli has forgotten that if you
289  // do an fft followed by an inverse fft you'll get
290  // N times your original data
291 
292  }
293 
294 
295  gsl_fft_halfcomplex_wavetable_free(hc);
296  gsl_fft_real_workspace_free(work);
297 }
298 
309 void GreenWakeFunction::compEnergy(const double K,
310  const double charge,
311  std::vector<double> lambda,
312  double *OutEnergy) {
313  int N = 2 * NBin_m - 1;
314  // Allocate Space for the zero padded lambda and its Fourier Transformed
315  std::vector<double> pLambda(N);
316 
317  gsl_fft_halfcomplex_wavetable *hc;
318  gsl_fft_real_wavetable *real = gsl_fft_real_wavetable_alloc(N);
319  gsl_fft_real_workspace *work = gsl_fft_real_workspace_alloc(N);
320 
321  // fill the arrays with data
322  for(int i = 0; i < NBin_m; i ++) {
323  pLambda[N-i-1] = 0.0;
324  pLambda[i] = lambda[i];
325  }
326 
327  //FFT of the lambda
328  gsl_fft_real_transform(pLambda.data(), 1, N, real, work);
329  gsl_fft_real_wavetable_free(real);
330 
331 
332  // Convolution -> just a multiplication in Fourier space
333  pLambda[0] *= FftWField_m[0];
334  for(int i = 1; i < N; i += 2) {
335  double temp = pLambda[i];
336  pLambda[i] = FftWField_m[i] * pLambda[i] - FftWField_m[i+1] * pLambda[i+1];
337  pLambda[i+1] = FftWField_m[i] * pLambda[i+1] + FftWField_m[i+1] * temp;
338  }
339 
340  // IFFT
341  hc = gsl_fft_halfcomplex_wavetable_alloc(N);
342 
343  gsl_fft_halfcomplex_inverse(pLambda.data(), 1, N, hc, work);
344 
345  // Write the result to the output:
346  for(int i = 0; i < NBin_m; i ++) {
347  OutEnergy[i] = -charge * K * pLambda[i] / (2.0 * NBin_m) * N; // CKR: I doubt that the multiplication with N is correct,
348  // put it here to get the same result as with FFTW
349  // My suspicion: S. Pauli has forgotten that if you
350  // do an fft followed by an inverse fft you'll get
351  // N times your original data
352 
353  }
354 
355 
356  gsl_fft_halfcomplex_wavetable_free(hc);
357  gsl_fft_real_workspace_free(work);
358 }
359 
366 void GreenWakeFunction::CalcWakeFFT(double spacing) {
367  // Set integration properties
368  double a = 1, b = 1000000;
369  unsigned int N = 1000000;
370  int M = 2 * NBin_m - 1;
371 
372  gsl_fft_real_wavetable *real = gsl_fft_real_wavetable_alloc(M);
373  gsl_fft_real_workspace *work = gsl_fft_real_workspace_alloc(M);
374 
375  const std::pair<int, int> myDist = distrIndices(NBin_m);
376  const int lowIndex = myDist.first;
377  const int hiIndex = myDist.second;
378 
379  for(int i = 0; i < M; i ++) {
380  FftWField_m[i] = 0.0;
381  }
382 
387  // if (Ippl::myNode() != Ippl::getNodes()-1) {
388  for(int i = lowIndex; i <= hiIndex; i ++) {
389  Wake w(i * spacing, Z0_m, radius_m, sigma_m, acMode_m, tau_m, direction_m);
390  FftWField_m[i] = simpson(w, a, b, N);
391  }
392  // } else {
393  // //IFF: changed to <= with new distr
394  // for (int i = lowIndex; i <= hiIndex; i ++) {
395  // Wake w(i*spacing, Z0_m, radius_m, sigma_m, acMode_m, tau_m, direction_m);
396  // FftWField[i] = simpson(w,a,b,N);
397  // }
398  // }
399 
403  reduce(&(FftWField_m[0]), &(FftWField_m[0]) + NBin_m, &(FftWField_m[0]), OpAddAssign());
404 
405 #ifdef ENABLE_WAKE_TESTS_FFT_OUT
406  std::vector<double> wf(2*NBin_m-1);
407  for(int i = 0; i < 2 * NBin_m - 1; ++ i) {
408  wf[i] = FftWField_m[i];
409  }
410 #endif
411  // calculate the FFT of the Wakefield
412  gsl_fft_real_transform(FftWField_m.data(), 1, M, real, work);
413 
414 
415 #ifdef ENABLE_WAKE_TESTS_FFT_OUT
416  ofstream f2("FFTwake.dat");
417  f2 << "# FFT of the Wake calculated in Opal" << "\n"
418  << "# Z0 = " << Z0_m << "\n"
419  << "# radius = " << radius_m << "\n"
420  << "# sigma = " << sigma_m << "\n"
421  << "# mode = " << acMode_m << "\n"
422  << "# tau = " << tau_m << "\n"
423  << "# direction = " << direction_m << "\n"
424  << "# spacing = " << spacing << "\n"
425  << "# Lbunch = " << NBin_m << "\n";
426 
427  f2 << "0\t" << FftWField_m[0] << "\t0.0\t" << wf[0] << "\n";
428  for(int i = 1; i < M; i += 2) {
429  f2 << (i + 1) / 2 << "\t"
430  << FftWField_m[i] << "\t"
431  << FftWField_m[i + 1] << "\t"
432  << wf[(i+1)/2] << "\n";
433  }
434 
435 
436  f2.flush();
437  f2.close();
438 #endif
439  gsl_fft_real_wavetable_free(real);
440  gsl_fft_real_workspace_free(work);
441 }
442 
446 void GreenWakeFunction::setWakeFromFile(int NBin_m, double spacing) {
447  Inform msg("readSDDS ");
448  std::string name;
449  char temp[256];
450  int Np;
451  double dummy;
452  gsl_fft_real_wavetable *real;
453  gsl_fft_real_workspace *work;
454  std::ifstream fs;
455 
456  fs.open(filename_m.c_str());
457 
458  if(fs.fail()) {
459  throw GeneralClassicException("GreenWakeFunction::setWakeFromFile",
460  "Open file operation failed, please check if \""
461  + filename_m + "\" really exists.");
462  }
463 
464  fs >> name;
465  msg << " SSDS1 read = " << name << endl;
466  if(name.compare("SDDS1") != 0) {
467  throw GeneralClassicException("GreenWakeFunction::setWakeFromFile",
468  " No SDDS1 File. A SDDS1 file should start with a SDDS1 String. Check file \""
469  + filename_m + "\" ");
470  }
471 
472  for(int i = 0; i < 6; i++) {
473  fs.getline(temp, 256);
474  msg << "line " << i << " : " << temp << endl;
475  }
476 
477  fs >> Np;
478  msg << " header read" << endl;
479  if(Np <= 0) {
480  throw GeneralClassicException("GreenWakeFunction::setWakeFromFile",
481  " The particle number should be bigger than zero! Please check the first line of file \""
482  + filename_m + "\".");
483  }
484 
485  msg << " Np = " << Np << endl;
486  std::vector<double> wake(Np);
487  std::vector<double> dist(Np);
488 
489  // read the wakefunction
490  for(int i = 0; i < Np; i ++) {
491  if(!fs.eof()) {
492  fs >> dist[i] >> wake[i] >> dummy;
493  }
494  if(fs.eof()) {
495  throw GeneralClassicException("GreenWakeFunction::setWakeFromFile",
496  " End of file reached before the whole wakefield is imported, please check file \""
497  + filename_m + "\".");
498  }
499  }
500  // if needed interpolate the wake in a way that the wake form the file fits to the wake needs in the code (??)
501 
502  FftWField_m.resize(NBin_m);
503 
504  for(int i = 0; i < NBin_m; i ++) {
505  int j = 0;
506  while(dist[j] < i * spacing) {
507  j++;
508  }
509  -- j; // i * spacing should probably between dist[j] and dist[j+1]
510  // linear interpolation
511  FftWField_m[i] = wake[j] + ((wake[j+1] - wake[j]) / (dist[j+1] - dist[j]) * (i * spacing - dist[j]));
512  }
513 
514  real = gsl_fft_real_wavetable_alloc(NBin_m);
515  work = gsl_fft_real_workspace_alloc(NBin_m);
516 
517  gsl_fft_real_transform(FftWField_m.data(), 1, NBin_m, real, work);
518 
519  gsl_fft_real_wavetable_free(real);
520  gsl_fft_real_workspace_free(work);
521 }
522 
523 const std::string GreenWakeFunction::getType() const {
524  return "GreenWakeFunction";
525 }
Inform * gmsg
Definition: Main.cpp:62
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
PartBunchBase< T, Dim >::ConstIterator begin(PartBunchBase< T, Dim > const &bunch)
FLieGenerator< T, N > real(const FLieGenerator< std::complex< T >, N > &)
Take real part of a complex generator.
@ LONGITUDINAL
@ TRANSVERSAL
bool reduce(Communicate &, InputIterator, InputIterator, OutputIterator, const ReduceOp &, bool *IncludeVal=0)
Definition: GlobalComm.hpp:55
std::complex< double > a
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 PAssert(c)
Definition: PAssert.h:102
const std::string name
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:51
FRONT * fs
Definition: hypervolume.cpp:59
ParticleAttrib< Vector_t > Ef
ParticlePos_t & R
double getChargePerParticle() const
get the macro particle charge
size_t getLocalNum() const
void calcBeamParameters()
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)
std::vector< double > FftWField_m
FFT of the zero padded wakefield.
void setWakeFromFile(int NBin, double spacing)
reads in the wakefield from file
int direction_m
direction either 1="Longitudinal" 2= "Transversal"
int NBin_m
divides the particle bunch in NBin slices
std::string filename_m
filename of the wakefield
void compEnergy(const double K, const double charge, const double *lambda, double *OutEnergy)
just a Testfunction! Calculate the energy of the Wakefunction with the lambda
std::vector< double > lineDensity_m
save the line Density of the particle bunch
GreenWakeFunction(const std::string &name, std::vector< Filter * > filters, int NBIN, double Z0, double radius, double sigma, int acMode, double tau, int direction, bool constLength, std::string fname)
double simpson(F &f, double a, double b, unsigned int N)
Simpson-Integration from the function f from a to b with N steps.
bool constLength_m
true if the length of the particle bunch is considered as constant
void apply(PartBunchBase< double, 3 > *bunch)
int acMode_m
conductivity either 1="AC" or 2="DC"
double sigma_m
material constant
void CalcWakeFFT(double spacing)
Calculate the FFT of the Wakefunction.
virtual const std::string getType() const
double Z0_m
impedance
std::pair< int, int > distrIndices(int vectLen)
given a vector of length N, distribute the indexes among the available processors
double tau_m
material constant
std::vector< Filter * > filters_m
const unsigned int nBins_m
Definition: WakeFunction.h:43
Definition: Inform.h:42
static int getNodes()
Definition: IpplInfo.cpp:670
static int myNode()
Definition: IpplInfo.cpp:691