6#ifndef PECLET_CORE_DECOMP_BLOCK_DECOMPOSER_HPP
7#define PECLET_CORE_DECOMP_BLOCK_DECOMPOSER_HPP
44 for (
int i = 0; i < Dim; ++i)
56 for (
int i = 0; i < Dim; ++i)
71 cellExtent_ = cellExtent;
81 assert(weights.size() ==
static_cast<std::size_t
>([&] {
83 for (int i = 0; i < Dim; ++i)
87 "weights array must cover the global grid (x-fastest)");
89 for (
int i = 0; i < Dim; ++i)
94 std::size_t
numBlocks()
const {
return origins_.size(); }
96 const std::vector<IVec<Dim>>&
origins()
const {
return origins_; }
97 const std::vector<IVec<Dim>>&
sizes()
const {
return sizes_; }
104 while (tree_[node].splitDim != -1) {
105 node = (g[tree_[node].splitDim] < tree_[node].splitValue) ? 2 * node + 1 : 2 * node + 2;
107 return static_cast<int>(tree_[node].splitValue);
115 void flattenTree(std::vector<int>& splitDim, std::vector<Index>& splitVal)
const {
116 splitDim.resize(tree_.size());
117 splitVal.resize(tree_.size());
118 for (std::size_t i = 0; i < tree_.size(); ++i) {
119 splitDim[i] = tree_[i].splitDim;
120 splitVal[i] = tree_[i].splitValue;
126 Index idx = g[Dim - 1];
127 for (
int i = Dim - 2; i >= 0; --i) {
128 idx *= globalSize_[i];
137 for (
int i = 0; i < Dim; ++i) {
138 g[i] = lin % globalSize_[i];
139 lin /= globalSize_[i];
152 for (
int k = 0; k < Dim; ++k) {
153 assert(ratio[k] >= 1 && globalSize_[k] % ratio[k] == 0 &&
154 "coarsened(): global size not divisible by ratio (build the decomposition aligned)");
155 c.globalSize_[k] = globalSize_[k] / ratio[k];
156 c.align_[k] = align_[k] / ratio[k] >= 1 ? align_[k] / ratio[k] : 1;
159 for (
auto& nd : c.tree_)
160 if (nd.splitDim != -1) {
161 assert(nd.splitValue % ratio[nd.splitDim] == 0 &&
162 "coarsened(): split not divisible by ratio (build the decomposition aligned)");
163 nd.splitValue /= ratio[nd.splitDim];
165 c.origins_.resize(origins_.size());
166 c.sizes_.resize(sizes_.size());
167 for (std::size_t b = 0; b < origins_.size(); ++b)
168 for (
int k = 0; k < Dim; ++k) {
169 assert(origins_[b][k] % ratio[k] == 0 && sizes_[b][k] % ratio[k] == 0 &&
170 "coarsened(): block origin/size not divisible by ratio (build aligned)");
171 c.origins_[b][k] = origins_[b][k] / ratio[k];
172 c.sizes_[b][k] = sizes_[b][k] / ratio[k];
193 for (
int k = 0; k < Dim; ++k) {
194 assert(ratio[k] >= 1 &&
"refined(): ratio must be >= 1");
195 f.globalSize_[k] = globalSize_[k] * ratio[k];
196 f.align_[k] = align_[k] * ratio[k];
197 f.cellExtent_[k] = 1;
200 for (
auto& nd : f.tree_)
201 if (nd.splitDim != -1)
202 nd.splitValue *= ratio[nd.splitDim];
203 f.origins_.resize(origins_.size());
204 f.sizes_.resize(sizes_.size());
205 for (std::size_t b = 0; b < origins_.size(); ++b)
206 for (
int k = 0; k < Dim; ++k) {
207 f.origins_[b][k] = origins_[b][k] * ratio[k];
208 f.sizes_[b][k] = sizes_[b][k] * ratio[k];
216 Index splitValue = 0;
228 std::size_t numSub, std::size_t numTotal,
229 const std::vector<Real>* weights)
const;
233 IVec<Dim> cellExtent_{};
234 std::vector<IVec<Dim>> origins_;
235 std::vector<IVec<Dim>> sizes_;
236 std::vector<TreeNode> tree_;
240void BlockDecomposer<Dim>::initImpl(std::size_t numBlocks, IVec<Dim> globalSize,
241 const std::vector<Real>* weights) {
242 globalSize_ = globalSize;
243 for (
int k = 0; k < Dim; ++k)
244 if (cellExtent_[k] <= 0)
256 std::stack<StackBlock> stack;
257 stack.push(StackBlock{IVec<Dim>{}, globalSize, numBlocks, 0});
260 while (!stack.empty()) {
261 StackBlock cur = stack.top();
264 if (
static_cast<std::size_t
>(cur.treeIndx) >= tree_.size()) {
265 tree_.resize(cur.treeIndx + 1);
268 if (cur.numSub > 1) {
275 for (
int k = 1; k < Dim; ++k) {
276 if (cur.size[k] * cellExtent_[k] > cur.size[kLargest] * cellExtent_[kLargest])
279 std::size_t numSub = cur.numSub / 2;
280 Index szSub = splitPosition(cur.origin, cur.size, kLargest, numSub, cur.numSub, weights);
285 const Index a = align_[kLargest];
286 if (a > 1 && cur.size[kLargest] >= 2 * a) {
287 szSub = ((szSub + a / 2) / a) * a;
290 if (szSub > cur.size[kLargest] - a)
291 szSub = cur.size[kLargest] - a;
294 StackBlock left = cur;
295 left.numSub = numSub;
296 left.size[kLargest] = szSub;
297 left.treeIndx = 2 * cur.treeIndx + 1;
299 StackBlock right = cur;
300 right.origin[kLargest] += szSub;
301 right.size[kLargest] -= szSub;
302 right.numSub = cur.numSub - numSub;
303 right.treeIndx = 2 * cur.treeIndx + 2;
305 tree_[cur.treeIndx] = TreeNode{kLargest, cur.origin[kLargest] + szSub};
309 tree_[cur.treeIndx] = TreeNode{-1, leafIndex++};
310 origins_.push_back(cur.origin);
311 sizes_.push_back(cur.size);
317Index BlockDecomposer<Dim>::splitPosition(
const IVec<Dim>& origin,
const IVec<Dim>& size,
318 int kLargest, std::size_t numSub, std::size_t numTotal,
319 const std::vector<Real>* weights)
const {
320 const Index n = size[kLargest];
324 if (weights ==
nullptr || n <= 1) {
325 return static_cast<Index>(std::round(
static_cast<double>(n) *
static_cast<double>(numSub) /
326 static_cast<double>(numTotal)));
331 std::vector<double> slab(
static_cast<std::size_t
>(n), 0.0);
332 IVec<Dim> bgn = origin, end{};
333 for (
int i = 0; i < Dim; ++i)
334 end[i] = origin[i] + size[i];
335 forEachInBox<Dim>(bgn, end, [&](
const IVec<Dim>& g) {
336 slab[
static_cast<std::size_t
>(g[kLargest] - origin[kLargest])] += (*weights)[linearGlobal(g)];
340 for (
double w : slab)
342 const double target = total *
static_cast<double>(numSub) /
static_cast<double>(numTotal);
347 double bestDist = std::numeric_limits<double>::max();
349 for (
Index s = 1; s < n; ++s) {
350 cum += slab[
static_cast<std::size_t
>(s - 1)];
351 const double dist = std::abs(cum - target);
352 if (dist <= bestDist) {
void flattenTree(std::vector< int > &splitDim, std::vector< Index > &splitVal) const
Flatten the implicit ORB tree into two parallel arrays for a device-callable ownerOf: for node i,...
Block< Dim > block(std::size_t b) const
const std::vector< IVec< Dim > > & sizes() const
void init(std::size_t numBlocks, IVec< Dim > globalSize, const IVec< Dim > &align, const IVec< Dim > &cellExtent)
Anisotropic-cell aligned ORB: cellExtent[k] is how many underlying FINE cells one cell of this grid s...
void init(std::size_t numBlocks, IVec< Dim > globalSize)
Build the decomposition of a globalSize cell grid into numBlocks blocks (equal cell count).
BlockDecomposer(std::size_t numBlocks, IVec< Dim > globalSize, const std::vector< Real > &weights)
const std::vector< IVec< Dim > > & origins() const
const IVec< Dim > & globalSize() const
void init(std::size_t numBlocks, IVec< Dim > globalSize, const IVec< Dim > &align)
Aligned ORB: force every split position (and hence every block origin/size) on axis k to be a multipl...
void init(std::size_t numBlocks, IVec< Dim > globalSize, const std::vector< Real > &weights)
Weighted ORB: balance the total weight per block instead of the cell count.
std::size_t numBlocks() const
BlockDecomposer()=default
BlockDecomposer(std::size_t numBlocks, IVec< Dim > globalSize)
Index linearGlobal(const IVec< Dim > &g) const
Global multi-index -> global linear index (x-fastest: I = x + y*nx + z*nx*ny).
BlockDecomposer< Dim > refined(const IVec< Dim > &ratio) const
Derive the NESTED fine decomposition: the exact inverse of coarsened() — global size,...
BlockDecomposer(std::size_t numBlocks, IVec< Dim > globalSize, const IVec< Dim > &align)
Aligned ORB (coarsenable): see the aligned init() below.
int ownerOf(const IVec< Dim > &g) const
Owning block index of a global cell coordinate. Caller must wrap into [0, globalSize) first.
BlockDecomposer< Dim > coarsened(const IVec< Dim > &ratio) const
Derive the NESTED coarse decomposition: each block, split value and the global size divided by ratio ...
IVec< Dim > multiGlobal(Index lin) const
Global linear index -> global multi-index (inverse of linearGlobal).
std::array< Index, Dim > IVec
Multi-dimensional integer index / coordinate (x-fastest order by convention).
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
A rank-owned axis-aligned block of the global cell grid.
IVec< Dim > size
extent in cells along each axis
IVec< Dim > origin
inclusive lower corner in global cell coordinates