#include "replicateGenerator.hpp" #include #include "omptl/omptl_algorithm" #include #include #include #include namespace CosmoTool { template class CellCompare { public: CellCompare(int k) { rank = k; } bool operator()(const KDCell *a, const KDCell *b) const { return (a->coord[rank] < b->coord[rank]); } protected: int rank; }; template KDTree::~KDTree() { } template KDTree::KDTree(Cell *cells, uint32_t Ncells) { periodic = false; base_cell = cells; numNodes = Ncells; nodes = new Node[numNodes]; sortingHelper = new Cell *[Ncells]; for (uint32_t i = 0; i < Ncells; i++) sortingHelper[i] = &cells[i]; optimize(); } template void KDTree::optimize() { coords absoluteMin, absoluteMax; std::cout << "Optimizing the tree..." << std::endl; uint32_t activeCells = gatherActiveCells(sortingHelper, numNodes); std::cout << " number of active cells = " << activeCells << std::endl; lastNode = 0; for (int i = 0; i < N; i++) { absoluteMin[i] = std::numeric_limits::max(); absoluteMax[i] = -std::numeric_limits::max(); } // Find min and max corner for (uint32_t i = 0; i < activeCells; i++) { KDCell *cell = sortingHelper[i]; for (int k = 0; k < N; k++) { if (cell->coord[k] < absoluteMin[k]) absoluteMin[k] = cell->coord[k]; if (cell->coord[k] > absoluteMax[k]) absoluteMax[k] = cell->coord[k]; } } std::cout << " rebuilding the tree..." << std::endl; root = buildTree(sortingHelper, activeCells, 0, absoluteMin, absoluteMax); std::cout << " done." << std::endl; } template uint32_t KDTree::getIntersection(const coords& x, CoordType r, KDTree::Cell **cells, uint32_t numCells) throw (NotEnoughCells) { RecursionInfoCells info; memcpy(info.x, x, sizeof(x)); info.r = r; info.r2 = r*r; info.cells = cells; info.currentRank = 0; info.numCells = numCells; info.distances = 0; recursiveIntersectionCells(info, root, 0); if (periodic) { ReplicateGenerator r(x, replicate); do { coords x_new; r.getPosition(info.x); recursiveIntersectionCells(info, root, 0); } while (r.next()); } return info.currentRank; } template uint32_t KDTree::getIntersection(const coords& x, CoordType r, Cell **cells, CoordType *distances, uint32_t numCells) throw (NotEnoughCells) { RecursionInfoCells info; memcpy(info.x, x, sizeof(x)); info.r = r; info.r2 = r*r; info.cells = cells; info.currentRank = 0; info.numCells = numCells; info.distances = distances; recursiveIntersectionCells(info, root, 0); if (periodic) { ReplicateGenerator r(x, replicate); do { coords x_new; r.getPosition(info.x); recursiveIntersectionCells(info, root, 0); } while (r.next()); } return info.currentRank; } template uint32_t KDTree::countCells(const coords& x, CoordType r) { RecursionInfoCells info; memcpy(info.x, x, sizeof(x)); info.r = r; info.r2 = r*r; info.cells = 0; info.currentRank = 0; info.numCells = 0; info.distances = 0; recursiveIntersectionCells(info, root, 0); if (periodic) { ReplicateGenerator r(x, replicate); do { coords x_new; r.getPosition(info.x); recursiveIntersectionCells(info, root, 0); } while (r.next()); } return info.currentRank; } template template void KDTree::recursiveIntersectionCells(RecursionInfoCells& info, Node *node, int level) throw (NotEnoughCells) { int axis = level % N; CoordType d2 = 0; #if __KD_TREE_ACTIVE_CELLS == 1 if (node->value->active) #endif { for (int j = 0; j < 3; j++) { CoordType delta = info.x[j]-node->value->coord[j]; d2 += delta*delta; } if (d2 < info.r2) { if (!justCount) { if (info.currentRank == info.numCells) throw NotEnoughCells(); info.cells[info.currentRank] = node->value; if (info.distances) info.distances[info.currentRank] = d2; } info.currentRank++; } } // The hypersphere intersects the left child node if (((info.x[axis]+info.r) > node->minBound[axis]) && ((info.x[axis]-info.r) < node->value->coord[axis])) { if (node->children[0] != 0) recursiveIntersectionCells(info, node->children[0], level+1); } if (((info.x[axis]+info.r) > node->value->coord[axis]) && ((info.x[axis]-info.r) < node->maxBound[axis])) { if (node->children[1] != 0) recursiveIntersectionCells(info, node->children[1], level+1); } } template uint32_t gatherActiveCells(KDCell **cells, uint32_t Ncells) { uint32_t swapId = Ncells-1; uint32_t i = 0; #if __KD_TREE_ACTIVE_CELLS == 1 while (!cells[swapId]->active && swapId > 0) swapId--; while (i < swapId) { if (!cells[i]->active) { std::swap(cells[i], cells[swapId]); while (!cells[swapId]->active && swapId > i) { swapId--; } } i++; } #endif return swapId+1; } template void KD_default_cell_splitter::operator()(KDCell **cells, uint32_t Ncells, uint32_t& split_index, int axis, typename KDDef::KDCoordinates minBound, typename KDDef::KDCoordinates maxBound) { CellCompare compare(axis); omptl::sort(cells,cells+Ncells,compare); // std::sort(cells, cells+Ncells, compare); split_index = Ncells/2; } template KDTreeNode *KDTree::buildTree(Cell **cell0, uint32_t Ncells, uint32_t depth, coords minBound, coords maxBound) { if (Ncells == 0) return 0; Node *node; int axis = depth % N; uint32_t mid; coords tmpBound; #pragma omp critical node = &nodes[lastNode++]; // Isolate the environment splitter(cell0, Ncells, mid, axis, minBound, maxBound); node->value = *(cell0+mid); memcpy(&node->minBound[0], &minBound[0], sizeof(coords)); memcpy(&node->maxBound[0], &maxBound[0], sizeof(coords)); memcpy(tmpBound, maxBound, sizeof(coords)); tmpBound[axis] = node->value->coord[axis]; depth++; #pragma omp task private(tmpBound) { node->children[0] = buildTree(cell0, mid, depth, minBound, tmpBound); } memcpy(tmpBound, minBound, sizeof(coords)); tmpBound[axis] = node->value->coord[axis]; #pragma omp task private(tmpBound) { node->children[1] = buildTree(cell0+mid+1, Ncells-mid-1, depth, tmpBound, maxBound); } #pragma omp taskwait #ifdef __KD_TREE_NUMNODES node->numNodes = (node->children[0] != 0) ? node->children[0]->numNodes : 0; node->numNodes += (node->children[1] != 0) ? node->children[1]->numNodes : 0; node->numNodes++; #endif return node; } template uint32_t KDTree::countActives() const { uint32_t numActive = 0; for (uint32_t i = 0; i < lastNode; i++) { #if __KD_TREE_ACTIVE_CELLS == 1 if (nodes[i].value->active) #endif numActive++; } return numActive; } template typename KDDef::CoordType KDTree::computeDistance(const Cell *cell, const coords& x) const { CoordType d2 = 0; for (int i = 0; i < N; i++) { CoordType delta = cell->coord[i] - x[i]; d2 += delta*delta; } return d2; } template void KDTree::recursiveNearest( Node *node, int level, const coords& x, CoordType& R2, Cell *& best) { CoordType d2 = 0; int axis = level % N; Node *other, *go; if (x[axis] < node->value->coord[axis]) { // The best is potentially in 0. go = node->children[0]; other = node->children[1]; } else { // If not it is in 1. go = node->children[1]; other = node->children[0]; if (go == 0) { go = other; other = 0; } } if (go != 0) { recursiveNearest(go, level+1, x, R2,best); } else { CoordType thisR2 = computeDistance(node->value, x); if (thisR2 < R2) { R2 = thisR2; best = node->value; } return; } // Check if current node is not the nearest CoordType thisR2 = computeDistance(node->value, x); if (thisR2 < R2) { R2 = thisR2; best = node->value; } // Now we found the best. We check whether the hypersphere // intersect the hyperplane of the other branch CoordType delta1; delta1 = x[axis]-node->value->coord[axis]; if (delta1*delta1 < R2) { // The hypersphere intersects the hyperplane. Try the // other branch if (other != 0) { recursiveNearest(other, level+1, x, R2, best); } } } template KDCell * KDTree::getNearestNeighbour(const coords& x) { CoordType R2 = INFINITY; Cell *best = 0; recursiveNearest(root, 0, x, R2, best); if (periodic) { ReplicateGenerator r(x, replicate); do { coords x_new; r.getPosition(x_new); recursiveNearest(root, 0, x_new, R2, best); } while (r.next()); } return best; } template void KDTree::recursiveMultipleNearest(RecursionMultipleInfo& info, Node *node, int level) { CoordType d2 = 0; int axis = level % N; Node *other, *go; if (info.x[axis] < node->value->coord[axis]) { // The best is potentially in 0. go = node->children[0]; other = node->children[1]; } else { // If not it is in 1. go = node->children[1]; other = node->children[0]; // if (go == 0) // { // go = other; //other = 0; //} } if (go != 0) { recursiveMultipleNearest(info, go, level+1); } // Check if current node is not the nearest CoordType thisR2 = computeDistance(node->value, info.x); info.queue.push(node->value, thisR2); info.traversed++; // if (go == 0) // return; // Now we found the best. We check whether the hypersphere // intersect the hyperplane of the other branch CoordType delta1; delta1 = info.x[axis]-node->value->coord[axis]; if (delta1*delta1 < info.queue.getMaxPriority()) { // The hypersphere intersects the hyperplane. Try the // other branch if (other != 0) { recursiveMultipleNearest(info, other, level+1); } } } template void KDTree::getNearestNeighbours(const coords& x, uint32_t N2, Cell **cells) { RecursionMultipleInfo info(x, cells, N2); for (int i = 0; i < N2; i++) cells[i] = 0; recursiveMultipleNearest(info, root, 0); if (periodic) { ReplicateGenerator r(x, replicate); do { coords x_new; r.getPosition(info.x); recursiveMultipleNearest(info, root, 0); } while (r.next()); } // std::cout << "Traversed = " << info.traversed << std::endl; } template void KDTree::getNearestNeighbours(const coords& x, uint32_t N2, Cell **cells, CoordType *distances) { RecursionMultipleInfo info(x, cells, N2); for (int i = 0; i < N2; i++) cells[i] = 0; recursiveMultipleNearest(info, root, 0); if (periodic) { ReplicateGenerator r(x, replicate); do { coords x_new; r.getPosition(info.x); recursiveMultipleNearest(info, root, 0); } while (r.next()); } memcpy(distances, info.queue.getPriorities(), sizeof(CoordType)*N2); } #ifdef __KD_TREE_SAVE_ON_DISK #define KDTREE_DISK_SIGNATURE "KDTREE" #define KDTREE_DISK_SIGNATURE_LEN 7 template struct KDTreeOnDisk { long cell_id; long children_node[2]; typename KDDef::KDCoordinates minBound, maxBound; }; struct KDTreeHeader { char id[KDTREE_DISK_SIGNATURE_LEN]; long nodesUsed, numCells; long rootId; }; template void KDTree::saveTree(std::ostream& o) const { KDTreeHeader h; strncpy(h.id, KDTREE_DISK_SIGNATURE, KDTREE_DISK_SIGNATURE_LEN); h.nodesUsed = lastNode; h.numCells = numNodes; h.rootId = root - nodes; o.write((char*)&h, sizeof(h)); for (long i = 0; i < lastNode; i++) { KDTreeOnDisk node_on_disk; node_on_disk.cell_id = nodes[i].value - base_cell; if (nodes[i].children[0] == 0) node_on_disk.children_node[0] = -1L; else node_on_disk.children_node[0] = nodes[i].children[0] - nodes; assert((node_on_disk.children_node[0] == -1) || ((node_on_disk.children_node[0] >= 0) && (node_on_disk.children_node[0] < lastNode))); if (nodes[i].children[1] == 0) node_on_disk.children_node[1] = -1L; else node_on_disk.children_node[1] = nodes[i].children[1] - nodes; assert((node_on_disk.children_node[1] == -1) || ((node_on_disk.children_node[1] >= 0) && (node_on_disk.children_node[1] < lastNode))); memcpy(node_on_disk.minBound, nodes[i].minBound, sizeof(coords)); memcpy(node_on_disk.maxBound, nodes[i].maxBound, sizeof(coords)); o.write((char *)&node_on_disk, sizeof(node_on_disk)); } } template KDTree::KDTree(std::istream& in, Cell *cells, uint32_t Ncells) throw (InvalidOnDiskKDTree) { KDTreeHeader h; if (!in) throw InvalidOnDiskKDTree(); in.read((char *)&h, sizeof(h)); if (!in || strncmp(h.id, KDTREE_DISK_SIGNATURE, KDTREE_DISK_SIGNATURE_LEN) != 0) { std::cerr << "KDTree Signature invalid" << std::endl; throw InvalidOnDiskKDTree(); } if (h.numCells != Ncells || h.nodesUsed < 0) { std::cerr << "The number of cells has changed (" << h.numCells << " != " << Ncells << ") or nodesUsed=" << h.nodesUsed << std::endl; throw InvalidOnDiskKDTree(); } base_cell = cells; nodes = new Node[h.nodesUsed]; lastNode = h.nodesUsed; numNodes = Ncells; for (long i = 0; i < lastNode; i++) { KDTreeOnDisk node_on_disk; in.read((char *)&node_on_disk, sizeof(node_on_disk)); if (!in) { std::cerr << "End-of-file reached" << std::endl; delete[] nodes; throw InvalidOnDiskKDTree(); } if (node_on_disk.cell_id > numNodes || node_on_disk.cell_id < 0 || node_on_disk.children_node[0] > lastNode || node_on_disk.children_node[0] < -1 || node_on_disk.children_node[1] > lastNode || node_on_disk.children_node[1] < -1) { delete[] nodes; std::cerr << "Invalid cell id or children node id invalid" << std::endl; std::cerr << node_on_disk.cell_id << std::endl << node_on_disk.children_node[0] << std::endl << node_on_disk.children_node[1] << std::endl; throw InvalidOnDiskKDTree(); } nodes[i].value = base_cell + node_on_disk.cell_id; if (node_on_disk.children_node[0] == -1) nodes[i].children[0] = 0; else nodes[i].children[0] = nodes + node_on_disk.children_node[0]; if (node_on_disk.children_node[1] == -1) nodes[i].children[1] = 0; else nodes[i].children[1] = nodes + node_on_disk.children_node[1]; memcpy(nodes[i].minBound, node_on_disk.minBound, sizeof(coords)); memcpy(nodes[i].maxBound, node_on_disk.maxBound, sizeof(coords)); int c; for (c = 0; c < N; c++) if (nodes[i].value->coord[c] < nodes[i].minBound[c] || nodes[i].value->coord[c] > nodes[i].maxBound[c]) break; if (c != N) { delete[] nodes; std::cerr << "Coordinates of the cell inconsistent with the boundaries" << std::endl << " X=" << nodes[i].value->coord[0] << " B=[" << nodes[i].minBound[0] << "," << nodes[i].maxBound[0] << "]" << std::endl << " Y=" << nodes[i].value->coord[1] << " B=[" << nodes[i].minBound[1] << "," << nodes[i].maxBound[1] << "]" << std::endl << " Z=" << nodes[i].value->coord[2] << " B=[" << nodes[i].minBound[2] << "," << nodes[i].maxBound[2] << "]" << std::endl; throw InvalidOnDiskKDTree(); } } root = &nodes[h.rootId]; sortingHelper = new Cell *[Ncells]; for (uint32_t i = 0; i < Ncells; i++) sortingHelper[i] = &cells[i]; } #endif };