OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
GreenWakeFunction.cpp
Go to the documentation of this file.
4 #ifdef ENABLE_WAKE_TESTS
5 #include "Solvers/TestLambda.h" // used for tests
6 #endif
7 
8 #include <fstream>
9 #include <string>
10 #include <vector>
11 #include <istream>
12 #include <iostream> // Needed for stream I/O
13 #include <iomanip> // Needed for I/O manipulators
14 #include "gsl/gsl_fft_real.h"
15 #include "gsl/gsl_fft_halfcomplex.h"
16 
17 using namespace std;
18 
19 //IFF: TEST
20 //#define ENABLE_WAKE_DEBUG
21 //#define ENABLE_WAKE_DUMP
22 //#define ENABLE_WAKE_TESTS
23 //#define ENABLE_WAKE_TESTS_FFT_OUT
24 //#define readWakeFromFile
25 //#define WakeFile "test.sdds"
26 
27 
47  ElementBase *element,
48  vector<Filter *> filters,
49  int NBIN,
50  double Z0,
51  double radius,
52  double sigma,
53  int acMode,
54  double tau,
55  int direction,
56  bool constLength,
57  std::string fname):
58  WakeFunction(name, element, NBIN),
59  lineDensity_m(),
60  //~ FftWField_m(0),
61  NBin_m(NBIN),
62  Z0_m(Z0),
63  radius_m(radius),
64  sigma_m(sigma),
65  acMode_m(acMode),
66  tau_m(tau),
67  direction_m(direction),
68  constLength_m(constLength),
69  filename_m(fname),
70  filters_m(filters.begin(), filters.end()) {
71 #ifdef ENABLE_WAKE_DEBUG
72  *gmsg << "* ************* W A K E ************************************************************ " << endl;
73  *gmsg << "* Entered GreenWakeFunction::GreenWakeFunction " << '\n';
74  *gmsg << "* ********************************************************************************** " << endl;
75 #endif
76 }
77 
79  //~ if(FftWField_m != 0) {
80  //~ delete[] FftWField_m;
81  //~ }
82 }
83 
84 
95 pair<int, int> GreenWakeFunction::distrIndices(int vectLen) {
96 
97  pair<int, int> dist;
98 
99  //IFF: properly distribute remainder
100  int rem = vectLen - (vectLen / Ippl::getNodes()) * Ippl::getNodes();
101  int tmp = (rem > Ippl::myNode()) ? 1 : 0;
102  int locBunchRange = vectLen / Ippl::getNodes() + tmp;
103 
104  dist.first = locBunchRange * Ippl::myNode() + (1 - tmp) * rem;
105  dist.second = dist.first + locBunchRange - 1;
106 
107  return dist;
108 }
109 
111 #ifdef ENABLE_WAKE_TESTS
112  // overwrite the line density
113  testApply(bunch);
114 #else
115 
116  Vector_t rmin, rmax;
117  double charge = bunch->getChargePerParticle();
118  // CKR: was bunch->Q[1] changed it;
119  //FIXME: why 1? bunch,getTotalCharge()
120  // or bunch->getChargePerParticle()?
121  double K = 0; // constant to normalize the lineDensity_m to 1
122  double spacing, mindist;
123  std::vector<double> OutEnergy(NBin_m);
124 
125  bunch->calcBeamParameters();
126  bunch->get_bounds(rmin, rmax);
127  //FIXME IFF: do we have unitless r's here? is that what we want?
128 
129  mindist = rmin(2);
130  switch(direction_m) {
131  case LONGITUDINAL:
132  spacing = abs(rmax(2) - rmin(2));
133  break; //FIXME: Kann mann das Spacing immer ändern?
134  case TRANSVERSAL:
135  spacing = rmax(0) * rmax(0) + rmax(1) * rmax(1);
136  break;
137  default:
138  throw GeneralClassicException("GreenWakeFunction", "invalid direction specified");
139  }
140  assert(NBin_m > 0);
141  spacing /= (NBin_m - 1); //FIXME: why -1? CKR: because grid spacings = grid points - 1
142 
143  // Calculate the Wakefield if needed
144  if(FftWField_m.empty()) {
145  FftWField_m.resize(2*NBin_m-1);
146  if(filename_m != "") {
147  setWakeFromFile(NBin_m, spacing);
148  } else {
149  CalcWakeFFT(spacing);
150  }
151  } else if(!constLength_m) {
152  CalcWakeFFT(spacing);
153  }
154 
155  // Calculate the line density of the particle bunch
156  std::pair<double, double> meshInfo;
157  bunch->calcLineDensity(nBins_m, lineDensity_m, meshInfo);
158 
159 #ifdef ENABLE_WAKE_DEBUG
160  *gmsg << "* ************* W A K E ************************************************************ " << endl;
161  *gmsg << "* GreenWakeFunction::apply lineDensity_m.size() = " << lineDensity_m.size() << endl;
162  *gmsg << "* ********************************************************************************** " << endl;
163 #endif
164 
165  // smooth the line density of the particle bunch
166  for(vector<Filter *>::const_iterator fit = filters_m.begin(); fit != filters_m.end(); ++fit) {
167  (*fit)->apply(lineDensity_m);
168  }
169 
170  for(unsigned int i = 0; i < lineDensity_m.size(); i++) {
171  K += lineDensity_m[i];
172  }
173  K = 1 / K;
174 
175  // compute the kick due to the wakefield
176  compEnergy(K, charge, lineDensity_m, OutEnergy.data());
177 
178  // Add the right OutEnergy[i] to all the particles
179  //FIXME: can we specify LONG AND TRANS?
180  switch(direction_m) {
181  case LONGITUDINAL:
182  for(unsigned int i = 0; i < bunch->getLocalNum(); i++) {
183 
184  //FIXME: Stimmt das????????? (von den einheiten)
185  // calculate bin containing particle
186  int idx = (int)(floor((bunch->R[i](2) - mindist) / spacing));
187  //IFF: should be ok
188  if(idx == NBin_m) idx--;
189  assert(idx >= 0 && idx < NBin_m);
190  double dE = OutEnergy[idx];
191  bunch->Ef[i](2) += dE;
192 
193  }
194  break;
195 
196  case TRANSVERSAL:
197  for(unsigned int i = 0; i < bunch->getLocalNum(); i++) {
198 
199  // calculate bin containing particle
200  int idx = (int)(floor((bunch->R[i](2) - mindist) / spacing));
201  //IFF: should be ok
202  if(idx == NBin_m) idx--;
203  assert(idx >= 0 && idx < NBin_m);
204  double dE = OutEnergy[idx];
205 
206  // ACHTUNG spacing auch in transversal richtung
207  double dist = sqrt(bunch->R[i](0) * bunch->R[i](0) + bunch->R[i](1) * bunch->R[i](1));
208  assert(dist > 0);
209  bunch->Ef[i](0) += dE * bunch->R[i](0) / dist;
210  bunch->Ef[i](1) += dE * bunch->R[i](1) / dist;
211 
212  }
213  break;
214 
215  default:
216  throw GeneralClassicException("GreenWakeFunction", "invalid direction specified");
217  }
218 
219 #ifdef ENABLE_WAKE_DUMP
220  ofstream f2("OutEnergy.dat");
221  f2 << "# Energy of the Wake calculated in Opal\n"
222  << "# Z0 = " << Z0_m << "\n"
223  << "# radius = " << radius << "\n"
224  << "# sigma = " << sigma << "\n"
225  << "# c = " << c << "\n"
226  << "# acMode = " << acMode << "\n"
227  << "# tau = " << tau << "\n"
228  << "# direction = " << direction << "\n"
229  << "# spacing = " << spacing << "\n"
230  << "# Lbunch = " << NBin_m << "\n";
231  for(int i = 0; i < NBin_m; i++) {
232  f2 << i + 1 << " " << OutEnergy[i] << "\n";
233  }
234  f2.flush();
235  f2.close();
236 #endif
237 
238 #endif //ENABLE_WAKE_TESTS
239 }
240 
245 #ifdef ENABLE_WAKE_TESTS
246  double spacing;
247  // determine K and charge
248  double charge = 0.8e-9; // nC
249  double K = 0.20536314319923724e-9; //K normalizes nC data in lambda.h?
250  spacing = 1e-6; //IFF: charge in testLambda.h in 1um spacings
251  NBin_m = 294;
252  std::vector<double> OutEnergy(NBin_m);
253 
254  if(FftWField_m.empty()) {
255  FftWField_m.resize(2*NBin_m - 1);
256  CalcWakeFFT(spacing);
257  } else if(!constLength_m) {
258  CalcWakeFFT(spacing);
259  }
260 
261  compEnergy(K, charge, testLambda, OutEnergy.data());
262 
263  ofstream f2("OutEnergy.dat");
264  f2 << "# Energy of the Wake calculated in Opal\n"
265  << "# Z0 = " << Z0_m << "\n"
266  << "# radius = " << radius_m << "\n"
267  << "# sigma = " << sigma_m << "\n"
268  << "# acMode = " << acMode_m << "\n"
269  << "# tau = " << tau_m << "\n"
270  << "# direction = " << direction_m << "\n"
271  << "# spacing = " << spacing_m << "\n"
272  << "# Lbunch = " << NBin_m << "\n";
273  for(int i = 0; i < NBin_m; i++) {
274  f2 << i + 1 << " " << OutEnergy[i] << "\n";
275  }
276  f2.flush();
277  f2.close();
278 #endif
279 }
280 
291  const double charge,
292  const double *lambda,
293  double *OutEnergy) {
294  int N = 2 * NBin_m - 1;
295  // Allocate Space for the zero padded lambda and its Fourier Transformed
296  std::vector<double> pLambda(N);
297 
298  gsl_fft_halfcomplex_wavetable *hc;
299  gsl_fft_real_wavetable *real = gsl_fft_real_wavetable_alloc(N);
300  gsl_fft_real_workspace *work = gsl_fft_real_workspace_alloc(N);
301 
302  // fill the arrays with data
303  for(int i = 0; i < NBin_m; i ++) {
304  pLambda[N-i-1] = 0.0;
305  pLambda[i] = lambda[i];
306  }
307 
308  //FFT of the lambda
309  gsl_fft_real_transform(pLambda.data(), 1, N, real, work);
310  gsl_fft_real_wavetable_free(real);
311 
312  // convolution -> multiplication in Fourier space
313  pLambda[0] *= FftWField_m[0];
314  for(int i = 1; i < N; i += 2) {
315  double temp = pLambda[i];
316  pLambda[i] = FftWField_m[i] * pLambda[i] - FftWField_m[i+1] * pLambda[i+1];
317  pLambda[i+1] = FftWField_m[i] * pLambda[i+1] + FftWField_m[i+1] * temp;
318  }
319 
320  // inverse transform to get c, the convolution of a and b;
321  hc = gsl_fft_halfcomplex_wavetable_alloc(N);
322 
323  gsl_fft_halfcomplex_inverse(pLambda.data(), 1, N, hc, work);
324 
325  // Write the result to the output:
326  for(int i = 0; i < NBin_m; i ++) {
327  OutEnergy[i] = -charge * K * pLambda[i] / (2.0 * NBin_m) * N; // CKR: I doubt that the multiplication with N is correct,
328  // put it here to get the same result as with FFTW
329  // My suspicion: S. Pauli has forgotten that if you
330  // do an fft followed by an inverse fft you'll get
331  // N times your original data
332 
333  }
334 
335 
336  gsl_fft_halfcomplex_wavetable_free(hc);
337  gsl_fft_real_workspace_free(work);
338 }
339 
351  const double charge,
352  vector<double> lambda,
353  double *OutEnergy) {
354  int N = 2 * NBin_m - 1;
355  // Allocate Space for the zero padded lambda and its Fourier Transformed
356  std::vector<double> pLambda(N);
357 
358  gsl_fft_halfcomplex_wavetable *hc;
359  gsl_fft_real_wavetable *real = gsl_fft_real_wavetable_alloc(N);
360  gsl_fft_real_workspace *work = gsl_fft_real_workspace_alloc(N);
361 
362  // fill the arrays with data
363  for(int i = 0; i < NBin_m; i ++) {
364  pLambda[N-i-1] = 0.0;
365  pLambda[i] = lambda[i];
366  }
367 
368  //FFT of the lambda
369  gsl_fft_real_transform(pLambda.data(), 1, N, real, work);
370  gsl_fft_real_wavetable_free(real);
371 
372 
373  // Convolution -> just a multiplication in Fourier space
374  pLambda[0] *= FftWField_m[0];
375  for(int i = 1; i < N; i += 2) {
376  double temp = pLambda[i];
377  pLambda[i] = FftWField_m[i] * pLambda[i] - FftWField_m[i+1] * pLambda[i+1];
378  pLambda[i+1] = FftWField_m[i] * pLambda[i+1] + FftWField_m[i+1] * temp;
379  }
380 
381  // IFFT
382  hc = gsl_fft_halfcomplex_wavetable_alloc(N);
383 
384  gsl_fft_halfcomplex_inverse(pLambda.data(), 1, N, hc, work);
385 
386  // Write the result to the output:
387  for(int i = 0; i < NBin_m; i ++) {
388  OutEnergy[i] = -charge * K * pLambda[i] / (2.0 * NBin_m) * N; // CKR: I doubt that the multiplication with N is correct,
389  // put it here to get the same result as with FFTW
390  // My suspicion: S. Pauli has forgotten that if you
391  // do an fft followed by an inverse fft you'll get
392  // N times your original data
393 
394  }
395 
396 
397  gsl_fft_halfcomplex_wavetable_free(hc);
398  gsl_fft_real_workspace_free(work);
399 }
400 
407 void GreenWakeFunction::CalcWakeFFT(double spacing) {
408  // Set integration properties
409  double a = 1, b = 1000000;
410  unsigned int N = 1000000;
411  int M = 2 * NBin_m - 1;
412 
413  gsl_fft_real_wavetable *real = gsl_fft_real_wavetable_alloc(M);
414  gsl_fft_real_workspace *work = gsl_fft_real_workspace_alloc(M);
415 
416  const pair<int, int> myDist = distrIndices(NBin_m);
417  const int lowIndex = myDist.first;
418  const int hiIndex = myDist.second;
419 
420 #ifdef ENABLE_WAKE_TESTS
421  ofstream file;
422 
423  if(Ippl::myNode() == 0) {
424  file.open("wake.dat");
425  file << "# Wake calculated in Opal" << "\n"
426  << "# Z0 = " << Z0_m << "\n"
427  << "# radius = " << radius_m << "\n"
428  << "# sigma = " << sigma_m << "\n"
429  << "# mode = " << acMode_m << "\n"
430  << "# tau = " << tau_m << "\n"
431  << "# direction = " << direction_m << "\n"
432  << "# spacing = " << spacing << "\n"
433  << "# Lbunch = " << NBin_m << "\n";
434  }
435 #endif
436 
437  for(int i = 0; i < M; i ++) {
438  FftWField_m[i] = 0.0;
439  }
440 
445  // if (Ippl::myNode() != Ippl::getNodes()-1) {
446  for(int i = lowIndex; i <= hiIndex; i ++) {
447  Wake w(i * spacing, Z0_m, radius_m, sigma_m, acMode_m, tau_m, direction_m);
448  FftWField_m[i] = simpson(w, a, b, N);
449  }
450  // } else {
451  // //IFF: changed to <= with new distr
452  // for (int i = lowIndex; i <= hiIndex; i ++) {
453  // Wake w(i*spacing, Z0_m, radius_m, sigma_m, acMode_m, tau_m, direction_m);
454  // FftWField[i] = simpson(w,a,b,N);
455  // }
456  // }
457 
461  reduce(&(FftWField_m[0]), &(FftWField_m[0]) + NBin_m, &(FftWField_m[0]), OpAddAssign());
462 
463 
464 #ifdef ENABLE_WAKE_TESTS
465  if(Ippl::myNode() == 0) {
466  for(int i = 0; i < NBin_m; i++) {
467  file << i + 1 << " " << FftWField_m[i] << "\n";
468  }
469  file.flush();
470  file.close();
471  }
472 #endif
473 
474 #ifdef ENABLE_WAKE_TESTS_FFT_OUT
475  std::vector<double> wf(2*NBin_m-1);
476  for(int i = 0; i < 2 * NBin_m - 1; ++ i) {
477  wf[i] = FftWField_m[i];
478  }
479 #endif
480  // calculate the FFT of the Wakefield
481  gsl_fft_real_transform(FftWField_m.data(), 1, M, real, work);
482 
483 
484 #ifdef ENABLE_WAKE_TESTS_FFT_OUT
485  ofstream f2("FFTwake.dat");
486  f2 << "# FFT of the Wake calculated in Opal" << "\n"
487  << "# Z0 = " << Z0_m << "\n"
488  << "# radius = " << radius_m << "\n"
489  << "# sigma = " << sigma_m << "\n"
490  << "# mode = " << acMode_m << "\n"
491  << "# tau = " << tau_m << "\n"
492  << "# direction = " << direction_m << "\n"
493  << "# spacing = " << spacing << "\n"
494  << "# Lbunch = " << NBin_m << "\n";
495 
496  f2 << "0\t" << FftWField_m[0] << "\t0.0\t" << wf[0] << "\n";
497  for(int i = 1; i < M; i += 2) {
498  f2 << (i + 1) / 2 << "\t"
499  << FftWField_m[i] << "\t"
500  << FftWField_m[i + 1] << "\t"
501  << wf[(i+1)/2] << "\n";
502  }
503 
504 
505  f2.flush();
506  f2.close();
507 #endif
508  gsl_fft_real_wavetable_free(real);
509  gsl_fft_real_workspace_free(work);
510 }
511 
515 void GreenWakeFunction::setWakeFromFile(int NBin_m, double spacing) {
516  Inform msg("readSDDS ");
517  std::string name;
518  char temp[256];
519  int Np;
520  double dummy;
521  gsl_fft_real_wavetable *real;
522  gsl_fft_real_workspace *work;
523  std::ifstream fs;
524 
525  fs.open(filename_m.c_str());
526 
527  if(fs.fail()) {
528  throw GeneralClassicException("GreenWakeFunction::setWakeFromFile",
529  "Open file operation failed, please check if \""
530  + filename_m + "\" really exists.");
531  }
532 
533  fs >> name;
534  msg << " SSDS1 read = " << name << endl;
535  if(name.compare("SDDS1") != 0) {
536  throw GeneralClassicException("GreenWakeFunction::setWakeFromFile",
537  " No SDDS1 File. A SDDS1 file should start with a SDDS1 String. Check file \""
538  + filename_m + "\" ");
539  }
540 
541  for(int i = 0; i < 6; i++) {
542  fs.getline(temp, 256);
543  msg << "line " << i << " : " << temp << endl;
544  }
545 
546  fs >> Np;
547  msg << " header read" << endl;
548  if(Np <= 0) {
549  throw GeneralClassicException("GreenWakeFunction::setWakeFromFile",
550  " The particle number should be bigger than zero! Please check the first line of file \""
551  + filename_m + "\".");
552  }
553 
554  msg << " Np = " << Np << endl;
555  std::vector<double> wake(Np);
556  std::vector<double> dist(Np);
557 
558  // read the wakefunction
559  for(int i = 0; i < Np; i ++) {
560  if(!fs.eof()) {
561  fs >> dist[i] >> wake[i] >> dummy;
562  }
563  if(fs.eof()) {
564  throw GeneralClassicException("GreenWakeFunction::setWakeFromFile",
565  " End of file reached before the whole wakefield is imported, please check file \""
566  + filename_m + "\".");
567  }
568  }
569  // if needed interpolate the wake in a way that the wake form the file fits to the wake needs in the code (??)
570 
571  FftWField_m.resize(NBin_m);
572 
573  for(int i = 0; i < NBin_m; i ++) {
574  int j = 0;
575  while(dist[j] < i * spacing) {
576  j++;
577  }
578  -- j; // i * spacing should probably between dist[j] and dist[j+1]
579  // linear interpolation
580  FftWField_m[i] = wake[j] + ((wake[j+1] - wake[j]) / (dist[j+1] - dist[j]) * (i * spacing - dist[j]));
581  }
582 
583  real = gsl_fft_real_wavetable_alloc(NBin_m);
584  work = gsl_fft_real_workspace_alloc(NBin_m);
585 
586  gsl_fft_real_transform(FftWField_m.data(), 1, NBin_m, real, work);
587 
588  gsl_fft_real_wavetable_free(real);
589  gsl_fft_real_workspace_free(work);
590 }
591 
592 const std::string GreenWakeFunction::getType() const {
593  return "GreenWakeFunction";
594 }
static int getNodes()
Definition: IpplInfo.cpp:773
std::vector< double > FftWField_m
FFT of the zero padded wakefield.
void setWakeFromFile(int NBin, double spacing)
reads in the wakefield from file
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
constexpr double e
The value of .
Definition: Physics.h:40
double Z0_m
impedance
ParticleAttrib< Vector_t > Ef
const unsigned int nBins_m
Definition: WakeFunction.hh:27
Interface for basic beam line object.
Definition: ElementBase.h:128
GreenWakeFunction(const std::string &name, ElementBase *element, std::vector< Filter * > filters, int NBIN, double Z0, double radius, double sigma, int acMode, double tau, int direction, bool constLength, std::string fname)
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
Inform * gmsg
Definition: Main.cpp:21
static int myNode()
Definition: IpplInfo.cpp:794
FRONT * fs
Definition: hypervolume.cpp:59
double sigma_m
material constant
std::pair< int, int > distrIndices(int vectLen)
given a vector of length N, distribute the indexes among the available processors ...
FLieGenerator< T, N > real(const FLieGenerator< std::complex< T >, N > &)
Take real part of a complex generator.
virtual const std::string getType() const
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 reduce(Communicate &, InputIterator, InputIterator, OutputIterator, const ReduceOp &, bool *IncludeVal=0)
Definition: GlobalComm.hpp:55
void calcBeamParameters()
const double testLambda[294]
Definition: TestLambda.h:3
int acMode_m
conductivity either 1=&quot;AC&quot; or 2=&quot;DC&quot;
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:52
int direction_m
direction either 1=&quot;Longitudinal&quot; 2= &quot;Transversal&quot;
void testApply(PartBunchBase< double, 3 > *bunch)
Just a test function.
double getChargePerParticle() const
get the macro particle charge
void apply(PartBunchBase< double, 3 > *bunch)
double radius_m
radius
double tau_m
material constant
void CalcWakeFFT(double spacing)
Calculate the FFT of the Wakefunction.
size_t getLocalNum() const
std::vector< Filter * > filters_m
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
int NBin_m
divides the particle bunch in NBin slices
bool constLength_m
true if the length of the particle bunch is considered as constant
const std::string name
std::string filename_m
filename of the wakefield
ParticlePos_t & R
void get_bounds(Vector_t &rmin, Vector_t &rmax)
#define K
Definition: integrate.cpp:118
Definition: Inform.h:41
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
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.
PETE_TUTree< FnFloor, typename T::PETE_Expr_t > floor(const PETE_Expr< T > &l)
Definition: PETE.h:816
Inform & endl(Inform &inf)
Definition: Inform.cpp:42