diff --git a/.gitignore b/.gitignore index 7cb0517e69caefe1d0bae5d0a1edcc75fe953c71..73294838f88d0d0d6b41d0eaa17ccedfbc29d9a3 100644 --- a/.gitignore +++ b/.gitignore @@ -79,7 +79,7 @@ tests/testRiemannTRRS tests/testRiemannHLLC tests/testMatrixInversion tests/testVoronoi1D -tests/threadpool_test +tests/testVoronoi3D theory/latex/swift.pdf theory/kernel/kernels.pdf diff --git a/src/hydro/Shadowswift/voronoi3d_algorithm.h b/src/hydro/Shadowswift/voronoi3d_algorithm.h new file mode 100644 index 0000000000000000000000000000000000000000..165798bad1584173cb0df42894a1ca01091e2c0a --- /dev/null +++ b/src/hydro/Shadowswift/voronoi3d_algorithm.h @@ -0,0 +1,1426 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +#ifndef SWIFT_VORONOI3D_ALGORITHM_H +#define SWIFT_VORONOI3D_ALGORITHM_H + +#include <math.h> +#include "inline.h" +#include "voronoi3d_cell.h" + +/* Tolerance parameter used to decide when to use more precise geometric + criteria */ +#define VORONOI3D_TOLERANCE 1.e-5f + +/* Box boundary flags used to signal cells neighbouring the box boundary + These values correspond to the top range of possible 64-bit integers, and + we make the strong assumption that there will never be a particle that has + one of these as particle ID. */ +#define VORONOI3D_BOX_FRONT 18446744073709551600llu +#define VORONOI3D_BOX_BACK 18446744073709551601llu +#define VORONOI3D_BOX_TOP 18446744073709551602llu +#define VORONOI3D_BOX_BOTTOM 18446744073709551603llu +#define VORONOI3D_BOX_LEFT 18446744073709551604llu +#define VORONOI3D_BOX_RIGHT 18446744073709551605llu + +/* Bottom front left corner and side lengths of the large box that contains all + particles and is used as initial cell at the start of the construction */ +/* We should make sure that this box is either so large a particle can never + fall outside (by using FLT_MAX if that works), or is initialized to be larger + than the (periodic) simulation box */ +#define VORONOI3D_BOX_ANCHOR_X -1.0f +#define VORONOI3D_BOX_ANCHOR_Y -1.0f +#define VORONOI3D_BOX_ANCHOR_Z -1.0f +#define VORONOI3D_BOX_SIDE_X 3.0f +#define VORONOI3D_BOX_SIDE_Y 3.0f +#define VORONOI3D_BOX_SIDE_Z 3.0f + +/******************************************************************************* + * 3D specific methods + * + * Most of these methods are based on the source code of voro++: + * http://math.lbl.gov/voro++/ + ******************************************************************************/ + +/** + * @brief Print the contents of a 3D Voronoi cell + * + * @param cell 3D Voronoi cell + */ +__attribute__((always_inline)) INLINE void voronoi_print_cell( + struct voronoi_cell *cell) { + + int i, j; + + for (i = 0; i < cell->nvert; i++) { + message("%i: %g %g %g (%i)", i, cell->vertices[3 * i], + cell->vertices[3 * i + 1], cell->vertices[3 * i + 2], + cell->orders[i]); + for (j = 0; j < cell->orders[i]; j++) { + message("%i (%i)", cell->edges[cell->offsets[i] + j], + cell->edgeindices[cell->offsets[i] + j]); + } + } + message("\n"); +} + +/** + * @brief Get the index of the vertex pointed to by the given edge of the given + * vertex + */ +__attribute__((always_inline)) INLINE int voronoi_get_edge( + struct voronoi_cell *c, int vertex, int edge) { + return c->edges[c->offsets[vertex] + edge]; +} + +/** + * @brief Get the index of vertex in the edge list of the vertex pointed to by + * the given edge of the given vertex + */ +__attribute__((always_inline)) INLINE int voronoi_get_edgeindex( + struct voronoi_cell *c, int vertex, int edge) { + return c->edgeindices[c->offsets[vertex] + edge]; +} + +/** + * @brief Set the index of the vertex pointed to by the given edge of the given + * vertex + */ +__attribute__((always_inline)) INLINE void voronoi_set_edge( + struct voronoi_cell *c, int vertex, int edge, int value) { + c->edges[c->offsets[vertex] + edge] = value; +} + +/** + * @brief Set the index of vertex in the edge list of the vertex pointed to by + * the given edge of the given vertex + */ +__attribute__((always_inline)) INLINE void voronoi_set_edgeindex( + struct voronoi_cell *c, int vertex, int edge, int value) { + c->edgeindices[c->offsets[vertex] + edge] = value; +} + +/** + * @brief Get the neighbour for the given edge of the given vertex + */ +__attribute__((always_inline)) INLINE int voronoi_get_ngb( + struct voronoi_cell *c, int vertex, int edge) { + return c->ngbs[c->offsets[vertex] + edge]; +} + +/** + * @brief Set the neighbour for the given edge of the given vertex + */ +__attribute__((always_inline)) INLINE void voronoi_set_ngb( + struct voronoi_cell *c, int vertex, int edge, int value) { + c->ngbs[c->offsets[vertex] + edge] = value; +} + +/** + * @brief Check if the given vertex is above, below or on the cutting plane + */ +__attribute__((always_inline)) INLINE int voronoi_test_vertex( + float *v, float *dx, float r2, float *test, float *teststack, + int *teststack_size) { + + *test = v[0] * dx[0] + v[1] * dx[1] + v[2] * dx[2] - r2; + + teststack[*teststack_size] = *test; + *teststack_size = *teststack_size + 1; + if (*teststack_size == 2 * VORONOI3D_MAXNUMVERT) { + *teststack_size = 0; + } + + if (*test < -VORONOI3D_TOLERANCE) { + return -1; + } + if (*test > VORONOI3D_TOLERANCE) { + return 1; + } + return 0; +} + +/** + * @brief Initialize the cell as a cube with side 2*h, centered on the particle + * position + */ +__attribute__((always_inline)) INLINE void voronoi_initialize( + struct voronoi_cell *cell) { + + cell->nvert = 8; + + /* (0, 0, 0) -- 0 */ + cell->vertices[0] = VORONOI3D_BOX_ANCHOR_X - cell->x[0]; + cell->vertices[1] = VORONOI3D_BOX_ANCHOR_Y - cell->x[1]; + cell->vertices[2] = VORONOI3D_BOX_ANCHOR_Z - cell->x[2]; + + /* (0, 0, 1)-- 1 */ + cell->vertices[3] = VORONOI3D_BOX_ANCHOR_X - cell->x[0]; + cell->vertices[4] = VORONOI3D_BOX_ANCHOR_Y - cell->x[1]; + cell->vertices[5] = + VORONOI3D_BOX_ANCHOR_Z + VORONOI3D_BOX_SIDE_Z - cell->x[2]; + + /* (0, 1, 0) -- 2 */ + cell->vertices[6] = VORONOI3D_BOX_ANCHOR_X - cell->x[0]; + cell->vertices[7] = + VORONOI3D_BOX_ANCHOR_Y + VORONOI3D_BOX_SIDE_Y - cell->x[1]; + cell->vertices[8] = VORONOI3D_BOX_ANCHOR_Z - cell->x[2]; + + /* (0, 1, 1) -- 3 */ + cell->vertices[9] = VORONOI3D_BOX_ANCHOR_X - cell->x[0]; + cell->vertices[10] = + VORONOI3D_BOX_ANCHOR_Y + VORONOI3D_BOX_SIDE_Y - cell->x[1]; + cell->vertices[11] = + VORONOI3D_BOX_ANCHOR_Z + VORONOI3D_BOX_SIDE_Z - cell->x[2]; + + /* (1, 0, 0) -- 4 */ + cell->vertices[12] = + VORONOI3D_BOX_ANCHOR_X + VORONOI3D_BOX_SIDE_X - cell->x[0]; + cell->vertices[13] = VORONOI3D_BOX_ANCHOR_Y - cell->x[1]; + cell->vertices[14] = VORONOI3D_BOX_ANCHOR_Z - cell->x[2]; + + /* (1, 0, 1) -- 5 */ + cell->vertices[15] = + VORONOI3D_BOX_ANCHOR_X + VORONOI3D_BOX_SIDE_X - cell->x[0]; + cell->vertices[16] = VORONOI3D_BOX_ANCHOR_Y - cell->x[1]; + cell->vertices[17] = + VORONOI3D_BOX_ANCHOR_Z + VORONOI3D_BOX_SIDE_Z - cell->x[2]; + + /* (1, 1, 0) -- 6 */ + cell->vertices[18] = + VORONOI3D_BOX_ANCHOR_X + VORONOI3D_BOX_SIDE_X - cell->x[0]; + cell->vertices[19] = + VORONOI3D_BOX_ANCHOR_Y + VORONOI3D_BOX_SIDE_Y - cell->x[1]; + cell->vertices[20] = VORONOI3D_BOX_ANCHOR_Z - cell->x[2]; + + /* (1, 1, 1) -- 7 */ + cell->vertices[21] = + VORONOI3D_BOX_ANCHOR_X + VORONOI3D_BOX_SIDE_X - cell->x[0]; + cell->vertices[22] = + VORONOI3D_BOX_ANCHOR_Y + VORONOI3D_BOX_SIDE_Y - cell->x[1]; + cell->vertices[23] = + VORONOI3D_BOX_ANCHOR_Z + VORONOI3D_BOX_SIDE_Z - cell->x[2]; + + cell->orders[0] = 3; + cell->orders[1] = 3; + cell->orders[2] = 3; + cell->orders[3] = 3; + cell->orders[4] = 3; + cell->orders[5] = 3; + cell->orders[6] = 3; + cell->orders[7] = 3; + + /* edges are ordered counterclockwise w.r.t. a vector pointing from the + cell generator to the vertex + (0, 0, 0) corner */ + cell->offsets[0] = 0; + cell->edges[0] = 1; + cell->edges[1] = 2; + cell->edges[2] = 4; + cell->edgeindices[0] = 0; + cell->edgeindices[1] = 2; + cell->edgeindices[2] = 0; + + /* (0, 0, 1) corner */ + cell->offsets[1] = 3; + cell->edges[3] = 0; + cell->edges[4] = 5; + cell->edges[5] = 3; + cell->edgeindices[3] = 0; + cell->edgeindices[4] = 2; + cell->edgeindices[5] = 1; + + /* (0, 1, 0) corner */ + cell->offsets[2] = 6; + cell->edges[6] = 3; + cell->edges[7] = 6; + cell->edges[8] = 0; + cell->edgeindices[6] = 0; + cell->edgeindices[7] = 0; + cell->edgeindices[8] = 1; + + /* (0, 1, 1) corner */ + cell->offsets[3] = 9; + cell->edges[9] = 2; + cell->edges[10] = 1; + cell->edges[11] = 7; + cell->edgeindices[9] = 0; + cell->edgeindices[10] = 2; + cell->edgeindices[11] = 0; + + /* (1, 0, 0) corner */ + cell->offsets[4] = 12; + cell->edges[12] = 0; + cell->edges[13] = 6; + cell->edges[14] = 5; + cell->edgeindices[12] = 2; + cell->edgeindices[13] = 2; + cell->edgeindices[14] = 0; + + /* (1, 0, 1) corner */ + cell->offsets[5] = 15; + cell->edges[15] = 4; + cell->edges[16] = 7; + cell->edges[17] = 1; + cell->edgeindices[15] = 2; + cell->edgeindices[16] = 1; + cell->edgeindices[17] = 1; + + /* (1, 1, 0) corner */ + cell->offsets[6] = 18; + cell->edges[18] = 2; + cell->edges[19] = 7; + cell->edges[20] = 4; + cell->edgeindices[18] = 1; + cell->edgeindices[19] = 2; + cell->edgeindices[20] = 1; + + /* (1, 1, 1) corner */ + cell->offsets[7] = 21; + cell->edges[21] = 3; + cell->edges[22] = 5; + cell->edges[23] = 6; + cell->edgeindices[21] = 2; + cell->edgeindices[22] = 1; + cell->edgeindices[23] = 1; + + /* ngbs[3*i+j] is the neighbour corresponding to the plane clockwise of + edge j of vertex i (when going from edge j to vertex i) + we set them to a ridiculously large value to be able to track faces without + neighbour */ + cell->ngbs[0] = VORONOI3D_BOX_FRONT; /* (000) - (001) */ + cell->ngbs[1] = VORONOI3D_BOX_LEFT; /* (000) - (010) */ + cell->ngbs[2] = VORONOI3D_BOX_BOTTOM; /* (000) - (100) */ + + cell->ngbs[3] = VORONOI3D_BOX_LEFT; /* (001) - (000) */ + cell->ngbs[4] = VORONOI3D_BOX_FRONT; /* (001) - (101) */ + cell->ngbs[5] = VORONOI3D_BOX_TOP; /* (001) - (011) */ + + cell->ngbs[6] = VORONOI3D_BOX_LEFT; /* (010) - (011) */ + cell->ngbs[7] = VORONOI3D_BOX_BACK; /* (010) - (110) */ + cell->ngbs[8] = VORONOI3D_BOX_BOTTOM; /* (010) - (000) */ + + cell->ngbs[9] = VORONOI3D_BOX_BACK; /* (011) - (010) */ + cell->ngbs[10] = VORONOI3D_BOX_LEFT; /* (011) - (001) */ + cell->ngbs[11] = VORONOI3D_BOX_TOP; /* (011) - (111) */ + + cell->ngbs[12] = VORONOI3D_BOX_FRONT; /* (100) - (000) */ + cell->ngbs[13] = VORONOI3D_BOX_BOTTOM; /* (100) - (110) */ + cell->ngbs[14] = VORONOI3D_BOX_RIGHT; /* (100) - (101) */ + + cell->ngbs[15] = VORONOI3D_BOX_FRONT; /* (101) - (100) */ + cell->ngbs[16] = VORONOI3D_BOX_RIGHT; /* (101) - (111) */ + cell->ngbs[17] = VORONOI3D_BOX_TOP; /* (101) - (001) */ + + cell->ngbs[18] = VORONOI3D_BOX_BOTTOM; /* (110) - (010) */ + cell->ngbs[19] = VORONOI3D_BOX_BACK; /* (110) - (111) */ + cell->ngbs[20] = VORONOI3D_BOX_RIGHT; /* (110) - (100) */ + + cell->ngbs[21] = VORONOI3D_BOX_BACK; /* (111) - (011) */ + cell->ngbs[22] = VORONOI3D_BOX_TOP; /* (111) - (101) */ + cell->ngbs[23] = VORONOI3D_BOX_RIGHT; /* (111) - (110) */ +} + +/** + * @brief Intersect particle pi with particle pj and adapt its Voronoi cell + * structure + * + * odx = x_i - x_j!!! + */ +__attribute__((always_inline)) INLINE void voronoi_intersect( + float *odx, struct voronoi_cell *c, unsigned long long ngb) { + + float dx[3]; + float r2; + float u = 0.0f, l = 0.0f, q = 0.0f; + int up = -1, us = -1, uw = -1, lp = -1, ls = -1, lw = -1, qp = -1, qs = -1, + qw = -1; + int complicated = -1; + + float teststack[2 * VORONOI3D_MAXNUMVERT]; + int teststack_size = 0; + + dx[0] = -0.5f * odx[0]; + dx[1] = -0.5f * odx[1]; + dx[2] = -0.5f * odx[2]; + r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + + uw = voronoi_test_vertex(&c->vertices[0], dx, r2, &u, teststack, + &teststack_size); + up = 0; + complicated = 0; + if (uw == 0) { + + complicated = 1; + + } else { + + if (uw == 1) { + + lp = voronoi_get_edge(c, up, 0); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + us = 1; + while (us < c->orders[up] && l >= u) { + lp = voronoi_get_edge(c, up, us); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + us++; + } + us--; + if (l >= u) { + error("Cell completely gone! This should not happen."); + } + ls = voronoi_get_edgeindex(c, up, us); + + while (lw == 1) { + u = l; + up = lp; + us = 0; + while (us < ls && l >= u) { + lp = voronoi_get_edge(c, up, us); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + us++; + } + if (l >= u) { + us++; + while (us < c->orders[up] && l >= u) { + lp = voronoi_get_edge(c, up, us); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, + teststack, &teststack_size); + us++; + } + if (l >= u) { + error("Cell completely gone! This should not happen."); + } + } + us--; + ls = voronoi_get_edgeindex(c, up, us); + } + if (lw == 0) { + up = lp; + complicated = 1; + } + + } else { /* if(uw == 1) */ + + qp = voronoi_get_edge(c, up, 0); + qw = voronoi_test_vertex(&c->vertices[3 * qp], dx, r2, &q, teststack, + &teststack_size); + us = 1; + while (us < c->orders[up] && u >= q) { + qp = voronoi_get_edge(c, up, us); + qw = voronoi_test_vertex(&c->vertices[3 * qp], dx, r2, &q, teststack, + &teststack_size); + us++; + } + if (u >= q) { + /* cell unaltered */ + return; + } else { + us--; + } + + while (qw == -1) { + qs = voronoi_get_edgeindex(c, up, us); + u = q; + up = qp; + us = 0; + while (us < qs && u >= q) { + qp = voronoi_get_edge(c, up, us); + qw = voronoi_test_vertex(&c->vertices[3 * qp], dx, r2, &q, teststack, + &teststack_size); + us++; + } + if (u >= q) { + us++; + while (us < c->orders[up] && u >= q) { + qp = voronoi_get_edge(c, up, us); + qw = voronoi_test_vertex(&c->vertices[3 * qp], dx, r2, &q, + teststack, &teststack_size); + us++; + } + if (u >= q) { + /* cell unaltered */ + return; + } + } + us--; + } + if (qw == 1) { + lp = up; + ls = us; + l = u; + up = qp; + us = voronoi_get_edgeindex(c, lp, ls); + u = q; + } else { + up = qp; + complicated = 1; + } + + } /* if(uw == 1) */ + + } /* if(uw == 0) */ + + int vindex = -1; + int visitflags[VORONOI3D_MAXNUMVERT]; + int dstack[VORONOI3D_MAXNUMVERT]; + int dstack_size = 1; + float r = 0.0f; + int cs = -1, rp = -1; + int double_edge = 0; + int i = -1, j = -1, k = -1; + + /* initialize visitflags */ + for (i = 0; i < VORONOI3D_MAXNUMVERT; i++) { + visitflags[i] = 0; + } + + if (complicated) { + + lp = voronoi_get_edge(c, up, 0); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + + if (lw != -1) { + + rp = lw; + i = 1; + lp = voronoi_get_edge(c, up, i); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + while (lw != -1) { + i++; + if (i == c->orders[up]) { + error("Cell completely gone! This should not happen."); + } + lp = voronoi_get_edge(c, up, i); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + } + + j = i + 1; + while (j < c->orders[up]) { + lp = voronoi_get_edge(c, up, j); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + if (lw != -1) { + break; + } + j++; + } + + if (j == c->orders[up] && i == 1 && rp == 0) { + k = c->orders[up]; + double_edge = 1; + } else { + k = j - i + 2; + } + + /* create new order k vertex */ + vindex = c->nvert; + c->nvert++; + if (c->nvert == VORONOI3D_MAXNUMVERT) { + error("Too many vertices!"); + } + c->orders[vindex] = k; + c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; + if (c->offsets[vindex] + k >= VORONOI3D_MAXNUMEDGE) { + error("Too many edges!"); + } + + k = 1; + + visitflags[vindex] = 0; + c->vertices[3 * vindex + 0] = c->vertices[3 * up + 0]; + c->vertices[3 * vindex + 1] = c->vertices[3 * up + 1]; + c->vertices[3 * vindex + 2] = c->vertices[3 * up + 2]; + + us = i - 1; + if (i < 0) { + i = c->orders[up] - 1; + } + while (i < j) { + qp = voronoi_get_edge(c, up, i); + qs = voronoi_get_edgeindex(c, up, i); + voronoi_set_ngb(c, vindex, k, voronoi_get_ngb(c, up, i)); + voronoi_set_edge(c, vindex, k, qp); + voronoi_set_edgeindex(c, vindex, k, qs); + voronoi_set_edge(c, qp, qs, vindex); + voronoi_set_edgeindex(c, qp, qs, k); + voronoi_set_edge(c, up, i, -1); + i++; + k++; + } + if (i == c->orders[up]) { + qs = 0; + } else { + qs = i; + } + + } else { /* if(lw != -1) */ + + i = c->orders[up] - 1; + lp = voronoi_get_edge(c, up, i); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + while (lw == -1) { + i--; + if (i == 0) { + /* cell unaltered */ + return; + } + lp = voronoi_get_edge(c, up, i); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + } + + j = 1; + qp = voronoi_get_edge(c, up, j); + qw = voronoi_test_vertex(&c->vertices[3 * qp], dx, r2, &q, teststack, + &teststack_size); + while (qw == -1) { + j++; + qp = voronoi_get_edge(c, up, j); + qw = voronoi_test_vertex(&c->vertices[3 * qp], dx, r2, &l, teststack, + &teststack_size); + } + + if (i == j && qw == 0) { + double_edge = 1; + k = c->orders[up]; + } else { + k = c->orders[up] - i + j + 1; + } + + /* create new order k vertex */ + vindex = c->nvert; + c->nvert++; + if (c->nvert == VORONOI3D_MAXNUMVERT) { + error("Too many vertices!"); + } + c->orders[vindex] = k; + c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; + if (c->offsets[vindex] + k >= VORONOI3D_MAXNUMEDGE) { + error("Too many edges!"); + } + k = 1; + + visitflags[vindex] = 0; + c->vertices[3 * vindex + 0] = c->vertices[3 * up + 0]; + c->vertices[3 * vindex + 1] = c->vertices[3 * up + 1]; + c->vertices[3 * vindex + 2] = c->vertices[3 * up + 2]; + + us = i; + i++; + while (i < c->orders[up]) { + qp = voronoi_get_edge(c, up, i); + qs = voronoi_get_edgeindex(c, up, i); + voronoi_set_ngb(c, vindex, k, voronoi_get_ngb(c, up, i)); + voronoi_set_edge(c, vindex, k, qp); + voronoi_set_edgeindex(c, vindex, k, qs); + voronoi_set_edge(c, qp, qs, vindex); + voronoi_set_edgeindex(c, qp, qs, k); + voronoi_set_edge(c, up, i, -1); + i++; + k++; + } + i = 0; + while (i < j) { + qp = voronoi_get_edge(c, up, i); + qs = voronoi_get_edgeindex(c, up, i); + voronoi_set_ngb(c, vindex, k, voronoi_get_ngb(c, up, i)); + voronoi_set_edge(c, vindex, k, qp); + voronoi_set_edgeindex(c, vindex, k, qs); + voronoi_set_edge(c, qp, qs, vindex); + voronoi_set_edgeindex(c, qp, qs, k); + voronoi_set_edge(c, up, i, -1); + i++; + k++; + } + qs = j; + } + + if (!double_edge) { + voronoi_set_ngb(c, vindex, k, voronoi_get_ngb(c, up, qs)); + voronoi_set_ngb(c, vindex, 0, ngb); + } else { + voronoi_set_ngb(c, vindex, 0, voronoi_get_ngb(c, up, qs)); + } + + dstack[0] = up; + + cs = k; + qp = up; + q = u; + i = voronoi_get_edge(c, up, us); + us = voronoi_get_edgeindex(c, up, us); + up = i; + visitflags[qp] = -vindex; + + } else { /* if(complicated) */ + + r = u / (u - l); + l = 1.0f - r; + + /* create new order 3 vertex */ + vindex = c->nvert; + c->nvert++; + if (c->nvert == VORONOI3D_MAXNUMVERT) { + error("Too many vertices!"); + } + c->orders[vindex] = 3; + c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; + if (c->offsets[vindex] + 3 >= VORONOI3D_MAXNUMEDGE) { + error("Too many edges!"); + } + + visitflags[vindex] = 0; + c->vertices[3 * vindex + 0] = + c->vertices[3 * lp + 0] * r + c->vertices[3 * up + 0] * l; + c->vertices[3 * vindex + 1] = + c->vertices[3 * lp + 1] * r + c->vertices[3 * up + 1] * l; + c->vertices[3 * vindex + 2] = + c->vertices[3 * lp + 2] * r + c->vertices[3 * up + 2] * l; + + dstack[0] = up; + + voronoi_set_edge(c, vindex, 1, lp); + voronoi_set_edgeindex(c, vindex, 1, ls); + voronoi_set_edge(c, lp, ls, vindex); + voronoi_set_edgeindex(c, lp, ls, 1); + voronoi_set_edge(c, up, us, -1); + + voronoi_set_ngb(c, vindex, 0, ngb); + voronoi_set_ngb(c, vindex, 1, voronoi_get_ngb(c, up, us)); + voronoi_set_ngb(c, vindex, 2, voronoi_get_ngb(c, lp, ls)); + + qs = us + 1; + if (qs == c->orders[up]) { + qs = 0; + } + qp = up; + q = u; + + cs = 2; + + } /* if(complicated) */ + + int cp = -1; + int iqs = -1; + int new_double_edge = -1; + + cp = vindex; + rp = vindex; + while (qp != up || qs != us) { + + lp = voronoi_get_edge(c, qp, qs); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + if (lw == 0) { + + k = 1; + if (double_edge) { + k = 0; + } + qs = voronoi_get_edgeindex(c, qp, qs); + qp = lp; + iqs = qs; + + k++; + qs++; + if (qs == c->orders[qp]) { + qs = 0; + } + lp = voronoi_get_edge(c, qp, qs); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + while (lw == -1) { + k++; + qs++; + if (qs == c->orders[qp]) { + qs = 0; + } + lp = voronoi_get_edge(c, qp, qs); + lw = voronoi_test_vertex(&c->vertices[3 * lp], dx, r2, &l, teststack, + &teststack_size); + } + + j = visitflags[qp]; + if (qp == up && qs == us) { + new_double_edge = 0; + if (j > 0) { + k += c->orders[j]; + } + } else { + if (j > 0) { + k += c->orders[j]; + if (lw == 0) { + i = -visitflags[lp]; + if (i > 0) { + if (voronoi_get_edge(c, i, c->orders[i] - 1) == j) { + new_double_edge = 1; + k--; + } else { + new_double_edge = 0; + } + } else { + if (j == rp && lp == up && voronoi_get_edge(c, qp, qs) == us) { + new_double_edge = 1; + k--; + } else { + new_double_edge = 0; + } + } + } else { + new_double_edge = 0; + } + } else { + if (lw == 0) { + i = -visitflags[lp]; + if (i == cp) { + new_double_edge = 1; + k--; + } else { + new_double_edge = 0; + } + } else { + new_double_edge = 0; + } + } + } + + if (j > 0) { + error("Case not handled!"); + } + + /* create new order k vertex */ + vindex = c->nvert; + c->nvert++; + if (c->nvert == VORONOI3D_MAXNUMVERT) { + fprintf(stderr, "dx: %g %g %g\n", dx[0], dx[1], dx[2]); + fprintf(stderr, "r2: %g\n", r2); + fprintf(stderr, "u: %g, l: %g, q: %g\n", u, l, q); + fprintf(stderr, "up: %i, us: %i, uw: %i\n", up, us, uw); + fprintf(stderr, "lp: %i, ls: %i, lw: %i\n", lp, ls, lw); + fprintf(stderr, "qp: %i, qs: %i, qw: %i\n", qp, qs, qw); + fprintf(stderr, "complicated: %i\n", complicated); + fprintf(stderr, "vindex: %i\n", vindex); + for (int ic = 0; ic < VORONOI3D_MAXNUMVERT; ic++) { + fprintf(stderr, "visitflags[%i]: %i\n", ic, visitflags[ic]); + } + for (int ic = 0; ic < VORONOI3D_MAXNUMVERT; ic++) { + fprintf(stderr, "dstack[%i]: %i\n", ic, dstack[ic]); + } + fprintf(stderr, "dstack_size: %i\n", dstack_size); + for (int ic = 0; ic < VORONOI3D_MAXNUMVERT; ic++) { + fprintf(stderr, "teststack[%i]: %g\n", ic, teststack[ic]); + } + fprintf(stderr, "teststack_size: %i\n", teststack_size); + fprintf(stderr, "r: %g\n", r); + fprintf(stderr, "cs: %i, rp: %i\n", cs, rp); + fprintf(stderr, "double_edge: %i\n", double_edge); + fprintf(stderr, "i: %i, j: %i, k: %i\n", i, j, k); + fprintf(stderr, "cp: %i, iqs: %i\n", cp, iqs); + fprintf(stderr, "new_double_edge: %i\n", new_double_edge); + error("Too many vertices!"); + } + c->orders[vindex] = k; + c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; + if (c->offsets[vindex] + k >= VORONOI3D_MAXNUMEDGE) { + error("Too many edges!"); + } + + visitflags[vindex] = 0; + c->vertices[3 * vindex + 0] = c->vertices[3 * qp + 0]; + c->vertices[3 * vindex + 1] = c->vertices[3 * qp + 1]; + c->vertices[3 * vindex + 2] = c->vertices[3 * qp + 2]; + visitflags[qp] = -vindex; + dstack[dstack_size] = qp; + dstack_size++; + j = vindex; + i = 0; + + if (!double_edge) { + voronoi_set_ngb(c, j, i, ngb); + voronoi_set_edge(c, j, i, cp); + voronoi_set_edgeindex(c, j, i, cs); + voronoi_set_edge(c, cp, cs, j); + voronoi_set_edgeindex(c, cp, cs, i); + i++; + } + + qs = iqs; + iqs = k - 1; + if (new_double_edge) { + iqs = k; + } + while (i < iqs) { + qs++; + if (qs == c->orders[qp]) { + qs = 0; + } + lp = voronoi_get_edge(c, qp, qs); + ls = voronoi_get_edgeindex(c, qp, qs); + voronoi_set_ngb(c, j, i, voronoi_get_ngb(c, qp, qs)); + voronoi_set_edge(c, j, i, lp); + voronoi_set_edgeindex(c, j, i, ls); + voronoi_set_edge(c, lp, ls, j); + voronoi_set_edgeindex(c, lp, ls, i); + voronoi_set_edge(c, qp, qs, -1); + i++; + } + qs++; + if (qs == c->orders[qp]) { + qs = 0; + } + cs = i; + cp = j; + + if (new_double_edge) { + voronoi_set_ngb(c, j, 0, voronoi_get_ngb(c, qp, qs)); + } else { + voronoi_set_ngb(c, j, cs, voronoi_get_ngb(c, qp, qs)); + } + + double_edge = new_double_edge; + + } else { /* if(lw == 0) */ + + if (lw == 1) { + qs = voronoi_get_edgeindex(c, qp, qs) + 1; + if (qs == c->orders[lp]) { + qs = 0; + } + qp = lp; + q = l; + dstack[dstack_size] = qp; + dstack_size++; + } else { + + r = q / (q - l); + l = 1.0f - r; + + /* create new order 3 vertex */ + vindex = c->nvert; + c->nvert++; + if (c->nvert == VORONOI3D_MAXNUMVERT) { + fprintf(stderr, "dx: %g %g %g\n", dx[0], dx[1], dx[2]); + fprintf(stderr, "r2: %g\n", r2); + fprintf(stderr, "u: %g, l: %g, q: %g\n", u, l, q); + fprintf(stderr, "up: %i, us: %i, uw: %i\n", up, us, uw); + fprintf(stderr, "lp: %i, ls: %i, lw: %i\n", lp, ls, lw); + fprintf(stderr, "qp: %i, qs: %i, qw: %i\n", qp, qs, qw); + fprintf(stderr, "complicated: %i\n", complicated); + fprintf(stderr, "vindex: %i\n", vindex); + for (int ic = 0; ic < VORONOI3D_MAXNUMVERT; ic++) { + fprintf(stderr, "visitflags[%i]: %i\n", ic, visitflags[ic]); + } + for (int ic = 0; ic < VORONOI3D_MAXNUMVERT; ic++) { + fprintf(stderr, "dstack[%i]: %i\n", ic, dstack[ic]); + } + fprintf(stderr, "dstack_size: %i\n", dstack_size); + for (int ic = 0; ic < VORONOI3D_MAXNUMVERT; ic++) { + fprintf(stderr, "teststack[%i]: %g\n", ic, teststack[ic]); + } + fprintf(stderr, "teststack_size: %i\n", teststack_size); + fprintf(stderr, "r: %g\n", r); + fprintf(stderr, "cs: %i, rp: %i\n", cs, rp); + fprintf(stderr, "double_edge: %i\n", double_edge); + fprintf(stderr, "i: %i, j: %i, k: %i\n", i, j, k); + fprintf(stderr, "cp: %i, iqs: %i\n", cp, iqs); + fprintf(stderr, "new_double_edge: %i\n", new_double_edge); + error("Too many vertices!"); + } + c->orders[vindex] = 3; + c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1]; + if (c->offsets[vindex] + 3 >= VORONOI3D_MAXNUMEDGE) { + error("Too many edges!"); + } + + c->vertices[3 * vindex + 0] = + c->vertices[3 * lp + 0] * r + c->vertices[3 * qp + 0] * l; + c->vertices[3 * vindex + 1] = + c->vertices[3 * lp + 1] * r + c->vertices[3 * qp + 1] * l; + c->vertices[3 * vindex + 2] = + c->vertices[3 * lp + 2] * r + c->vertices[3 * qp + 2] * l; + + ls = voronoi_get_edgeindex(c, qp, qs); + voronoi_set_edge(c, vindex, 0, cp); + voronoi_set_edge(c, vindex, 1, lp); + voronoi_set_edgeindex(c, vindex, 0, cs); + voronoi_set_edgeindex(c, vindex, 1, ls); + voronoi_set_edge(c, lp, ls, vindex); + voronoi_set_edgeindex(c, lp, ls, 1); + voronoi_set_edge(c, cp, cs, vindex); + voronoi_set_edgeindex(c, cp, cs, 0); + voronoi_set_edge(c, qp, qs, -1); + + voronoi_set_ngb(c, vindex, 0, ngb); + voronoi_set_ngb(c, vindex, 1, voronoi_get_ngb(c, qp, qs)); + voronoi_set_ngb(c, vindex, 2, voronoi_get_ngb(c, lp, ls)); + + qs++; + if (qs == c->orders[qp]) { + qs = 0; + } + cp = vindex; + cs = 2; + } /* if(lw == 1) */ + + } /* if(lw == 0) */ + + } /* while() */ + + voronoi_set_edge(c, cp, cs, rp); + voronoi_set_edge(c, rp, 0, cp); + voronoi_set_edgeindex(c, cp, cs, 0); + voronoi_set_edgeindex(c, rp, 0, cs); + + for (i = 0; i < dstack_size; i++) { + for (j = 0; j < c->orders[dstack[i]]; j++) { + if (voronoi_get_edge(c, dstack[i], j) >= 0) { + dstack[dstack_size] = voronoi_get_edge(c, dstack[i], j); + dstack_size++; + voronoi_set_edge(c, dstack[i], j, -1); + voronoi_set_edgeindex(c, dstack[i], j, -1); + } + } + } + + /* remove deleted vertices from all arrays */ + struct voronoi_cell new_cell; + int m, n; + for (vindex = 0; vindex < c->nvert; vindex++) { + j = vindex; + /* find next edge that is not deleted */ + while (j < c->nvert && voronoi_get_edge(c, j, 0) < 0) { + j++; + } + + if (j == c->nvert) { + /* ready */ + break; + } + + /* copy vertices */ + new_cell.vertices[3 * vindex + 0] = c->vertices[3 * j + 0]; + new_cell.vertices[3 * vindex + 1] = c->vertices[3 * j + 1]; + new_cell.vertices[3 * vindex + 2] = c->vertices[3 * j + 2]; + + /* copy order */ + new_cell.orders[vindex] = c->orders[j]; + + /* set offset */ + if (vindex) { + new_cell.offsets[vindex] = + new_cell.offsets[vindex - 1] + new_cell.orders[vindex - 1]; + } else { + new_cell.offsets[vindex] = 0; + } + + /* copy edges, edgeindices and ngbs */ + for (k = 0; k < c->orders[j]; k++) { + voronoi_set_edge(&new_cell, vindex, k, voronoi_get_edge(c, j, k)); + voronoi_set_edgeindex(&new_cell, vindex, k, + voronoi_get_edgeindex(c, j, k)); + voronoi_set_ngb(&new_cell, vindex, k, voronoi_get_ngb(c, j, k)); + } + + /* update other edges */ + for (k = 0; k < c->orders[j]; k++) { + m = voronoi_get_edge(c, j, k); + n = voronoi_get_edgeindex(c, j, k); + if (m < vindex) { + voronoi_set_edge(&new_cell, m, n, vindex); + } else { + voronoi_set_edge(c, m, n, vindex); + } + } + + /* deactivate edge */ + voronoi_set_edge(c, j, 0, -1); + } + new_cell.nvert = vindex; + + new_cell.x[0] = c->x[0]; + new_cell.x[1] = c->x[1]; + new_cell.x[2] = c->x[2]; + new_cell.centroid[0] = c->centroid[0]; + new_cell.centroid[1] = c->centroid[1]; + new_cell.centroid[2] = c->centroid[2]; + new_cell.volume = c->volume; + new_cell.nface = c->nface; + + /* Update the cell values. */ + voronoi3d_cell_copy(&new_cell, c); +} + +/** + * @brief Get the volume of the tetrahedron made up by the four given vertices + * + * The vertices are expected to be oriented. No idea how though... + * + * @param v1 First vertex. + * @param v2 Second vertex. + * @param v3 Third vertex. + * @param v4 Fourth vertex. + * @return Volume of the tetrahedron. + */ +__attribute__((always_inline)) INLINE float voronoi_volume_tetrahedron( + float *v1, float *v2, float *v3, float *v4) { + + float V; + float r1[3], r2[3], r3[3]; + + r1[0] = v2[0] - v1[0]; + r1[1] = v2[1] - v1[1]; + r1[2] = v2[2] - v1[2]; + r2[0] = v3[0] - v1[0]; + r2[1] = v3[1] - v1[1]; + r2[2] = v3[2] - v1[2]; + r3[0] = v4[0] - v1[0]; + r3[1] = v4[1] - v1[1]; + r3[2] = v4[2] - v1[2]; + V = fabs(r1[0] * r2[1] * r3[2] + r1[1] * r2[2] * r3[0] + + r1[2] * r2[0] * r3[1] - r1[2] * r2[1] * r3[0] - + r2[2] * r3[1] * r1[0] - r3[2] * r1[1] * r2[0]); + V /= 6.; + return V; +} + +/** + * @brief Get the centroid of the tetrahedron made up by the four given vertices + * + * This time, there is no need to orient the vertices. + * + * @param centroid Array to store the centroid in. + * @param v1 First vertex. + * @param v2 Second vertex. + * @param v3 Third vertex. + * @param v4 Fourth vertex. + */ +__attribute__((always_inline)) INLINE void voronoi_centroid_tetrahedron( + float *centroid, float *v1, float *v2, float *v3, float *v4) { + + centroid[0] = 0.25f * (v1[0] + v2[0] + v3[0] + v4[0]); + centroid[1] = 0.25f * (v1[1] + v2[1] + v3[1] + v4[1]); + centroid[2] = 0.25f * (v1[2] + v2[2] + v3[2] + v4[2]); +} + +/** + * @brief Calculate the volume and centroid of a 3D Voronoi cell + * + * @param cell 3D Voronoi cell. + */ +__attribute__((always_inline)) INLINE void voronoi_calculate_cell( + struct voronoi_cell *cell) { + + float v1[3], v2[3], v3[3], v4[3]; + int i, j, k, l, m, n; + float tcentroid[3]; + float tvol; + + /* we need to calculate the volume of the tetrahedra formed by the first + vertex and the triangles that make up the other faces + since we do not store faces explicitly, this means keeping track of the + edges that have been processed somehow + we follow the method used in voro++ and "flip" processed edges to + negative values + this also means that we need to process all triangles corresponding to + an edge at once */ + cell->volume = 0.0f; + v1[0] = cell->vertices[0]; + v1[1] = cell->vertices[1]; + v1[2] = cell->vertices[2]; + cell->centroid[0] = 0.0f; + cell->centroid[1] = 0.0f; + cell->centroid[2] = 0.0f; + + /* loop over all vertices (except the first one) */ + for (i = 1; i < cell->nvert; i++) { + + v2[0] = cell->vertices[3 * i + 0]; + v2[1] = cell->vertices[3 * i + 1]; + v2[2] = cell->vertices[3 * i + 2]; + + /* loop over the edges of the vertex*/ + for (j = 0; j < cell->orders[i]; j++) { + + k = voronoi_get_edge(cell, i, j); + + if (k >= 0) { + + /* mark the edge as processed */ + voronoi_set_edge(cell, i, j, -k - 1); + + l = voronoi_get_edgeindex(cell, i, j) + 1; + if (l == cell->orders[k]) { + l = 0; + } + v3[0] = cell->vertices[3 * k + 0]; + v3[1] = cell->vertices[3 * k + 1]; + v3[2] = cell->vertices[3 * k + 2]; + m = voronoi_get_edge(cell, k, l); + voronoi_set_edge(cell, k, l, -1 - m); + + while (m != i) { + n = voronoi_get_edgeindex(cell, k, l) + 1; + if (n == cell->orders[m]) { + n = 0; + } + v4[0] = cell->vertices[3 * m + 0]; + v4[1] = cell->vertices[3 * m + 1]; + v4[2] = cell->vertices[3 * m + 2]; + tvol = voronoi_volume_tetrahedron(v1, v2, v3, v4); + cell->volume += tvol; + voronoi_centroid_tetrahedron(tcentroid, v1, v2, v3, v4); + cell->centroid[0] += tcentroid[0] * tvol; + cell->centroid[1] += tcentroid[1] * tvol; + cell->centroid[2] += tcentroid[2] * tvol; + k = m; + l = n; + v3[0] = v4[0]; + v3[1] = v4[1]; + v3[2] = v4[2]; + m = voronoi_get_edge(cell, k, l); + voronoi_set_edge(cell, k, l, -1 - m); + } /* while() */ + + } /* if(k >= 0) */ + + } /* for(j) */ + + } /* for(i) */ + + cell->centroid[0] /= cell->volume; + cell->centroid[1] /= cell->volume; + cell->centroid[2] /= cell->volume; + + /* centroid was calculated relative w.r.t. particle position */ + cell->centroid[0] += cell->x[0]; + cell->centroid[1] += cell->x[1]; + cell->centroid[2] += cell->x[2]; +} + +/** + * @brief Calculate the faces for a 3D Voronoi cell. This reorganizes the + * internal variables of the cell, so no new neighbours can be added after + * this method has been called! + * + * @param cell 3D Voronoi cell. + */ +__attribute__((always_inline)) INLINE void voronoi_calculate_faces( + struct voronoi_cell *cell) { + + int i, j, k, l, m, n; + float area; + float midpoint[3]; + float u[3], v[3], w[3]; + float loc_area; + unsigned long long newngbs[VORONOI3D_MAXNUMEDGE]; + + cell->nface = 0; + for (i = 0; i < cell->nvert; ++i) { + + for (j = 0; j < cell->orders[i]; ++j) { + + k = voronoi_get_edge(cell, i, j); + + if (k >= 0) { + + newngbs[cell->nface] = voronoi_get_ngb(cell, i, j); + area = 0.; + midpoint[0] = 0.; + midpoint[1] = 0.; + midpoint[2] = 0.; + voronoi_set_edge(cell, i, j, -1 - k); + l = voronoi_get_edgeindex(cell, i, j) + 1; + if (l == cell->orders[k]) { + l = 0; + } + m = voronoi_get_edge(cell, k, l); + voronoi_set_edge(cell, k, l, -1 - m); + + while (m != i) { + n = voronoi_get_edgeindex(cell, k, l) + 1; + if (n == cell->orders[m]) { + n = 0; + } + u[0] = cell->vertices[3 * k + 0] - cell->vertices[3 * i + 0]; + u[1] = cell->vertices[3 * k + 1] - cell->vertices[3 * i + 1]; + u[2] = cell->vertices[3 * k + 2] - cell->vertices[3 * i + 2]; + v[0] = cell->vertices[3 * m + 0] - cell->vertices[3 * i + 0]; + v[1] = cell->vertices[3 * m + 1] - cell->vertices[3 * i + 1]; + v[2] = cell->vertices[3 * m + 2] - cell->vertices[3 * i + 2]; + w[0] = u[1] * v[2] - u[2] * v[1]; + w[1] = u[2] * v[0] - u[0] * v[2]; + w[2] = u[0] * v[1] - u[1] * v[0]; + loc_area = sqrtf(w[0] * w[0] + w[1] * w[1] + w[2] * w[2]); + area += loc_area; + midpoint[0] += loc_area * (cell->vertices[3 * k + 0] + + cell->vertices[3 * i + 0] + + cell->vertices[3 * m + 0]); + midpoint[1] += loc_area * (cell->vertices[3 * k + 1] + + cell->vertices[3 * i + 1] + + cell->vertices[3 * m + 1]); + midpoint[2] += loc_area * (cell->vertices[3 * k + 2] + + cell->vertices[3 * i + 2] + + cell->vertices[3 * m + 2]); + k = m; + l = n; + m = voronoi_get_edge(cell, k, l); + voronoi_set_edge(cell, k, l, -1 - m); + } + + cell->face_areas[cell->nface] = 0.5f * area; + cell->face_midpoints[cell->nface][0] = midpoint[0] / area / 3.0f; + cell->face_midpoints[cell->nface][1] = midpoint[1] / area / 3.0f; + cell->face_midpoints[cell->nface][2] = midpoint[2] / area / 3.0f; + /* face midpoint was calculated relative to particle position */ + cell->face_midpoints[cell->nface][0] += cell->x[0]; + cell->face_midpoints[cell->nface][1] += cell->x[1]; + cell->face_midpoints[cell->nface][2] += cell->x[2]; + cell->nface++; + + if (cell->nface == VORONOI3D_MAXFACE) { + error("Too many faces!"); + } + + } /* if(k >= 0) */ + + } /* for(j) */ + + } /* for(i) */ + + /* Overwrite the old neighbour array. */ + for (i = 0; i < cell->nface; ++i) { + cell->ngbs[i] = newngbs[i]; + } +} + +/******************************************************************************* + * voronoi_algorithm interface implementations + * + * If you change any function parameters below, you also have to change them in + * the 1D and 2D algorithm! + ******************************************************************************/ + +/** + * @brief Initialize a 3D Voronoi cell + * + * @param cell 3D Voronoi cell to initialize. + * @param x Position of the generator of the cell-> + */ +__attribute__((always_inline)) INLINE void voronoi_cell_init( + struct voronoi_cell *cell, double *x) { + + cell->x[0] = x[0]; + cell->x[1] = x[1]; + cell->x[2] = x[2]; + + voronoi_initialize(cell); + + cell->volume = 0.0f; + cell->centroid[0] = 0.0f; + cell->centroid[1] = 0.0f; + cell->centroid[2] = 0.0f; + cell->nface = 0; +} + +/** + * @brief Interact a #D Voronoi cell with a particle with given relative + * position and ID + * + * @param cell 3D Voronoi cell. + * @param dx Relative position of the interacting generator w.r.t. the cell + * generator (in fact: dx = generator - neighbour). + * @param id ID of the interacting neighbour. + */ +__attribute__((always_inline)) INLINE void voronoi_cell_interact( + struct voronoi_cell *cell, float *dx, unsigned long long id) { + + voronoi_intersect(dx, cell, id); +} + +/** + * @brief Finalize a 3D Voronoi cell + * + * @param cell 3D Voronoi cell. + * @return Maximal radius that could still change the structure of the cell-> + */ +__attribute__((always_inline)) INLINE float voronoi_cell_finalize( + struct voronoi_cell *cell) { + + int i; + float max_radius, v[3], v2; + + /* Calculate the volume and centroid of the cell. */ + voronoi_calculate_cell(cell); + /* Calculate the faces. */ + voronoi_calculate_faces(cell); + + /* Loop over the vertices and calculate the maximum radius. */ + max_radius = 0.0f; + for (i = 0; i < cell->nvert; ++i) { + v[0] = cell->vertices[3 * i]; + v[1] = cell->vertices[3 * i + 1]; + v[2] = cell->vertices[3 * i + 2]; + v2 = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; + max_radius = fmaxf(max_radius, v2); + } + max_radius = sqrtf(max_radius); + + return max_radius; +} + +/** + * @brief Get the oriented surface area and midpoint of the face between a + * 3D Voronoi cell and the given neighbour + * + * @param cell 3D Voronoi cell. + * @param ngb ID of a particle that is possibly a neighbour of this cell-> + * @param A Array to store the oriented surface area in. + * @param midpoint Array to store the relative position of the face in. + * @return 0 if the given neighbour is not a neighbour, 1 otherwise. + */ +__attribute__((always_inline)) INLINE int voronoi_get_face( + struct voronoi_cell *cell, unsigned long long ngb, float *A, + float *midpoint) { + + return 0; +} + +/** + * @brief Get the centroid of a 3D Voronoi cell + * + * @param cell 3D Voronoi cell. + * @param centroid Array to store the centroid in. + */ +__attribute__((always_inline)) INLINE void voronoi_get_centroid( + struct voronoi_cell *cell, float *centroid) { + + centroid[0] = cell->centroid[0]; + centroid[1] = cell->centroid[1]; + centroid[2] = cell->centroid[2]; +} + +#endif // SWIFT_VORONOI3D_ALGORITHM_H diff --git a/src/hydro/Shadowswift/voronoi3d_cell.h b/src/hydro/Shadowswift/voronoi3d_cell.h new file mode 100644 index 0000000000000000000000000000000000000000..5ed79792051e3a437b839c1473ee9eca3e37e948 --- /dev/null +++ b/src/hydro/Shadowswift/voronoi3d_cell.h @@ -0,0 +1,153 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +#ifndef SWIFT_VORONOI1D_CELL_H +#define SWIFT_VORONOI1D_CELL_H + +/* Maximal number of neighbours that can be stored in a voronoi_cell struct */ +#define VORONOI3D_MAXNUMNGB 100 +/* Maximal number of vertices that can be stored in a voronoi_cell struct */ +#define VORONOI3D_MAXNUMVERT 500 +/* Maximal number of edges that can be stored in a voronoi_cell struct */ +#define VORONOI3D_MAXNUMEDGE 1500 +/* Maximal number of faces that can be stored in a voronoi_cell struct */ +#define VORONOI3D_MAXFACE 100 + +/* 3D Voronoi cell */ +struct voronoi_cell { + + /* The position of the generator of the cell. */ + double x[3]; + + /* The volume of the 3D cell. */ + float volume; + + /* The centroid of the cell. */ + float centroid[3]; + + /* Number of cell vertices. */ + int nvert; + + /* Vertex coordinates. */ + float vertices[3 * VORONOI3D_MAXNUMVERT]; + + /* Number of edges for every vertex. */ + int orders[VORONOI3D_MAXNUMVERT]; + + /* Offsets of the edges, edgeindices and neighbours corresponding to a + particular vertex in the internal arrays */ + int offsets[VORONOI3D_MAXNUMVERT]; + + /* Edge information. */ + int edges[VORONOI3D_MAXNUMEDGE]; + + /* Additional edge information. */ + int edgeindices[VORONOI3D_MAXNUMEDGE]; + + /* Neighbour information. */ + unsigned long long ngbs[VORONOI3D_MAXNUMEDGE]; + + /* Number of faces of the cell. */ + int nface; + + /* Surface areas of the cell faces. */ + float face_areas[VORONOI3D_MAXFACE]; + + /* Midpoints of the cell faces. */ + float face_midpoints[VORONOI3D_MAXFACE][3]; +}; + +/** + * @brief Copy the contents of the 3D Voronoi cell pointed to by source into the + * 3D Voronoi cell pointed to by destination + * + * @param source Pointer to a 3D Voronoi cell to read from. + * @param destination Pointer to a 3D Voronoi cell to write to. + */ +__attribute__((always_inline)) INLINE void voronoi3d_cell_copy( + struct voronoi_cell *source, struct voronoi_cell *destination) { + + /* Copy the position of the generator of the cell. */ + destination->x[0] = source->x[0]; + destination->x[1] = source->x[1]; + destination->x[2] = source->x[2]; + + /* Copy the volume of the 3D cell. */ + destination->volume = source->volume; + + /* Copy the centroid of the cell. */ + destination->centroid[0] = source->centroid[0]; + destination->centroid[1] = source->centroid[1]; + destination->centroid[2] = source->centroid[2]; + + /* Copy the number of cell vertices. */ + destination->nvert = source->nvert; + + /* Copy the vertex coordinates. We only copy the 3*nvert first coordinates. */ + for (int i = 0; i < 3 * source->nvert; ++i) { + destination->vertices[i] = source->vertices[i]; + } + + /* Copy the number of edges for every vertex. Again, we only copy the nvert + first values. */ + for (int i = 0; i < source->nvert; ++i) { + destination->orders[i] = source->orders[i]; + } + + /* Copy the nvert first values of the offsets. */ + for (int i = 0; i < source->nvert; ++i) { + destination->offsets[i] = source->offsets[i]; + } + + /* Copy the edge information. No idea how many edges we have, so we copy + everything. */ + for (int i = 0; i < VORONOI3D_MAXNUMEDGE; ++i) { + destination->edges[i] = source->edges[i]; + } + + /* Copy all additional edge information. */ + for (int i = 0; i < VORONOI3D_MAXNUMEDGE; ++i) { + destination->edgeindices[i] = source->edgeindices[i]; + } + + /* Copy neighbour information. Since neighbours are stored per edge, the total + number of neighbours in this list is larger than numngb and we copy + everything. */ + for (int i = 0; i < VORONOI3D_MAXNUMEDGE; ++i) { + destination->ngbs[i] = source->ngbs[i]; + } + + /* Copy the number of faces of the cell. */ + destination->nface = source->nface; + + /* Copy the surface areas of the cell faces. We only copy the nface first + values. */ + for (int i = 0; i < source->nface; ++i) { + destination->face_areas[i] = source->face_areas[i]; + } + + /* Copy the midpoints of the cell faces. */ + for (int i = 0; i < source->nface; ++i) { + destination->face_midpoints[i][0] = source->face_midpoints[i][0]; + destination->face_midpoints[i][1] = source->face_midpoints[i][1]; + destination->face_midpoints[i][2] = source->face_midpoints[i][2]; + } +} + +#endif // SWIFT_VORONOI1D_CELL_H diff --git a/src/hydro/Shadowswift/voronoi_algorithm.h b/src/hydro/Shadowswift/voronoi_algorithm.h index cf1cd50a9719556aaec23ca8903b81554a9115d0..1ea24a225648c66ef73bb9d91ef2fd47eccd36d6 100644 --- a/src/hydro/Shadowswift/voronoi_algorithm.h +++ b/src/hydro/Shadowswift/voronoi_algorithm.h @@ -26,7 +26,6 @@ #warning "2D moving mesh not implemented yet!" #include "voronoi2d_algorithm.h" #elif defined(HYDRO_DIMENSION_3D) -#warning "3D moving mesh not implemented yet!" #include "voronoi3d_algorithm.h" #else #error "You have to select a dimension for the hydro!" diff --git a/src/hydro/Shadowswift/voronoi_cell.h b/src/hydro/Shadowswift/voronoi_cell.h index 1f8e310275c7d8e2b042c19accffdb5301d568e9..8d0b2d7794344fa5a4be86693073aa3c2aecc742 100644 --- a/src/hydro/Shadowswift/voronoi_cell.h +++ b/src/hydro/Shadowswift/voronoi_cell.h @@ -26,7 +26,6 @@ #warning "2D moving mesh not implemented yet!" #include "voronoi2d_cell.h" #elif defined(HYDRO_DIMENSION_3D) -#warning "3D moving mesh not implemented yet!" #include "voronoi3d_algorithm.h" #else #error "You have to select a dimension for the hydro!" diff --git a/tests/Makefile.am b/tests/Makefile.am index d893bf446cb55781f8798ddf876886df484c5b7c..cbb154c787f1f0b52b4ee23f95e1d26060133e4f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -25,7 +25,7 @@ TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetr testPair.sh testPairPerturbed.sh test27cells.sh test27cellsPerturbed.sh \ testParser.sh testSPHStep test125cells.sh testKernelGrav testFFT \ testAdiabaticIndex testRiemannExact testRiemannTRRS testRiemannHLLC \ - testMatrixInversion testVoronoi1D testThreadpool + testMatrixInversion testVoronoi1D testVoronoi3D testThreadpool # List of test programs to compile check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \ @@ -33,7 +33,8 @@ check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \ testKernel testKernelGrav testFFT testInteractions testMaths \ testSymmetry testThreadpool \ testAdiabaticIndex testRiemannExact testRiemannTRRS \ - testRiemannHLLC testMatrixInversion testVoronoi1D + testRiemannHLLC testMatrixInversion testVoronoi1D \ + testVoronoi3D # Sources for the individual programs testGreetings_SOURCES = testGreetings.c @@ -78,6 +79,8 @@ testMatrixInversion_SOURCES = testMatrixInversion.c testVoronoi1D_SOURCES = testVoronoi1D.c +testVoronoi3D_SOURCES = testVoronoi3D.c + testThreadpool_SOURCES = testThreadpool.c # Files necessary for distribution diff --git a/tests/testVoronoi3D.c b/tests/testVoronoi3D.c new file mode 100644 index 0000000000000000000000000000000000000000..644a9508af2c8b4e1c2630367f972e8af5f40ee4 --- /dev/null +++ b/tests/testVoronoi3D.c @@ -0,0 +1,129 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com). + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +#include <stdlib.h> +#include "error.h" +#include "hydro/Shadowswift/voronoi3d_algorithm.h" + +/** + * @brief Check if voronoi_volume_tetrahedron() works + */ +void test_voronoi_volume_tetrahedron() { + float v1[3] = {0., 0., 0.}; + float v2[3] = {0., 0., 1.}; + float v3[3] = {0., 1., 0.}; + float v4[3] = {1., 0., 0.}; + + float V = voronoi_volume_tetrahedron(v1, v2, v3, v4); + assert(V == 1.0f / 6.0f); +} + +/** + * @brief Check if voronoi_centroid_tetrahedron() works + */ +void test_voronoi_centroid_tetrahedron() { + float v1[3] = {0., 0., 0.}; + float v2[3] = {0., 0., 1.}; + float v3[3] = {0., 1., 0.}; + float v4[3] = {1., 0., 0.}; + + float centroid[3]; + voronoi_centroid_tetrahedron(centroid, v1, v2, v3, v4); + assert(centroid[0] == 0.25f); + assert(centroid[1] == 0.25f); + assert(centroid[2] == 0.25f); +} + +/** + * @brief Check if voronoi_calculate_cell() works + */ +void test_calculate_cell() { + struct voronoi_cell cell; + + cell.x[0] = 0.5f; + cell.x[1] = 0.5f; + cell.x[2] = 0.5f; + + /* Initialize the cell to a large cube. */ + voronoi_initialize(&cell); + /* Calculate the volume and centroid of the large cube. */ + voronoi_calculate_cell(&cell); + + /* Update these values if you ever change to another large cube! */ + assert(cell.volume == 27.0f); + assert(cell.centroid[0] = 0.5f); + assert(cell.centroid[1] = 0.5f); + assert(cell.centroid[2] = 0.5f); +} + +int main() { + + /* Check basic Voronoi cell functions */ + test_voronoi_volume_tetrahedron(); + test_voronoi_centroid_tetrahedron(); + test_calculate_cell(); + + /* Create a Voronoi cell */ + double x[3] = {0.5f, 0.5f, 0.5f}; + struct voronoi_cell cell; + voronoi_cell_init(&cell, x); + + /* Interact with neighbours */ + float x0[3] = {0.5f, 0.0f, 0.0f}; + float x1[3] = {-0.5f, 0.0f, 0.0f}; + float x2[3] = {0.0f, 0.5f, 0.0f}; + float x3[3] = {0.0f, -0.5f, 0.0f}; + float x4[3] = {0.0f, 0.0f, 0.5f}; + float x5[3] = {0.0f, 0.0f, -0.5f}; + voronoi_cell_interact(&cell, x0, 1); + voronoi_cell_interact(&cell, x1, 2); + voronoi_cell_interact(&cell, x2, 3); + voronoi_cell_interact(&cell, x3, 4); + voronoi_cell_interact(&cell, x4, 5); + voronoi_cell_interact(&cell, x5, 6); + + /* Interact with some more neighbours to check if they are properly ignored */ + float xE0[3] = {0.6f, 0.0f, 0.1f}; + float xE1[3] = {-0.7f, 0.2f, 0.04f}; + voronoi_cell_interact(&cell, xE0, 7); + voronoi_cell_interact(&cell, xE1, 8); + + /* Finalize cell and check results */ + voronoi_cell_finalize(&cell); + + if (cell.volume != 0.125f) { + error("Wrong volume: %g!", cell.volume); + } + if (fabs(cell.centroid[0] - 0.5f) > 1.e-5f || + fabs(cell.centroid[1] - 0.5f) > 1.e-5f || + fabs(cell.centroid[2] - 0.5f) > 1.e-5f) { + error("Wrong centroid: %g %g %g!", cell.centroid[0], cell.centroid[1], + cell.centroid[2]); + } + /* Check neighbour order */ + // TODO + + /* Check face method */ + float A[3]; + float midpoint[3]; + voronoi_get_face(&cell, 1, A, midpoint); + // TODO + + return 0; +}