Node.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #include "Node.h"
  2. #include <vector>
  3. #include <iostream>
  4. #include <math.h>
  5. using namespace std;
  6. /*
  7. Node constructor. Used recursively to build the Barnes-Hut tree. px, py, pz
  8. denote the corner (lowest value for each dimension) of the box of size pSize.
  9. pBodies is a vector containing all the nodes that must be placed in this box.
  10. */
  11. Node::Node(const vector<Node*> &pBodies, const double pSize,
  12. const double px, const double py, const double pz){
  13. size = pSize; // Required later for treeWalk
  14. // Divide into subnodes (octants)
  15. vector<Node*> subBodies[2][2][2];
  16. for (int i = 0; i < pBodies.size(); i++){
  17. int xIndex, yIndex, zIndex;
  18. if (pBodies[i]->COM[0] < (px + (size / 2))) xIndex = 0;
  19. else xIndex = 1;
  20. if (pBodies[i]->COM[1] < (py + (size / 2))) yIndex = 0;
  21. else yIndex = 1;
  22. if (pBodies[i]->COM[2] < (pz + (size / 2))) zIndex = 0;
  23. else zIndex = 1;
  24. subBodies[xIndex][yIndex][zIndex].push_back(pBodies[i]);
  25. }
  26. // Recursively place the nodes
  27. // g++ will unroll these loops
  28. for (int i = 0; i < 2; i++){
  29. for (int j = 0; j < 2; j++){
  30. for (int k = 0; k < 2; k++){
  31. switch(subBodies[i][j][k].size()){
  32. case 0: continue;
  33. case 1:
  34. subBodies[i][j][k][0]->size = size/2;
  35. children.push_back(subBodies[i][j][k][0]);
  36. break;
  37. default:
  38. children.push_back(new Node(subBodies[i][j][k], size/2,
  39. px + size/2*i, py + size/2*j, pz + size/2*k));
  40. }
  41. }
  42. }
  43. }
  44. // Recursively calculate the COM
  45. memset(COM, 0, sizeof(COM)); // Set COM to 0s
  46. m = 0.; // mass
  47. for (int i = 0; i < children.size(); i++){
  48. m += children[i]->m;
  49. for (int j = 0; j < 3; j++)
  50. COM[j] += children[i]->m * children[i]->COM[j];
  51. }
  52. // COM only relevant if there is mass in the octant
  53. if (m > 0) for (int i = 0; i < 3; i++) COM[i] /= m;
  54. }
  55. /*
  56. Node constructor. Used to build the leaf nodes directly from the data passed
  57. from Python using ctypes.
  58. */
  59. Node::Node(const double* pr_vec, const double pm){
  60. // Initialize a node (leaf)
  61. for (int i = 0; i < 3; i++) COM[i] = pr_vec[i];
  62. memset(g, 0, sizeof(g)); // Set g to 0s
  63. m = pm; // mass
  64. }
  65. /*
  66. Calculate the acceleration at the this node. Used recursively calling
  67. treeWalk(topNode, thetamax). This is O(log n) and will be called for
  68. each node in the tree: O(n log n). Soft defines the characteristic
  69. plummer softening scale.
  70. */
  71. void Node::treeWalk(const Node &node,
  72. const double thetamax, const double soft){
  73. // Calculate distance to node
  74. double delta[3];
  75. for (int i = 0; i < 3; i++) delta[i] = node.COM[i] - COM[i];
  76. double r = sqrt(delta[0]*delta[0] + delta[1]*delta[1] + delta[2]*delta[2]);
  77. if (r==0) return; // Do not interact with self
  78. // If it satisfies the size/r < thetamax criterion, add the g contribution
  79. if (node.children.size() == 0 || node.size/r < thetamax){
  80. double tripler = (r+soft) * (r+soft) * r;
  81. for(int i = 0; i < 3; i++) g[i] += node.m * delta[i] / tripler;
  82. }
  83. else{ // Otherwise recurse into its children
  84. for (int i = 0; i < node.children.size(); i++){
  85. treeWalk((*node.children[i]), thetamax, soft);
  86. }
  87. }
  88. }