diff --git a/src/fof.c b/src/fof.c index 8e738431371ace1a92614390de8d1b71f56af7e5..d3ca066e8ff8db2262c2bee402a6ce3ca0bc804d 100644 --- a/src/fof.c +++ b/src/fof.c @@ -27,6 +27,8 @@ #include "threadpool.h" #include "engine.h" +MPI_Datatype fof_mpi_type; + /* Initialises parameters for the FOF search. */ void fof_init(struct space *s, long long Ngas, long long Ngparts) { @@ -35,6 +37,15 @@ void fof_init(struct space *s, long long Ngas, long long Ngparts) { const int total_nr_dmparts = Ngparts - Ngas; const double l_x = 0.2 * (s->dim[0] / cbrt(total_nr_dmparts)); s->l_x2 = l_x * l_x; + +#ifdef WITH_MPI + if (MPI_Type_contiguous(sizeof(struct fof_mpi) / sizeof(unsigned char), MPI_BYTE, + &fof_mpi_type) != MPI_SUCCESS || + MPI_Type_commit(&fof_mpi_type) != MPI_SUCCESS) { + error("Failed to create MPI type for fof."); + } +#endif + } /* Finds the root ID of the group a particle exists in. */ @@ -129,6 +140,38 @@ static void rec_fof_search_pair(struct cell *ci, struct cell *cj, struct space * } +/* Recurse on a pair of cells (one local, one foreign( and perform a FOF search between cells that are within + * range. */ +static void rec_fof_search_pair_foreign(struct cell *ci, struct cell *cj, struct space *s, + const double *dim, + const double search_r2) { + + /* Find the shortest distance between cells, remembering to account for + * boundary conditions. */ + const double r2 = cell_min_dist(ci, cj, dim); + + /* Return if cells are out of range of each other. */ + if (r2 > search_r2) return; + + /* Recurse on both cells if they are both split. */ + if (ci->split && cj->split) { + for (int k = 0; k < 8; k++) { + if (ci->progeny[k] != NULL) { + + for (int l = 0; l < 8; l++) + if (cj->progeny[l] != NULL) + rec_fof_search_pair_foreign(ci->progeny[k], cj->progeny[l], s, dim, search_r2); + } + } + } + /* Perform FOF search between pairs of cells that are within the linking + * length and not the same cell. */ + else if (ci != cj) + fof_search_pair_cells_foreign(s, ci, cj); + else if (ci == cj) error("Pair FOF called on same cell!!!"); + +} + /* Recurse on a cell and perform a FOF search between cells that are within * range. */ static void rec_fof_search_self(struct cell *ci, struct space *s, @@ -401,6 +444,98 @@ void fof_search_pair_cells(struct space *s, struct cell *ci, struct cell *cj) { } } +/* Perform a FOF search between a local and foreign cell using the Union-Find algorithm. + * Communicate any links found between particles to the appropriate node.*/ +void fof_search_pair_cells_foreign(struct space *s, struct cell *ci, struct cell *cj) { + + const size_t count_i = ci->gcount; + const size_t count_j = cj->gcount; + struct gpart *gparts_i = ci->gparts; + struct gpart *gparts_j = cj->gparts; + const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; + const double l_x2 = s->l_x2; + int *group_id = s->group_id; + + /* Make a list of particle offsets into the global gparts array. */ + int *const offset_i = group_id + (ptrdiff_t)(gparts_i - s->gparts); + + /* Account for boundary conditions.*/ + double shift[3] = {0.0, 0.0, 0.0}; + + struct fof_mpi *fof_send; + + if (posix_memalign((void**)&fof_send, SWIFT_STRUCT_ALIGNMENT, + count_i * count_j * sizeof(struct fof_mpi)) != 0) + error("Error while allocating memory for FOF MPI communication"); + + //bzero(fof_send, count_i * count_j* sizeof(struct part)); + + int send_count = 0; + + /* Get the relative distance between the pairs, wrapping. */ + const int periodic = s->periodic; + double diff[3]; + for (int k = 0; k < 3; k++) { + diff[k] = cj->loc[k] - ci->loc[k]; + if (periodic && diff[k] < -dim[k] / 2) + shift[k] = dim[k]; + else if (periodic && diff[k] > dim[k] / 2) + shift[k] = -dim[k]; + else + shift[k] = 0.0; + diff[k] += shift[k]; + } + + /* Loop over particles and find which particles belong in the same group. */ + for (size_t i = 0; i < count_i; i++) { + + struct gpart *pi = &gparts_i[i]; + const double pix = pi->x[0] - shift[0]; + const double piy = pi->x[1] - shift[1]; + const double piz = pi->x[2] - shift[2]; + + /* Find the root of pi. */ + int root_i = fof_find(offset_i[i], group_id); + + for (size_t j = 0; j < count_j; j++) { + + struct gpart *pj = &gparts_j[j]; + const double pjx = pj->x[0]; + const double pjy = pj->x[1]; + const double pjz = pj->x[2]; + + /* Compute pairwise distance, remembering to account for boundary + * conditions. */ + float dx[3], r2 = 0.0f; + dx[0] = pix - pjx; + dx[1] = piy - pjy; + dx[2] = piz - pjz; + + for (int k = 0; k < 3; k++) r2 += dx[k] * dx[k]; + + /* Hit or miss? */ + if (r2 < l_x2) { + + /* Store the particle IDs and group ID for communication. */ + fof_send[send_count].local_pid = pi->id_or_neg_offset; + fof_send[send_count].foreign_pid = pj->id_or_neg_offset; + fof_send[send_count++].root_i = root_i; + } + } + } + + if(send_count > 0) + message("Rank: %d sending %d links to rank %d for testing.", engine_rank, send_count, cj->nodeID); + + //MPI_Request request; + + /* Send communication of linked particles to other rank.*/ + //MPI_Isend(fof_send, send_count, fof_mpi_type, cj->nodeID, 0, MPI_COMM_WORLD, &request); + + free(fof_send); + +} + /* Perform a FOF search on gravity particles using the cells and applying the * Union-Find algorithm.*/ void fof_search_tree_serial(struct space *s) { @@ -577,6 +712,50 @@ void fof_search_tree_mapper(void *map_data, int num_elements, } } +/** + * @brief Search foreign cells for links and communicate any found to the appropriate node. + * + * @param s Pointer to a #space. + */ +void fof_search_foreign_cells(struct space *s) { + + struct cell *cells = s->cells_top; + const size_t nr_cells = s->nr_cells; + const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; + const double search_r2 = s->l_x2; + + message("Searching foreign cells for links."); + + /* Loop over cells and find which cells are in range of each other to perform + * the FOF search. */ + for (size_t cid = 0; cid < nr_cells; cid++) { + + struct cell *restrict ci = &cells[cid]; + + /* Only search local cells for foreign neighbours. */ + if(ci->nodeID == engine_rank) { + + /* Skip empty cells. */ + if(ci->gcount == 0) continue; + + /* Loop over all top-level cells skipping over the cells already searched. + */ + for (size_t cjd = cid + 1; cjd < nr_cells; cjd++) { + + struct cell *restrict cj = &cells[cjd]; + + /* Only perform pair FOF search between a local and foreign cell. */ + if(cj->nodeID != engine_rank) { + /* Skip empty cells. */ + if(cj->gcount == 0) continue; + + rec_fof_search_pair_foreign(ci, cj, s, dim, search_r2); + } + } + } + } +} + /* Perform a FOF search on gravity particles using the cells and applying the * Union-Find algorithm.*/ void fof_search_tree(struct space *s) { @@ -621,6 +800,9 @@ void fof_search_tree(struct space *s) { threadpool_map(&s->e->threadpool, fof_search_tree_mapper, s->cells_top, nr_cells, sizeof(struct cell), 1, s); + /* Find any particle links with other nodes. */ + fof_search_foreign_cells(s); + /* Calculate the total number of particles in each group, group mass and the total number of groups. */ for (size_t i = 0; i < nr_gparts; i++) { const int root = fof_find(i, group_id); diff --git a/src/fof.h b/src/fof.h index 5e8809c298455565b2ba2653c52806d2c34b863b..5387c28631c4bcd61f482e3f9ebf7ba1871d36db 100644 --- a/src/fof.h +++ b/src/fof.h @@ -32,8 +32,28 @@ void fof_init(struct space *s, long long Ngas, long long Ngparts); void fof_search_serial(struct space *s); void fof_search_cell(struct space *s, struct cell *c); 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); 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_id, int *num_in_groups); +/* MPI message required for FOF. */ +struct fof_mpi { + + /* The local particle ID of the sending rank.*/ + long long local_pid; + + /* The foreign particle ID of the receiving rank.*/ + long long foreign_pid; + + /* The local particle's root ID of the sending rank.*/ + int root_i; + +} SWIFT_STRUCT_ALIGN; + +#ifdef WITH_MPI +/* MPI data type for the particle transfers */ +extern MPI_Datatype fof_mpi_type; +#endif + #endif /* SWIFT_FOF_H */