diff --git a/src/fof.c b/src/fof.c index 4a099cf8bca4551a50765ccb1c99477156daffce..f39bfe3ef95673aa856a98e6a8e71886fcac6259 100644 --- a/src/fof.c +++ b/src/fof.c @@ -725,8 +725,8 @@ void fof_search_tree_serial(struct space *s) { group_id[i] = group_id[root - node_offset]; } - fof_dump_group_data("fof_output_tree_serial.dat", nr_gparts, group_index, - group_size, group_id, group_mass, 1); + //fof_dump_group_data("fof_output_tree_serial.dat", nr_gparts, group_index, + // group_size, group_id, group_mass, 1); int num_parts_in_groups = 0; int max_group_size = 0, max_group_index = 0, max_group_mass_id = 0; @@ -833,6 +833,7 @@ void fof_search_foreign_cells(struct space *s) { int *group_index = s->fof_data.group_index; int *group_size = s->fof_data.group_size; double *group_mass = s->fof_data.group_mass; + struct fof_CoM *group_CoM = s->fof_data.group_CoM; const int nr_gparts = s->nr_gparts; const size_t nr_cells = s->nr_cells; const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; @@ -1023,6 +1024,11 @@ void fof_search_foreign_cells(struct space *s) { group_links[group_link_count].group_i = local_root; group_links[group_link_count].group_i_size = group_size[local_root - node_offset]; group_links[group_link_count].group_i_mass = group_mass[local_root - node_offset]; + + /* TODO: The CoM of groups linked across MPI domains will not be wrapped correctly as each rank will centre it within their own domain first.*/ + group_links[group_link_count].group_i_CoM.x = group_CoM[local_root - node_offset].x; + group_links[group_link_count].group_i_CoM.y = group_CoM[local_root - node_offset].y; + group_links[group_link_count].group_i_CoM.z = group_CoM[local_root - node_offset].z; group_links[group_link_count++].group_j = foreign_root; } @@ -1065,6 +1071,7 @@ void fof_search_foreign_cells(struct space *s) { /* Transform the group IDs to a local list going from 0-group_count so a union-find can be performed. */ int *global_group_index = NULL, *global_group_id = NULL, *global_group_size = NULL; double *global_group_mass = NULL; + struct fof_CoM *global_group_CoM = NULL; int group_count = 0; if (posix_memalign((void**)&global_group_index, SWIFT_STRUCT_ALIGNMENT, @@ -1083,8 +1090,13 @@ void fof_search_foreign_cells(struct space *s) { global_group_link_count * sizeof(double)) != 0) error("Error while allocating memory for the displacement in memory for the global group link list"); + if (posix_memalign((void**)&global_group_CoM, SWIFT_STRUCT_ALIGNMENT, + global_group_link_count * sizeof(struct fof_CoM)) != 0) + error("Error while allocating memory for the global group CoM."); + bzero(global_group_size, global_group_link_count * sizeof(int)); bzero(global_group_mass, global_group_link_count * sizeof(double)); + bzero(global_group_CoM, global_group_link_count * sizeof(struct fof_CoM)); /* Compress the list of group links across an MPI domain by removing the symmetric cases. */ /* Store each group ID once along with its size. */ @@ -1113,6 +1125,12 @@ void fof_search_foreign_cells(struct space *s) { if(!found_i) { global_group_size[group_count] += global_group_links[i].group_i_size; global_group_mass[group_count] += global_group_links[i].group_i_mass; + + /* TODO: The CoM of groups linked across MPI domains will not be wrapped correctly as each rank will centre it within their own domain first.*/ + global_group_CoM[group_count].x += global_group_links[i].group_i_CoM.x; + global_group_CoM[group_count].y += global_group_links[i].group_i_CoM.y; + global_group_CoM[group_count].z += global_group_links[i].group_i_CoM.z; + global_group_id[group_count++] = group_i; } @@ -1122,6 +1140,12 @@ void fof_search_foreign_cells(struct space *s) { if(global_group_links[j].group_i == group_j) { global_group_size[group_count] += global_group_links[j].group_i_size; global_group_mass[group_count] += global_group_links[j].group_i_mass; + + /* TODO: The CoM of groups linked across MPI domains will not be wrapped correctly as each rank will centre it within their own domain first.*/ + global_group_CoM[group_count].x += global_group_links[j].group_i_CoM.x; + global_group_CoM[group_count].y += global_group_links[j].group_i_CoM.y; + global_group_CoM[group_count].z += global_group_links[j].group_i_CoM.z; + break; } } @@ -1195,6 +1219,10 @@ void fof_search_foreign_cells(struct space *s) { group_index[group_id - node_offset] = new_root; group_size[group_id - node_offset] -= global_group_size[i]; group_mass[group_id - node_offset] -= global_group_mass[i]; + group_CoM[group_id - node_offset].x -= global_group_CoM[i].x; + group_CoM[group_id - node_offset].y -= global_group_CoM[i].y; + group_CoM[group_id - node_offset].z -= global_group_CoM[i].z; + } /* If the group linked to a local root update its size. */ @@ -1202,6 +1230,10 @@ void fof_search_foreign_cells(struct space *s) { new_root != group_id) { group_size[new_root - node_offset] += global_group_size[i]; group_mass[new_root - node_offset] += global_group_mass[i]; + group_CoM[new_root - node_offset].x += global_group_CoM[i].x; + group_CoM[new_root - node_offset].y += global_group_CoM[i].y; + group_CoM[new_root - node_offset].z += global_group_CoM[i].z; + } } @@ -1234,6 +1266,7 @@ void fof_search_tree(struct space *s) { long long *group_id; int *group_index, *group_size; double *group_mass; + struct fof_CoM *group_CoM; int num_groups = 0, num_parts_in_groups = 0, max_group_size = 0; double max_group_mass = 0; ticks tic = getticks(); @@ -1278,6 +1311,8 @@ void fof_search_tree(struct space *s) { /* Allocate and initialise array of particle group IDs. */ if(s->fof_data.group_index != NULL) free(s->fof_data.group_index); if(s->fof_data.group_id != NULL) free(s->fof_data.group_id); + if(s->fof_data.group_mass != NULL) free(s->fof_data.group_mass); + if(s->fof_data.group_CoM != NULL) free(s->fof_data.group_CoM); /* Allocate and initialise a group index array. */ if (posix_memalign((void **)&s->fof_data.group_index, 32, nr_gparts * sizeof(int)) != 0) @@ -1295,6 +1330,10 @@ void fof_search_tree(struct space *s) { if (posix_memalign((void **)&s->fof_data.group_mass, 32, nr_gparts * sizeof(double)) != 0) error("Failed to allocate list of group masses for FOF search."); + /* Allocate and initialise a group mass array. */ + if (posix_memalign((void **)&s->fof_data.group_CoM, 32, nr_gparts * sizeof(struct fof_CoM)) != 0) + error("Failed to allocate list of group CoM for FOF search."); + /* Initial group ID is particle offset into array. */ for (size_t i = 0; i < nr_gparts; i++) s->fof_data.group_index[i] = i; for (size_t i = 0; i < nr_gparts; i++) s->fof_data.group_id[i] = gparts[i].id_or_neg_offset; @@ -1303,22 +1342,76 @@ void fof_search_tree(struct space *s) { group_size = s->fof_data.group_size; group_id = s->fof_data.group_id; group_mass = s->fof_data.group_mass; + group_CoM = s->fof_data.group_CoM; message("Rank: %d, Allocated group_index array of size %zu", engine_rank, s->nr_gparts); bzero(group_size, nr_gparts * sizeof(int)); bzero(group_mass, nr_gparts * sizeof(double)); + bzero(group_CoM, nr_gparts * sizeof(struct fof_CoM)); /* Activate all the regular tasks */ threadpool_map(&s->e->threadpool, fof_search_tree_mapper, s->cells_top, nr_cells, sizeof(struct cell), 1, s); - /* Calculate the total number of particles in each group, group mass and group ID. */ + struct fof_CoM *group_bc; + int *com_set; + const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; + + /* Allocate and initialise a group boundary condition array. */ + if (posix_memalign((void **)&group_bc, 32, nr_gparts * sizeof(struct fof_CoM)) != 0) + error("Failed to allocate list of group CoM for FOF search."); + + if (posix_memalign((void **)&com_set, 32, nr_gparts * sizeof(int)) != 0) + error("Failed to allocate list of whether the CoM has been initialised."); + + bzero(group_bc, nr_gparts * sizeof(struct fof_CoM)); + bzero(com_set, nr_gparts * sizeof(int)); + + /* Calculate the total number of particles in each group, group mass, group ID and group CoM. */ for (size_t i = 0; i < nr_gparts; i++) { int root = fof_find(i, group_index); group_size[root]++; group_mass[root] += gparts[i].mass; group_id[i] = group_id[root]; + + double x = gparts[i].x[0]; + double y = gparts[i].x[1]; + double z = gparts[i].x[2]; + + /* Use the CoM location set by the first particle added to the group. */ + if(com_set[root]) { + + /* Periodically wrap particle positions if they are located on the other side of the domain than the CoM location. */ + if(group_bc[root].x > 0.5 * dim[0] && x < 0.5 * dim[0]) x += dim[0]; + else if(group_bc[root].x == 0.0 && x > 0.5 * dim[0]) x -= dim[0]; + + if(group_bc[root].y > 0.5 * dim[1] && y < 0.5 * dim[1]) y += dim[1]; + else if(group_bc[root].y == 0.0 && y > 0.5 * dim[1]) y -= dim[1]; + + if(group_bc[root].z > 0.5 * dim[2] && z < 0.5 * dim[2]) z += dim[2]; + else if(group_bc[root].z == 0.0 && z > 0.5 * dim[2]) z -= dim[2]; + + } + else { + + com_set[root] = 1; + + /* Use the first particle to set the CoM location in the domain. */ + if(x > 0.5 * dim[0]) group_bc[root].x = dim[0]; + else group_bc[root].x = 0.0; + + if(y > 0.5 * dim[1]) group_bc[root].y = dim[1]; + else group_bc[root].y = 0.0; + + if(z > 0.5 * dim[2]) group_bc[root].z = dim[2]; + else group_bc[root].z = 0.0; + } + + group_CoM[root].x += gparts[i].mass * x; + group_CoM[root].y += gparts[i].mass * y; + group_CoM[root].z += gparts[i].mass * z; + } #ifdef WITH_MPI @@ -1332,15 +1425,18 @@ void fof_search_tree(struct space *s) { snprintf(output_file_name + strlen(output_file_name), FILENAME_BUFFER_SIZE, ".dat"); #endif - /* Dump group data. */ - fof_dump_group_data(output_file_name, nr_gparts, group_index, - group_size, group_id, group_mass, min_group_size); - int num_groups_local = 0, num_parts_in_groups_local = 0, max_group_size_local = 0; double max_group_mass_local = 0; for (size_t i = 0; i < nr_gparts; i++) { + /* Find the group's CoM. */ + if (group_size[i] >= min_group_size) { + group_CoM[i].x = group_CoM[i].x / group_mass[i]; + group_CoM[i].y = group_CoM[i].y / group_mass[i]; + group_CoM[i].z = group_CoM[i].z / group_mass[i]; + } + /* Find the total number of groups. */ if(group_index[i] == i + node_offset && group_size[i] >= min_group_size) num_groups_local++; @@ -1366,6 +1462,9 @@ void fof_search_tree(struct space *s) { max_group_size = max_group_size_local; max_group_mass = max_group_mass_local; #endif /* WITH_MPI */ + + /* Dump group data. */ + fof_dump_group_data(output_file_name, s); if(engine_rank == 0) { message( @@ -1383,17 +1482,31 @@ void fof_search_tree(struct space *s) { } /* Dump FOF group data. */ -void fof_dump_group_data(char *out_file, const size_t nr_gparts, int *group_index, - int *group_size, long long *group_id, double *group_mass, const int min_group_size) { +void fof_dump_group_data(char *out_file, struct space *s) { FILE *file = fopen(out_file, "w"); - fprintf(file, "# %7s %7s %7s %7s %7s\n", "ID", "Root ID", "Group Size", "Group Mass", "Group ID"); + const int min_group_size = s->fof_data.min_group_size, nr_gparts = s->nr_gparts; + + int *group_index = s->fof_data.group_index; + int *group_size = s->fof_data.group_size; + long long *group_id = s->fof_data.group_id; + double *group_mass = s->fof_data.group_mass; + struct fof_CoM *group_CoM = s->fof_data.group_CoM; + const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; + + fprintf(file, "# %7s %7s %7s %7s %7s %7s %7s %7s\n", "ID", "Root ID", "Group Size", "Group Mass", "x CoM", "y CoM", "z CoM", "Group ID"); fprintf(file, "#---------------------------------------\n"); for (size_t i = 0; i < nr_gparts; i++) { if(group_size[i] >= min_group_size) { const int root = fof_find_global(i - node_offset, group_index); - fprintf(file, " %7zu %7d %7d %7e %10lld\n", i, root, group_size[i], group_mass[i], group_id[i]); + + /* Box wrap the CoM. */ + const double CoM_x = box_wrap(group_CoM[i].x, 0., dim[0]); + const double CoM_y = box_wrap(group_CoM[i].y, 0., dim[1]); + const double CoM_z = box_wrap(group_CoM[i].z, 0., dim[2]); + + fprintf(file, " %7zu %7d %7d %7e %7e %7e %7e %10lld\n", i, root, group_size[i], group_mass[i], CoM_x, CoM_y, CoM_z, group_id[i]); } } diff --git a/src/fof.h b/src/fof.h index 3ee4f3be95d54399348dc2213ab0bb6bd6f5f575..49269b0c1855e21f1a6d0a92a826ad6884497a5a 100644 --- a/src/fof.h +++ b/src/fof.h @@ -37,7 +37,10 @@ struct fof_mpi { int group_i_size; /* The local group's mass.*/ - float group_i_mass; + double group_i_mass; + + /* The local group's CoM.*/ + struct fof_CoM group_i_CoM; /* The foreign particle's root ID.*/ int group_j; @@ -52,7 +55,7 @@ void fof_search_pair_cells(struct space *s, struct cell *ci, struct cell *cj); void fof_search_pair_cells_foreign(struct space *s, struct cell *ci, struct cell *cj, size_t *link_count, struct fof_mpi *part_links); void fof_search_tree_serial(struct space *s); void fof_search_tree(struct space *s); -void fof_dump_group_data(char *out_file, const size_t nr_gparts, int *group_index, int *num_in_groups, long long *group_id, double *group_mass, const int min_group_size); +void fof_dump_group_data(char *out_file, struct space *s); #ifdef WITH_MPI /* MPI data type for the particle transfers */ diff --git a/src/space.c b/src/space.c index 4efadcb34f96317ec1b6697a7c567e4039bf12bb..3fb38b5730138950c692547ee6e0283a00459cf1 100644 --- a/src/space.c +++ b/src/space.c @@ -3217,6 +3217,7 @@ void space_clean(struct space *s) { free(s->fof_data.group_index); free(s->fof_data.group_size); free(s->fof_data.group_mass); + free(s->fof_data.group_CoM); } /** diff --git a/src/space.h b/src/space.h index 96cafe6a340e4390535c77baac5b80db3e120cdf..8c553a06d10e7597f66b51ab87006d13faef646d 100644 --- a/src/space.h +++ b/src/space.h @@ -41,12 +41,19 @@ struct cell; struct cosmology; +struct fof_CoM { + + double x, y, z; + +} SWIFT_STRUCT_ALIGN; + struct fof { long long *group_id; int *group_index; int *group_size; double *group_mass; + struct fof_CoM *group_CoM; int min_group_size; char base_name[PARSER_MAX_LINE_SIZE];