OPAL (Object Oriented Parallel Accelerator Library)  2021.1.99
OPAL
FFTPoissonSolver.cpp
Go to the documentation of this file.
1 // -*- C++ -*-
2 /***************************************************************************
3  *
4  *
5  * FFTPoissonSolver.cc
6  *
7  *
8  *
9  *
10  *
11  *
12  *
13  ***************************************************************************/
14 
16 #include "Algorithms/PartBunch.h"
17 #include "Physics/Physics.h"
18 #include "Utility/IpplTimings.h"
19 #include "BasicActions/Option.h"
20 #include "Utilities/Options.h"
21 #include "Utilities/Util.h"
22 #include <fstream>
23 
24 extern Inform *gmsg;
26 // a little helper class to specialize the action of the Green's function
27 // calculation. This should be specialized for each dimension
28 // to the proper action for computing the Green's function. The first
29 // template parameter is the full type of the Field to compute, and the second
30 // is the dimension of the data, which should be specialized.
31 
32 template<unsigned int Dim>
33 struct SpecializedGreensFunction { };
34 
35 template<>
36 struct SpecializedGreensFunction<3> {
37  template<class T, class FT, class FT2>
38  static void calculate(Vektor<T, 3> &hrsq, FT &grn, FT2 *grnI) {
39  grn = grnI[0] * hrsq[0] + grnI[1] * hrsq[1] + grnI[2] * hrsq[2];
40  grn = 1.0 / sqrt(grn);
41  grn[0][0][0] = grn[0][0][1];
42  }
43 };
44 
46 
47 // constructor
48 
49 
50 FFTPoissonSolver::FFTPoissonSolver(Mesh_t *mesh, FieldLayout_t *fl, std::string greensFunction, std::string bcz):
51  mesh_m(mesh),
52  layout_m(fl),
53  mesh2_m(nullptr),
54  layout2_m(nullptr),
55  mesh3_m(nullptr),
56  layout3_m(nullptr),
57  mesh4_m(nullptr),
58  layout4_m(nullptr)
59 {
60  bcz_m = (bcz == std::string("PERIODIC")); // for DC beams, the z direction has periodic boundary conditions
61  integratedGreens_m = (greensFunction == std::string("INTEGRATED"));
62 
64 
65  GreensFunctionTimer_m = IpplTimings::getTimer("SF: GreensFTotal");
66  ComputePotential_m = IpplTimings::getTimer("ComputePotential");
67 }
68 
69 
70 FFTPoissonSolver::FFTPoissonSolver(PartBunch &beam, std::string greensFunction):
71  mesh_m(&beam.getMesh()),
72  layout_m(&beam.getFieldLayout()),
73  mesh2_m(nullptr),
74  layout2_m(nullptr),
75  mesh3_m(nullptr),
76  layout3_m(nullptr),
77  mesh4_m(nullptr),
78  layout4_m(nullptr)
79 {
80  integratedGreens_m = (greensFunction == std::string("INTEGRATED"));
81 
83 
84  GreensFunctionTimer_m = IpplTimings::getTimer("SF: GreensFTotal");
85  ComputePotential_m = IpplTimings::getTimer("ComputePotential");
86 }
87 
89 // destructor
91 
93 
95 
96  // For efficiency in the FFT's, we can use a parallel decomposition
97  // which can be serial in the first dimension.
98  e_dim_tag decomp[3];
99  e_dim_tag decomp2[3];
100  for(int d = 0; d < 3; ++ d) {
101  decomp[d] = layout_m->getRequestedDistribution(d);
102  decomp2[d] = layout_m->getRequestedDistribution(d);
103  }
104 
105  if (bcz_m) {
106  // The FFT's require double-sized field sizes in order to
107  // simulate an isolated system. The FFT of the charge density field, rho,
108  // would otherwise mimic periodic boundary conditions, i.e. as if there were
109  // several beams set a periodic distance apart. The double-sized fields in x and
110  // alleviate this problem, in z we have periodic BC's
111  for (int i = 0; i < 2; ++ i) {
112  hr_m[i] = mesh_m->get_meshSpacing(i);
113  nr_m[i] = domain_m[i].length();
114  domain2_m[i] = Index(2 * nr_m[i] + 1);
115  }
116 
117  hr_m[2] = mesh_m->get_meshSpacing(2);
118  nr_m[2] = domain_m[2].length();
119  domain2_m[2] = Index(nr_m[2] + 1);
120 
121  for (int i = 0; i < 2 * 3; ++ i) {
124  }
125  // z-direction
130  }
131  else {
132  // The FFT's require double-sized field sizes in order to
133  // simulate an isolated system. The FFT of the charge density field, rho,
134  // would otherwise mimic periodic boundary conditions, i.e. as if there were
135  // several beams set a periodic distance apart. The double-sized fields
136  // alleviate this problem.
137  for (int i = 0; i < 3; ++ i) {
138  hr_m[i] = mesh_m->get_meshSpacing(i);
139  nr_m[i] = domain_m[i].length();
140  domain2_m[i] = Index(2 * nr_m[i] + 1);
141  }
142 
143  for (int i = 0; i < 2 * 3; ++ i) {
146  }
147  }
148 
149  // create double sized mesh and layout objects for the use in the FFT's
150  mesh2_m = std::unique_ptr<Mesh_t>(new Mesh_t(domain2_m));
151  layout2_m = std::unique_ptr<FieldLayout_t>(new FieldLayout_t(*mesh2_m, decomp));
152 
154 
155  NDIndex<3> tmpdomain;
156  // Create the domain for the transformed (complex) fields. Do this by
157  // taking the domain from the doubled mesh, permuting it to the right, and
158  // setting the 2nd dimension to have n/2 + 1 elements.
159  if (bcz_m)
160  domain3_m[0] = Index(nr_m[2] + 1);
161  else
162  domain3_m[0] = Index(2 * nr_m[2] + 1);
163  domain3_m[1] = Index(nr_m[0] + 2);
164  domain3_m[2] = Index(2 * nr_m[1] + 1);
165 
166  // create mesh and layout for the new real-to-complex FFT's, for the
167  // complex transformed fields
168  mesh3_m = std::unique_ptr<Mesh_t>(new Mesh_t(domain3_m));
169  layout3_m = std::unique_ptr<FieldLayout_t>(new FieldLayout_t(*mesh3_m, decomp2));
170 
174 
175  // helper field for sin
177 
178  for (int i = 0; i < 3; ++ i) {
179  domain4_m[i] = Index(nr_m[i] + 2);
180  }
181  mesh4_m = std::unique_ptr<Mesh_t>(new Mesh_t(domain4_m));
182  layout4_m = std::unique_ptr<FieldLayout_t>(new FieldLayout_t(*mesh4_m, decomp));
183 
185 
186  // create a domain used to indicate to the FFT's how to construct it's
187  // temporary fields. This is the same as the complex field's domain,
188  // but permuted back to the left.
189  tmpdomain = layout3_m->getDomain();
190  for (int i = 0; i < 3; ++ i)
191  domainFFTConstruct_m[i] = tmpdomain[(i+1) % 3];
192 
193  // create the FFT object
194  fft_m = std::unique_ptr<FFT_t>(new FFT_t(layout2_m->getDomain(), domainFFTConstruct_m));
195 
196  // these are fields that are used for calculating the Green's function.
197  // they eliminate some calculation at each time-step.
198  for (int i = 0; i < 3; ++ i) {
200  grnIField_m[i][domain2_m] = where(lt(domain2_m[i], nr_m[i]),
201  domain2_m[i] * domain2_m[i],
202  (2 * nr_m[i] - domain2_m[i]) *
203  (2 * nr_m[i] - domain2_m[i]));
204  }
205 }
206 
208 // given a charge-density field rho and a set of mesh spacings hr,
209 // compute the electric potential from the image charge by solving
210 // the Poisson's equation
211 
212 void FFTPoissonSolver::computePotential(Field_t &rho, Vector_t hr, double zshift) {
213 
214  // use grid of complex doubled in both dimensions
215  // and store rho in lower left quadrant of doubled grid
216  rho2_m = 0.0;
217 
218  rho2_m[domain_m] = rho[domain_m];
219 
220  // needed in greens function
221  hr_m = hr;
222  // FFT double-sized charge density
223  // we do a backward transformation so that we dont have to account for the normalization factor
224  // that is used in the forward transformation of the IPPL FFT
225  fft_m->transform(-1, rho2_m, rho2tr_m);
226 
227  // must be called if the mesh size has changed
228  // have to check if we can do G with h = (1,1,1)
229  // and rescale later
230 
231  // Do image charge.
232  // The minus sign is due to image charge.
233  // Convolute transformed charge density with shifted green's function.
235  shiftedIntGreensFunction(zshift);
237 
238  // Multiply transformed charge density and
239  // transformed Green's function. Don't divide
240  // by (2*nx_m)*(2*ny_m), as Ryne does; this
241  // normalization is done in POOMA's fft routine.
243 
244  // Inverse FFT to find image charge potential, rho2_m equals the electrostatic potential.
245  fft_m->transform(+1, imgrho2tr_m, rho2_m);
246 
247  // Re-use rho to store image potential. Flip z coordinate since this is a mirror image.
248  Index I = nr_m[0];
249  Index J = nr_m[1];
250  Index K = nr_m[2];
251  rho[I][J][K] = rho2_m[I][J][nr_m[2] - K - 1];
252 
253 }
254 
255 
257 // given a charge-density field rho and a set of mesh spacings hr,
258 // compute the electric field and put in eg by solving the Poisson's equation
259 
261 
263 
264  // use grid of complex doubled in both dimensions
265  // and store rho in lower left quadrant of doubled grid
266  rho2_m = 0.0;
267 
268  rho2_m[domain_m] = rho[domain_m];
269 
270  // needed in greens function
271  hr_m = hr;
272 
273  // FFT double-sized charge density
274  // we do a backward transformation so that we dont have to account for the normalization factor
275  // that is used in the forward transformation of the IPPL FFT
276  fft_m->transform(-1, rho2_m, rho2tr_m);
277 
278  // must be called if the mesh size has changed
279  // have to check if we can do G with h = (1,1,1)
280  // and rescale later
284  else
285  greensFunction();
287  // multiply transformed charge density
288  // and transformed Green function
289  // Don't divide by (2*nx_m)*(2*ny_m), as Ryne does;
290  // this normalization is done in POOMA's fft routine.
291  rho2tr_m *= grntr_m;
292 
293  // inverse FFT, rho2_m equals to the electrostatic potential
294  fft_m->transform(+1, rho2tr_m, rho2_m);
295  // end convolution
296 
297  // back to physical grid
298  // reuse the charge density field to store the electrostatic potential
299  rho[domain_m] = rho2_m[domain_m];
301 }
302 
304 // calculate the FFT of the Green's function for the given field
306 
307  //hr_m[0]=hr_m[1]=hr_m[2]=1;
308 
309  Vector_t hrsq(hr_m * hr_m);
311  // Green's function calculation complete at this point.
312  // The next step is to FFT it.
313  // FFT of Green's function
314 
315  // we do a backward transformation so that we dont have to account for the normalization factor
316  // that is used in the forward transformation of the IPPL FFT
317  fft_m->transform(-1, rho2_m, grntr_m);
318 }
319 
339 
340 
341 
347  NDIndex<3> idx = layout4_m->getLocalNDIndex();
348  double cellVolume = hr_m[0] * hr_m[1] * hr_m[2];
349  tmpgreen_m = 0.0;
350 
351  for(int k = idx[2].first(); k <= idx[2].last() + 1; k++) {
352  for(int j = idx[1].first(); j <= idx[1].last() + 1; j++) {
353  for(int i = idx[0].first(); i <= idx[0].last() + 1; i++) {
354 
355  Vector_t vv = Vector_t(0.0);
356  vv(0) = i * hr_m[0] - hr_m[0] / 2;
357  vv(1) = j * hr_m[1] - hr_m[1] / 2;
358  vv(2) = k * hr_m[2] - hr_m[2] / 2;
359 
360  double r = sqrt(vv(0) * vv(0) + vv(1) * vv(1) + vv(2) * vv(2));
361  double tmpgrn = -vv(2) * vv(2) * atan(vv(0) * vv(1) / (vv(2) * r)) / 2;
362  tmpgrn += -vv(1) * vv(1) * atan(vv(0) * vv(2) / (vv(1) * r)) / 2;
363  tmpgrn += -vv(0) * vv(0) * atan(vv(1) * vv(2) / (vv(0) * r)) / 2;
364  tmpgrn += vv(1) * vv(2) * log(vv(0) + r);
365  tmpgrn += vv(0) * vv(2) * log(vv(1) + r);
366  tmpgrn += vv(0) * vv(1) * log(vv(2) + r);
367 
368  tmpgreen_m[i][j][k] = tmpgrn / cellVolume;
369 
370  }
371  }
372  }
373 
374 
375  //assign seems to have problems when we need values that are on another CPU, i.e. [I+1]
376  /*assign(rho2_m[I][J][K] ,
377  tmpgreen_m[I+1][J+1][K+1] - tmpgreen_m[I][J+1][K+1] -
378  tmpgreen_m[I+1][J][K+1] + tmpgreen_m[I][J][K+1] -
379  tmpgreen_m[I+1][J+1][K] + tmpgreen_m[I][J+1][K] +
380  tmpgreen_m[I+1][J][K] - tmpgreen_m[I][J][K]);*/
381 
382  Index I = nr_m[0] + 1;
383  Index J = nr_m[1] + 1;
384  Index K = nr_m[2] + 1;
385 
386  // the actual integration
387  rho2_m = 0.0;
388  rho2_m[I][J][K] = tmpgreen_m[I+1][J+1][K+1];
389  rho2_m[I][J][K] += tmpgreen_m[I+1][J][K];
390  rho2_m[I][J][K] += tmpgreen_m[I][J+1][K];
391  rho2_m[I][J][K] += tmpgreen_m[I][J][K+1];
392  rho2_m[I][J][K] -= tmpgreen_m[I+1][J+1][K];
393  rho2_m[I][J][K] -= tmpgreen_m[I+1][J][K+1];
394  rho2_m[I][J][K] -= tmpgreen_m[I][J+1][K+1];
395  rho2_m[I][J][K] -= tmpgreen_m[I][J][K];
396 
397  mirrorRhoField();
398 
399  fft_m->transform(-1, rho2_m, grntr_m);
400 
401 }
402 
404 
405  tmpgreen_m = 0.0;
406  Field_t grn2(*mesh4_m, *layout4_m);
407  grn2 = 0.0;
408  NDIndex<3> idx = layout4_m->getLocalNDIndex();
409  double cellVolume = hr_m[0] * hr_m[1] * hr_m[2];
410 
411  for(int k = idx[2].first(); k <= idx[2].last(); k++) {
412  for(int j = idx[1].first(); j <= idx[1].last(); j++) {
413  for(int i = idx[0].first(); i <= idx[0].last(); i++) {
414 
415  Vector_t vv = Vector_t(0.0);
416  vv(0) = i * hr_m[0] - hr_m[0] / 2;
417  vv(1) = j * hr_m[1] - hr_m[1] / 2;
418  vv(2) = k * hr_m[2] - hr_m[2] / 2 + zshift;
419 
420  double r = sqrt(vv(0) * vv(0) + vv(1) * vv(1) + vv(2) * vv(2));
421  double tmpgrn = -vv(2) * vv(2) * atan(vv(0) * vv(1) / (vv(2) * r)) / 2;
422  tmpgrn += -vv(1) * vv(1) * atan(vv(0) * vv(2) / (vv(1) * r)) / 2;
423  tmpgrn += -vv(0) * vv(0) * atan(vv(1) * vv(2) / (vv(0) * r)) / 2;
424  tmpgrn += vv(1) * vv(2) * log(vv(0) + r);
425  tmpgrn += vv(0) * vv(2) * log(vv(1) + r);
426  tmpgrn += vv(0) * vv(1) * log(vv(2) + r);
427 
428  tmpgreen_m[i][j][k] = tmpgrn / cellVolume;
429 
430  }
431  }
432  }
433 
434  for(int k = idx[2].first(); k <= idx[2].last(); k++) {
435  for(int j = idx[1].first(); j <= idx[1].last(); j++) {
436  for(int i = idx[0].first(); i <= idx[0].last(); i++) {
437 
438  Vector_t vv = Vector_t(0.0);
439  vv(0) = i * hr_m[0] - hr_m[0] / 2;
440  vv(1) = j * hr_m[1] - hr_m[1] / 2;
441  vv(2) = k * hr_m[2] - hr_m[2] / 2 + zshift - nr_m[2] * hr_m[2];
442 
443  double r = sqrt(vv(0) * vv(0) + vv(1) * vv(1) + vv(2) * vv(2));
444  double tmpgrn = -vv(2) * vv(2) * atan(vv(0) * vv(1) / (vv(2) * r)) / 2;
445  tmpgrn += -vv(1) * vv(1) * atan(vv(0) * vv(2) / (vv(1) * r)) / 2;
446  tmpgrn += -vv(0) * vv(0) * atan(vv(1) * vv(2) / (vv(0) * r)) / 2;
447  tmpgrn += vv(1) * vv(2) * log(vv(0) + r);
448  tmpgrn += vv(0) * vv(2) * log(vv(1) + r);
449  tmpgrn += vv(0) * vv(1) * log(vv(2) + r);
450 
451  grn2[i][j][k] = tmpgrn / cellVolume;
452 
453  }
454  }
455  }
468  Index I = nr_m[0] + 1;
469  Index J = nr_m[1] + 1;
470  Index K = nr_m[2] + 1;
471 
472  // the actual integration
473  rho2_m = 0.0;
474  rho2_m[I][J][K] = tmpgreen_m[I+1][J+1][K+1];
475  rho2_m[I][J][K] += tmpgreen_m[I+1][J][K];
476  rho2_m[I][J][K] += tmpgreen_m[I][J+1][K];
477  rho2_m[I][J][K] += tmpgreen_m[I][J][K+1];
478  rho2_m[I][J][K] -= tmpgreen_m[I+1][J+1][K];
479  rho2_m[I][J][K] -= tmpgreen_m[I+1][J][K+1];
480  rho2_m[I][J][K] -= tmpgreen_m[I][J+1][K+1];
481  rho2_m[I][J][K] -= tmpgreen_m[I][J][K];
482 
483  tmpgreen_m = 0.0;
484  tmpgreen_m[I][J][K] = grn2[I+1][J+1][K+1];
485  tmpgreen_m[I][J][K] += grn2[I+1][J][K];
486  tmpgreen_m[I][J][K] += grn2[I][J+1][K];
487  tmpgreen_m[I][J][K] += grn2[I][J][K+1];
488  tmpgreen_m[I][J][K] -= grn2[I+1][J+1][K];
489  tmpgreen_m[I][J][K] -= grn2[I+1][J][K+1];
490  tmpgreen_m[I][J][K] -= grn2[I][J+1][K+1];
491  tmpgreen_m[I][J][K] -= grn2[I][J][K];
492 
494 
495  fft_m->transform(-1, rho2_m, grntr_m);
496 }
497 
499 
500  Index aI(0, 2 * nr_m[0]);
501  Index aJ(0, 2 * nr_m[1]);
502 
503  Index J(0, nr_m[1]);
504  Index K(0, nr_m[2]);
505 
506  Index IE(nr_m[0] + 1, 2 * nr_m[0]);
507  Index JE(nr_m[1] + 1, 2 * nr_m[1]);
508  Index KE(nr_m[2] + 1, 2 * nr_m[2]);
509 
510  Index mirroredIE = 2 * nr_m[0] - IE;
511  Index mirroredJE = 2 * nr_m[1] - JE;
512  Index mirroredKE = 2 * nr_m[2] - KE;
513 
514  rho2_m[0][0][0] = rho2_m[0][0][1];
515 
516  rho2_m[IE][J ][K ] = rho2_m[mirroredIE][J ][K ];
517  rho2_m[aI][JE][K ] = rho2_m[aI ][mirroredJE][K ];
518  if (!bcz_m)
519  rho2_m[aI][aJ][KE] = rho2_m[aI ][aJ ][mirroredKE];
520 
521 }
522 
524 
525  Index aI(0, 2 * nr_m[0]);
526  Index aK(0, 2 * nr_m[2]);
527 
528  Index I(0, nr_m[0]);
529  Index J(0, nr_m[1]);
530  Index K(0, nr_m[2]);
531 
532  Index IE(nr_m[0] + 1, 2 * nr_m[0]);
533  Index JE(nr_m[1] + 1, 2 * nr_m[1]);
534  Index KE(nr_m[2] + 1, 2 * nr_m[2]);
535 
536  Index mirroredIE = 2*nr_m[0] - IE;
537  Index mirroredJE = 2*nr_m[1] - JE;
538  Index shiftedKE = KE - nr_m[2];
539 
540  if (!bcz_m) {
541  rho2_m[I ][J ][KE] = ggrn2[I ][J ][shiftedKE];
542  rho2_m[IE][J ][aK] = rho2_m[mirroredIE][J ][aK ];
543  rho2_m[aI][JE][aK] = rho2_m[aI ][mirroredJE][aK ];
544  } else {
545  rho2_m[IE][J ][K] = rho2_m[mirroredIE][J ][K ];
546  rho2_m[aI][JE][K] = rho2_m[aI ][mirroredJE][K ];
547  }
548 }
549 
551  os << "* ************* F F T P o i s s o n S o l v e r ************************************ " << endl;
552  os << "* h " << hr_m << '\n';
553  os << "* ********************************************************************************** " << endl;
554  return os;
555 }
Tps< T > log(const Tps< T > &x)
Natural logarithm.
Definition: TpsMath.h:182
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
UniformCartesian< 3, double > Mesh_t
Definition: PBunchDefs.h:22
CenteredFieldLayout< 3, Mesh_t, Center_t > FieldLayout_t
Definition: PBunchDefs.h:28
Inform * gmsg
Definition: Main.cpp:62
e_dim_tag
Definition: FieldLayout.h:55
PETE_TTTree< OpWhere, typename Cond_t::PETE_Expr_t, typename True_t::PETE_Expr_t, PETE_Scalar< Vektor< T, Dim > > > where(const PETE_Expr< Cond_t > &c, const PETE_Expr< True_t > &t, const Vektor< T, Dim > &f)
PETE_TUTree< FnArcTan, typename T::PETE_Expr_t > atan(const PETE_Expr< T > &l)
Definition: PETE.h:727
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
bool lt(double x, double y)
static void calculate(Vektor< T, 3 > &hrsq, FT &grn, FT2 *grnI)
NDIndex< 3 > domainFFTConstruct_m
FieldLayout_t * layout_m
std::unique_ptr< Mesh_t > mesh4_m
void shiftedIntGreensFunction(double zshift)
compute the shifted integrated Green function as described in Three-dimensional quasistatic model for...
std::unique_ptr< FieldLayout_t > layout3_m
void computePotential(Field_t &rho, Vector_t hr, double zshift)
FFT< RCTransform, 3, double > FFT_t
IpplTimings::TimerRef GreensFunctionTimer_m
NDIndex< 3 > domain2_m
std::unique_ptr< FFT_t > fft_m
NDIndex< 3 > domain_m
FFTPoissonSolver(PartBunch &bunch, std::string greensFuntion)
std::unique_ptr< FieldLayout_t > layout2_m
BConds< Vector_t, 3, Mesh_t, Center_t > vbc_m
IField_t grnIField_m[3]
NDIndex< 3 > domain3_m
std::unique_ptr< Mesh_t > mesh3_m
Inform & print(Inform &os) const
NDIndex< 3 > domain4_m
std::unique_ptr< Mesh_t > mesh2_m
Vektor< int, 3 > nr_m
std::unique_ptr< FieldLayout_t > layout4_m
BConds< double, 3, Mesh_t, Center_t > bc_m
for defining the boundary conditions
IpplTimings::TimerRef ComputePotential_m
void integratedGreensFunction()
compute the integrated Green function as described in Three-dimensional quasistatic model for high br...
Definition: Vektor.h:32
void initialize(Layout_t &)
const NDIndex< Dim > & getDomain() const
Definition: FieldLayout.h:325
e_dim_tag getRequestedDistribution(unsigned int d) const
Definition: FieldLayout.h:405
MFLOAT get_meshSpacing(unsigned d) const
Definition: Index.h:237
Definition: Inform.h:42
static TimerRef getTimer(const char *nm)
Definition: IpplTimings.h:182
static void stopTimer(TimerRef t)
Definition: IpplTimings.h:192
static void startTimer(TimerRef t)
Definition: IpplTimings.h:187
Vektor< double, 3 > Vector_t
Definition: Vektor.h:6