OPAL (Object Oriented Parallel Accelerator Library) 2022.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"
29
30#include <algorithm>
31#include <array>
32#include <cmath>
33#include <limits>
34#include <set>
35
36template<class PBase>
38{
39public:
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;
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 }
187private:
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
213 double gammaz_m;
219};
220
221
222#endif
const unsigned Dim
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:84
PETE_TUTree< FnFloor, typename T::PETE_Expr_t > floor(const PETE_Expr< T > &l)
Definition: PETE.h:733
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
Position_t
Definition: Types.h:43
std::size_t getBucketId(std::size_t i)
HashPairBuilderParallel(PBase &p_r, double gammaz)
void forEach(const Pred &pred_r, const OP &op_r)
static int myNode()
Definition: IpplInfo.cpp:691