Line data Source code
1 : //
2 : // Class Partitioner
3 : // Partition a domain into subdomains.
4 : //
5 :
6 : #include <algorithm>
7 : #include <numeric>
8 : #include <vector>
9 :
10 : namespace ippl {
11 : namespace detail {
12 :
13 : template <unsigned Dim>
14 : template <typename view_type>
15 0 : void Partitioner<Dim>::split(const NDIndex<Dim>& domain, view_type& view,
16 : const std::array<bool, Dim>& isParallel, int nSplits) const {
17 : using NDIndex_t = NDIndex<Dim>;
18 :
19 : // Recursively split the domain until we have generated all the domains.
20 0 : std::vector<NDIndex_t> domains_c(nSplits);
21 0 : NDIndex_t leftDomain;
22 :
23 : // Start with the whole domain.
24 0 : domains_c[0] = domain;
25 : int v;
26 0 : unsigned int d = 0;
27 :
28 : int v1, v2, rm, vtot, vl, vr;
29 : double a, lmax, len;
30 :
31 0 : for (v = nSplits, rm = 0; v > 1; v /= 2) {
32 0 : rm += (v % 2);
33 : }
34 :
35 0 : if (rm == 0) {
36 : // nSplits is a power of 2
37 :
38 0 : std::vector<NDIndex_t> copy_c(nSplits);
39 :
40 0 : for (v = 1; v < nSplits; v *= 2) {
41 : // Go to the next parallel dimension.
42 0 : while (!isParallel[d])
43 0 : if (++d == Dim)
44 0 : d = 0;
45 :
46 : // Split all the current nSplits.
47 : int i, j;
48 0 : for (i = 0, j = 0; i < v; ++i, j += 2) {
49 : // Split to the left and to the right, saving both.
50 0 : domains_c[i].split(copy_c[j], copy_c[j + 1], d);
51 : }
52 : // Copy back.
53 0 : std::copy(copy_c.begin(), copy_c.begin() + v * 2, domains_c.begin());
54 :
55 : // On to the next dimension.
56 0 : if (++d == Dim)
57 0 : d = 0;
58 : }
59 :
60 0 : } else {
61 0 : vtot = 1; // count the number of nSplits to make sure that it worked
62 : // nSplits is not a power of 2 so we need to do some fancy splitting
63 : // sorry... this would be much cleaner with recursion
64 : /*
65 : The way this works is to recursively split on the longest dimension.
66 : Suppose you request 11 nSplits. It will split the longest dimension
67 : in the ratio 5:6 and put the new domains in node 0 and node 5. Then
68 : it splits the longest dimension of the 0 domain and puts the results
69 : in node 0 and node 2 and then splits the longest dimension of node 5
70 : and puts the results in node 5 and node 8. etc.
71 : The logic is kind of bizarre, but it works.
72 : */
73 0 : for (v = 1; v < 2 * nSplits; ++v) {
74 : // kind of reverse the bits of v
75 0 : for (v2 = v, v1 = 1; v2 > 1; v2 /= 2) {
76 0 : v1 = 2 * v1 + (v2 % 2);
77 : }
78 0 : vl = 0;
79 0 : vr = nSplits;
80 :
81 0 : while (v1 > 1) {
82 0 : if ((v1 % 2) == 1) {
83 0 : vl = vl + (vr - vl) / 2;
84 : } else {
85 0 : vr = vl + (vr - vl) / 2;
86 : }
87 0 : v1 /= 2;
88 : }
89 :
90 0 : v2 = vl + (vr - vl) / 2;
91 :
92 0 : if (v2 > vl) {
93 0 : a = v2 - vl;
94 0 : a /= vr - vl;
95 0 : vr = v2;
96 0 : leftDomain = domains_c[vl];
97 0 : lmax = 0;
98 0 : d = std::numeric_limits<unsigned int>::max();
99 0 : for (unsigned int dd = 0; dd < Dim; ++dd) {
100 0 : if (isParallel[dd]) {
101 0 : if ((len = leftDomain[dd].length()) > lmax) {
102 0 : lmax = len;
103 0 : d = dd;
104 : }
105 : }
106 : }
107 0 : NDIndex_t temp;
108 0 : domains_c[vl].split(temp, domains_c[vr], d, a);
109 0 : domains_c[vl] = temp;
110 0 : ++vtot;
111 : }
112 : }
113 :
114 0 : v = vtot;
115 : }
116 :
117 : // Make sure v is the same number of nSplits at this stage.
118 0 : PAssert_EQ(v, nSplits);
119 :
120 0 : for (size_t i = 0; i < domains_c.size(); ++i) {
121 0 : view(i) = domains_c[i];
122 : }
123 0 : }
124 : } // namespace detail
125 : } // namespace ippl
|