IPPL (Independent Parallel Particle Layer)
IPPL
Loading...
Searching...
No Matches
OrthogonalRecursiveBisection.hpp
Go to the documentation of this file.
2
3namespace ippl {
4
5 template <class Field, class Tp>
7 const Field& rho) {
8 bf_m.initialize(mesh, fl);
9 bf_m = rho;
10 }
11
12 template <class Field, class Tp>
13 template <typename Attrib>
15 const Attrib& R, FieldLayout<Dim>& fl, const bool& isFirstRepartition) {
16 // Timings
17 static IpplTimings::TimerRef tbasicOp = IpplTimings::getTimer("basicOperations");
18 static IpplTimings::TimerRef tperpReduction = IpplTimings::getTimer("perpReduction");
19 static IpplTimings::TimerRef tallReduce = IpplTimings::getTimer("allReduce");
20 static IpplTimings::TimerRef tscatter = IpplTimings::getTimer("scatterR");
21
22 // Scattering of particle positions in field
23 // In case of first repartition we know the density from the
24 // analytical expression and we use that for load balancing
25 // and create particles. Note the particles are created only
26 // after the first repartition and hence we cannot call scatter
27 // before it.
29 if (!isFirstRepartition) {
30 scatterR(R);
31 }
32
33 IpplTimings::stopTimer(tscatter);
34
36
37 // Get number of ranks
38 auto& comm = fl.comm;
39 int nprocs = comm.size();
40
41 // Start with whole domain and total number of nodes
42 std::vector<NDIndex<Dim>> domains = {fl.getDomain()};
43 std::vector<int> procs = {nprocs};
44
45 // Arrays for reduction
46 std::vector<Tf> reduced, reducedRank;
47
48 // Start recursive repartition loop
49 unsigned int it = 0;
50 int maxprocs = nprocs;
51 IpplTimings::stopTimer(tbasicOp);
52
53 while (maxprocs > 1) {
54 // Find cut axis
56 int cutAxis = findCutAxis(domains[it]);
57 IpplTimings::stopTimer(tbasicOp);
58
59 // Reserve space
60 IpplTimings::startTimer(tperpReduction);
61 reduced.resize(domains[it][cutAxis].length());
62 reducedRank.resize(domains[it][cutAxis].length());
63
64 std::fill(reducedRank.begin(), reducedRank.end(), 0.0);
65 std::fill(reduced.begin(), reduced.end(), 0.0);
66
67 // Peform reduction with field of weights and communicate to the other ranks
68 perpendicularReduction(reducedRank, cutAxis, domains[it]);
69 IpplTimings::stopTimer(tperpReduction);
70
71 // Communicate to all the reduced weights
72 IpplTimings::startTimer(tallReduce);
73 comm.allreduce(reducedRank.data(), reduced.data(), reducedRank.size(), std::plus<Tf>());
74 IpplTimings::stopTimer(tallReduce);
75
76 // Find median of reduced weights
78 // Initialize median to some value (1 is lower bound value)
79 int median = 1;
80 median = findMedian(reduced);
81 IpplTimings::stopTimer(tbasicOp);
82
83 // Cut domains and procs
85 cutDomain(domains, procs, it, cutAxis, median);
86
87 // Update max procs
88 maxprocs = 0;
89 for (unsigned int i = 0; i < procs.size(); i++) {
90 if (procs[i] > maxprocs) {
91 maxprocs = procs[i];
92 it = i;
93 }
94 }
95 IpplTimings::stopTimer(tbasicOp);
96
97 // Clear all arrays
98 IpplTimings::startTimer(tperpReduction);
99 reduced.clear();
100 reducedRank.clear();
101 IpplTimings::stopTimer(tperpReduction);
102 }
103
104 // Check that no plane was obtained in the repartition
105 IpplTimings::startTimer(tbasicOp);
106 for (const auto& domain : domains) {
107 for (const auto& axis : domain) {
108 if (axis.length() == 1) {
109 return false;
110 }
111 }
112 }
113
114 // Update FieldLayout with new indices
115 fl.updateLayout(domains);
116
117 // Update local field with new layout
118 bf_m.updateLayout(fl);
119 IpplTimings::stopTimer(tbasicOp);
120
121 return true;
122 }
123
124 template <class Field, class Tp>
126 // Find longest domain size
127 return std::distance(dom.begin(), std::max_element(dom.begin(), dom.end(),
128 [&](const Index& a, const Index& b) {
129 return a.length() < b.length();
130 }));
131 }
132
133 template <class Field, class Tp>
135 std::vector<Tf>& rankWeights, unsigned int cutAxis, NDIndex<Dim>& dom) {
136 // Check if domains overlap, if not no need for reduction
137 NDIndex<Dim> lDom = bf_m.getOwned();
138 if (lDom[cutAxis].first() > dom[cutAxis].last()
139 || lDom[cutAxis].last() < dom[cutAxis].first()) {
140 return;
141 }
142
143 // Get field's local weights
144 int nghost = bf_m.getNghost();
145 const auto data = bf_m.getView();
146
147 // Determine the iteration bounds of the reduction
148 int cutAxisFirst =
149 std::max(lDom[cutAxis].first(), dom[cutAxis].first()) - lDom[cutAxis].first() + nghost;
150 int cutAxisLast =
151 std::min(lDom[cutAxis].last(), dom[cutAxis].last()) - lDom[cutAxis].first() + nghost;
152
153 // Set iterator for where to write in the reduced array
154 unsigned int arrayStart = 0;
155 if (dom[cutAxis].first() < lDom[cutAxis].first()) {
156 arrayStart = lDom[cutAxis].first() - dom[cutAxis].first();
157 }
158
159 // Find all the perpendicular axes
160 using exec_space = typename Field::execution_space;
161 using index_type = typename RangePolicy<Dim, exec_space>::index_type;
162 Kokkos::Array<index_type, Dim> begin, end;
163 for (unsigned d = 0; d < Dim; d++) {
164 if (d == cutAxis) {
165 continue;
166 }
167
168 int inf = std::max(lDom[d].first(), dom[d].first()) - lDom[d].first() + nghost;
169 int sup = std::min(lDom[d].last(), dom[d].last()) - lDom[d].first() + nghost;
170 // inf and sup bounds must be within the domain to reduce, if not no need to reduce
171 if (sup < inf) {
172 return;
173 }
174
175 begin[d] = inf;
176 // The +1 is for Kokkos loop
177 end[d] = sup + 1;
178 }
179
180 // Iterate along cutAxis
181 for (int i = cutAxisFirst; i <= cutAxisLast; i++) {
182 begin[cutAxis] = i;
183 end[cutAxis] = i + 1;
184
185 // Reducing over perpendicular plane defined by cutAxis
186 Tf tempRes = Tf(0);
187
188 using index_array_type = typename RangePolicy<Dim, exec_space>::index_array_type;
190 "ORB weight reduction", createRangePolicy<Dim, exec_space>(begin, end),
191 KOKKOS_LAMBDA(const index_array_type& args, Tf& weight) {
192 weight += apply(data, args);
193 },
194 Kokkos::Sum<Tf>(tempRes));
195
196 Kokkos::fence();
197
198 rankWeights[arrayStart++] = tempRes;
199 }
200 }
201
202 template <class Field, class Tp>
204 // Special case when array must be cut in half in order to not have planes
205 if (w.size() == 4) {
206 return 1;
207 }
208 // Get total sum of array
209 Tf tot = std::accumulate(w.begin(), w.end(), Tf(0));
210
211 // Find position of median as half of total in array
212 Tf half = 0.5 * tot;
213 Tf curr = Tf(0);
214 // Do not need to iterate to full extent since it must not give planes
215 for (unsigned int i = 0; i < w.size() - 1; i++) {
216 curr += w[i];
217 if (curr >= half) {
218 // If all particles are in the first plane, cut at 1 so to have size 2
219 if (i == 0) {
220 return 1;
221 }
222 Tf previous = curr - w[i];
223 // curr - half < half - previous
224 if ((curr + previous) <= tot
225 && curr != half) { // if true then take current i, otherwise i-1
226 if (i == w.size() - 2) {
227 return (i - 1);
228 } else {
229 return i;
230 }
231 } else {
232 return (i > 1) ? (i - 1) : 1;
233 }
234 }
235 }
236 // If all particles are in the last plane, cut two indices before the end so to have size 2
237 return w.size() - 3;
238 }
239
240 template <class Field, class Tp>
242 std::vector<int>& procs, int it,
243 int cutAxis, int median) {
244 // Cut domains[it] in half at median along cutAxis
245 NDIndex<Dim> leftDom, rightDom;
246 domains[it].split(leftDom, rightDom, cutAxis, median + domains[it][cutAxis].first());
247 domains[it] = leftDom;
248 domains.insert(domains.begin() + it + 1, rightDom);
249
250 // Cut procs in half
251 int temp = procs[it];
252 procs[it] = procs[it] / 2;
253 procs.insert(procs.begin() + it + 1, temp - procs[it]);
254 }
255
256 template <class Field, class Tp>
257 template <typename Attrib>
259 using vector_type = typename mesh_type::vector_type;
260 static_assert(
261 Kokkos::SpaceAccessibility<typename Attrib::memory_space,
262 typename Field::memory_space>::accessible,
263 "Particle attribute memory space must be accessible from ORB field memory space");
264
265 // Reset local field
266 bf_m = 0.0;
267 // Get local data
268 auto view = bf_m.getView();
269 const mesh_type& mesh = bf_m.get_mesh();
270 const FieldLayout<Dim>& layout = bf_m.getLayout();
271 const NDIndex<Dim>& lDom = layout.getLocalNDIndex();
272 const int nghost = bf_m.getNghost();
273
274 // Get spacings
275 const vector_type& dx = mesh.getMeshSpacing();
276 const vector_type& origin = mesh.getOrigin();
277 const vector_type invdx = 1.0 / dx;
278
279 using policy_type = Kokkos::RangePolicy<size_t, typename Field::execution_space>;
280
281 Kokkos::parallel_for(
282 "ParticleAttrib::scatterR", policy_type(0, r.getParticleCount()),
283 KOKKOS_LAMBDA(const size_t idx) {
284 // Find nearest grid point
285 Vector<Tp, Dim> l = (r(idx) - origin) * invdx + 0.5;
286 Vector<int, Dim> index = l;
287 Vector<Tf, Dim> whi = l - index;
288 Vector<Tf, Dim> wlo = 1.0 - whi;
289
290 Vector<size_t, Dim> args = index - lDom.first() + nghost;
291
292 // Scatter
293 scatterToField(std::make_index_sequence<1 << Dim>{}, view, wlo, whi, args);
294 });
295
296 bf_m.accumulateHalo();
297 }
298
299} // namespace ippl
constexpr KOKKOS_INLINE_FUNCTION auto first()
Definition AbsorbingBC.h:10
Definition Archive.h:20
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) apply(const View &view, const Coords &coords)
RangePolicy< Dim, PolicyArgs... >::policy_type createRangePolicy(const Kokkos::Array< typename RangePolicy< Dim, PolicyArgs... >::index_type, Dim > &begin, const Kokkos::Array< typename RangePolicy< Dim, PolicyArgs... >::index_type, Dim > &end)
void parallel_reduce(const std::string &name, const ExecPolicy &policy, const FunctorType &functor, ReducerArgument &&... reducer)
int size() const noexcept
void cutDomain(std::vector< NDIndex< Dim > > &domains, std::vector< int > &procs, int it, int cutAxis, int median)
void initialize(FieldLayout< Dim > &fl, mesh_type &mesh, const Field &rho)
void perpendicularReduction(std::vector< Tf > &rankWeights, unsigned int cutAxis, NDIndex< Dim > &dom)
bool binaryRepartition(const Attrib &R, FieldLayout< Dim > &fl, const bool &isFirstRepartition)
const NDIndex< Dim > & getDomain() const
const NDIndex_t & getLocalNDIndex() const
mpi::Communicator comm
void updateLayout(const std::vector< NDIndex_t > &domains)
KOKKOS_INLINE_FUNCTION constexpr iterator begin()
Definition NDIndex.hpp:186
KOKKOS_INLINE_FUNCTION Vector< int, Dim > first() const
Definition NDIndex.hpp:170
KOKKOS_INLINE_FUNCTION bool split(NDIndex< Dim > &l, NDIndex< Dim > &r, unsigned d, int i) const
Definition NDIndex.hpp:113
KOKKOS_INLINE_FUNCTION constexpr iterator end()
Definition NDIndex.hpp:191
KOKKOS_INLINE_FUNCTION vector_type getOrigin() const
Definition Mesh.hpp:10
virtual KOKKOS_INLINE_FUNCTION const vector_type & getMeshSpacing() const =0
Timing::TimerRef TimerRef
static TimerRef getTimer(const char *nm)
static void stopTimer(TimerRef t)
static void startTimer(TimerRef t)
::ippl::Vector< index_type, Dim > index_array_type
typename policy_type::array_index_type index_type