/*******************************************************************************
* This file is part of SWIFT.
* Coypright (c) 2012 Pedro Gonnet (pedro.gonnet@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
/* Local headers. */
#include "const.h"
#include "atomic.h"
#include "cycle.h"
#include "lock.h"
#include "task.h"
#include "timers.h"
#include "part.h"
#include "space.h"
#include "multipole.h"
#include "cell.h"
#include "error.h"
#include "inline.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 ) {
int k, count = 1;
/* Sum up the progeny if split. */
if ( c->split )
for ( 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 ) {
int k, count = 1;
struct cell *temp;
/* Unpack the current pcell. */
c->h_max = pc->h_max;
c->dt_min = FLT_MAX; // pc->dt_min;
c->dt_max = FLT_MAX; // pc->dt_max;
c->count = pc->count;
c->tag = pc->tag;
/* Fill the progeny recursively, depth-first. */
for ( k = 0 ; k < 8 ; k++ )
if ( pc->progeny[k] >= 0 ) {
temp = space_getcell( s );
temp->count = 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.0;
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. */
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 ( struct cell *c , struct part *parts ) {
int k, ind = 0;
c->parts = parts;
/* Fill the progeny recursively, depth-first. */
if ( c->split )
for ( k = 0 ; k < 8 ; k++ )
if ( c->progeny[k] != NULL )
ind += cell_link( c->progeny[k] , &parts[ind] );
/* Return the total number of unpacked cells. */
return c->count;
}
/**
* @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 ) {
int k, count = 1;
/* Start by packing the data of the current cell. */
pc->h_max = c->h_max;
pc->dt_min = c->dt_min;
pc->dt_max = c->dt_max;
pc->count = c->count;
c->tag = pc->tag = atomic_inc(&cell_next_tag) % cell_max_tag;
/* Fill in the progeny, depth-first recursion. */
for ( 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. */
return count;
}
/**
* @brief Lock a cell and hold its parents.
*
* @param c The #cell.
*/
int cell_locktree( struct cell *c ) {
struct cell *finger, *finger2;
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. */
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 ( finger2 = c->parent ; finger2 != finger ; finger2 = finger2->parent )
__sync_fetch_and_sub( &finger2->hold , 1 );
/* 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 ) {
struct cell *finger, *finger2;
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. */
for ( finger = c->parent ; finger != NULL ; finger = finger->parent ) {
/* Lock this cell. */
if ( lock_trylock( &finger->glock ) != 0 )
break;
/* Increment the hold. */
__sync_fetch_and_add( &finger->ghold , 1 );
/* 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 ( finger2 = c->parent ; finger2 != finger ; finger2 = finger2->parent )
__sync_fetch_and_sub( &finger2->ghold , 1 );
/* Unlock this cell. */
if ( lock_unlock( &c->glock ) != 0 )
error( "Failed to unlock cell." );
/* Admit defeat. */
TIMER_TOC(timer_locktree);
return 1;
}
}
/**
* @brief Unock a cell's parents.
*
* @param c The #cell.
*/
void cell_unlocktree( struct cell *c ) {
struct cell *finger;
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 ( finger = c->parent ; finger != NULL ; finger = finger->parent )
__sync_fetch_and_sub( &finger->hold , 1 );
TIMER_TOC(timer_locktree);
}
void cell_gunlocktree( struct cell *c ) {
struct cell *finger;
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 ( finger = c->parent ; finger != NULL ; finger = finger->parent )
__sync_fetch_and_sub( &finger->ghold , 1 );
TIMER_TOC(timer_locktree);
}
/**
* @brief Sort the parts into eight bins along the given pivots.
*
* @param c The #cell array to be sorted.
*/
void cell_split ( struct cell *c ) {
int i, j, k, count = c->count, gcount = c->gcount;
struct part temp, *parts = c->parts;
struct xpart xtemp, *xparts = c->xparts;
struct gpart gtemp, *gparts = c->gparts;
int left[8], right[8];
double pivot[3];
/* Init the pivots. */
for ( 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 ) {
temp = parts[i]; parts[i] = parts[j]; parts[j] = temp;
xtemp = xparts[i]; xparts[i] = xparts[j]; xparts[j] = xtemp;
}
}
/* for ( k = 0 ; k <= j ; k++ )
if ( parts[k].x[0] > pivot[0] )
error( "cell_split: sorting failed." );
for ( k = i ; k < count ; k++ )
if ( parts[k].x[0] < pivot[0] )
error( "cell_split: sorting failed." ); */
left[1] = i; right[1] = count - 1;
left[0] = 0; right[0] = j;
/* Split along the y axis, twice. */
for ( 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 ) {
temp = parts[i]; parts[i] = parts[j]; parts[j] = temp;
xtemp = xparts[i]; xparts[i] = xparts[j]; xparts[j] = xtemp;
}
}
/* 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)." ); */
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 ( 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 ) {
temp = parts[i]; parts[i] = parts[j]; parts[j] = temp;
xtemp = xparts[i]; xparts[i] = xparts[j]; xparts[j] = xtemp;
}
}
/* 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)." );
} */
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 ( 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. */
for ( k = 0 ; k < count ; k++ )
if ( parts[k].gpart != NULL )
parts[k].gpart->part = &parts[k];
/* Verify that _all_ the parts have been assigned to a cell. */
/* for ( 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 ( 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 ( 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 ( 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)." ); */
/* 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 ) {
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 ( 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 ) {
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 ( 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 ) {
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 ( 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. */
for ( k = 0 ; k < gcount ; k++ )
if ( gparts[k].id > 0 )
gparts[k].part->gpart = &gparts[k];
}