OPAL (Object Oriented Parallel Accelerator Library)  2024.1
OPAL
HashPairBuilderParallel.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 /***************************************************************************
3  *
4  * The IPPL Framework
5  *
6  *
7  * Visit http://people.web.psi.ch/adelmann/ for more details
8  *
9  ***************************************************************************/
10 
11 #ifndef HASH_PAIR_BUILDER_PARALLEL_H
12 #define HASH_PAIR_BUILDER_PARALLEL_H
13 
14 /*
15  * HashPairBuilderParallel - class for finding
16  * particle pairs in the P3M solver and
17  * compute particle-particle interactions
18  * between them
19  *
20  * This class follows the Hockney and Eastwood approach
21  * to efficiently find particle pairs. In this version
22  * of the code a local chaining mesh per processor
23  * is used to avoid looping empty buckets.
24  */
25 
26 
27 #include "Message/Communicate.h"
28 #include "Utility/IpplException.h"
29 
30 #include <algorithm>
31 #include <array>
32 #include <cmath>
33 #include <limits>
34 #include <set>
35 
36 template<class PBase>
38 {
39 public:
40  enum { Dim = PBase::Dim };
41  typedef typename PBase::Position_t Position_t;
42 
43  HashPairBuilderParallel(PBase& p_r, double gammaz) : particles_mr(p_r),
44  gammaz_m(gammaz)
45  { hr_m = p_r.get_hr(); }
46 
47  template<class Pred, class OP>
48  void forEach(const Pred& pred_r, const OP& op_r)
49  {
50  constexpr std::size_t END = std::numeric_limits<std::size_t>::max();
51  std::size_t size = particles_mr.getLocalNum()+particles_mr.getGhostNum();
52 
53  NDIndex<3> locDomain = particles_mr.getFieldLayout().getLocalNDIndex();
54 
55  rmin_m = particles_mr.getMesh().get_origin();
56  //To move to the boosted frame
57  rmin_m[2] *= gammaz_m;
58  hr_m[2] *= gammaz_m;
59 
60  //compute local chaining mesh
61  Vektor<double,3> extentLLocal, extentRLocal, domainWidthLocal;
62  for (unsigned i=0; i<3; ++i) {
63  extentLLocal[i] = locDomain[i].first()*hr_m[i]+rmin_m[i];
64  extentRLocal[i] = rmin_m[i]+(locDomain[i].last()+1)*hr_m[i];
65  domainWidthLocal[i] = extentRLocal[i]-extentLLocal[i];
66 
67  //make sure that the chaining mesh covers the whole domain
68  //and has a gridwidth > r_cut
69  bucketsPerDim_m[i] = std::floor(domainWidthLocal[i]/pred_r.getRange(i));
70 
71  if(bucketsPerDim_m[i] == 0) {
72  bucketsPerDim_m[i] = 1;
73  }
74 
75  hChaining_m[i] = domainWidthLocal[i]/bucketsPerDim_m[i];
76  }
77 
78 
79  //extend the chaining mesh by one layer of chaining cells in each dimension
80  rmin_m = extentLLocal-hChaining_m;
81  rmax_m = extentRLocal+hChaining_m;
82  bucketsPerDim_m+=2;
83 
84  std::size_t Nbucket = bucketsPerDim_m[0]*bucketsPerDim_m[1]*bucketsPerDim_m[2];
85 
86  //index of first particle in this bucket
87  std::vector<std::size_t> buckets(Nbucket);
88  //index of next particle in this bucket. END indicates last particle of bucket
89  std::vector<std::size_t> next(size);
90 
91  std::fill(buckets.begin(), buckets.end(), END);
92  std::fill(next.begin(), next.end(), END);
93 
94  //As per Hockney and Eastwood ``Computer simulation using particles" (section 8.4.3)
95  //we use Newton's third law
96  //in the force calculation and hence out of the total 27
97  //interactions in 3D we interact only
98  //with 13 neighboring cells + 1 self cell interaction
99  unsigned neigh = 14;
100 
101  int offset[14][3] = {{ 1, 1, 1}, { 0, 1, 1}, {-1, 1, 1},
102  { 1, 0, 1}, { 0, 0, 1}, {-1, 0, 1},
103  { 1,-1, 1}, { 0,-1, 1}, {-1,-1, 1},
104  { 1, 1, 0}, { 0, 1, 0}, {-1, 1, 0},
105  { 1, 0, 0}, { 0, 0, 0}};
106 
107  //assign all particles to a bucket
108  for(std::size_t i = 0;i<size;++i)
109  {
110  std::size_t bucketId = getBucketId(i);
111  if(bucketId >= Nbucket) {
112  std::cout << "Bucket with id: " << bucketId << " is wrong" << std::endl;
113  std::cout << "Rank: " << Ippl::myNode() << std::endl;
114  std::cout << "Buckets: " << bucketsPerDim_m << std::endl;
115  std::cout << "Particle coords: " << particles_mr.R[i] << std::endl;
116  std::cout << "rmin_m: " << rmin_m << "rmax_m: " << rmax_m << std::endl;
117  throw IpplException("HashPairBuilderParallel::forEach",
118  "Particle outside the local domain");
119  }
120  next[i] = buckets[bucketId];
121  buckets[bucketId] = i;
122  }
123 
124  //loop over all buckets
125  for (int bx=0; bx<bucketsPerDim_m[0]; ++bx) {
126  for (int by=0; by<bucketsPerDim_m[1]; ++by) {
127  for (int bz=0; bz<bucketsPerDim_m[2]; ++bz) {
128  unsigned bucketIdSelf = bz*bucketsPerDim_m[1]*bucketsPerDim_m[0]
129  +by*bucketsPerDim_m[0]+bx;
130  //compute index of neighboring bucket to interact with
131  for (unsigned n=0; n<neigh;++n){
132  int bxNeigh, byNeigh, bzNeigh;
133 
134  bxNeigh = bx+offset[n][0];
135  byNeigh = by+offset[n][1];
136  bzNeigh = bz+offset[n][2];
137 
138  if (bxNeigh >= 0 && bxNeigh<bucketsPerDim_m[0] &&
139  byNeigh >= 0 && byNeigh<bucketsPerDim_m[1] &&
140  bzNeigh >= 0 && bzNeigh<bucketsPerDim_m[2]) {
141 
142  unsigned bucketIdNeigh =
143  bzNeigh*bucketsPerDim_m[1]*bucketsPerDim_m[0]
144  +byNeigh*bucketsPerDim_m[0]+bxNeigh;
145 
146  //i is index of particle considered in active chaining cell,
147  //j is index of neighbor particle considered
148  std::size_t i = buckets[bucketIdSelf];
149  std::size_t j;
150 
151  //loop over all particles in self cell
152  //self offset avoids double counting in self cell
153  int selfOffset = 0;
154  while (i != END) {
155  j = buckets[bucketIdNeigh];
156  //increase offset by number of processed particles in self cell
157  for (int o=0;o<selfOffset;o++){
158  j = next[j];
159  }
160  //loop over all particles in neighbor cell
161  while(j != END) {
162  if(pred_r(particles_mr.R[i], particles_mr.R[j])) {
163  if (i!=j) {
164  op_r(i, j, particles_mr);
165  }
166  }
167  j = next[j];
168  }
169  i = next[i];
170  //adjust selfOffset
171  if (bucketIdSelf==bucketIdNeigh) {
172  selfOffset++;
173  }
174  else {
175  selfOffset=0;
176  }
177  }
178  }
179  }
180 
181  }
182  }
183  }
184 
185 
186  }
187 private:
188 
189  //returns the bucket id of particle i
190  std::size_t getBucketId(std::size_t i)
191  {
192 
193  Vektor<int,3> loc;
194  bool isInside, isOutsideMin, isOutsideMax;
195  int indInside;
196  for (unsigned d=0; d<3; ++d) {
197  indInside = (particles_mr.R[i][d]-rmin_m[d])/hChaining_m[d];
198  isInside = (particles_mr.R[i][d] > rmin_m[d]) && (particles_mr.R[i][d] < rmax_m[d]);
199  isOutsideMin = (particles_mr.R[i][d] <= rmin_m[d]);
200  isOutsideMax = (particles_mr.R[i][d] >= rmax_m[d]);
201 
202  //if the particle is inside the bucket take the inside index otherwise assign it to either first or
203  //last bucket in that dimension. (int)isOutsideMin * 0 is written explicitly for the ease of code
204  //understanding.
205  loc[d] = ((int)isInside * indInside) + ((int)isOutsideMin * 0) + ((int)isOutsideMax * (bucketsPerDim_m[d]-1));
206  }
207 
208  std::size_t bucketId = loc[2]*bucketsPerDim_m[1]*bucketsPerDim_m[0]+loc[1]*bucketsPerDim_m[0]+loc[0];
209  return bucketId;
210  }
211 
212  PBase& particles_mr;
213  double gammaz_m;
219 };
220 
221 
222 #endif
std::size_t getBucketId(std::size_t i)
Position_t
Definition: Types.h:43
static int myNode()
Definition: IpplInfo.cpp:691
HashPairBuilderParallel(PBase &p_r, double gammaz)
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:84
const unsigned Dim
void forEach(const Pred &pred_r, const OP &op_r)
PETE_TUTree< FnFloor, typename T::PETE_Expr_t > floor(const PETE_Expr< T > &l)
Definition: PETE.h:733