/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
* Matthieu Schaller (matthieu.schaller@durham.ac.uk)
* 2015 Peter W. Draper (p.w.draper@durham.ac.uk)
* 2016 John A. Regan (john.a.regan@durham.ac.uk)
* Tom Theuns (tom.theuns@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 "../config.h"
/* Some standard headers. */
#include
#include
#include
#include
#include
#include
#include
/* MPI headers. */
#ifdef WITH_MPI
#include
#endif
/* Switch off timers. */
#ifdef TIMER
#undef TIMER
#endif
/* This object's header. */
#include "cell.h"
/* Local headers. */
#include "atomic.h"
#include "error.h"
#include "gravity.h"
#include "hydro.h"
#include "hydro_properties.h"
#include "space.h"
#include "timers.h"
/* Global variables. */
int cell_next_tag = 0;
/**
* @brief Get the size of the cell subtree.
*
* @param c The #cell.
*/
int cell_getsize(struct cell *c) {
/* Number of cells in this subtree. */
int count = 1;
/* Sum up the progeny if split. */
if (c->split)
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL) count += cell_getsize(c->progeny[k]);
/* Return the final count. */
return count;
}
/**
* @brief Unpack the data of a given cell and its sub-cells.
*
* @param pc An array of packed #pcell.
* @param c The #cell in which to unpack the #pcell.
* @param s The #space in which the cells are created.
*
* @return The number of cells created.
*/
int cell_unpack(struct pcell *pc, struct cell *c, struct space *s) {
/* Unpack the current pcell. */
c->h_max = pc->h_max;
c->ti_end_min = pc->ti_end_min;
c->ti_end_max = pc->ti_end_max;
c->count = pc->count;
c->gcount = pc->gcount;
c->tag = pc->tag;
/* Number of new cells created. */
int count = 1;
/* Fill the progeny recursively, depth-first. */
for (int k = 0; k < 8; k++)
if (pc->progeny[k] >= 0) {
struct cell *temp = space_getcell(s);
temp->count = 0;
temp->gcount = 0;
temp->loc[0] = c->loc[0];
temp->loc[1] = c->loc[1];
temp->loc[2] = c->loc[2];
temp->h[0] = c->h[0] / 2;
temp->h[1] = c->h[1] / 2;
temp->h[2] = c->h[2] / 2;
temp->dmin = c->dmin / 2;
if (k & 4) temp->loc[0] += temp->h[0];
if (k & 2) temp->loc[1] += temp->h[1];
if (k & 1) temp->loc[2] += temp->h[2];
temp->depth = c->depth + 1;
temp->split = 0;
temp->dx_max = 0.f;
temp->nodeID = c->nodeID;
temp->parent = c;
c->progeny[k] = temp;
c->split = 1;
count += cell_unpack(&pc[pc->progeny[k]], temp, s);
}
/* Return the total number of unpacked cells. */
c->pcell_size = count;
return count;
}
/**
* @brief Link the cells recursively to the given #part array.
*
* @param c The #cell.
* @param parts The #part array.
*
* @return The number of particles linked.
*/
int cell_link_parts(struct cell *c, struct part *parts) {
c->parts = parts;
/* Fill the progeny recursively, depth-first. */
if (c->split) {
int offset = 0;
for (int k = 0; k < 8; k++) {
if (c->progeny[k] != NULL)
offset += cell_link_parts(c->progeny[k], &parts[offset]);
}
}
/* Return the total number of linked particles. */
return c->count;
}
/**
* @brief Link the cells recursively to the given #gpart array.
*
* @param c The #cell.
* @param gparts The #gpart array.
*
* @return The number of particles linked.
*/
int cell_link_gparts(struct cell *c, struct gpart *gparts) {
c->gparts = gparts;
/* Fill the progeny recursively, depth-first. */
if (c->split) {
int offset = 0;
for (int k = 0; k < 8; k++) {
if (c->progeny[k] != NULL)
offset += cell_link_gparts(c->progeny[k], &gparts[offset]);
}
}
/* Return the total number of linked particles. */
return c->gcount;
}
/**
* @brief Pack the data of the given cell and all it's sub-cells.
*
* @param c The #cell.
* @param pc Pointer to an array of packed cells in which the
* cells will be packed.
*
* @return The number of packed cells.
*/
int cell_pack(struct cell *c, struct pcell *pc) {
/* Start by packing the data of the current cell. */
pc->h_max = c->h_max;
pc->ti_end_min = c->ti_end_min;
pc->ti_end_max = c->ti_end_max;
pc->count = c->count;
pc->gcount = c->gcount;
c->tag = pc->tag = atomic_inc(&cell_next_tag) % cell_max_tag;
/* Fill in the progeny, depth-first recursion. */
int count = 1;
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL) {
pc->progeny[k] = count;
count += cell_pack(c->progeny[k], &pc[count]);
} else
pc->progeny[k] = -1;
/* Return the number of packed cells used. */
c->pcell_size = count;
return count;
}
int cell_pack_ti_ends(struct cell *c, int *ti_ends) {
/* Pack this cell's data. */
ti_ends[0] = c->ti_end_min;
/* Fill in the progeny, depth-first recursion. */
int count = 1;
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL) {
count += cell_pack_ti_ends(c->progeny[k], &ti_ends[count]);
}
/* Return the number of packed values. */
return count;
}
int cell_unpack_ti_ends(struct cell *c, int *ti_ends) {
/* Unpack this cell's data. */
c->ti_end_min = ti_ends[0];
/* Fill in the progeny, depth-first recursion. */
int count = 1;
for (int k = 0; k < 8; k++)
if (c->progeny[k] != NULL) {
count += cell_unpack_ti_ends(c->progeny[k], &ti_ends[count]);
}
/* Return the number of packed values. */
return count;
}
/**
* @brief Lock a cell and hold its parents.
*
* @param c The #cell.
*/
int cell_locktree(struct cell *c) {
TIMER_TIC
/* First of all, try to lock this cell. */
if (c->hold || lock_trylock(&c->lock) != 0) {
TIMER_TOC(timer_locktree);
return 1;
}
/* Did somebody hold this cell in the meantime? */
if (c->hold) {
/* Unlock this cell. */
if (lock_unlock(&c->lock) != 0) error("Failed to unlock cell.");
/* Admit defeat. */
TIMER_TOC(timer_locktree);
return 1;
}
/* Climb up the tree and lock/hold/unlock. */
struct cell *finger;
for (finger = c->parent; finger != NULL; finger = finger->parent) {
/* Lock this cell. */
if (lock_trylock(&finger->lock) != 0) break;
/* Increment the hold. */
atomic_inc(&finger->hold);
/* Unlock the cell. */
if (lock_unlock(&finger->lock) != 0) error("Failed to unlock cell.");
}
/* If we reached the top of the tree, we're done. */
if (finger == NULL) {
TIMER_TOC(timer_locktree);
return 0;
}
/* Otherwise, we hit a snag. */
else {
/* Undo the holds up to finger. */
for (struct cell *finger2 = c->parent; finger2 != finger;
finger2 = finger2->parent)
atomic_dec(&finger2->hold);
/* Unlock this cell. */
if (lock_unlock(&c->lock) != 0) error("Failed to unlock cell.");
/* Admit defeat. */
TIMER_TOC(timer_locktree);
return 1;
}
}
int cell_glocktree(struct cell *c) {
TIMER_TIC
/* First of all, try to lock this cell. */
if (c->ghold || lock_trylock(&c->glock) != 0) {
TIMER_TOC(timer_locktree);
return 1;
}
/* Did somebody hold this cell in the meantime? */
if (c->ghold) {
/* Unlock this cell. */
if (lock_unlock(&c->glock) != 0) error("Failed to unlock cell.");
/* Admit defeat. */
TIMER_TOC(timer_locktree);
return 1;
}
/* Climb up the tree and lock/hold/unlock. */
struct cell *finger;
for (finger = c->parent; finger != NULL; finger = finger->parent) {
/* Lock this cell. */
if (lock_trylock(&finger->glock) != 0) break;
/* Increment the hold. */
atomic_inc(&finger->ghold);
/* Unlock the cell. */
if (lock_unlock(&finger->glock) != 0) error("Failed to unlock cell.");
}
/* If we reached the top of the tree, we're done. */
if (finger == NULL) {
TIMER_TOC(timer_locktree);
return 0;
}
/* Otherwise, we hit a snag. */
else {
/* Undo the holds up to finger. */
for (struct cell *finger2 = c->parent; finger2 != finger;
finger2 = finger2->parent)
atomic_dec(&finger2->ghold);
/* Unlock this cell. */
if (lock_unlock(&c->glock) != 0) error("Failed to unlock cell.");
/* Admit defeat. */
TIMER_TOC(timer_locktree);
return 1;
}
}
/**
* @brief Unlock a cell's parents.
*
* @param c The #cell.
*/
void cell_unlocktree(struct cell *c) {
TIMER_TIC
/* First of all, try to unlock this cell. */
if (lock_unlock(&c->lock) != 0) error("Failed to unlock cell.");
/* Climb up the tree and unhold the parents. */
for (struct cell *finger = c->parent; finger != NULL; finger = finger->parent)
atomic_dec(&finger->hold);
TIMER_TOC(timer_locktree);
}
void cell_gunlocktree(struct cell *c) {
TIMER_TIC
/* First of all, try to unlock this cell. */
if (lock_unlock(&c->glock) != 0) error("Failed to unlock cell.");
/* Climb up the tree and unhold the parents. */
for (struct cell *finger = c->parent; finger != NULL; finger = finger->parent)
atomic_dec(&finger->ghold);
TIMER_TOC(timer_locktree);
}
/**
* @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->parts - s->parts.
*/
void cell_split(struct cell *c, ptrdiff_t parts_offset) {
int i, j;
const int count = c->count, gcount = c->gcount;
struct part *parts = c->parts;
struct xpart *xparts = c->xparts;
struct gpart *gparts = c->gparts;
int left[8], right[8];
double pivot[3];
/* Init the pivots. */
for (int k = 0; k < 3; k++) pivot[k] = c->loc[k] + c->h[k] / 2;
/* Split along the x-axis. */
i = 0;
j = count - 1;
while (i <= j) {
while (i <= count - 1 && parts[i].x[0] <= pivot[0]) i += 1;
while (j >= 0 && parts[j].x[0] > pivot[0]) j -= 1;
if (i < j) {
struct part temp = parts[i];
parts[i] = parts[j];
parts[j] = temp;
struct xpart xtemp = xparts[i];
xparts[i] = xparts[j];
xparts[j] = xtemp;
}
}
#ifdef SWIFT_DEBUG_CHECKS
for (int k = 0; k <= j; k++)
if (parts[k].x[0] > pivot[0]) error("cell_split: sorting failed.");
for (int k = i; k < count; k++)
if (parts[k].x[0] < pivot[0]) error("cell_split: sorting failed.");
#endif
left[1] = i;
right[1] = count - 1;
left[0] = 0;
right[0] = j;
/* Split along the y axis, twice. */
for (int k = 1; k >= 0; k--) {
i = left[k];
j = right[k];
while (i <= j) {
while (i <= right[k] && parts[i].x[1] <= pivot[1]) i += 1;
while (j >= left[k] && parts[j].x[1] > pivot[1]) j -= 1;
if (i < j) {
struct part temp = parts[i];
parts[i] = parts[j];
parts[j] = temp;
struct xpart xtemp = xparts[i];
xparts[i] = xparts[j];
xparts[j] = xtemp;
}
}
#ifdef SWIFT_DEBUG_CHECKS
for (int kk = left[k]; kk <= j; kk++)
if (parts[kk].x[1] > pivot[1]) {
message("ival=[%i,%i], i=%i, j=%i.", left[k], right[k], i, j);
error("sorting failed (left).");
}
for (int kk = i; kk <= right[k]; kk++)
if (parts[kk].x[1] < pivot[1]) error("sorting failed (right).");
#endif
left[2 * k + 1] = i;
right[2 * k + 1] = right[k];
left[2 * k] = left[k];
right[2 * k] = j;
}
/* Split along the z axis, four times. */
for (int k = 3; k >= 0; k--) {
i = left[k];
j = right[k];
while (i <= j) {
while (i <= right[k] && parts[i].x[2] <= pivot[2]) i += 1;
while (j >= left[k] && parts[j].x[2] > pivot[2]) j -= 1;
if (i < j) {
struct part temp = parts[i];
parts[i] = parts[j];
parts[j] = temp;
struct xpart xtemp = xparts[i];
xparts[i] = xparts[j];
xparts[j] = xtemp;
}
}
#ifdef SWIFT_DEBUG_CHECKS
for (int kk = left[k]; kk <= j; kk++)
if (parts[kk].x[2] > pivot[2]) {
message("ival=[%i,%i], i=%i, j=%i.", left[k], right[k], i, j);
error("sorting failed (left).");
}
for (int kk = i; kk <= right[k]; kk++)
if (parts[kk].x[2] < pivot[2]) {
message("ival=[%i,%i], i=%i, j=%i.", left[k], right[k], i, j);
error("sorting failed (right).");
}
#endif
left[2 * k + 1] = i;
right[2 * k + 1] = right[k];
left[2 * k] = left[k];
right[2 * k] = j;
}
/* Store the counts and offsets. */
for (int k = 0; k < 8; k++) {
c->progeny[k]->count = right[k] - left[k] + 1;
c->progeny[k]->parts = &c->parts[left[k]];
c->progeny[k]->xparts = &c->xparts[left[k]];
}
/* Re-link the gparts. */
part_relink_gparts(parts, count, parts_offset);
#ifdef SWIFT_DEBUG_CHECKS
/* Verify that _all_ the parts have been assigned to a cell. */
for (int k = 1; k < 8; k++)
if (&c->progeny[k - 1]->parts[c->progeny[k - 1]->count] !=
c->progeny[k]->parts)
error("Particle sorting failed (internal consistency).");
if (c->progeny[0]->parts != c->parts)
error("Particle sorting failed (left edge).");
if (&c->progeny[7]->parts[c->progeny[7]->count] != &c->parts[count])
error("Particle sorting failed (right edge).");
/* Verify a few sub-cells. */
for (int k = 0; k < c->progeny[0]->count; k++)
if (c->progeny[0]->parts[k].x[0] > pivot[0] ||
c->progeny[0]->parts[k].x[1] > pivot[1] ||
c->progeny[0]->parts[k].x[2] > pivot[2])
error("Sorting failed (progeny=0).");
for (int k = 0; k < c->progeny[1]->count; k++)
if (c->progeny[1]->parts[k].x[0] > pivot[0] ||
c->progeny[1]->parts[k].x[1] > pivot[1] ||
c->progeny[1]->parts[k].x[2] <= pivot[2])
error("Sorting failed (progeny=1).");
for (int k = 0; k < c->progeny[2]->count; k++)
if (c->progeny[2]->parts[k].x[0] > pivot[0] ||
c->progeny[2]->parts[k].x[1] <= pivot[1] ||
c->progeny[2]->parts[k].x[2] > pivot[2])
error("Sorting failed (progeny=2).");
#endif
/* Now do the same song and dance for the gparts. */
/* Split along the x-axis. */
i = 0;
j = gcount - 1;
while (i <= j) {
while (i <= gcount - 1 && gparts[i].x[0] <= pivot[0]) i += 1;
while (j >= 0 && gparts[j].x[0] > pivot[0]) j -= 1;
if (i < j) {
struct gpart gtemp = gparts[i];
gparts[i] = gparts[j];
gparts[j] = gtemp;
}
}
left[1] = i;
right[1] = gcount - 1;
left[0] = 0;
right[0] = j;
/* Split along the y axis, twice. */
for (int k = 1; k >= 0; k--) {
i = left[k];
j = right[k];
while (i <= j) {
while (i <= right[k] && gparts[i].x[1] <= pivot[1]) i += 1;
while (j >= left[k] && gparts[j].x[1] > pivot[1]) j -= 1;
if (i < j) {
struct gpart gtemp = gparts[i];
gparts[i] = gparts[j];
gparts[j] = gtemp;
}
}
left[2 * k + 1] = i;
right[2 * k + 1] = right[k];
left[2 * k] = left[k];
right[2 * k] = j;
}
/* Split along the z axis, four times. */
for (int k = 3; k >= 0; k--) {
i = left[k];
j = right[k];
while (i <= j) {
while (i <= right[k] && gparts[i].x[2] <= pivot[2]) i += 1;
while (j >= left[k] && gparts[j].x[2] > pivot[2]) j -= 1;
if (i < j) {
struct gpart gtemp = gparts[i];
gparts[i] = gparts[j];
gparts[j] = gtemp;
}
}
left[2 * k + 1] = i;
right[2 * k + 1] = right[k];
left[2 * k] = left[k];
right[2 * k] = j;
}
/* Store the counts and offsets. */
for (int k = 0; k < 8; k++) {
c->progeny[k]->gcount = right[k] - left[k] + 1;
c->progeny[k]->gparts = &c->gparts[left[k]];
}
/* Re-link the parts. */
part_relink_parts(gparts, gcount, parts - parts_offset);
}
/**
* @brief Initialises all particles to a valid state even if the ICs were stupid
*
* @param c Cell to act upon
* @param data Unused parameter
*/
void cell_init_parts(struct cell *c, void *data) {
struct part *restrict p = c->parts;
struct xpart *restrict xp = c->xparts;
const size_t count = c->count;
for (size_t i = 0; i < count; ++i) {
p[i].ti_begin = 0;
p[i].ti_end = 0;
xp[i].v_full[0] = p[i].v[0];
xp[i].v_full[1] = p[i].v[1];
xp[i].v_full[2] = p[i].v[2];
hydro_first_init_part(&p[i], &xp[i]);
hydro_init_part(&p[i]);
hydro_reset_acceleration(&p[i]);
}
c->ti_end_min = 0;
c->ti_end_max = 0;
}
/**
* @brief Initialises all g-particles to a valid state even if the ICs were
*stupid
*
* @param c Cell to act upon
* @param data Unused parameter
*/
void cell_init_gparts(struct cell *c, void *data) {
struct gpart *restrict gp = c->gparts;
const size_t gcount = c->gcount;
for (size_t i = 0; i < gcount; ++i) {
gp[i].ti_begin = 0;
gp[i].ti_end = 0;
gravity_first_init_gpart(&gp[i]);
gravity_init_gpart(&gp[i]);
}
c->ti_end_min = 0;
c->ti_end_max = 0;
}
/**
* @brief Converts hydro quantities to a valid state after the initial density
*calculation
*
* @param c Cell to act upon
* @param data Unused parameter
*/
void cell_convert_hydro(struct cell *c, void *data) {
struct part *p = c->parts;
for (int i = 0; i < c->count; ++i) {
hydro_convert_quantities(&p[i]);
}
}
/**
* @brief Cleans the links in a given cell.
*
* @param c Cell to act upon
* @param data Unused parameter
*/
void cell_clean_links(struct cell *c, void *data) {
c->density = NULL;
c->nr_density = 0;
c->force = NULL;
c->nr_force = 0;
}