openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
kdpart.hpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) NumeriCor GmbH
2 // SPDX-License-Identifier: Apache-2.0
3 
12 #ifndef _KDPART_HPP
13 #define _KDPART_HPP
14 
15 #include <algorithm>
16 #include <iostream>
17 #include <math.h>
18 #include <numeric>
19 #include <vector>
20 
21 #ifdef KDPART_MPI
22 #include <mpi.h>
23 #endif
24 
25 namespace kdpart {
26 
28 template<class S>
29 struct vec3 {
30  S x = S(), y = S(), z = S();
31 
32  void get(const S* p) {
33  x = p[0], y = p[1], z = p[2];
34  }
35  void set(S* p) {
36  p[0] = x, p[1] = y, p[2] = z;
37  }
38 };
39 
41 template<class S>
42 struct bbox {
45 };
46 
48 enum axis {
49  X = 0, Y, Z, UNSET
50 };
51 
53 template<class T, class S>
54 struct elem {
56  T eidx = -1;
57 };
58 
60 template<class T, class S>
61 struct partition {
62  T pidx = T(-1);
63  T cnt = T(-1);
65  std::vector<elem<T,S> > * elems = NULL;
66 
68  if(elems) delete elems;
69  }
70 };
71 
76 inline bool is_power_of_two(double val)
77 {
78  double log_two = log(val) / log(2.0);
79  int log_two_int = log_two;
80 
81  return (fabs(double(log_two_int) - log_two) < 1e-6);
82 }
83 
84 
85 
92 template<class T, class S>
93 struct mixed_pair {
94  T v1;
95  S v2;
96 };
97 
99 template<class T, class S>
100 bool operator< (const mixed_pair<T,S> & lhs, const mixed_pair<T,S> & rhs)
101 {
102  return lhs.v1 < rhs.v1;
103 }
104 
116 template<typename V, typename W>
117 V clamp(const V val, const W start, const W end) {
118  if(val < start) return start;
119  if(val > end) return end;
120  return val;
121 }
122 
124 template<class T>
125 inline void dsp_from_cnt(const std::vector<T> & cnt, std::vector<T> & dsp)
126 {
127  dsp.resize(cnt.size()+1);
128  dsp[0] = 0;
129  for(size_t i=0; i<cnt.size(); i++) dsp[i+1] = dsp[i] + cnt[i];
130 }
131 
133 template<class T>
134 inline void cnt_from_dsp(const std::vector<T> & dsp, std::vector<T> & cnt)
135 {
136  cnt.resize(dsp.size() - 1);
137  for(size_t i=0; i<dsp.size()-1; i++) cnt[i] = dsp[i+1] - dsp[i];
138 }
139 
140 template<class V, class W>
141 void sort_copy(std::vector<V> & v1, std::vector<W> & v2)
142 {
143  assert(v1.size() == v2.size());
144 
145  std::vector<mixed_pair<V,W> > pair_array(v1.size());
146 
147  for(size_t i=0; i<v1.size(); i++) {
148  pair_array[i].v1 = v1[i];
149  pair_array[i].v2 = v2[i];
150  }
151 
152  std::sort(pair_array.begin(), pair_array.end());
153 
154  for(size_t i=0; i<v1.size(); i++) {
155  v1[i] = pair_array[i].v1;
156  v2[i] = pair_array[i].v2;
157  }
158 }
159 
160 
170 #define KD_ORDER_INC 5.0
171 
172 #define KD_MIN_SIZE 64
173 
174 #ifdef KDPART_MPI
175 template<class T, class S>
177 {
178  private:
179  // the kdtree_partitioner members
180  std::vector<kdpart::partition<T,S> > _layout;
181  T _cur_part_number;
182  MPI_Comm _comm;
183 
185  inline kdpart::bbox<S> get_bbox(const std::vector<elem<T,S> > & elems)
186  {
187  double minmax[6], minmax_red[6];
188 
189  minmax[0] = 1e100;
190  minmax[1] = 1e100;
191  minmax[2] = 1e100;
192  minmax[3] = -1e100;
193  minmax[4] = -1e100;
194  minmax[5] = -1e100;
195 
196  for(size_t i=0; i<elems.size(); i++) {
197  kdpart::vec3<S> p = elems[i].ctr;
198 
199  if(minmax[0] > p.x) minmax[0] = p.x;
200  if(minmax[1] > p.y) minmax[1] = p.y;
201  if(minmax[2] > p.z) minmax[2] = p.z;
202  if(minmax[3] < p.x) minmax[3] = p.x;
203  if(minmax[4] < p.y) minmax[4] = p.y;
204  if(minmax[5] < p.z) minmax[5] = p.z;
205  }
206 
207  MPI_Allreduce(minmax, minmax_red, 3, MPI_DOUBLE, MPI_MIN, _comm);
208  MPI_Allreduce(minmax+3, minmax_red+3, 3, MPI_DOUBLE, MPI_MAX, _comm);
209 
210  kdpart::bbox<S> box;
211  kdpart::vec3<S> & min = box.bounds[0];
212  kdpart::vec3<S> & max = box.bounds[1];
213 
214  min.x = minmax_red[0];
215  min.y = minmax_red[1];
216  min.z = minmax_red[2];
217  max.x = minmax_red[3];
218  max.y = minmax_red[4];
219  max.z = minmax_red[5];
220 
221  return box;
222  }
223 
225  inline kdpart::axis get_longest_axis(const kdpart::bbox<S> & box)
226  {
227  const kdpart::vec3<S> & min = box.bounds[0];
228  const kdpart::vec3<S> & max = box.bounds[1];
229 
230  S x = fabs(min.x - max.x);
231  S y = fabs(min.y - max.y);
232  S z = fabs(min.z - max.z);
233 
234  return x > y && x > z ? X : y > z ? Y : Z;
235  }
236 
244  inline void print_layout(const T split_pos = -1)
245  {
246  int rank; MPI_Comm_rank(_comm, &rank);
247 
248  if(rank != 0) return;
249 
250  if(split_pos > -1) {
251  T idx = 0;
252  while(idx < split_pos) {
253  printf("%d : %d \n", int(_layout[idx].pidx), int(_layout[idx].cnt));
254  idx++;
255  }
256 
257  printf("----\n");
258  printf("%d : %d \n", int(_layout[idx].pidx), int(_layout[idx].cnt));
259  printf("%d : %d \n", int(_layout[idx+1].pidx), int(_layout[idx+1].cnt));
260  printf("----\n");
261  idx += 2;
262 
263  while(size_t(idx) < _layout.size() && _layout[idx].pidx > -1) {
264  printf("%d : %d \n", int(_layout[idx].pidx), int(_layout[idx].cnt));
265  idx++;
266  }
267  printf("\n");
268  }
269  else {
270  T idx = 0;
271  while(size_t(idx) < _layout.size() && _layout[idx].pidx > -1) {
272  printf("%d : %d \n", int(_layout[idx].pidx), int(_layout[idx].cnt));
273  idx++;
274  }
275  printf("\n");
276  }
277  }
278 
279  inline void get_parallel_median_split(std::vector<mixed_pair<S,T>> & vals,
280  double min, double max,
281  std::vector<bool> & on_left_side)
282  {
283  int size, rank;
284  MPI_Comm_size(_comm, &size);
285  MPI_Comm_rank(_comm, &rank);
286 
287  on_left_side.resize(vals.size());
288  std::sort(vals.begin(), vals.end());
289 
290  size_t nelem = vals.size();
291  double bucket_size = (max*1.05 - min) / double(size);
292  std::vector<int> buckets(size_t(size), int(0));
293  for(size_t i=0; i<nelem; i++) {
294  int idx = (vals[i].v1 - min) / bucket_size;
295  idx = kdpart::clamp(idx, 0, size-1);
296  buckets[idx]++;
297  }
298 
299  std::vector<int> glob_buckets(size_t(size), int(0));
300  MPI_Allreduce(buckets.data(), glob_buckets.data(), size, MPI_INT, MPI_SUM, _comm);
301 
302  // compute the global number of values and consequently the half number
303  int glob_sum = std::accumulate(glob_buckets.begin(), glob_buckets.end(), 0);
304  int lhalf = (glob_sum + 1) / 2;
305  // figure out the bucket index we have to go through to get the median value
306  int dsp = 0, bucket_idx = 0;
307  while(bucket_idx < size && (dsp + glob_buckets[bucket_idx]) <= lhalf) {
308  dsp += glob_buckets[bucket_idx];
309  bucket_idx++;
310  }
311 
312  // sanity check. we could clamp here, but its better to throw an error since
313  // an illegal index should not occur.
314  if(bucket_idx < 0 || bucket_idx >= size) {
315  fprintf(stderr, "Error: Illegal bucket index !!\n");
316  }
317 
318  // containers for the values we compute the median on
319  std::vector<double> loc_val_bucket, glob_val_bucket;
320  // containers for the split decision
321  std::vector<short> global_split_bucket, split_bucket(buckets[bucket_idx]);
322 
323  loc_val_bucket.resize(buckets[bucket_idx]);
324  for(size_t i=0, widx=0; i<nelem; i++) {
325  int idx = (vals[i].v1 - min) / bucket_size;
326  idx = kdpart::clamp(idx, 0, size-1);
327 
328  if(idx == bucket_idx)
329  loc_val_bucket[widx++] = vals[i].v1;
330  }
331 
332  std::vector<int> rcnt(size), rdsp(size);
333  MPI_Gather(&buckets[bucket_idx], 1, MPI_INT, rcnt.data(), 1, MPI_INT, bucket_idx, _comm);
334  kdpart::dsp_from_cnt(rcnt, rdsp);
335 
336  if(rank == bucket_idx) {
337  glob_val_bucket.resize(glob_buckets[bucket_idx]);
338  global_split_bucket.resize(glob_buckets[bucket_idx]);
339  }
340 
341  // the elements of the split bucket are communicated to the associated rank
342  MPI_Gatherv(loc_val_bucket.data(), buckets[bucket_idx], MPI_DOUBLE,
343  glob_val_bucket.data(), rcnt.data(), rdsp.data(), MPI_DOUBLE,
344  bucket_idx, _comm);
345 
346 
347  // the rank owning the bucket where the median split will take place is categorizing
348  // his elements into left and right
349  if(rank == bucket_idx) {
350  size_t bsize = glob_val_bucket.size();
351 
352  std::vector<int> bucket_perm(bsize);
353  for(size_t i=0; i<bsize; i++) bucket_perm[i] = i;
354 
355  sort_copy(glob_val_bucket, bucket_perm);
356  int loc_half_idx = lhalf - dsp;
357 
358  // sanity check
359  if(loc_half_idx < 0 || loc_half_idx >= int(glob_val_bucket.size())) {
360  fprintf(stderr, "Error: Illegal local val index!!\n");
361  }
362 
363  for(int i=0; i <= loc_half_idx; i++)
364  global_split_bucket[bucket_perm[i]] = 1;
365 
366  for(int i=loc_half_idx+1; i < int(bsize); i++)
367  global_split_bucket[bucket_perm[i]] = 0;
368  }
369 
370  MPI_Scatterv(global_split_bucket.data(), rcnt.data(), rdsp.data(), MPI_SHORT,
371  split_bucket.data(), buckets[bucket_idx], MPI_SHORT,
372  bucket_idx, _comm);
373 
374  for(size_t i=0, ridx=0; i<nelem; i++) {
375  int pidx = vals[i].v2;
376  int idx = (vals[i].v1 - min) / bucket_size;
377  idx = kdpart::clamp(idx, 0, size-1);
378 
379  if(idx < bucket_idx)
380  on_left_side[pidx] = true;
381  else if(idx == bucket_idx)
382  on_left_side[pidx] = split_bucket[ridx++] == 1;
383  else
384  on_left_side[pidx] = false;
385  }
386  }
387 
395  inline void median_split(kdpart::partition<T,S> parent,
396  kdpart::partition<T,S> & lchild,
397  kdpart::partition<T,S> & rchild)
398  {
399  kdpart::axis longest_axis = get_longest_axis(parent.box);
400  size_t nelem = parent.elems->size();
401 
402  std::vector<mixed_pair<S,T>> vals(nelem);
403 
404  double min = 0, max = 0;
405  switch(longest_axis) {
406  case X: {
407  min = parent.box.bounds[0].x, max = parent.box.bounds[1].x;
408  for(size_t i=0; i<nelem; i++) {
409  const kdpart::vec3<S> & p = (*parent.elems)[i].ctr;
410  vals[i].v1 = p.x;
411  vals[i].v2 = i;
412  }
413  break;
414  }
415  case Y: {
416  min = parent.box.bounds[0].y, max = parent.box.bounds[1].y;
417  for(size_t i=0; i<nelem; i++) {
418  const kdpart::vec3<S> & p = (*parent.elems)[i].ctr;
419  vals[i].v1 = p.y;
420  vals[i].v2 = i;
421  }
422  break;
423  }
424  case Z: {
425  min = parent.box.bounds[0].z, max = parent.box.bounds[1].z;
426  for(size_t i=0; i<nelem; i++) {
427  const kdpart::vec3<S> & p = (*parent.elems)[i].ctr;
428  vals[i].v1 = p.z;
429  vals[i].v2 = i;
430  }
431  break;
432  }
433  case UNSET: break;
434  }
435 
436  // we compute the median value in parallel
437  std::vector<bool> on_left;
438  get_parallel_median_split(vals, min, max, on_left);
439 
440  // then we split the local elements based on the median
441  size_t left_size = 0, right_size = 0;
442 
443  for(size_t i=0; i<nelem; i++) {
444  if(on_left[i]) left_size++;
445  else right_size++;
446  }
447 
448  lchild.elems = new std::vector<elem<T,S> >(left_size), rchild.elems = new std::vector<elem<T,S> >(right_size);
449 
450  left_size = 0, right_size = 0;
451  for(size_t i=0; i<nelem; i++) {
452  if(on_left[i]) (*lchild.elems)[left_size++ ] = (*parent.elems)[i];
453  else (*rchild.elems)[right_size++] = (*parent.elems)[i];
454  }
455 
456  // remove elements of parent partition
457  delete parent.elems;
458  parent.elems = NULL;
459 
460  int buff[2] = {int(lchild.elems->size()), int(rchild.elems->size())};
461  MPI_Allreduce(MPI_IN_PLACE, buff, 2, MPI_INT, MPI_SUM, _comm);
462  lchild.cnt = buff[0], rchild.cnt = buff[1];
463 
464  if(lchild.cnt == 0 || rchild.cnt == 0) {
465  fprintf(stderr, "Error: Empty partitioning!!\n");
466  }
467 
468  lchild.box = get_bbox(*lchild.elems);
469  rchild.box = get_bbox(*rchild.elems);
470  }
471 
477  inline void update_layout(const T split_pos) {
478  assert(size_t(split_pos) < _layout.size());
479 
480  T idx_at_split = _layout[split_pos].pidx;
481  T start = _cur_part_number - 1, stop = split_pos + 1;
482 
483  // increment partition index for partitions after the split
484  for(T i = start; i > stop; i--) {
485  _layout[i] = _layout[i-1]; // copy partition
486  _layout[i].pidx++; // increment partition index
487  }
488 
489  // set partition indices for the split
490  median_split(_layout[split_pos], _layout[split_pos], _layout[split_pos+1]);
491  _layout[split_pos].pidx = idx_at_split;
492  _layout[split_pos+1].pidx = idx_at_split+1;
493 
494  // print layout
495  // print_layout(split_pos);
496  }
497 
503  inline T get_split_pos()
504  {
505  T idx = 0, max = _layout[0].cnt, maxidx = 0;
506 
507  while(idx < _cur_part_number && _layout[idx].cnt > -1) {
508  if(max < _layout[idx].cnt) {
509  max = _layout[idx].cnt;
510  maxidx = idx;
511  }
512  idx++;
513  }
514 
515  return maxidx;
516  }
517 
518  public:
519  inline void operator() (const MPI_Comm comm, const std::vector<S> & ctr, const int req_part,
520  std::vector<T> & part_vec)
521  {
522  _comm = comm;
523 
524  assert(ctr.size() % 3 == 0); // coord components need to be a multiple of 3
525 
526  // get domain sizes
527  long int l_numelem = ctr.size() / 3, g_numelem;
528  MPI_Allreduce(&l_numelem, &g_numelem, 1, MPI_LONG, MPI_SUM, _comm);
529  assert(g_numelem > 0);
530 
531  int size, rank;
532  MPI_Comm_size(_comm, &size); MPI_Comm_rank(_comm, &rank);
533 
534  T npart = req_part;
535 
536  bool redistribute_remainder = false;
537  T npart_old = npart;
538 
546  if(!kdpart::is_power_of_two(npart)) {
547  npart = (log(double(npart)) / log(2.0)) + KD_ORDER_INC;
548  npart = pow(2, npart);
549 
550  // we can always afford to split at least into KD_MIN_SIZE parts. if the initial npart was very small,
551  // the computed new npart is still below KD_MIN_SIZE. so we set it explicitly
552  if(npart < KD_MIN_SIZE && g_numelem > KD_MIN_SIZE) npart = KD_MIN_SIZE;
553 
554  redistribute_remainder = true;
555  }
556 
557  assert(g_numelem > (long int)npart);
558 
559  // initialize the first partition
560  _cur_part_number = 1;
561  _layout.resize(size_t(npart));
562  _layout[0].pidx = 0;
563  _layout[0].elems = new std::vector<kdpart::elem<T,S> >(l_numelem);
564  _layout[0].cnt = g_numelem;
565 
566  std::vector<kdpart::elem<T,S> > & elems = *_layout[0].elems;
567 
568  // convert the elements into an array of center-points
569  for(long int i=0; i<l_numelem; i++) {
570  elems[i].ctr.get(ctr.data() + i*3); // get elem center coord
571  elems[i].eidx = i; // get elem index
572  }
573 
574  _layout[0].box = get_bbox(elems);
575 
576  /*
577  * Main splitting loop: We iterate until we have enough partitions. In each
578  * iteration, we split the largest partition into two. Thus we can get to any
579  * number of partitions.
580  *
581  */
582  while(_cur_part_number < npart) {
583  T split_pos = get_split_pos();
584  _cur_part_number++;
585 
586  update_layout(split_pos);
587  }
588 
589  // in case the requested number of partitions was not a power of two,
590  // we have computed more partitions than we have processes and we need to re-
591  // index the computed partitions into [0, size]
592  if(redistribute_remainder) {
593  // number of partitions we will at least assign to a process
594  T base_size = npart / npart_old;
595  // number of partitions we will assign to a subset of processes to reduce
596  // the remainder (npart / npart_old)
597  T extended_size = base_size + 1;
598  T remainder = npart % npart_old;
599 
600  // 'remainder' many processes get chunks of size 'extended_size'
601  for(T i=0; i<remainder; i++) {
602  for(T j=0; j<extended_size; j++)
603  _layout[i*extended_size+j].pidx = i;
604  }
605 
606  // '_layout.size() - remainder' many processes get chunks of size 'base_size'
607  for(T i=remainder*extended_size, pidx=remainder; i<T(_layout.size()); i+=base_size, pidx++) {
608  for(T j=0; j<base_size; j++)
609  _layout[i+j].pidx = pidx;
610  }
611 
612  // print_layout();
613  }
614 
615  // assign partition index to the individual elements
616  part_vec.assign(l_numelem, T(-1));
617  for(const kdpart::partition<T,S> & p : _layout) {
618  for(const kdpart::elem<T,S> & e : (*p.elems)) {
619  part_vec[e.eidx] = p.pidx;
620  }
621  }
622  }
623 };
624 #endif
625 
626 template<class T, class S>
628 
629  private:
630  // the kdpart members
631  std::vector<kdpart::partition<T,S> > _layout;
632  T _cur_part_number;
633 
635  inline kdpart::bbox<S> get_bbox(const std::vector<elem<T,S> > & elems)
636  {
637  kdpart::bbox<S> box;
638  kdpart::vec3<S> & min = box.bounds[0];
639  kdpart::vec3<S> & max = box.bounds[1];
640  kdpart::vec3<S> p = elems[0].ctr;
641 
642  min.x = p.x, min.y = p.y, min.z = p.z;
643  max.x = p.x, max.y = p.y, max.z = p.z;
644 
645  for(size_t i=1; i<elems.size(); i++) {
646  p = elems[i].ctr;
647 
648  if(min.x > p.x) min.x = p.x;
649  if(min.y > p.y) min.y = p.y;
650  if(min.z > p.z) min.z = p.z;
651  if(max.x < p.x) max.x = p.x;
652  if(max.y < p.y) max.y = p.y;
653  if(max.z < p.z) max.z = p.z;
654  }
655 
656  return box;
657  }
658 
660  inline kdpart::axis get_longest_axis(const kdpart::bbox<S> & box)
661  {
662  const kdpart::vec3<S> & min = box.bounds[0];
663  const kdpart::vec3<S> & max = box.bounds[1];
664 
665  S x = fabs(min.x - max.x);
666  S y = fabs(min.y - max.y);
667  S z = fabs(min.z - max.z);
668 
669  return x > y && x > z ? X : y > z ? Y : Z;
670  }
671 
679  inline void print_layout(const T split_pos = -1)
680  {
681  if(split_pos > -1) {
682  T idx = 0;
683  while(idx < split_pos) {
684  printf("%d : %d \n", int(_layout[idx].pidx), int(_layout[idx].cnt));
685  idx++;
686  }
687 
688  printf("----\n");
689  printf("%d : %d \n", int(_layout[idx].pidx), int(_layout[idx].cnt));
690  printf("%d : %d \n", int(_layout[idx+1].pidx), int(_layout[idx+1].cnt));
691  printf("----\n");
692  idx += 2;
693 
694  while(size_t(idx) < _layout.size() && _layout[idx].pidx > -1) {
695  printf("%d : %d \n", int(_layout[idx].pidx), int(_layout[idx].cnt));
696  idx++;
697  }
698  printf("\n");
699  }
700  else {
701  T idx = 0;
702  while(size_t(idx) < _layout.size() && _layout[idx].pidx > -1) {
703  printf("%d : %d \n", int(_layout[idx].pidx), int(_layout[idx].cnt));
704  idx++;
705  }
706  printf("\n");
707  }
708  }
709 
717  inline void median_split(kdpart::partition<T,S> parent,
719  {
720 
721  kdpart::axis longest_axis = get_longest_axis(parent.box);
722  size_t nelem = parent.elems->size();
723 
724  // put the (coord, idx) pairs into a vector and sort them
725  std::vector<mixed_pair<S,T> > pairs(nelem);
726  for(size_t i=0; i<nelem; i++) {
727  const kdpart::vec3<S> & p = (*parent.elems)[i].ctr;
728  S val = S();
729  switch(longest_axis) {
730  case X: val = p.x; break;
731  case Y: val = p.y; break;
732  case Z: val = p.z; break;
733  case UNSET: break;
734  }
735 
736  pairs[i] = {val, T(i)};
737  }
738  std::sort(pairs.begin(), pairs.end());
739 
740  // we then copy the first half into left children and the other half into
741  // right children
742  size_t lnum = (nelem + 1) / 2, rnum = nelem - lnum;
743  lchild.elems = new std::vector<elem<T,S> >(lnum), rchild.elems = new std::vector<elem<T,S> >(rnum);
744 
745  for(size_t i=0; i<lnum; i++) {
746  T lidx = pairs[i].v2;
747  (*lchild.elems)[i] = (*parent.elems)[lidx];
748  }
749 
750  for(size_t i=0; i<rnum; i++) {
751  T lidx = pairs[lnum + i].v2;
752  (*rchild.elems)[i] = (*parent.elems)[lidx];
753  }
754 
755  // remove elements of parent partition
756  delete parent.elems;
757  parent.elems = NULL;
758 
759  lchild.cnt = lchild.elems->size();
760  rchild.cnt = rchild.elems->size();
761  lchild.box = get_bbox(*lchild.elems);
762  rchild.box = get_bbox(*rchild.elems);
763  }
764 
770  inline void update_layout(const T split_pos) {
771  assert(size_t(split_pos) < _layout.size());
772 
773  T idx_at_split = _layout[split_pos].pidx;
774  T start = _cur_part_number - 1, stop = split_pos + 1;
775 
776  // increment partition index for partitions after the split
777  for(T i = start; i > stop; i--) {
778  _layout[i] = _layout[i-1]; // copy partition
779  _layout[i].pidx++; // increment partition index
780  }
781 
782  // set partition indices for the split
783  median_split(_layout[split_pos], _layout[split_pos], _layout[split_pos+1]);
784  _layout[split_pos].pidx = idx_at_split;
785  _layout[split_pos+1].pidx = idx_at_split+1;
786 
787  // print layout
788  // print_layout(split_pos);
789  }
790 
796  inline T get_split_pos()
797  {
798  T idx = 0, max = _layout[0].cnt, maxidx = 0;
799 
800  while(idx < _cur_part_number && _layout[idx].cnt > -1) {
801  if(max < _layout[idx].cnt) {
802  max = _layout[idx].cnt;
803  maxidx = idx;
804  }
805  idx++;
806  }
807 
808  return maxidx;
809  }
810 
811  public:
812  inline void operator() (const std::vector<S> & ctr, T npart, std::vector<T> & part_vec)
813  {
814  assert(ctr.size() % 3 == 0);
815 
816  size_t nelem = ctr.size() / 3;
817  assert(nelem > size_t(npart));
818 
819  bool redistribute_remainder = false;
820  T npart_old = npart;
821 
829  if(!is_power_of_two(npart)) {
830  npart = (log(double(npart)) / log(2.0)) + 5.0;
831  npart = pow(2, npart);
832 
833  // we can always afford to split at least into KD_MIN_SIZE parts. if the initial npart was very small,
834  // the computed new npart is still below KD_MIN_SIZE. so we set it explicitly
835  if(npart < KD_MIN_SIZE && nelem > KD_MIN_SIZE) npart = KD_MIN_SIZE;
836 
837  redistribute_remainder = true;
838  }
839 
840  // initialize _layout and _cnt
841  _layout.resize(size_t(npart));
842  part_vec.assign(nelem, T(-1));
843 
844  _cur_part_number = 1;
845  _layout[0].pidx = 0;
846  _layout[0].elems = new std::vector<kdpart::elem<T,S> >(nelem);
847  std::vector<kdpart::elem<T,S> > & elems = *_layout[0].elems;
848 
849  for(size_t i=0; i<nelem; i++) {
850  elems[i].ctr.get(ctr.data() + i*3); // get elem center coord
851  elems[i].eidx = i; // get elem index
852  }
853 
854  _layout[0].box = get_bbox(elems);
855 
856  // splitting loop
857  while(_cur_part_number < npart) {
858  T split_pos = get_split_pos();
859  _cur_part_number++;
860 
861  update_layout(split_pos);
862  }
863 
864  if(redistribute_remainder) {
865  T base_size = npart / npart_old, extended_size = base_size + 1;
866  T remainder = npart % npart_old;
867 
868  for(T i=0; i<remainder; i++) {
869  for(T j=0; j<extended_size; j++)
870  _layout[i*extended_size+j].pidx = i;
871  }
872 
873  // the rest gets reindexed ascendingly, starting with index "remainder"
874  for(T i=remainder*extended_size, pidx=remainder; i<T(_layout.size()); i+=base_size, pidx++) {
875  for(T j=0; j<base_size; j++)
876  _layout[i+j].pidx = pidx;
877  }
878 
879  // print_layout();
880  }
881 
882  // assign partition index to the individual elements
883  for(const kdpart::partition<T,S> & p : _layout) {
884  for(const kdpart::elem<T,S> & e : (*p.elems)) {
885  part_vec[e.eidx] = p.pidx;
886  }
887  }
888  }
889 };
890 
891 }
892 
893 #endif
void operator()(const MPI_Comm comm, const std::vector< S > &ctr, const int req_part, std::vector< T > &part_vec)
Definition: kdpart.hpp:519
void operator()(const std::vector< S > &ctr, T npart, std::vector< T > &part_vec)
Definition: kdpart.hpp:812
#define KD_MIN_SIZE
Definition: kdpart.hpp:172
#define KD_ORDER_INC
The value we add to the power of two if we need higher partitioning resolution.
Definition: kdpart.hpp:170
void sort_copy(std::vector< V > &v1, std::vector< W > &v2)
Definition: kdpart.hpp:141
axis
split axis
Definition: kdpart.hpp:48
@ X
Definition: kdpart.hpp:49
@ Z
Definition: kdpart.hpp:49
@ UNSET
Definition: kdpart.hpp:49
@ Y
Definition: kdpart.hpp:49
V clamp(const V val, const W start, const W end)
Clamp a value into an interval [start, end].
Definition: kdpart.hpp:117
bool operator<(const mixed_pair< T, S > &lhs, const mixed_pair< T, S > &rhs)
sorting operator
Definition: kdpart.hpp:100
void cnt_from_dsp(const std::vector< T > &dsp, std::vector< T > &cnt)
Compute counts from displacements.
Definition: kdpart.hpp:134
bool is_power_of_two(double val)
Check if a given value is an integer power of two.
Definition: kdpart.hpp:76
void dsp_from_cnt(const std::vector< T > &cnt, std::vector< T > &dsp)
Compute displacements from counts.
Definition: kdpart.hpp:125
constexpr T min(T a, T b)
Definition: ion_type.h:18
constexpr T max(T a, T b)
Definition: ion_type.h:16
Bounding box struct.
Definition: kdpart.hpp:42
kdpart::vec3< S > bounds[2]
the bounds. bounds[0] = lower left (min), bounds[1] = upper right (max)
Definition: kdpart.hpp:44
element definition
Definition: kdpart.hpp:54
T eidx
element index
Definition: kdpart.hpp:56
vec3< S > ctr
element center location
Definition: kdpart.hpp:55
Combined floating point and integer pair.
Definition: kdpart.hpp:93
the struct holding all partition data
Definition: kdpart.hpp:61
kdpart::bbox< S > box
(global) partition bounding box
Definition: kdpart.hpp:64
T cnt
(global) partition size
Definition: kdpart.hpp:63
std::vector< elem< T, S > > * elems
(local) elements in partition
Definition: kdpart.hpp:65
T pidx
partition index
Definition: kdpart.hpp:62
minimalistic internal point struct
Definition: kdpart.hpp:29
void get(const S *p)
Definition: kdpart.hpp:32
void set(S *p)
Definition: kdpart.hpp:35