Program Listing for File build_non_adaptive_tree.h

Return to documentation for file (include/build_non_adaptive_tree.h)

#ifndef build_non_adaptive_tree_h
#define build_non_adaptive_tree_h
#include "exafmm_t.h"
#include "hilbert.h"
#include "fmm_base.h"

namespace exafmm_t {
  template <typename T>
  void get_bounds(const Bodies<T>& sources, const Bodies<T>& targets, vec3& x0, real_t& r0) {
    vec3 Xmin = sources[0].X;
    vec3 Xmax = sources[0].X;
    for (size_t b=0; b<sources.size(); ++b) {
      Xmin = min(sources[b].X, Xmin);
      Xmax = max(sources[b].X, Xmax);
    }
    for (size_t b=0; b<targets.size(); ++b) {
      Xmin = min(targets[b].X, Xmin);
      Xmax = max(targets[b].X, Xmax);
    }
    x0 = (Xmax + Xmin) / 2;
    r0 = fmax(max(x0-Xmin), max(Xmax-x0));
    r0 *= 1.00001;
  }

  template <typename T>
  void sort_bodies(Node<T>* const node, Body<T>* const bodies, int begin, int end,
                   std::vector<int>& size, std::vector<int>& offsets) {
    if (end == begin) {
      size.resize(8, 0);
      offsets.resize(8, begin);
      return;
    }
    // Count number of bodies in each octant
    size.resize(8, 0);
    vec3 X = node->x;  // the center of the node
    for (int i=begin; i<end; i++) {
      vec3& x = bodies[i].X;
      int octant = (x[0] > X[0]) + ((x[1] > X[1]) << 1) + ((x[2] > X[2]) << 2);
      size[octant]++;
    }
    // Exclusive scan to get offsets
    offsets.resize(8);
    std::vector<int> counter(8);
    int offset = begin;
    for (int i=0; i<8; i++) {
      offsets[i] = offset;
      offset += size[i];
      counter[i] = offsets[i] - begin;   // counter for local buffer
    }
    // Sort bodies by octant
    Bodies<T> buffer(end-begin);
    for (int i=begin; i<end; i++) {
      vec3& x = bodies[i].X;
      int octant = (x[0] > X[0]) + ((x[1] > X[1]) << 1) + ((x[2] > X[2]) << 2);
      buffer[counter[octant]].X = bodies[i].X;
      buffer[counter[octant]].q = bodies[i].q;
      buffer[counter[octant]].ibody = bodies[i].ibody;
      counter[octant]++;
    }
    // copy sorted bodies in buffer back to bodies
    for (int i=begin; i<end; ++i) {
      bodies[i].X = buffer[i-begin].X;
      bodies[i].q = buffer[i-begin].q;
      bodies[i].ibody = buffer[i-begin].ibody;
    }
  }

  template <typename T>
  void build_tree(Body<T>* sources, int source_begin, int source_end,
                  Body<T>* targets, int target_begin, int target_end,
                  Node<T>* node, Nodes<T>& nodes,
                  NodePtrs<T>& leafs, NodePtrs<T>& nonleafs, FmmBase<T>& fmm) {
    node->idx = int(node-&nodes[0]);  // current node's index in nodes
    node->nsrcs = source_end - source_begin;
    node->ntrgs = target_end - target_begin;
    node->up_equiv.resize(fmm.nsurf, (T)(0.));
    node->dn_equiv.resize(fmm.nsurf, (T)(0.));
    ivec3 iX = get3DIndex(node->x, node->level, fmm.x0, fmm.r0);
    node->key = getKey(iX, node->level);

    // for the ghost (empty) nodes which are not at leaf level
    if(node->nsrcs==0 && node->ntrgs==0) {
      node->is_leaf = true;
      return;
    }

    if (node->level == fmm.depth) {
      node->is_leaf = true;
      node->trg_value.resize(node->ntrgs*4, (T)(0.));   // initialize target result vector
      leafs.push_back(node);
      // Copy sources and targets' coords and values to leafs
      Body<T>* first_source = sources + source_begin;
      Body<T>* first_target = targets + target_begin;
      for (Body<T>* B=first_source; B<first_source+node->nsrcs; ++B) {
        for (int d=0; d<3; ++d) {
          node->src_coord.push_back(B->X[d]);
        }
        node->isrcs.push_back(B->ibody);
        node->src_value.push_back(B->q);
      }
      for (Body<T>* B=first_target; B<first_target+node->ntrgs; ++B) {
        for (int d=0; d<3; ++d) {
          node->trg_coord.push_back(B->X[d]);
        }
        node->itrgs.push_back(B->ibody);
      }
      return;
    }
    // Sort bodies and save in buffer
    std::vector<int> source_size, source_offsets;
    std::vector<int> target_size, target_offsets;
    sort_bodies<T>(node, sources, source_begin, source_end, source_size, source_offsets);
    sort_bodies<T>(node, targets, target_begin, target_end, target_size, target_offsets);
    node->is_leaf = false;
    nonleafs.push_back(node);
    assert(nodes.capacity() >= nodes.size()+NCHILD);

    nodes.resize(nodes.size()+NCHILD);
    Node<T> * child = &nodes.back() - NCHILD + 1;
    node->children.resize(8, nullptr);
    for (int c=0; c<8; c++) {
      node->children[c] = &child[c];
      child[c].x = node->x;
      child[c].r = node->r / 2;
      for (int d=0; d<3; d++) {
        child[c].x[d] += child[c].r * (((c & 1 << d) >> d) * 2 - 1);
      }
      child[c].parent = node;
      child[c].octant = c;
      child[c].level = node->level + 1;
#if DEBUG
      cout << "create node at level: " << child[c].level << " octant: " << c
           << " sources range: " << source_offsets[c] << " " << source_offsets[c] + source_size[c]
           << " targets range: " << target_offsets[c] << " " << target_offsets[c] + target_size[c]
           << " sources address: " << sources << " targets address: " << targets << endl;
#endif
      build_tree(sources, source_offsets[c], source_offsets[c] + source_size[c],
                 targets, target_offsets[c], target_offsets[c] + target_size[c],
                 &child[c], nodes, leafs, nonleafs, fmm);
    }
  }

  template <typename T>
  Nodes<T> build_tree(Bodies<T>& sources, Bodies<T>& targets,
                      NodePtrs<T>& leafs, NodePtrs<T>& nonleafs, FmmBase<T>& fmm) {
    Nodes<T> nodes(1);
    nodes[0].parent = nullptr;
    nodes[0].octant = 0;
    nodes[0].x = fmm.x0;
    nodes[0].r = fmm.r0;
    nodes[0].level = 0;
    nodes.reserve((pow(8, fmm.depth+1)-1) / 7);  // reserve for 8^(level+1)/7 nodes
    build_tree(&sources[0], 0, sources.size(),
               &targets[0], 0, targets.size(),
               &nodes[0], nodes, leafs, nonleafs, fmm);
    return nodes;
  }
}
#endif