/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) * Matthieu Schaller (schaller@strw.leidenuniv.nl) * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * ******************************************************************************/ /* Config parameters. */ #include /* This object's header. */ #include "cell.h" /* Local headers. */ #include "memswap.h" /** * @brief Sort the parts into eight bins along the given pivots. * * @param c The #cell array to be sorted. * @param parts_offset Offset of the cell parts array relative to the * space's parts array, i.e. c->hydro.parts - s->parts. * @param sparts_offset Offset of the cell sparts array relative to the * space's sparts array, i.e. c->stars.parts - s->stars.parts. * @param bparts_offset Offset of the cell bparts array relative to the * space's bparts array, i.e. c->black_holes.parts - * s->black_holes.parts. * @param sinks_offset Offset of the cell sink array relative to the * space's sink array, i.e. c->sinks.parts - s->sinks.parts. * @param buff A buffer with at least max(c->hydro.count, c->grav.count) * entries, used for sorting indices. * @param sbuff A buffer with at least max(c->stars.count, c->grav.count) * entries, used for sorting indices for the sparts. * @param bbuff A buffer with at least max(c->black_holes.count, c->grav.count) * entries, used for sorting indices for the sparts. * @param gbuff A buffer with at least max(c->hydro.count, c->grav.count) * entries, used for sorting indices for the gparts. * @param sinkbuff A buffer with at least max(c->sinks.count, c->grav.count) * entries, used for sorting indices for the sinks. */ void cell_split(struct cell *c, const ptrdiff_t parts_offset, const ptrdiff_t sparts_offset, const ptrdiff_t bparts_offset, const ptrdiff_t sinks_offset, struct cell_buff *restrict buff, struct cell_buff *restrict sbuff, struct cell_buff *restrict bbuff, struct cell_buff *restrict gbuff, struct cell_buff *restrict sinkbuff) { const int count = c->hydro.count, gcount = c->grav.count, scount = c->stars.count, bcount = c->black_holes.count, sink_count = c->sinks.count; struct part *parts = c->hydro.parts; struct xpart *xparts = c->hydro.xparts; struct gpart *gparts = c->grav.parts; struct spart *sparts = c->stars.parts; struct bpart *bparts = c->black_holes.parts; struct sink *sinks = c->sinks.parts; const double pivot[3] = {c->loc[0] + c->width[0] / 2, c->loc[1] + c->width[1] / 2, c->loc[2] + c->width[2] / 2}; int bucket_count[8] = {0, 0, 0, 0, 0, 0, 0, 0}; int bucket_offset[9]; #ifdef SWIFT_DEBUG_CHECKS /* Check that the buffs are OK. */ for (int k = 0; k < count; k++) { if (buff[k].x[0] != parts[k].x[0] || buff[k].x[1] != parts[k].x[1] || buff[k].x[2] != parts[k].x[2]) error("Inconsistent buff contents."); } for (int k = 0; k < gcount; k++) { if (gbuff[k].x[0] != gparts[k].x[0] || gbuff[k].x[1] != gparts[k].x[1] || gbuff[k].x[2] != gparts[k].x[2]) error("Inconsistent gbuff contents."); } for (int k = 0; k < scount; k++) { if (sbuff[k].x[0] != sparts[k].x[0] || sbuff[k].x[1] != sparts[k].x[1] || sbuff[k].x[2] != sparts[k].x[2]) error("Inconsistent sbuff contents."); } for (int k = 0; k < bcount; k++) { if (bbuff[k].x[0] != bparts[k].x[0] || bbuff[k].x[1] != bparts[k].x[1] || bbuff[k].x[2] != bparts[k].x[2]) error("Inconsistent bbuff contents."); } for (int k = 0; k < sink_count; k++) { if (sinkbuff[k].x[0] != sinks[k].x[0] || sinkbuff[k].x[1] != sinks[k].x[1] || sinkbuff[k].x[2] != sinks[k].x[2]) error("Inconsistent sinkbuff contents."); } #endif /* SWIFT_DEBUG_CHECKS */ /* Fill the buffer with the indices. */ for (int k = 0; k < count; k++) { const int bid = (buff[k].x[0] >= pivot[0]) * 4 + (buff[k].x[1] >= pivot[1]) * 2 + (buff[k].x[2] >= pivot[2]); bucket_count[bid]++; buff[k].ind = bid; } /* Set the buffer offsets. */ bucket_offset[0] = 0; for (int k = 1; k <= 8; k++) { bucket_offset[k] = bucket_offset[k - 1] + bucket_count[k - 1]; bucket_count[k - 1] = 0; } /* Run through the buckets, and swap particles to their correct spot. */ for (int bucket = 0; bucket < 8; bucket++) { for (int k = bucket_offset[bucket] + bucket_count[bucket]; k < bucket_offset[bucket + 1]; k++) { int bid = buff[k].ind; if (bid != bucket) { struct part part = parts[k]; struct xpart xpart = xparts[k]; struct cell_buff temp_buff = buff[k]; while (bid != bucket) { int j = bucket_offset[bid] + bucket_count[bid]++; while (buff[j].ind == bid) { j++; bucket_count[bid]++; } memswap(&parts[j], &part, sizeof(struct part)); memswap(&xparts[j], &xpart, sizeof(struct xpart)); memswap(&buff[j], &temp_buff, sizeof(struct cell_buff)); if (parts[j].gpart) parts[j].gpart->id_or_neg_offset = -(j + parts_offset); bid = temp_buff.ind; } parts[k] = part; xparts[k] = xpart; buff[k] = temp_buff; if (parts[k].gpart) parts[k].gpart->id_or_neg_offset = -(k + parts_offset); } bucket_count[bid]++; } } /* Store the counts and offsets. */ for (int k = 0; k < 8; k++) { c->progeny[k]->hydro.count = bucket_count[k]; c->progeny[k]->hydro.count_total = c->progeny[k]->hydro.count; c->progeny[k]->hydro.parts = &c->hydro.parts[bucket_offset[k]]; c->progeny[k]->hydro.xparts = &c->hydro.xparts[bucket_offset[k]]; } #ifdef SWIFT_DEBUG_CHECKS /* Check that the buffs are OK. */ for (int k = 1; k < count; k++) { if (buff[k].ind < buff[k - 1].ind) error("Buff not sorted."); if (buff[k].x[0] != parts[k].x[0] || buff[k].x[1] != parts[k].x[1] || buff[k].x[2] != parts[k].x[2]) error("Inconsistent buff contents (k=%i).", k); } /* Verify that _all_ the parts have been assigned to a cell. */ for (int k = 1; k < 8; k++) if (&c->progeny[k - 1]->hydro.parts[c->progeny[k - 1]->hydro.count] != c->progeny[k]->hydro.parts) error("Particle sorting failed (internal consistency)."); if (c->progeny[0]->hydro.parts != c->hydro.parts) error("Particle sorting failed (left edge)."); if (&c->progeny[7]->hydro.parts[c->progeny[7]->hydro.count] != &c->hydro.parts[count]) error("Particle sorting failed (right edge)."); /* Verify a few sub-cells. */ for (int k = 0; k < c->progeny[0]->hydro.count; k++) if (c->progeny[0]->hydro.parts[k].x[0] >= pivot[0] || c->progeny[0]->hydro.parts[k].x[1] >= pivot[1] || c->progeny[0]->hydro.parts[k].x[2] >= pivot[2]) error("Sorting failed (progeny=0)."); for (int k = 0; k < c->progeny[1]->hydro.count; k++) if (c->progeny[1]->hydro.parts[k].x[0] >= pivot[0] || c->progeny[1]->hydro.parts[k].x[1] >= pivot[1] || c->progeny[1]->hydro.parts[k].x[2] < pivot[2]) error("Sorting failed (progeny=1)."); for (int k = 0; k < c->progeny[2]->hydro.count; k++) if (c->progeny[2]->hydro.parts[k].x[0] >= pivot[0] || c->progeny[2]->hydro.parts[k].x[1] < pivot[1] || c->progeny[2]->hydro.parts[k].x[2] >= pivot[2]) error("Sorting failed (progeny=2)."); for (int k = 0; k < c->progeny[3]->hydro.count; k++) if (c->progeny[3]->hydro.parts[k].x[0] >= pivot[0] || c->progeny[3]->hydro.parts[k].x[1] < pivot[1] || c->progeny[3]->hydro.parts[k].x[2] < pivot[2]) error("Sorting failed (progeny=3)."); for (int k = 0; k < c->progeny[4]->hydro.count; k++) if (c->progeny[4]->hydro.parts[k].x[0] < pivot[0] || c->progeny[4]->hydro.parts[k].x[1] >= pivot[1] || c->progeny[4]->hydro.parts[k].x[2] >= pivot[2]) error("Sorting failed (progeny=4)."); for (int k = 0; k < c->progeny[5]->hydro.count; k++) if (c->progeny[5]->hydro.parts[k].x[0] < pivot[0] || c->progeny[5]->hydro.parts[k].x[1] >= pivot[1] || c->progeny[5]->hydro.parts[k].x[2] < pivot[2]) error("Sorting failed (progeny=5)."); for (int k = 0; k < c->progeny[6]->hydro.count; k++) if (c->progeny[6]->hydro.parts[k].x[0] < pivot[0] || c->progeny[6]->hydro.parts[k].x[1] < pivot[1] || c->progeny[6]->hydro.parts[k].x[2] >= pivot[2]) error("Sorting failed (progeny=6)."); for (int k = 0; k < c->progeny[7]->hydro.count; k++) if (c->progeny[7]->hydro.parts[k].x[0] < pivot[0] || c->progeny[7]->hydro.parts[k].x[1] < pivot[1] || c->progeny[7]->hydro.parts[k].x[2] < pivot[2]) error("Sorting failed (progeny=7)."); #endif /* Now do the same song and dance for the sparts. */ for (int k = 0; k < 8; k++) bucket_count[k] = 0; /* Fill the buffer with the indices. */ for (int k = 0; k < scount; k++) { const int bid = (sbuff[k].x[0] > pivot[0]) * 4 + (sbuff[k].x[1] > pivot[1]) * 2 + (sbuff[k].x[2] > pivot[2]); bucket_count[bid]++; sbuff[k].ind = bid; } /* Set the buffer offsets. */ bucket_offset[0] = 0; for (int k = 1; k <= 8; k++) { bucket_offset[k] = bucket_offset[k - 1] + bucket_count[k - 1]; bucket_count[k - 1] = 0; } /* Run through the buckets, and swap particles to their correct spot. */ for (int bucket = 0; bucket < 8; bucket++) { for (int k = bucket_offset[bucket] + bucket_count[bucket]; k < bucket_offset[bucket + 1]; k++) { int bid = sbuff[k].ind; if (bid != bucket) { struct spart spart = sparts[k]; struct cell_buff temp_buff = sbuff[k]; while (bid != bucket) { int j = bucket_offset[bid] + bucket_count[bid]++; while (sbuff[j].ind == bid) { j++; bucket_count[bid]++; } memswap(&sparts[j], &spart, sizeof(struct spart)); memswap(&sbuff[j], &temp_buff, sizeof(struct cell_buff)); if (sparts[j].gpart) sparts[j].gpart->id_or_neg_offset = -(j + sparts_offset); bid = temp_buff.ind; } sparts[k] = spart; sbuff[k] = temp_buff; if (sparts[k].gpart) sparts[k].gpart->id_or_neg_offset = -(k + sparts_offset); } bucket_count[bid]++; } } /* Store the counts and offsets. */ for (int k = 0; k < 8; k++) { c->progeny[k]->stars.count = bucket_count[k]; c->progeny[k]->stars.count_total = c->progeny[k]->stars.count; c->progeny[k]->stars.parts = &c->stars.parts[bucket_offset[k]]; c->progeny[k]->stars.parts_rebuild = c->progeny[k]->stars.parts; } /* Now do the same song and dance for the bparts. */ for (int k = 0; k < 8; k++) bucket_count[k] = 0; /* Fill the buffer with the indices. */ for (int k = 0; k < bcount; k++) { const int bid = (bbuff[k].x[0] > pivot[0]) * 4 + (bbuff[k].x[1] > pivot[1]) * 2 + (bbuff[k].x[2] > pivot[2]); bucket_count[bid]++; bbuff[k].ind = bid; } /* Set the buffer offsets. */ bucket_offset[0] = 0; for (int k = 1; k <= 8; k++) { bucket_offset[k] = bucket_offset[k - 1] + bucket_count[k - 1]; bucket_count[k - 1] = 0; } /* Run through the buckets, and swap particles to their correct spot. */ for (int bucket = 0; bucket < 8; bucket++) { for (int k = bucket_offset[bucket] + bucket_count[bucket]; k < bucket_offset[bucket + 1]; k++) { int bid = bbuff[k].ind; if (bid != bucket) { struct bpart bpart = bparts[k]; struct cell_buff temp_buff = bbuff[k]; while (bid != bucket) { int j = bucket_offset[bid] + bucket_count[bid]++; while (bbuff[j].ind == bid) { j++; bucket_count[bid]++; } memswap(&bparts[j], &bpart, sizeof(struct bpart)); memswap(&bbuff[j], &temp_buff, sizeof(struct cell_buff)); if (bparts[j].gpart) bparts[j].gpart->id_or_neg_offset = -(j + bparts_offset); bid = temp_buff.ind; } bparts[k] = bpart; bbuff[k] = temp_buff; if (bparts[k].gpart) bparts[k].gpart->id_or_neg_offset = -(k + bparts_offset); } bucket_count[bid]++; } } /* Store the counts and offsets. */ for (int k = 0; k < 8; k++) { c->progeny[k]->black_holes.count = bucket_count[k]; c->progeny[k]->black_holes.count_total = c->progeny[k]->black_holes.count; c->progeny[k]->black_holes.parts = &c->black_holes.parts[bucket_offset[k]]; } /* Now do the same song and dance for the sinks. */ for (int k = 0; k < 8; k++) bucket_count[k] = 0; /* Fill the buffer with the indices. */ for (int k = 0; k < sink_count; k++) { const int bid = (sinkbuff[k].x[0] > pivot[0]) * 4 + (sinkbuff[k].x[1] > pivot[1]) * 2 + (sinkbuff[k].x[2] > pivot[2]); bucket_count[bid]++; sinkbuff[k].ind = bid; } /* Set the buffer offsets. */ bucket_offset[0] = 0; for (int k = 1; k <= 8; k++) { bucket_offset[k] = bucket_offset[k - 1] + bucket_count[k - 1]; bucket_count[k - 1] = 0; } /* Run through the buckets, and swap particles to their correct spot. */ for (int bucket = 0; bucket < 8; bucket++) { for (int k = bucket_offset[bucket] + bucket_count[bucket]; k < bucket_offset[bucket + 1]; k++) { int bid = sinkbuff[k].ind; if (bid != bucket) { struct sink sink = sinks[k]; struct cell_buff temp_buff = sinkbuff[k]; while (bid != bucket) { int j = bucket_offset[bid] + bucket_count[bid]++; while (sinkbuff[j].ind == bid) { j++; bucket_count[bid]++; } memswap(&sinks[j], &sink, sizeof(struct sink)); memswap(&sinkbuff[j], &temp_buff, sizeof(struct cell_buff)); if (sinks[j].gpart) sinks[j].gpart->id_or_neg_offset = -(j + sinks_offset); bid = temp_buff.ind; } sinks[k] = sink; sinkbuff[k] = temp_buff; if (sinks[k].gpart) sinks[k].gpart->id_or_neg_offset = -(k + sinks_offset); } bucket_count[bid]++; } } /* Store the counts and offsets. */ for (int k = 0; k < 8; k++) { c->progeny[k]->sinks.count = bucket_count[k]; c->progeny[k]->sinks.count_total = c->progeny[k]->sinks.count; c->progeny[k]->sinks.parts = &c->sinks.parts[bucket_offset[k]]; c->progeny[k]->sinks.parts_rebuild = c->progeny[k]->sinks.parts; } /* Finally, do the same song and dance for the gparts. */ for (int k = 0; k < 8; k++) bucket_count[k] = 0; /* Fill the buffer with the indices. */ for (int k = 0; k < gcount; k++) { const int bid = (gbuff[k].x[0] > pivot[0]) * 4 + (gbuff[k].x[1] > pivot[1]) * 2 + (gbuff[k].x[2] > pivot[2]); bucket_count[bid]++; gbuff[k].ind = bid; } /* Set the buffer offsets. */ bucket_offset[0] = 0; for (int k = 1; k <= 8; k++) { bucket_offset[k] = bucket_offset[k - 1] + bucket_count[k - 1]; bucket_count[k - 1] = 0; } /* Run through the buckets, and swap particles to their correct spot. */ for (int bucket = 0; bucket < 8; bucket++) { for (int k = bucket_offset[bucket] + bucket_count[bucket]; k < bucket_offset[bucket + 1]; k++) { int bid = gbuff[k].ind; if (bid != bucket) { struct gpart gpart = gparts[k]; struct cell_buff temp_buff = gbuff[k]; while (bid != bucket) { int j = bucket_offset[bid] + bucket_count[bid]++; while (gbuff[j].ind == bid) { j++; bucket_count[bid]++; } memswap_unaligned(&gparts[j], &gpart, sizeof(struct gpart)); memswap(&gbuff[j], &temp_buff, sizeof(struct cell_buff)); if (gparts[j].type == swift_type_gas) { parts[-gparts[j].id_or_neg_offset - parts_offset].gpart = &gparts[j]; } else if (gparts[j].type == swift_type_stars) { sparts[-gparts[j].id_or_neg_offset - sparts_offset].gpart = &gparts[j]; } else if (gparts[j].type == swift_type_sink) { sinks[-gparts[j].id_or_neg_offset - sinks_offset].gpart = &gparts[j]; } else if (gparts[j].type == swift_type_black_hole) { bparts[-gparts[j].id_or_neg_offset - bparts_offset].gpart = &gparts[j]; } bid = temp_buff.ind; } gparts[k] = gpart; gbuff[k] = temp_buff; if (gparts[k].type == swift_type_gas) { parts[-gparts[k].id_or_neg_offset - parts_offset].gpart = &gparts[k]; } else if (gparts[k].type == swift_type_stars) { sparts[-gparts[k].id_or_neg_offset - sparts_offset].gpart = &gparts[k]; } else if (gparts[k].type == swift_type_sink) { sinks[-gparts[k].id_or_neg_offset - sinks_offset].gpart = &gparts[k]; } else if (gparts[k].type == swift_type_black_hole) { bparts[-gparts[k].id_or_neg_offset - bparts_offset].gpart = &gparts[k]; } } bucket_count[bid]++; } } /* Store the counts and offsets. */ for (int k = 0; k < 8; k++) { c->progeny[k]->grav.count = bucket_count[k]; c->progeny[k]->grav.count_total = c->progeny[k]->grav.count; c->progeny[k]->grav.parts = &c->grav.parts[bucket_offset[k]]; c->progeny[k]->grav.parts_rebuild = c->progeny[k]->grav.parts; } } /** * @brief Re-arrange the #part in a top-level cell such that all the extra * ones for on-the-fly creation are located at the end of the array. * * @param c The #cell to sort. * @param parts_offset The offset between the first #part in the array and the * first #part in the global array in the space structure (for re-linking). */ void cell_reorder_extra_parts(struct cell *c, const ptrdiff_t parts_offset) { struct part *parts = c->hydro.parts; struct xpart *xparts = c->hydro.xparts; const int count_real = c->hydro.count; if (c->depth != 0 || c->nodeID != engine_rank) error("This function should only be called on local top-level cells!"); int first_not_extra = count_real; /* Find extra particles */ for (int i = 0; i < count_real; ++i) { if (parts[i].time_bin == time_bin_not_created) { /* Find the first non-extra particle after the end of the real particles */ while (parts[first_not_extra].time_bin == time_bin_not_created) { ++first_not_extra; } #ifdef SWIFT_DEBUG_CHECKS if (first_not_extra >= count_real + space_extra_parts) error("Looking for extra particles beyond this cell's range!"); #endif /* Swap everything, including g-part pointer */ memswap(&parts[i], &parts[first_not_extra], sizeof(struct part)); memswap(&xparts[i], &xparts[first_not_extra], sizeof(struct xpart)); if (parts[i].gpart) parts[i].gpart->id_or_neg_offset = -(i + parts_offset); } } #ifdef SWIFT_DEBUG_CHECKS for (int i = 0; i < c->hydro.count_total; ++i) { if (parts[i].time_bin == time_bin_not_created && i < c->hydro.count) { error("Extra particle before the end of the regular array"); } if (parts[i].time_bin != time_bin_not_created && i >= c->hydro.count) { error("Regular particle after the end of the regular array"); } } #endif } /** * @brief Re-arrange the #spart in a top-level cell such that all the extra * ones for on-the-fly creation are located at the end of the array. * * @param c The #cell to sort. * @param sparts_offset The offset between the first #spart in the array and * the first #spart in the global array in the space structure (for * re-linking). */ void cell_reorder_extra_sparts(struct cell *c, const ptrdiff_t sparts_offset) { struct spart *sparts = c->stars.parts; const int count_real = c->stars.count; if (c->depth != 0 || c->nodeID != engine_rank) error("This function should only be called on local top-level cells!"); int first_not_extra = count_real; /* Find extra particles */ for (int i = 0; i < count_real; ++i) { if (sparts[i].time_bin == time_bin_not_created) { /* Find the first non-extra particle after the end of the real particles */ while (sparts[first_not_extra].time_bin == time_bin_not_created) { ++first_not_extra; } #ifdef SWIFT_DEBUG_CHECKS if (first_not_extra >= count_real + space_extra_sparts) error("Looking for extra particles beyond this cell's range!"); #endif /* Swap everything, including g-part pointer */ memswap(&sparts[i], &sparts[first_not_extra], sizeof(struct spart)); if (sparts[i].gpart) sparts[i].gpart->id_or_neg_offset = -(i + sparts_offset); sparts[first_not_extra].gpart = NULL; #ifdef SWIFT_DEBUG_CHECKS if (sparts[first_not_extra].time_bin != time_bin_not_created) error("Incorrect swap occured!"); #endif } } #ifdef SWIFT_DEBUG_CHECKS for (int i = 0; i < c->stars.count_total; ++i) { if (sparts[i].time_bin == time_bin_not_created && i < c->stars.count) { error("Extra particle before the end of the regular array"); } if (sparts[i].time_bin != time_bin_not_created && i >= c->stars.count) { error("Regular particle after the end of the regular array"); } } #endif } /** * @brief Re-arrange the #sink in a top-level cell such that all the extra * ones for on-the-fly creation are located at the end of the array. * * @param c The #cell to sort. * @param sinks_offset The offset between the first #sink in the array and * the first #sink in the global array in the space structure (for * re-linking). */ void cell_reorder_extra_sinks(struct cell *c, const ptrdiff_t sinks_offset) { struct sink *sinks = c->sinks.parts; const int count_real = c->sinks.count; if (c->depth != 0 || c->nodeID != engine_rank) error("This function should only be called on local top-level cells!"); int first_not_extra = count_real; /* Find extra particles */ for (int i = 0; i < count_real; ++i) { if (sinks[i].time_bin == time_bin_not_created) { /* Find the first non-extra particle after the end of the real particles */ while (sinks[first_not_extra].time_bin == time_bin_not_created) { ++first_not_extra; } #ifdef SWIFT_DEBUG_CHECKS if (first_not_extra >= count_real + space_extra_sinks) error("Looking for extra particles beyond this cell's range!"); #endif /* Swap everything, including g-part pointer */ memswap(&sinks[i], &sinks[first_not_extra], sizeof(struct sink)); if (sinks[i].gpart) sinks[i].gpart->id_or_neg_offset = -(i + sinks_offset); sinks[first_not_extra].gpart = NULL; #ifdef SWIFT_DEBUG_CHECKS if (sinks[first_not_extra].time_bin != time_bin_not_created) error("Incorrect swap occured!"); #endif } } #ifdef SWIFT_DEBUG_CHECKS for (int i = 0; i < c->sinks.count_total; ++i) { if (sinks[i].time_bin == time_bin_not_created && i < c->sinks.count) { error("Extra particle before the end of the regular array"); } if (sinks[i].time_bin != time_bin_not_created && i >= c->sinks.count) { error("Regular particle after the end of the regular array"); } } #endif } /** * @brief Re-arrange the #gpart in a top-level cell such that all the extra * ones for on-the-fly creation are located at the end of the array. * * @param c The #cell to sort. * @param parts The global array of #part (for re-linking). * @param sparts The global array of #spart (for re-linking). * @param sinks The global array of #sink (for re-linking). */ void cell_reorder_extra_gparts(struct cell *c, struct part *parts, struct spart *sparts, struct sink *sinks, struct bpart *bparts) { struct gpart *gparts = c->grav.parts; const int count_real = c->grav.count; if (c->depth != 0 || c->nodeID != engine_rank) error("This function should only be called on local top-level cells!"); int first_not_extra = count_real; /* Find extra particles */ for (int i = 0; i < count_real; ++i) { if (gparts[i].time_bin == time_bin_not_created) { /* Find the first non-extra particle after the end of the real particles */ while (gparts[first_not_extra].time_bin == time_bin_not_created) { ++first_not_extra; } #ifdef SWIFT_DEBUG_CHECKS if (first_not_extra >= count_real + space_extra_gparts) error("Looking for extra particles beyond this cell's range!"); #endif /* Swap everything (including pointers) */ memswap_unaligned(&gparts[i], &gparts[first_not_extra], sizeof(struct gpart)); if (gparts[i].type == swift_type_gas) { parts[-gparts[i].id_or_neg_offset].gpart = &gparts[i]; } else if (gparts[i].type == swift_type_sink) { sinks[-gparts[i].id_or_neg_offset].gpart = &gparts[i]; } else if (gparts[i].type == swift_type_stars) { sparts[-gparts[i].id_or_neg_offset].gpart = &gparts[i]; } else if (gparts[i].type == swift_type_black_hole) { bparts[-gparts[i].id_or_neg_offset].gpart = &gparts[i]; } } } #ifdef SWIFT_DEBUG_CHECKS for (int i = 0; i < c->grav.count_total; ++i) { if (gparts[i].time_bin == time_bin_not_created && i < c->grav.count) { error("Extra particle before the end of the regular array"); } if (gparts[i].time_bin != time_bin_not_created && i >= c->grav.count) { error("Regular particle after the end of the regular array"); } } #endif }