#include <stdio.h>
#include <stdlib.h>
#include "particle.h"
#include "rebound.h"
#include "boundary.h"
#include "tree.h"
#ifdef MPI
#include "communication_mpi.h"
#endif
static int reb_reb_tree_get_octant_for_particle_in_cell(const struct reb_particle p, struct reb_treecell *node);
static struct reb_treecell *reb_tree_add_particle_to_cell(struct reb_simulation* const r, struct reb_treecell *node, int pt, struct reb_treecell *parent, int o);
void reb_tree_add_particle_to_tree(struct reb_simulation* const r, int pt){
if (r->tree_root==NULL){
r->tree_root = calloc(r->N_root_x*r->N_root_y*r->N_root_z,sizeof(struct reb_treecell*));
}
struct reb_particle p = r->particles[pt];
if (!isfinite(p.x) || !isfinite(p.y) || !isfinite(p.z)){
reb_simulation_error(r, "Particle has non-finite coordinates. Cannot add to tree.");
return;
}
int rootbox = reb_get_rootbox_for_particle(r, p);
#ifdef MPI
int N_root_per_node = r->N_root/r->mpi_num;
int proc_id = rootbox/N_root_per_node;
if (proc_id!=r->mpi_id) return;
#endif r->tree_root[rootbox] = reb_tree_add_particle_to_cell(r, r->tree_root[rootbox],pt,NULL,0);
}
static struct reb_treecell *reb_tree_add_particle_to_cell(struct reb_simulation* const r, struct reb_treecell *node, int pt, struct reb_treecell *parent, int o){
struct reb_particle* const particles = r->particles;
if (node == NULL) {
node = calloc(1, sizeof(struct reb_treecell));
struct reb_particle p = particles[pt];
if (parent == NULL){ node->w = r->root_size;
int i = ((int)floor((p.x + r->boxsize.x/2.)/r->root_size))%r->N_root_x;
int j = ((int)floor((p.y + r->boxsize.y/2.)/r->root_size))%r->N_root_y;
int k = ((int)floor((p.z + r->boxsize.z/2.)/r->root_size))%r->N_root_z;
node->x = -r->boxsize.x/2.+r->root_size*(0.5+(double)i);
node->y = -r->boxsize.y/2.+r->root_size*(0.5+(double)j);
node->z = -r->boxsize.z/2.+r->root_size*(0.5+(double)k);
}else{ node->w = parent->w/2.;
node->x = parent->x + node->w/2.*((o>>0)%2==0?1.:-1);
node->y = parent->y + node->w/2.*((o>>1)%2==0?1.:-1);
node->z = parent->z + node->w/2.*((o>>2)%2==0?1.:-1);
}
for (int i=0; i<8; i++){
node->oct[i] = NULL;
}
if (node->w<=0.0){
reb_simulation_error(r, "Tree cell has size zero.");
free(node);
return NULL;
}
node->pt = pt;
particles[pt].c = node;
return node;
}
if (node->pt >= 0) { int o1 = reb_reb_tree_get_octant_for_particle_in_cell(particles[node->pt], node);
int o2 = reb_reb_tree_get_octant_for_particle_in_cell(particles[pt], node);
if (o1==o2){ if (particles[pt].x == particles[node->pt].x && particles[pt].y == particles[node->pt].y && particles[pt].z == particles[node->pt].z){
reb_simulation_error(r, "Cannot add two particles with the same coordinates to the tree.");
return node;
}
}
node->oct[o1] = reb_tree_add_particle_to_cell(r, node->oct[o1], node->pt, node, o1);
node->oct[o2] = reb_tree_add_particle_to_cell(r, node->oct[o2], pt, node, o2);
node->pt = -2;
}else{ node->pt--;
int o = reb_reb_tree_get_octant_for_particle_in_cell(particles[pt], node);
node->oct[o] = reb_tree_add_particle_to_cell(r, node->oct[o], pt, node, o);
}
return node;
}
static int reb_reb_tree_get_octant_for_particle_in_cell(const struct reb_particle p, struct reb_treecell *node){
int octant = 0;
if (p.x < node->x) octant+=1;
if (p.y < node->y) octant+=2;
if (p.z < node->z) octant+=4;
return octant;
}
static int reb_tree_particle_is_inside_cell(const struct reb_simulation* const r, struct reb_treecell *node){
if (fabs(r->particles[node->pt].x-node->x) > node->w/2. ||
fabs(r->particles[node->pt].y-node->y) > node->w/2. ||
fabs(r->particles[node->pt].z-node->z) > node->w/2. ||
isnan(r->particles[node->pt].y)) {
return 0;
}
return 1;
}
static struct reb_treecell *reb_simulation_update_tree_cell(struct reb_simulation* const r, struct reb_treecell *node){
int test = -1;
if (node == NULL) {
return NULL;
}
if (node->pt < 0) {
for (int o=0; o<8; o++) {
node->oct[o] = reb_simulation_update_tree_cell(r, node->oct[o]);
}
node->pt = 0;
for (int o=0; o<8; o++) {
struct reb_treecell *d = node->oct[o];
if (d != NULL) {
if (d->pt >= 0) { node->pt--;
test = o;
}else{ node->pt += d->pt;
}
}
}
if (node->pt == 0) { free(node);
return NULL;
} else if (node->pt == -1) { node->pt = node->oct[test]->pt;
r->particles[node->pt].c = node;
free(node->oct[test]);
node->oct[test]=NULL;
return node;
}
return node;
}
if (reb_tree_particle_is_inside_cell(r, node) == 0) {
int oldpos = node->pt;
struct reb_particle reinsertme = r->particles[oldpos];
if (r->N){ (r->N)--;
r->particles[oldpos] = r->particles[r->N];
r->particles[oldpos].c->pt = oldpos;
if (!isnan(reinsertme.y)){ reb_simulation_add(r, reinsertme);
}
}
free(node);
return NULL;
} else {
r->particles[node->pt].c = node;
return node;
}
}
static void reb_simulation_update_tree_gravity_data_in_cell(const struct reb_simulation* const r, struct reb_treecell *node){
#ifdef QUADRUPOLE
node->mxx = 0;
node->mxy = 0;
node->mxz = 0;
node->myy = 0;
node->myz = 0;
node->mzz = 0;
#endif if (node->pt < 0) {
node->m = 0;
node->mx = 0;
node->my = 0;
node->mz = 0;
for (int o=0; o<8; o++) {
struct reb_treecell* d = node->oct[o];
if (d!=NULL){
reb_simulation_update_tree_gravity_data_in_cell(r, d);
double d_m = d->m;
node->mx += d->mx*d_m;
node->my += d->my*d_m;
node->mz += d->mz*d_m;
node->m += d_m;
}
}
double m_tot = node->m;
if (m_tot>0){
node->mx /= m_tot;
node->my /= m_tot;
node->mz /= m_tot;
}
#ifdef QUADRUPOLE
for (int o=0; o<8; o++) {
struct reb_treecell* d = node->oct[o];
if (d!=NULL){
double d_m = d->m;
double qx = d->mx - node->mx;
double qy = d->my - node->my;
double qz = d->mz - node->mz;
double qr2 = qx*qx + qy*qy + qz*qz;
node->mxx += d->mxx + d_m*(3.*qx*qx - qr2);
node->mxy += d->mxy + d_m*3.*qx*qy;
node->mxz += d->mxz + d_m*3.*qx*qz;
node->myy += d->myy + d_m*(3.*qy*qy - qr2);
node->myz += d->myz + d_m*3.*qy*qz;
}
}
node->mzz = -node->mxx -node->myy;
#endif }else{
struct reb_particle p = r->particles[node->pt];
node->m = p.m;
node->mx = p.x;
node->my = p.y;
node->mz = p.z;
}
}
void reb_simulation_update_tree_gravity_data(struct reb_simulation* const r){
for(int i=0;i<r->N_root;i++){
#ifdef MPI
if (reb_communication_mpi_rootbox_is_local(r, i)==1){
#endif if (r->tree_root[i]!=NULL){
reb_simulation_update_tree_gravity_data_in_cell(r, r->tree_root[i]);
}
#ifdef MPI
}
#endif }
}
void reb_simulation_update_tree(struct reb_simulation* const r){
if (r->tree_root==NULL){
r->tree_root = calloc(r->N_root_x*r->N_root_y*r->N_root_z,sizeof(struct reb_treecell*));
}
for(int i=0;i<r->N_root;i++){
#ifdef MPI
if (reb_communication_mpi_rootbox_is_local(r, i)==1){
#endif r->tree_root[i] = reb_simulation_update_tree_cell(r, r->tree_root[i]);
#ifdef MPI
}
#endif }
r->tree_needs_update= 0;
}
static void reb_tree_delete_cell(struct reb_treecell* node){
if (node==NULL){
return;
}
if (node->remote==1){
return;
}
for (int o=0; o<8; o++) {
reb_tree_delete_cell(node->oct[o]);
}
free(node);
}
void reb_tree_delete(struct reb_simulation* const r){
if (r->tree_root!=NULL){
for(int i=0;i<r->N_root;i++){
reb_tree_delete_cell(r->tree_root[i]);
}
free(r->tree_root);
r->tree_root = NULL;
}
}
#ifdef MPI
int reb_particles_get_rootbox_for_node(struct reb_simulation* const r, struct reb_treecell* node){
int i = ((int)floor((node->x + r->boxsize.x/2.)/r->root_size)+r->N_root_x)%r->N_root_x;
int j = ((int)floor((node->y + r->boxsize.y/2.)/r->root_size)+r->N_root_y)%r->N_root_y;
int k = ((int)floor((node->z + r->boxsize.z/2.)/r->root_size)+r->N_root_z)%r->N_root_z;
int index = (k*r->N_root_y+j)*r->N_root_x+i;
return index;
}
int reb_reb_tree_get_octant_for_cell_in_cell(struct reb_treecell* nnode, struct reb_treecell *node){
int octant = 0;
if (nnode->x < node->x) octant+=1;
if (nnode->y < node->y) octant+=2;
if (nnode->z < node->z) octant+=4;
return octant;
}
void reb_tree_add_essential_node_to_node(struct reb_treecell* nnode, struct reb_treecell* node){
int o = reb_reb_tree_get_octant_for_cell_in_cell(nnode, node);
if (node->oct[o]==NULL){
node->oct[o] = nnode;
}else{
reb_tree_add_essential_node_to_node(nnode, node->oct[o]);
}
}
void reb_tree_add_essential_node(struct reb_simulation* const r, struct reb_treecell* node){
node->remote = 1;
for (int o=0;o<8;o++){
node->oct[o] = NULL;
}
int index = reb_particles_get_rootbox_for_node(r, node);
if (r->tree_root[index]==NULL){
r->tree_root[index] = node;
}else{
reb_tree_add_essential_node_to_node(node, r->tree_root[index]);
}
}
void reb_tree_prepare_essential_tree_for_gravity(struct reb_simulation* const r){
for(int i=0;i<r->N_root;i++){
if (reb_communication_mpi_rootbox_is_local(r, i)==1){
reb_communication_mpi_prepare_essential_tree_for_gravity(r, r->tree_root[i]);
}else{
r->tree_root[i] = NULL;
}
}
}
void reb_tree_prepare_essential_tree_for_collisions(struct reb_simulation* const r){
for(int i=0;i<r->N_root;i++){
if (reb_communication_mpi_rootbox_is_local(r, i)==1){
reb_communication_mpi_prepare_essential_tree_for_collisions(r, r->tree_root[i]);
}else{
r->tree_root[i] = NULL;
}
}
}
#endif