OPAL (Object Oriented Parallel Accelerator Library) 2022.1
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
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
24extern 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
32template<unsigned int Dim>
34
35template<>
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
50FFTPoissonSolver::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
70FFTPoissonSolver::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) {
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
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.
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
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
387 // the actual integration
388 rho2_m = 0.0;
389 rho2_m[I][J][K] = tmpgreen_m[I+1][J+1][K+1];
390 rho2_m[I][J][K] += tmpgreen_m[I+1][J][K];
391 rho2_m[I][J][K] += tmpgreen_m[I][J+1][K];
392 rho2_m[I][J][K] += tmpgreen_m[I][J][K+1];
393 rho2_m[I][J][K] -= tmpgreen_m[I+1][J+1][K];
394 rho2_m[I][J][K] -= tmpgreen_m[I+1][J][K+1];
395 rho2_m[I][J][K] -= tmpgreen_m[I][J+1][K+1];
396 rho2_m[I][J][K] -= tmpgreen_m[I][J][K];
397
399
400 fft_m->transform(-1, rho2_m, grntr_m);
401
402}
403
405
406 tmpgreen_m = 0.0;
407 Field_t grn2(*mesh4_m, *layout4_m);
408 grn2 = 0.0;
409 NDIndex<3> idx = layout4_m->getLocalNDIndex();
410 double cellVolume = hr_m[0] * hr_m[1] * hr_m[2];
411
412 for(int k = idx[2].first(); k <= idx[2].last(); k++) {
413 for(int j = idx[1].first(); j <= idx[1].last(); j++) {
414 for(int i = idx[0].first(); i <= idx[0].last(); i++) {
415
416 Vector_t vv = Vector_t(0.0);
417 vv(0) = i * hr_m[0] - hr_m[0] / 2;
418 vv(1) = j * hr_m[1] - hr_m[1] / 2;
419 vv(2) = k * hr_m[2] - hr_m[2] / 2 + zshift;
420
421 double r = sqrt(vv(0) * vv(0) + vv(1) * vv(1) + vv(2) * vv(2));
422 double tmpgrn = -vv(2) * vv(2) * atan(vv(0) * vv(1) / (vv(2) * r)) / 2;
423 tmpgrn += -vv(1) * vv(1) * atan(vv(0) * vv(2) / (vv(1) * r)) / 2;
424 tmpgrn += -vv(0) * vv(0) * atan(vv(1) * vv(2) / (vv(0) * r)) / 2;
425 tmpgrn += vv(1) * vv(2) * log(vv(0) + r);
426 tmpgrn += vv(0) * vv(2) * log(vv(1) + r);
427 tmpgrn += vv(0) * vv(1) * log(vv(2) + r);
428
429 tmpgreen_m[i][j][k] = tmpgrn / cellVolume;
430
431 }
432 }
433 }
434
435 for(int k = idx[2].first(); k <= idx[2].last(); k++) {
436 for(int j = idx[1].first(); j <= idx[1].last(); j++) {
437 for(int i = idx[0].first(); i <= idx[0].last(); i++) {
438
439 Vector_t vv = Vector_t(0.0);
440 vv(0) = i * hr_m[0] - hr_m[0] / 2;
441 vv(1) = j * hr_m[1] - hr_m[1] / 2;
442 vv(2) = k * hr_m[2] - hr_m[2] / 2 + zshift - nr_m[2] * hr_m[2];
443
444 double r = sqrt(vv(0) * vv(0) + vv(1) * vv(1) + vv(2) * vv(2));
445 double tmpgrn = -vv(2) * vv(2) * atan(vv(0) * vv(1) / (vv(2) * r)) / 2;
446 tmpgrn += -vv(1) * vv(1) * atan(vv(0) * vv(2) / (vv(1) * r)) / 2;
447 tmpgrn += -vv(0) * vv(0) * atan(vv(1) * vv(2) / (vv(0) * r)) / 2;
448 tmpgrn += vv(1) * vv(2) * log(vv(0) + r);
449 tmpgrn += vv(0) * vv(2) * log(vv(1) + r);
450 tmpgrn += vv(0) * vv(1) * log(vv(2) + r);
451
452 grn2[i][j][k] = tmpgrn / cellVolume;
453
454 }
455 }
456 }
469 Index I = nr_m[0] + 1;
470 Index J = nr_m[1] + 1;
471 Index K = nr_m[2] + 1;
472
473 // the actual integration
474 rho2_m = 0.0;
475 rho2_m[I][J][K] = tmpgreen_m[I+1][J+1][K+1];
476 rho2_m[I][J][K] += tmpgreen_m[I+1][J][K];
477 rho2_m[I][J][K] += tmpgreen_m[I][J+1][K];
478 rho2_m[I][J][K] += tmpgreen_m[I][J][K+1];
479 rho2_m[I][J][K] -= tmpgreen_m[I+1][J+1][K];
480 rho2_m[I][J][K] -= tmpgreen_m[I+1][J][K+1];
481 rho2_m[I][J][K] -= tmpgreen_m[I][J+1][K+1];
482 rho2_m[I][J][K] -= tmpgreen_m[I][J][K];
483
484 tmpgreen_m = 0.0;
485 tmpgreen_m[I][J][K] = grn2[I+1][J+1][K+1];
486 tmpgreen_m[I][J][K] += grn2[I+1][J][K];
487 tmpgreen_m[I][J][K] += grn2[I][J+1][K];
488 tmpgreen_m[I][J][K] += grn2[I][J][K+1];
489 tmpgreen_m[I][J][K] -= grn2[I+1][J+1][K];
490 tmpgreen_m[I][J][K] -= grn2[I+1][J][K+1];
491 tmpgreen_m[I][J][K] -= grn2[I][J+1][K+1];
492 tmpgreen_m[I][J][K] -= grn2[I][J][K];
493
495
496 fft_m->transform(-1, rho2_m, grntr_m);
497}
498
500
501 Index aI(0, 2 * nr_m[0]);
502 Index aJ(0, 2 * nr_m[1]);
503
504 Index J(0, nr_m[1]);
505 Index K(0, nr_m[2]);
506
507 Index IE(nr_m[0] + 1, 2 * nr_m[0]);
508 Index JE(nr_m[1] + 1, 2 * nr_m[1]);
509 Index KE(nr_m[2] + 1, 2 * nr_m[2]);
510
511 Index mirroredIE = 2 * nr_m[0] - IE;
512 Index mirroredJE = 2 * nr_m[1] - JE;
513 Index mirroredKE = 2 * nr_m[2] - KE;
514
515 rho2_m[0][0][0] = rho2_m[0][0][1];
516
517 rho2_m[IE][J ][K ] = rho2_m[mirroredIE][J ][K ];
518 rho2_m[aI][JE][K ] = rho2_m[aI ][mirroredJE][K ];
519 if (!bcz_m)
520 rho2_m[aI][aJ][KE] = rho2_m[aI ][aJ ][mirroredKE];
521
522}
523
525
526 Index aI(0, 2 * nr_m[0]);
527 Index aK(0, 2 * nr_m[2]);
528
529 Index I(0, nr_m[0]);
530 Index J(0, nr_m[1]);
531 Index K(0, nr_m[2]);
532
533 Index IE(nr_m[0] + 1, 2 * nr_m[0]);
534 Index JE(nr_m[1] + 1, 2 * nr_m[1]);
535 Index KE(nr_m[2] + 1, 2 * nr_m[2]);
536
537 Index mirroredIE = 2*nr_m[0] - IE;
538 Index mirroredJE = 2*nr_m[1] - JE;
539 Index shiftedKE = KE - nr_m[2];
540
541 if (!bcz_m) {
542 rho2_m[I ][J ][KE] = ggrn2[I ][J ][shiftedKE];
543 rho2_m[IE][J ][aK] = rho2_m[mirroredIE][J ][aK ];
544 rho2_m[aI][JE][aK] = rho2_m[aI ][mirroredJE][aK ];
545 } else {
546 rho2_m[IE][J ][K] = rho2_m[mirroredIE][J ][K ];
547 rho2_m[aI][JE][K] = rho2_m[aI ][mirroredJE][K ];
548 }
549}
550
552 os << "* ************* F F T P o i s s o n S o l v e r ************************************ " << endl;
553 os << "* h " << hr_m << '\n';
554 os << "* ********************************************************************************** " << endl;
555 return os;
556}
UniformCartesian< 3, double > Mesh_t
Definition: PBunchDefs.h:22
CenteredFieldLayout< 3, Mesh_t, Center_t > FieldLayout_t
Definition: PBunchDefs.h:28
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
Inform * gmsg
Definition: Main.cpp:61
e_dim_tag
Definition: FieldLayout.h:55
PETE_TUTree< FnArcTan, typename T::PETE_Expr_t > atan(const PETE_Expr< T > &l)
Definition: PETE.h:727
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)
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