OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
P3MPoissonSolver.cpp
Go to the documentation of this file.
1 // -*- C++ -*-
2 /***************************************************************************
3  *
4  *
5  * P3MPoissonSolver.cc
6  *
7  *
8  *
9  *
10  *
11  *
12  *
13  ***************************************************************************/
14 
15 // include files
17 #include "Algorithms/PartBunch.h"
21 //#include "Particle/PairBuilder/HashPairBuilderPeriodicParallel_globCHaining.h"
23 #include "Structure/DataSink.h"
25 #include "Physics/Physics.h"
26 #include <fstream>
28 // a little helper class to specialize the action of the Green's function
29 // calculation. This should be specialized for each dimension
30 // to the proper action for computing the Green's function. The first
31 // template parameter is the full type of the Field to compute, and the second
32 // is the dimension of the data, which should be specialized.
33 
34 //const double ke=1./(4.*M_PI*8.8e-14);
35 const double ke=2.532638e8;
36 
37 template<unsigned int Dim>
38 struct SpecializedGreensFunction { };
39 
40 template<>
41 struct SpecializedGreensFunction<3> {
42  template<class T, class FT, class FT2>
43  static void calculate(Vektor<T, 3> &hrsq, FT &grn, FT2 *grnI, double alpha,double eps) {
44  double r;
45  NDIndex<3> elem0=NDIndex<3>(Index(0,0), Index(0,0),Index(0,0));
46  grn = grnI[0] * hrsq[0] + grnI[1] * hrsq[1] + grnI[2] * hrsq[2];
47  NDIndex<3> lDomain_m = grn.getLayout().getLocalNDIndex();
48  NDIndex<3> elem;
49  for (int i=lDomain_m[0].min(); i<=lDomain_m[0].max(); ++i) {
50  elem[0]=Index(i,i);
51  for (int j=lDomain_m[1].min(); j<=lDomain_m[1].max(); ++j) {
52  elem[1]=Index(j,j);
53  for (int k=lDomain_m[2].min(); k<=lDomain_m[2].max(); ++k) {
54  elem[2]=Index(k,k);
55  r = real(sqrt(grn.localElement(elem)));
56  if(elem==elem0) {
57  //grn.localElement(elem) = ke*dcomplex(2*alpha/sqrt(M_PI)) ;
58  grn.localElement(elem) = 0 ;
59  }
60  else
61  grn.localElement(elem) = ke*dcomplex(erf(alpha*r)/(r+eps));
62  }
63  }
64  }
65  }
66 };
67 
68 template<class T>
69 struct ApplyField {
70  ApplyField(T c, double r, double epsilon, double alpha) : C(c), R(r), eps(epsilon), a(alpha) {}
71  void operator()(std::size_t i, std::size_t j, PartBunch &P,Vektor<double,3> &shift) const
72  {
73  Vector_t diff = P.R[i] - (P.R[j]+shift);
74  double sqr = 0;
75 
76  for (unsigned d = 0; d<Dim; ++d)
77  sqr += diff[d]*diff[d];
78 
79  //compute r with softening parameter, unsoftened r is obtained by sqrt(sqr)
80  if(sqr!=0) {
81  double r = std::sqrt(sqr+eps*eps);
82  //for order two transition
83  if (P.Q[i]!=0 && P.Q[j]!=0) {
84  //compute potential energy
85  double phi =ke*(1.-erf(a*sqrt(sqr)))/r;
86 
87  //compute force
88  Vector_t Fij = ke*C*(diff/sqrt(sqr))*((2.*a*exp(-a*a*sqr))/(sqrt(M_PI)*r)+(1.-erf(a*sqrt(sqr)))/(r*r));
89 
90  //Actual Force is F_ij multiplied by Qi*Qj
91  //The electrical field on particle i is E=F/q_i and hence:
92  P.Ef[i] -= P.Q[j]*Fij;
93  P.Ef[j] += P.Q[i]*Fij;
94  //update potential per particle
95  P.Phi[i] += P.Q[j]*phi;
96  P.Phi[j] += P.Q[i]*phi;
97  }
98  }
99  }
100  T C;
101  double R;
102  double eps;
103  double a;
104 };
105 
106 
108 
109 // constructor
110 
111 
112 P3MPoissonSolver::P3MPoissonSolver(Mesh_t *mesh, FieldLayout_t *fl, double interaction_radius, double alpha, double eps):
113  mesh_m(mesh),
114  layout_m(fl),
115  interaction_radius_m(interaction_radius),
116  alpha_m(alpha),
117  eps_m(eps)
118 {
119  Inform msg("P3MPoissonSolver::P3MPoissonSolver ");
120 
122 
123  for (unsigned int i = 0; i < 2 * Dim; ++i) {
124  if (Ippl::getNodes()>1) {
126  //std periodic boundary conditions for gradient computations etc.
129  }
130  else {
132  //std periodic boundary conditions for gradient computations etc.
135  }
136  }
137 
138  for (unsigned int i = 0; i < 2 * Dim; ++i) {
139  if (Ippl::getNodes()>1) {
141  //std periodic boundary conditions for gradient computations etc.
144  }
145  else {
147  //std periodic boundary conditions for gradient computations etc.
150  }
151  }
152 
153 
154  // For efficiency in the FFT's, we can use a parallel decomposition
155  // which can be serial in the first dimension.
156  // e_dim_tag decomp[3];
157  // for(int d = 0; d < 3; ++d) {
158  // decomp[d] = layout_m->getRequestedDistribution(d);
159  // }
160 
161  // The FFT's require double-sized field sizes in order to (more closely
162  // do not understand this ...)
163  // simulate an isolated system. The FFT of the charge density field, rho,
164  // would otherwise mimic periodic boundary conditions, i.e. as if there were
165  // several beams set a periodic distance apart. The double-sized fields
166  // alleviate this problem.
167  for(unsigned int i = 0; i < 3; i++) {
168  hr_m[i] = mesh_m->get_meshSpacing(i);
169  nr_m[i] = domain_m[i].length();
170  }
171 
174 
178 
179  bool compressTemps = true;
180  // create the FFT object
181  fft_m = std::unique_ptr<FFTC_t>(new FFTC_t(layout_m->getDomain(), compressTemps));
182  fft_m->setDirectionName(+1, "forward");
183  fft_m->setDirectionName(-1, "inverse");
184 }
185 
187 
189 
190  for (unsigned int i = 0; i < 2 * Dim; ++i) {
191  if (Ippl::getNodes()>1) {
193  //std periodic boundary conditions for gradient computations etc.
196  }
197  else {
199  //std periodic boundary conditions for gradient computations etc.
202  }
203  }
204 
205  for (unsigned int i = 0; i < 2 * Dim; ++i) {
206  if (Ippl::getNodes()>1) {
208  //std periodic boundary conditions for gradient computations etc.
211  }
212  else {
214  //std periodic boundary conditions for gradient computations etc.
217  }
218  }
219 
220  for(unsigned int i = 0; i < 3; i++) {
221  hr_m[i] = mesh_m->get_meshSpacing(i);
222  nr_m[i] = domain_m[i].length();
223  }
224 
227 
231 
232  bool compressTemps = true;
233  if (fft_m)
234  fft_m.reset();
235 
236  // create the FFT object
237  fft_m = std::unique_ptr<FFTC_t>(new FFTC_t(layout_m->getDomain(), compressTemps));
238  fft_m->setDirectionName(+1, "forward");
239  fft_m->setDirectionName(-1, "inverse");
240 
241 }
242 
243 
244 
246 // destructor
248 }
249 
250 void P3MPoissonSolver::calculatePairForces(PartBunchBase<double, 3> *bunch, double interaction_radius, double alpha, double eps) {
251  if (interaction_radius>0){
252  if (Ippl::getNodes() > 1) {
253  PartBunch &tmpBunch = *(dynamic_cast<PartBunch*>(bunch));
255  HPB.for_each(RadiusCondition<double, Dim>(interaction_radius), ApplyField<double>(-1,interaction_radius,eps,alpha),extend_l, extend_r);
256  }
257  else {
258  PartBunch &tmpBunch = *(dynamic_cast<PartBunch*>(bunch));
260  HPB.for_each(RadiusCondition<double, Dim>(interaction_radius), ApplyField<double>(-1,interaction_radius,eps,alpha),extend_l, extend_r);
261  }
262  }
263 
264 }
265 
266 void P3MPoissonSolver::calculateGridForces(PartBunchBase<double, 3> *bunch, double interaction_radius, double alpha, double eps){
267 
268  Inform msg ("calculateGridForces ");
269  Vector_t l,h;
270 
271  // (1) scatter charge to charge density grid and transform to fourier space
272  rho_m[domain_m]=0;
273  bunch->Q.scatter(rho_m, bunch->R, IntrplCIC_t());
274 
275  rhocmpl_m[domain_m] = rho_m[domain_m]/(hr_m[0]*hr_m[1]*hr_m[2]);
276 
277  fft_m->transform("inverse", rhocmpl_m);
278 
279  // (2) compute Greens function in real space and transform to fourier space
280  IField_t grnIField[3];
281 
282  // This loop stores in grnIField_m[i] the index of the ith dimension mirrored at the central axis. e.g.
283  // grnIField_m[0]=[(0 1 2 3 ... 3 2 1) ; (0 1 2 3 ... 3 2 1; ...)]
284  for (int i = 0; i < 3; ++i) {
285  grnIField[i].initialize(*mesh_m, *layout_m);
286  grnIField[i][domain_m] = where(lt(domain_m[i], nr_m[i]/2),
287  domain_m[i] * domain_m[i],
288  (nr_m[i]-domain_m[i]) *
289  (nr_m[i]-domain_m[i]));
290  }
291  Vector_t hrsq(hr_m * hr_m);
292  SpecializedGreensFunction<3>::calculate(hrsq, grncmpl_m, grnIField, alpha, eps);
293 
294  //transform G -> Ghat and store in grncmpl_m
295  fft_m->transform("inverse", grncmpl_m);
296  //multiply in fourier space and obtain PhiHat in rhocmpl_m
297  rhocmpl_m *= grncmpl_m;
298 
299  // (3) Backtransformation: compute potential field in real space and E=-Grad Phi
300  //compute electrostatic potential Phi in real space by FFT PhiHat -> Phi and store it in rhocmpl_m
301  fft_m->transform("forward", rhocmpl_m);
302 
303  //take only the real part and store in phi_m (has periodic bc instead of interpolation bc)
304  phi_m = real(rhocmpl_m)*hr_m[0]*hr_m[1]*hr_m[2];
305  //dumpVTKScalar( phi_m, this,it, "Phi_m") ;
306 
307  //compute Electric field on the grid by -Grad(Phi) store in eg_m
308  eg_m = -Grad1Ord(phi_m, eg_m);
309 
310  //interpolate the electric field to the particle positions
311  bunch->Ef.gather(eg_m, bunch->R, IntrplCIC_t());
312  //interpolate electrostatic potenital to the particle positions
313  bunch->Phi.gather(phi_m, bunch->R, IntrplCIC_t());
314 }
315 
316 
317 
319 // given a charge-density field rho and a set of mesh spacings hr,
320 // compute the electric potential from the image charge by solving
321 // the Poisson's equation
322 
323 void P3MPoissonSolver::computePotential(Field_t &rho, Vector_t hr, double zshift) {
324 
325 
326 }
327 
329 
330  Inform m("computeAvgSpaceChargeForces ");
331 
332  const double N = static_cast<double>(bunch->getTotalNum());
333  double locAvgEf[Dim]={};
334  for (unsigned i=0; i<bunch->getLocalNum(); ++i) {
335  locAvgEf[0]+=fabs(bunch->Ef[i](0));
336  locAvgEf[1]+=fabs(bunch->Ef[i](1));
337  locAvgEf[2]+=fabs(bunch->Ef[i](2));
338  }
339 
340  reduce(&(locAvgEf[0]), &(locAvgEf[0]) + Dim,
341  &(globSumEf_m[0]), OpAddAssign());
342 
343  // m << "globSumEF = " << globSumEf_m[0] << "\t" << globSumEf_m[1] << "\t" << globSumEf_m[2] << endl;
344 
345  avgEF_m[0]=globSumEf_m[0]/N;
346  avgEF_m[1]=globSumEf_m[1]/N;
347  avgEF_m[2]=globSumEf_m[2]/N;
348 
349 }
350 
351 
353  Vektor<double,Dim> beam_center(0,0,0);
354  Vector_t Rrel;
355  double scFoc = sqrt(dot(avgEF_m,avgEF_m));
356  for (unsigned i=0; i<bunch->getLocalNum(); ++i) {
357  Rrel=bunch->R[i] - beam_center;
358  bunch->Ef[i] += Rrel/r*f*scFoc;
359  }
360 
361 }
362 
363 // given a charge-density field rho and a set of mesh spacings hr,
364 // compute the scalar potential in open space
366 
367 
368 }
369 
371 
372  Inform m("compute_temperature ");
373  // double loc_temp[Dim]={0.0,0.0,0.0};
374  double loc_avg_vel[Dim]={0.0,0.0,0.0};
375  double avg_vel[Dim]={0.0,0.0,0.0};
376 
377  for(unsigned long k = 0; k < bunch->getLocalNum(); ++k) {
378  for(unsigned i = 0; i < Dim; i++) {
379  loc_avg_vel[i] += bunch->P[k](i);
380  }
381  }
382  reduce(&(loc_avg_vel[0]), &(loc_avg_vel[0]) + Dim,
383  &(avg_vel[0]), OpAddAssign());
384 
385  const double N = static_cast<double>(bunch->getTotalNum());
386  avg_vel[0]=avg_vel[0]/N;
387  avg_vel[1]=avg_vel[1]/N;
388  avg_vel[2]=avg_vel[2]/N;
389 
390  m << "avg_vel[0]= " << avg_vel[0] << " avg_vel[1]= " << avg_vel[1] << " avg_vel[2]= " << avg_vel[2] << endl;
391 
392  /*
393  for(unsigned long k = 0; k < bunch.getLocalNum(); ++k) {
394  for(unsigned i = 0; i < Dim; i++) {
395  loc_temp[i] += (bunch.P[k](i)-avg_vel[i])*(bunch.P[k](i)-avg_vel[i]);
396  }
397  }
398  reduce(&(loc_temp[0]), &(loc_temp[0]) + Dim,
399  &(temperature[0]), OpAddAssign());
400  temperature[0]=temperature[0]/N;
401  temperature[1]=temperature[1]/N;
402  temperature[2]=temperature[2]/N;
403  */
404 }
405 
407  Inform msg("P3MPoissonSolver::test ");
408 
409  // set special conditions for this test
410  const double mi = 1.0;
411  const double qi = -1.0;
412  const double qom = qi/mi;
413  const double beam_radius = 0.001774;
414  const double f = 1.5;
415  const double dt = bunch->getdT();
416 
418  DataSink *ds = opal->getDataSink();
419 
420  // std::vector<std::pair<std::string, unsigned int> > collimatorLosses; // just empty
421  Vector_t FDext[6];
422 
423  bunch->Q = qi;
424  bunch->M = mi;
425 
426  bunch->calcBeamParameters();
427 
428  initFields();
429 
430  for (int i=0; i<3; i++) {
431  extend_r[i] = hr_m[i]*nr_m[i]/2;
432  extend_l[i] = -hr_m[i]*nr_m[i]/2;
433  }
434 
435  msg << *this << endl;
436 
437  // calculate initial space charge forces
440 
441  //avg space charge forces for constant focusing
443 
444  for (int it=0; it<1000; it++) {
445 
446  // advance the particle positions
447  // basic leapfrogging timestep scheme. velocities are offset
448  // by half a timestep from the positions.
449 
450  assign(bunch->R, bunch->R + dt * bunch->P);
451 
452  bunch->update();
453 
456  applyConstantFocusing(bunch,f,beam_radius);
457 
458  assign(bunch->P, bunch->P + dt * qom * bunch->Ef);
459 
460  if (it%10 == 0){
461  bunch->calcBeamParameters();
462  ds->dumpSDDS(bunch, FDext, it);
463  }
464  msg << "Finished iteration " << it << endl;
465  }
466 }
467 
468 
470  os << "* ************* P 3 M - P o i s s o n S o l v e r *************** " << endl;
471  os << "* h " << hr_m << '\n';
472  os << "* RC " << interaction_radius_m << '\n';
473  os << "* ALPHA " << alpha_m << '\n';
474  os << "* EPSILON " << eps_m << '\n';
475  os << "* Extend L " << extend_l << '\n';
476  os << "* Extend R " << extend_r << '\n';
477  os << "* hr " << hr_m << '\n';
478  os << "* nr " << nr_m << '\n';
479  os << "* *************************************************************** " << endl;
480  return os;
481 }
482 
483 /***************************************************************************
484  * $RCSfile: P3MPoissonSolver.cc,v $ $Author: adelmann $
485  * $Revision: 1.6 $ $Date: 2001/08/16 09:36:08 $
486  ***************************************************************************/
static int getNodes()
Definition: IpplInfo.cpp:773
ParticleAttrib< Vector_t > P
The global OPAL structure.
Definition: OpalData.h:54
NDIndex< 3 > domain_m
ParticleAttrib< Vector_t > Ef
Definition: TSVMeta.h:24
Definition: rbendmap.h:8
void applyConstantFocusing(PartBunchBase< double, 3 > *bunch, double f, double r)
void scatter(Field< T, Dim, M, C > &f, const ParticleAttrib< Vektor< PT, Dim > > &pp, const IntOp &intop) const
FTps< T, N > erf(const FTps< T, N > &x, int trunc=(FTps< T, N >::EXACT))
Error function.
Definition: FTpsMath.h:385
static void calculate(Vektor< T, 3 > &hrsq, FT &grn, FT2 *grnI, double alpha, double eps)
PETE_TUTree< FnFabs, typename T::PETE_Expr_t > fabs(const PETE_Expr< T > &l)
Definition: PETE.h:815
ParticleAttrib< double > Q
FFT< CCTransform, 3, double > FFTC_t
Definition: PBunchDefs.h:56
Tps< T > exp(const Tps< T > &x)
Exponential.
Definition: TpsMath.h:165
double getdT() const
std::complex< double > dcomplex
Definition: dcomplex.h:30
void for_each(const Pred &pred, const OP &op, Vektor< double, 3 > extend_l, Vektor< double, 3 > extend_r)
P3MPoissonSolver(Mesh_t *mesh, FieldLayout_t *fl, double interaction_radius, double alpha, double eps)
void test(PartBunchBase< double, 3 > *bunch)
BConds< Vector_t, Dim, Mesh_t, Center_t > vbc_m
void assign(const BareField< T, Dim > &a, RHS b, OP op, ExprTag< true >)
size_t getTotalNum() const
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
Definition: Vector3D.cpp:118
FLieGenerator< T, N > real(const FLieGenerator< std::complex< T >, N > &)
Take real part of a complex generator.
constexpr double alpha
The fine structure constant, no dimension.
Definition: Physics.h:79
bool reduce(Communicate &, InputIterator, InputIterator, OutputIterator, const ReduceOp &, bool *IncludeVal=0)
Definition: GlobalComm.hpp:55
static OpalData * getInstance()
Definition: OpalData.cpp:209
void compute_temperature(PartBunchBase< double, 3 > *bunch)
void calcBeamParameters()
Definition: Index.h:236
double globSumEf_m[Dim]
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:52
FieldLayout_t * layout_m
Vektor< int, 3 > nr_m
PETE_TBTree< OpLT, Index::PETE_Expr_t, PETE_Scalar< double > > lt(const Index &idx, double x)
Definition: IndexInlines.h:352
void for_each(const Pred &pred, const OP &op, Vektor< double, 3 > extend_l, Vektor< double, 3 > extend_r)
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)
void computeAvgSpaceChargeForces(PartBunchBase< double, 3 > *bunch)
std::unique_ptr< FFTC_t > fft_m
void computePotential(Field_t &rho, Vector_t hr, double zshift)
BConds< double, Dim, Mesh_t, Center_t > bcp_m
size_t getLocalNum() const
void operator()(std::size_t i, std::size_t j, PartBunch &P, Vektor< double, 3 > &shift) const
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
Vektor< double, 3 > extend_l
Inform & print(Inform &os) const
ParticleAttrib< double > Phi
BConds< double, Dim, Mesh_t, Center_t > bc_m
Field< Vektor< T, 3U >, 3U, UniformCartesian< 3U, MFLOAT >, Cell > & Grad1Ord(Field< T, 3U, UniformCartesian< 3U, MFLOAT >, Vert > &x, Field< Vektor< T, 3U >, 3U, UniformCartesian< 3U, MFLOAT >, Cell > &r)
Old Grad operator.
MFLOAT get_meshSpacing(unsigned d) const
void calculatePairForces(PartBunchBase< double, 3 > *bunch, double interaction_radius, double alpha, double eps)
ApplyField(T c, double r, double epsilon, double alpha)
void gather(const Field< T, Dim, M, C > &f, const ParticleAttrib< Vektor< PT, Dim > > &pp, const IntOp &)
DataSink * getDataSink()
Definition: OpalData.cpp:435
Particle Bunch.
Definition: PartBunch.h:30
void calculateGridForces(PartBunchBase< double, 3 > *bunch, double interaction_radius, double alpha, double eps)
void dumpSDDS(PartBunchBase< double, 3 > *beam, Vector_t FDext[], const double &azimuth=-1) const
Definition: DataSink.cpp:99
void initialize(Layout_t &)
const double ke
ParticleAttrib< double > M
ParticlePos_t & R
const unsigned Dim
Vektor< double, Dim > avgEF_m
Vektor< double, 3 > extend_r
Definition: Inform.h:41
T::PETE_Expr_t::PETE_Return_t min(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:95
const NDIndex< Dim > & getDomain() const
Definition: FieldLayout.h:325
Inform & endl(Inform &inf)
Definition: Inform.cpp:42