diff --git a/src/hydro/Shadowswift/voronoi2d_algorithm.h b/src/hydro/Shadowswift/voronoi2d_algorithm.h index d02f690cdc7357ce795c3f836c9519de759ee0e3..717c929f6ab02e4072f209bd0b1a8794c394e249 100644 --- a/src/hydro/Shadowswift/voronoi2d_algorithm.h +++ b/src/hydro/Shadowswift/voronoi2d_algorithm.h @@ -28,23 +28,39 @@ #include "minmax.h" #include "voronoi2d_cell.h" +/* IDs used to keep track of cells neighbouring walls of the simulation box + This will only work if these IDs are never used for actual particles (which + in practice means you want to have less than 2^63-4 (~9e18) particles in your + simulation) */ #define VORONOI2D_BOX_LEFT 18446744073709551602llu #define VORONOI2D_BOX_RIGHT 18446744073709551603llu #define VORONOI2D_BOX_TOP 18446744073709551604llu #define VORONOI2D_BOX_BOTTOM 18446744073709551605llu +/* Global variables used to store the extent of the simulation box */ extern float global_voronoi_box_anchor[2]; extern float global_voronoi_box_side[2]; +/* Macro used to declare the global variables in any piece of code that uses + this header file */ #define VORONOI_DECLARE_GLOBAL_VARIABLES() \ float global_voronoi_box_anchor[2]; \ float global_voronoi_box_side[2]; +/* Macros that make it easier to change the box extents that are used throughout + the code */ #define VORONOI2D_BOX_ANCHOR_X global_voronoi_box_anchor[0] #define VORONOI2D_BOX_ANCHOR_Y global_voronoi_box_anchor[1] #define VORONOI2D_BOX_SIDE_X global_voronoi_box_side[0] #define VORONOI2D_BOX_SIDE_Y global_voronoi_box_side[1] +/** + * @brief Set the values of the global variables that store the simulation box + * extents. + * + * @param anchor Corner of the box with the lowest coordinate values. + * @param side Side lengths of the box. + */ __attribute__((always_inline)) INLINE static void voronoi_set_box(float *anchor, float *side) { @@ -64,9 +80,12 @@ __attribute__((always_inline)) INLINE static void voronoi_set_box(float *anchor, __attribute__((always_inline)) INLINE void voronoi_cell_init( struct voronoi_cell *cell, double *x) { + /* Set the position of the generator of the cell (for reference) */ cell->x[0] = x[0]; cell->x[1] = x[1]; + /* Initialize the cell as a box with the same extents as the simulation box + (note: all vertex coordinates are relative w.r.t. the cell generator) */ cell->nvert = 4; cell->vertices[0][0] = VORONOI2D_BOX_ANCHOR_X - cell->x[0]; @@ -85,11 +104,16 @@ __attribute__((always_inline)) INLINE void voronoi_cell_init( VORONOI2D_BOX_ANCHOR_X + VORONOI2D_BOX_SIDE_X - cell->x[0]; cell->vertices[3][1] = VORONOI2D_BOX_ANCHOR_Y - cell->x[1]; + /* The neighbours are ordered such that neighbour i shares the face in between + vertices i and i+1 (with last vertex + 1 = first vertex) + We walk around the cell in clockwise direction */ cell->ngbs[0] = VORONOI2D_BOX_LEFT; cell->ngbs[1] = VORONOI2D_BOX_TOP; cell->ngbs[2] = VORONOI2D_BOX_RIGHT; cell->ngbs[3] = VORONOI2D_BOX_BOTTOM; + /* These variables are initialized to zero, we will compute them after the + neighbour iteration has finished */ cell->volume = 0.0f; cell->centroid[0] = 0.0f; cell->centroid[1] = 0.0f; @@ -109,6 +133,24 @@ __attribute__((always_inline)) INLINE void voronoi_cell_interact( float half_dx[2]; float r2; + float test; + int i; + int index_above1, index_above2; + int index_below1, index_below2; + int increment; + + /* The process of cutting the current cell with the midline of the generator + and the given relative neighbour position proceeds in two steps: + - first we need to locate an edge of the current cell that is intersected + by this midline. Such an edge does not necessarily exist; in this case + the given neighbour is not an actual neighbour of this cell + - Once we have an intersected edge, we create a new edge starting at the + intersection point. We follow the edges connected to the intersected + edge until we find another intersected edge, and use its intersection + point as end point of the new edge. */ + + /* First, we set up some variables that are used to check if a vertex is above + or below the midplane. */ /* we need a vector with half the size of the vector joining generator and neighbour, pointing to the neighbour */ @@ -118,7 +160,130 @@ __attribute__((always_inline)) INLINE void voronoi_cell_interact( /* we need the squared length of this vector */ r2 = half_dx[0] * half_dx[0] + half_dx[1] * half_dx[1]; - (void)r2; + /* a vertex v = (vx, vy) is above the midline if + vx*half_dx[0] + vy*half_dx[1] > r2 + i.e., if the length of the projected vertex position is longer than the + length of the vector pointing to the closest point on the midline (both + vectors originate at the position of the generator) + the vertex is below the midline if the projected position vector is shorter + if the projected position vector has the same length, the vertex is on the + midline */ + + /* start testing a random vertex: the first one */ + test = cell->vertices[0][0] * half_dx[0] + cell->vertices[0][1] * half_dx[1] - + r2; + if (test < 0.) { + /* vertex is below midline */ + + /* move on until we find a vertex that is above the midline */ + i = 1; + test = cell->vertices[i][0] * half_dx[0] + + cell->vertices[i][1] * half_dx[1] - r2; + while (i < cell->nvert && test < 0.) { + ++i; + test = cell->vertices[i][0] * half_dx[0] + + cell->vertices[i][1] * half_dx[1] - r2; + } + + /* loop finished, there are two possibilities: + - i == cell->nvert, all vertices lie below the midline and the given + neighbour is not an actual neighbour of this cell + - test >= 0., we found a vertex above (or on) the midline */ + if (i == cell->nvert) { + /* the given neighbour is not an actual neighbour: exit the routine */ + return; + } + + /* we have found an intersected edge: i-1 -> i + we store the index of the vertex above the midline, and set the increment + for the consecutive vertex search to +1 */ + index_below1 = i - 1; + index_above1 = i; + increment = 1; + } else { + /* vertex is above midline */ + + /* move on until we find a vertex that is below the midline */ + i = 1; + test = cell->vertices[i][0] * half_dx[0] + + cell->vertices[i][1] * half_dx[1] - r2; + while (i < cell->nvert && test > 0.) { + ++i; + test = cell->vertices[i][0] * half_dx[0] + + cell->vertices[i][1] * half_dx[1] - r2; + } + + /* loop finished, there are two possibilities: + - i == cell->nvert, all vertices lie above the midline. This should + never happen. + - test <= 0., we found a vertex below (or on) the midline */ + if (i == cell->nvert) { + /* fatal error! */ + error("Could not find a vertex below the midline!"); + } + + /* we have found an intersected edge: i-1 -> i + we store the index of the vertex above the midline, and set the increment + for the consecutive vertex search to -1 */ + index_below1 = i; + index_above1 = i - 1; + increment = -1; + } + + /* now we need to find the second intersected edge + we start from the vertex above the midline and search in the direction + opposite to the intersected edge direction until we find a vertex below the + midline */ + i = index_above1 + increment; + if (i < 0) { + i = cell->nvert - 1; + } + if (i == cell->nvert) { + i = 0; + } + test = cell->vertices[i][0] * half_dx[0] + cell->vertices[i][1] * half_dx[1] - + r2; + /* this loop can never deadlock, as we know there is at least 1 vertex below + the midline */ + while (test > 0.) { + i += increment; + if (i < 0) { + i = cell->nvert - 1; + } + if (i == cell->nvert) { + i = 0; + } + test = cell->vertices[i][0] * half_dx[0] + + cell->vertices[i][1] * half_dx[1] - r2; + } + + if (i == index_below1) { + /* we only found 1 intersected edge. This is impossible. */ + error("Only 1 intersected edge found!"); + } + + index_below2 = i; + index_above2 = i - increment; + if (index_above2 < 0) { + index_above2 = cell->nvert - 1; + } + if (index_above2 == cell->nvert) { + index_above2 = 0; + } + + if (increment < 0) { + /* interchange index_below and above 1 and 2 */ + i = index_below1; + index_below1 = index_below2; + index_below2 = i; + i = index_above1; + index_above1 = index_above2; + index_above2 = i; + } + + /* index_above1 now holds the first vertex to remove, and index_above2 the + last, in clockwise order + they can be the same */ } /**