diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ea95a65f7e0d937949b8c5a359cbc47d712ba0c..1ce98ccfbbcdb556a308777ece789c40caa03635 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,7 @@ if(ENABLE_GPU) elseif ( CUDA_VERSION_MAJOR EQUAL 10 AND CUDA_VERSION_MINOR EQUAL 2 ) message("CUDA is compatible") set(WARNING_SUPPRESSION_AND_OPTION_NVCC -Xcudafe "--display_error_number --diag_suppress=2976 --diag_suppress=2977 --diag_suppress=2979 --diag_suppress=186" --expt-extended-lambda) -elseif ( CUDA_VERSION_MAJOR EQUAL 11 AND CUDA_VERSION_MINOR EQUAL 0 ) + elseif ( CUDA_VERSION_MAJOR EQUAL 11 AND CUDA_VERSION_MINOR EQUAL 0 ) message("CUDA is compatible") set(WARNING_SUPPRESSION_AND_OPTION_NVCC -Xcudafe "--display_error_number --diag_suppress=3059 --diag_suppress=3058 --diag_suppress=3057 --diag_suppress=3056 --diag_suppress=611 --diag_suppress=186" --expt-extended-lambda) else() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4371f488abd5b16454d4357e3fa5dccac4022115..a20a228da68da0cb7f5d8a4e0ecfe6d8c629ca27 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -143,6 +143,8 @@ install(FILES Operators/Vector/vector_dist_operators_extensions.hpp install(FILES Draw/DrawParticles.hpp Draw/PointIterator.hpp Draw/PointIteratorSkin.hpp + Draw/DrawCircle.hpp + Draw/DrawSphere.hpp DESTINATION openfpm_numerics/include/Draw ) install(FILES interpolation/interpolation.hpp @@ -150,6 +152,14 @@ install(FILES interpolation/interpolation.hpp interpolation/z_spline.hpp DESTINATION openfpm_numerics/include/interpolation ) +install(FILES level_set/redistancing_Sussman/ComputeGradient.hpp + level_set/redistancing_Sussman/GaussFilter.hpp + level_set/redistancing_Sussman/HelpFunctions.hpp + level_set/redistancing_Sussman/HelpFunctionsForGrid.hpp + level_set/redistancing_Sussman/NarrowBand.hpp + level_set/redistancing_Sussman/RedistancingSussman.hpp + DESTINATION openfpm_numerics/include/level_set/redistancing_Sussman) + install(FILES DMatrix/EMatrix.hpp DESTINATION openfpm_numerics/include/DMatrix ) diff --git a/src/Draw/DrawCircle.hpp b/src/Draw/DrawCircle.hpp new file mode 100644 index 0000000000000000000000000000000000000000..062e4ca5f6167ce51c1cc8d810cb592183884242 --- /dev/null +++ b/src/Draw/DrawCircle.hpp @@ -0,0 +1,80 @@ +// +// Created by jstark on 2020-08-18. +// +/** + * @file Circle.hpp + * + * @brief Header file containing functions for creating a filled 2D circle of defined radius. + * + * @author Justina Stark + * @date August 2020 + */ +#ifndef ACCURACY_TESTS_CIRCLE_HPP +#define ACCURACY_TESTS_CIRCLE_HPP + +#include <iostream> + +/**@brief Checks if a point lays inside a circle of given radius and given center coordinates. + * + * @tparam coord_type Inferred type of the point coordinates x and y. + * @tparam radius_type Inferred type of radius the circle is supposed to have. + * @param x_coord X-coordinate of the point for that laying inside the circle should be checked. + * @param y_coord Y-coordinate of the point for that laying inside the circle should be checked. + * @param radius Radius of the filled circle. + * @param center_x X-coordinate of the circle center. + * @param center_y Y-coordinate of the circle center. + * @return True, if point lays inside the circle; false, if outside. + */ +template <typename coord_type, typename radius_type> +bool inside_circle(coord_type x_coord, coord_type y_coord, radius_type radius, float center_x=0, float center_y=0) +{ + return (x_coord - center_x) * (x_coord - center_x) + (y_coord - center_y) * (y_coord - center_y) <= radius * radius; +} + +/**@brief Creates a filled circle of given radius an given center coordinates on a 2D OpenFPM grid. + * + * @details The circle is represented in one of the grid properties via the following indicator function: + * @f[ \phi_{\text{indicator}} = \begin{cases} + * +1 & \text{point lies inside the circle} \\ + * -1 & \text{point lies outside the circle} \\ + * \end{cases} @f] + * + * @tparam Phi_0 Index of grid property that should store the indicator function representing the filled circle. + * @tparam grid_type Inferred type of the input grid. + * @tparam radius_type Inferred type of radius the circle is supposed to have. + * @param grid Input OpenFPM grid. + * @param radius Radius of the filled circle. + * @param center_x X-coordinate of the circle center. + * @param center_y Y-coordinate of the circle center. + */ +template <size_t Phi_0, typename grid_type, typename radius_type> +void init_grid_with_circle(grid_type & grid, radius_type radius, float center_x=0, float center_y=0) +{ + const size_t x = 0; + const size_t y = 1; + // assign pixel values to domain. For each pixel get factor_refinement number of grid points with corresponding value + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + auto gkey = grid.getGKey(key); + auto spacing = grid.getSpacing(); + auto i = gkey.get(x); + auto j = gkey.get(y); + double x_coord = i * spacing[x]; + double y_coord = j * spacing[y]; + + if (inside_circle(x_coord, y_coord, radius, center_x, center_y)) + { + grid.template get<Phi_0> (key) = 1; + } + else + { + grid.template get<Phi_0> (key) = -1; + } + ++dom; + } +} + + +#endif //ACCURACY_TESTS_CIRCLE_HPP diff --git a/src/Draw/DrawSphere.hpp b/src/Draw/DrawSphere.hpp new file mode 100644 index 0000000000000000000000000000000000000000..576bd17a78d06a0e1e73fd15f8e9157150efa7a7 --- /dev/null +++ b/src/Draw/DrawSphere.hpp @@ -0,0 +1,85 @@ +// +// Created by jstark on 2020-05-17. +// +/** + * @file Sphere.hpp + * + * @brief Header file containing functions for creating a filled 3D sphere of defined radius. + * + * @author Justina Stark + * @date May 2020 + */ +#ifndef ACCURACY_TESTS_SPHERE_HPP +#define ACCURACY_TESTS_SPHERE_HPP + +#include <iostream> +/**@brief Checks if a point lays inside a sphere of given radius and given center coordinates. + * + * @tparam coord_type Inferred type of the point coordinates x, y and z. + * @tparam radius_type Inferred type of radius the sphere is supposed to have. + * @param x_coord X-coordinate of the point for that laying inside the sphere should be checked. + * @param y_coord Y-coordinate of the point for that laying inside the sphere should be checked. + * @param z_coord Z-coordinate of the point for that laying inside the sphere should be checked. + * @param radius Radius of the filled sphere. + * @param center_x X-coordinate of the sphere center. + * @param center_y Y-coordinate of the sphere center. + * @param center_z Z-coordinate of the sphere center. + * @return True, if point lays inside the sphere; false, if outside. + */ +template <typename coord_type, typename radius_type> +bool inside_sphere(coord_type x_coord, coord_type y_coord, coord_type z_coord, radius_type radius, float center_x=0, float center_y=0, float center_z=0) +{ + return (x_coord - center_x) * (x_coord - center_x) + (y_coord - center_y) * (y_coord - center_y) + (z_coord - center_z) * (z_coord - center_z) <= radius * radius; +} + +/**@brief Creates a filled sphere of given radius an given center coordinates on a 3D OpenFPM grid. + * + * @details The sphere is represented in one of the grid properties via the following indicator function: + * @f[ \phi_{\text{indicator}} = \begin{cases} + * +1 & \text{point lies inside the sphere} \\ + * -1 & \text{point lies outside the sphere} \\ + * \end{cases} @f] + * + * @tparam Phi_0 Index of grid property that should store the indicator function representing the filled sphere. + * @tparam grid_type Inferred type of the input grid. + * @tparam radius_type Inferred type of radius the sphere is supposed to have. + * @param grid Input OpenFPM grid. + * @param radius Radius of the filled sphere. + * @param center_x X-coordinate of the sphere center. + * @param center_y Y-coordinate of the sphere center. + * @param center_z Z-coordinate of the sphere center. + */ +template <size_t Phi_0, typename grid_type, typename radius_type> +void init_grid_with_sphere(grid_type & grid, radius_type radius, float center_x=0, float center_y=0, float center_z=0) +{ + const size_t x = 0; + const size_t y = 1; + const size_t z = 2; + // assign pixel values to domain. For each pixel get factor_refinement number of grid points with corresponding value + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + auto gkey = grid.getGKey(key); + auto spacing = grid.getSpacing(); + auto i = gkey.get(x); + auto j = gkey.get(y); + auto k = gkey.get(z); + double x_coord = i * spacing[x]; + double y_coord = j * spacing[y]; + double z_coord = k * spacing[z]; + + if (inside_sphere(x_coord, y_coord, z_coord, radius, center_x, center_y, center_z)) + { + grid.template get<Phi_0> (key) = 1; + } + else + { + grid.template get<Phi_0> (key) = -1; + } + ++dom; + } +} + + +#endif //ACCURACY_TESTS_SPHERE_HPP diff --git a/src/FiniteDifference/Eno_Weno.hpp b/src/FiniteDifference/Eno_Weno.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dc2bf99a0d40e597643f09971e0bfe8364760adb --- /dev/null +++ b/src/FiniteDifference/Eno_Weno.hpp @@ -0,0 +1,153 @@ +/* + * + * Based on the description in Osher and Fedkiw, Level Set Methods and Dynamic Implicit Surfaces + * For HJ ENO method - Sec. 3.3 + * For HJ WENO method - Sec. 3.4 +*/ + +#ifndef __ENO_WENO_HPP__ +#define __ENO_WENO_HPP__ + +#include "Grid/grid_dist_id.hpp" + +double adjustWeights(double v1, double v2, double v3, double v4, double v5) +{ + double phix1, phix2, phix3; + double s1, s2, s3; + double a1, a2, a3; + double eps = 1e-6; + double w1, w2, w3; + + // Eqs. (3.25), (3.26), (3.27) + phix1 = (v1 / 3.0) - (7.0 * v2 / 6.0) + (11.0 * v3 / 6.0); + phix2 = -(v2 / 6.0) + (5.0 * v3 / 6.0) + (v4 / 3.0); + phix3 = (v3 / 3.0) + (5.0 * v4 / 6.0) - (v5 / 6.0); + + // Eqs. (3.32), (3.33), (3.34) + s1 = (13.0 / 12.0) * (v1 - 2*v2 + v3) * (v1 - 2*v2 + v3) + + 0.25 * (v1 - 4*v2 + 3*v3) * (v1 - 4*v2 + 3*v3); + s2 = (13.0 / 12.0) * (v2 - 2*v3 + v4) * (v2 - 2*v3 + v4) + + 0.25 * (v2 - v4) * (v2 - v4); + s3 = (13.0 / 12.0) * (v3 - 2*v4 + v5) * (v3 - 2*v4 + v5) + + 0.25 * (3*v3 - 4*v4 + v5) * (3*v3 - 4*v4 + v5); + + // Eqs. (3.35), (3.36), (3.37) + a1 = 0.1 / ((s1 + eps)*(s1 + eps)); + a2 = 0.6 / ((s2 + eps)*(s2 + eps)); + a3 = 0.3 / ((s3 + eps)*(s3 + eps)); + + // Eqs. (3.39), (3.40), (3.41) + w1 = a1 / (a1 + a2 + a3); + w2 = a2 / (a1 + a2 + a3); + w3 = a3 / (a1 + a2 + a3); + + // Eq. (3.28) + return (w1 * phix1 + w2 * phix2 + w3 * phix3); +} + +template<typename GridDist, typename GridKey, unsigned int field, unsigned int x> +double WENO_5_Plus(GridKey key, GridDist &gd) +{ + double c2, c3, c4, c5, c6; + double coeff = 1.0 / gd.spacing(x); + double df; + + c2 = (gd.template get<field>(key.move(x, -1)) - gd.template get<field>(key.move(x, -2))) * coeff; + c3 = (gd.template get<field>(key) - gd.template get<field>(key.move(x, -1))) * coeff; + c4 = (gd.template get<field>(key.move(x,1)) - gd.template get<field>(key)) * coeff; + c5 = (gd.template get<field>(key.move(x, 2)) - gd.template get<field>(key.move(x, 1))) * coeff; + c6 = (gd.template get<field>(key.move(x, 3)) - gd.template get<field>(key.move(x, 2))) * coeff; + df = adjustWeights(c6, c5, c4, c3, c2); + + return df; +} + +template<typename GridDist, typename GridKey, unsigned int field, unsigned int x> +double WENO_5_Minus(GridKey key, GridDist &gd) +{ + double c1, c2, c3, c4, c5; + double coeff = 1.0 / gd.spacing(x); + double df; + + c1 = (gd.template get<field>(key.move(x, -2)) - gd.template get<field>(key.move(x, -3))) * coeff; + c2 = (gd.template get<field>(key.move(x, -1)) - gd.template get<field>(key.move(x, -2))) * coeff; + c3 = (gd.template get<field>(key) - gd.template get<field>(key.move(x, -1))) * coeff; + c4 = (gd.template get<field>(key.move(x,1)) - gd.template get<field>(key)) * coeff; + c5 = (gd.template get<field>(key.move(x, 2)) - gd.template get<field>(key.move(x, 1))) * coeff; + + df = adjustWeights(c1, c2, c3, c4, c5); + + return df; +} + +template<typename GridDist, typename GridKey, unsigned int field, unsigned int x> +double ENO_3_Plus(GridKey key, GridDist &gd) +{ + double q1x, q2x, q3x; + double gridsize = gd.spacing(x); + double coeff = 1.0 / gridsize; + + q1x = (gd.template get<field>(key.move(x,1)) - gd.template get<field>(key)) * coeff; + double d2ix = (gd.template get<field>(key.move(x,1)) - 2*gd.template get<field>(key) + gd.template get<field>(key.move(x,-1))) * 0.5 * coeff * coeff; + double d2ip1x = (gd.template get<field>(key.move(x,2)) - 2*gd.template get<field>(key.move(x,1)) + gd.template get<field>(key)) * 0.5 * coeff * coeff; + if(abs(d2ix) <= abs(d2ip1x)) + { + q2x = -d2ix * gridsize; + double d3p1hx = (gd.template get<field>(key.move(x,1)) - 3*gd.template get<field>(key) + 3*gd.template get<field>(key.move(x,-1)) - gd.template get<field>(key.move(x,-2))) * coeff * coeff * coeff / 6.0; + double d3p3hx = (gd.template get<field>(key.move(x,2)) - 3*gd.template get<field>(key.move(x,1)) + 3*gd.template get<field>(key) - gd.template get<field>(key.move(x,-1))) * coeff * coeff * coeff / 6.0; + if(abs(d3p1hx) <= abs(d3p3hx)) + q3x = -d3p1hx * gridsize * gridsize; + else + q3x = -d3p3hx * gridsize * gridsize; + } + else + { + q2x = -d2ip1x * gridsize; + double d3p1hx = (gd.template get<field>(key.move(x,2)) - 3*gd.template get<field>(key.move(x,1)) + 3*gd.template get<field>(key) - gd.template get<field>(key.move(x,-1))) * coeff * coeff * coeff / 6.0; + double d3p3hx = (gd.template get<field>(key.move(x,3)) - 3*gd.template get<field>(key.move(x,2)) + 3*gd.template get<field>(key.move(x,1)) - gd.template get<field>(key)) * coeff * coeff * coeff / 6.0; + if(abs(d3p1hx) <= abs(d3p3hx)) + q3x = d3p1hx * 2 * gridsize * gridsize; + else + q3x = d3p3hx * 2 * gridsize * gridsize; + + } + return (q1x + q2x + q3x); +} + +template<typename GridDist, typename GridKey, unsigned int field, unsigned int x> +double ENO_3_Minus(GridKey key, GridDist &gd) +{ + double q1x, q2x, q3x; + double gridsize = gd.spacing(x); + double coeff = 1.0 / gridsize; + + q1x = (gd.template get<field>(key) - gd.template get<field>(key.move(x, -1))) * coeff; + double d2im1x = (gd.template get<field>(key) - 2*gd.template get<field>(key.move(x, -1)) + gd.template get<field>(key.move(x, -2))) * 0.5 * coeff * coeff; + double d2ix = (gd.template get<field>(key.move(x,1)) - 2*gd.template get<field>(key) + gd.template get<field>(key.move(x,-1))) * 0.5 * coeff * coeff; + + if(abs(d2im1x) <= abs(d2ix)) + { + q2x = d2im1x * gridsize; + double d3p1hx = (gd.template get<field>(key) - 3*gd.template get<field>(key.move(x,-1)) + 3*gd.template get<field>(key.move(x,-2)) - gd.template get<field>(key.move(x,-3))) * coeff * coeff * coeff / 6.0; + double d3p3hx = (gd.template get<field>(key.move(x,1)) - 3*gd.template get<field>(key) + 3*gd.template get<field>(key.move(x,-1)) - gd.template get<field>(key.move(x,-2))) * coeff * coeff * coeff / 6.0; + if(abs(d3p1hx) <= abs(d3p3hx)) + q3x = d3p1hx * 2 * gridsize * gridsize; + else + q3x = d3p3hx * 2 * gridsize * gridsize; + } + else + { + q2x = d2ix * gridsize; + double d3p1hx = (gd.template get<field>(key.move(x,1)) - 3*gd.template get<field>(key) + 3*gd.template get<field>(key.move(x,-1)) - gd.template get<field>(key.move(x,-2))) * coeff * coeff * coeff / 6.0; + double d3p3hx = (gd.template get<field>(key.move(x,2)) - 3*gd.template get<field>(key.move(x,1)) + 3*gd.template get<field>(key) - gd.template get<field>(key.move(x,-1))) * coeff * coeff * coeff / 6.0; + if(abs(d3p1hx) <= abs(d3p3hx)) + q3x = -d3p1hx * gridsize * gridsize; + else + q3x = -d3p3hx * gridsize * gridsize; + } + + return (q1x + q2x + q3x); +} + +#endif + diff --git a/src/level_set/redistancing_Sussman/ComputeGradient.hpp b/src/level_set/redistancing_Sussman/ComputeGradient.hpp new file mode 100644 index 0000000000000000000000000000000000000000..21ef4431c9c1d1af724732848dfbca35d4245f5b --- /dev/null +++ b/src/level_set/redistancing_Sussman/ComputeGradient.hpp @@ -0,0 +1,184 @@ +// +// Created by jstark on 2020-05-08. +// +/** + * @file ComputeGradient.hpp + * + * @brief Header file containing the functions to compute an upwind gradient and the magnitude of gradient. + * + * @details Upwind gradient is computed according to <a href="https://www.researchgate.net/publication/2642502_An_Efficient_Interface_Preserving_Level_Set_Re + * -Distancing_Algorithm_And_Its_Application_To_Interfacial_Incompressible_Fluid_Flow.html">M. Sussman and E. Fatemi, + * “Efficient, interface-preserving level set redistancing algorithm and its application to interfacial + * incompressible fluid flow†(1999)</a> ). + * + * @author Justina Stark + * @date May 2020 + */ +#ifndef REDISTANCING_SUSSMAN_COMPUTEGRADIENT_HPP +#define REDISTANCING_SUSSMAN_COMPUTEGRADIENT_HPP + +// Include standard library header files +#include <iostream> +#include <fstream> +#include <typeinfo> +#include <cmath> +#include <array> +// Include OpenFPM header files +#include "Grid/grid_dist_id.hpp" + +/**@brief Computes the forward finite difference of a scalar property on the current grid node. + * + * @tparam Phi Size_t index of property for which the gradient should be computed. + * @tparam gridtype Type of input grid. + * @tparam keytype Type of key variable. + * @param grid Grid, on which the gradient should be computed. + * @param key Key that contains the index of the current grid node. + * @param d Variable (size_t) that contains the dimension. + * @return Forward finite difference for the property under index Phi on the current node with index key. + */ +template <size_t Phi, typename gridtype, typename keytype> +double forward_FD(gridtype & grid, keytype & key, size_t d) +{ + return (grid.template get<Phi> (key.move(d, 1)) - grid.template get<Phi> (key)) / grid.getSpacing()[d]; +} +/**@brief Computes the backward finite difference of a scalar property on the current grid node. + * + * @tparam Phi Size_t index of property for which the gradient should be computed. + * @tparam gridtype Type of input grid. + * @tparam keytype Type of key variable. + * @param grid Grid, on which the gradient should be computed. + * @param key Key that contains the index of the current grid node. + * @param d Variable (size_t) that contains the dimension. + * @return Backward finite difference for the property under index Phi on the current node with index key. + */ +template <size_t Phi, typename gridtype, typename keytype> +double backward_FD(gridtype & grid, keytype & key, size_t d) +{ + return (grid.template get<Phi> (key) - grid.template get<Phi> (key.move(d, -1))) / grid.getSpacing()[d]; +} +/**@brief Get the first order upwind finite difference of a scalar property on the current grid node. + * + * @details Calls #forward_FD() and #backward_FD() and chooses between dplus and dminus by upwinding. + * + * @tparam Phi Size_t index of property for which the gradient should be computed. + * @tparam Phi_0_sign Size_t index of property that contains the initial sign of that property for which the upwind FD + * should be computed. + * @tparam gridtype Type of input grid. + * @tparam keytype Type of key variable. + * @param grid Grid, on which the gradient should be computed. + * @param key Key that contains the index of the current grid node. + * @param d Variable (size_t) that contains the dimension. + * @return First order upwind finite difference for the property under index Phi on the current node with index key. + */ +template <size_t Phi, size_t Phi_0_sign, typename gridtype, typename keytype> +double upwind_FD(gridtype &grid, keytype &key, size_t d) +{ + double dplus = 0, dminus = 0, dphi = 0; + + dplus = forward_FD<Phi>(grid, key, d); + dminus = backward_FD<Phi>(grid, key, d); + int sign_phi0 = grid.template get<Phi_0_sign> (key); + + if (dplus * sign_phi0 < 0 + && (dminus + dplus) * sign_phi0 < 0) dphi = dplus; + + else if (dminus * sign_phi0 > 0 + && (dminus + dplus) * sign_phi0 > 0) dphi = dminus; + + else if (dminus * sign_phi0 < 0 + && dplus * sign_phi0 > 0) dphi = 0; + + //else if (-10e-12 <= dminus <= 10e-12 && -10e-12 <= dplus <= 10e-12) dphi = 0; + + return dphi; +} +/**@brief Computes 1st order upwind finite difference inside and forward/backward finite difference at the grid borders. + * + * @details Checks if a point lays within the grid or at the border. For the internal grid points it calls upwind + * finite difference #upwind_FD(). For the border points, simply #forward_FD() and #backward_FD() is used, + * respectively, + * depending on the side of the border. + * + * @tparam Phi Size_t index of property for which the gradient should be computed. + * @tparam Phi_0_sign Size_t index of property that contains the initial sign of that property for which the upwind FD + * should be computed. + * @tparam Phi_grad Size_t index of property where the gradient result should be stored. + * @tparam gridtype Type of input grid. + * @param grid Grid, on which the gradient should be computed. + */ +// Use upwinding for inner grid points and one sided backward / forward stencil at border grid points +template <size_t Phi, size_t Phi_0_sign, size_t Phi_grad, typename gridtype> +void get_first_order_gradient_depending_on_point_position(gridtype &grid) +{ + grid.template ghost_get<Phi>(); + auto dom = grid.getDomainIterator(); + while (dom.isNext()) + { + auto key = dom.get(); + auto key_g = grid.getGKey(key); + + for(size_t d = 0; d < gridtype::dims; d++ ) + { + if (key_g.get(d) > 0 && key_g.get(d) < grid.size(d) - 1) // if point lays not at the border of the grid + { + grid.template get<Phi_grad> (key) [d] = upwind_FD<Phi, Phi_0_sign>(grid, key, d); + } + + else if (key_g.get(d) == 0) // if point lays at left border, use right sided kernel + { + grid.template get<Phi_grad> (key) [d] = forward_FD<Phi>(grid, key, d); + } + + else if (key_g.get(d) >= grid.size(d) - 1) // if point lays at right border, use left sided kernel + { + grid.template get<Phi_grad> (key) [d] = backward_FD<Phi>(grid, key, d); + } + } + ++dom; + } +} +/**@brief Calls #get_first_order_gradient_depending_on_point_position(). Computes 1st order upwind finite difference. + * +* @tparam Phi Size_t index of property for which the gradient should be computed. + * @tparam Phi_0_sign Size_t index of property that contains the initial sign of that property for which the upwind FD + * should be computed. + * @tparam Phi_grad Size_t index of property where the gradient result should be stored. + * @tparam gridtype Type of input grid. + * @param grid Grid, on which the gradient should be computed. + */ +/*Approximate the initial gradients between neighbor particles using fwd and bwd differences. + These gradients are needed for the re-distancing step according to Sussman & Fatemi 1999.*/ +template <size_t Phi_in, size_t Phi_0_sign, size_t Phi_grad_out, typename gridtype> +void get_upwind_gradient(gridtype & grid) +{ + get_first_order_gradient_depending_on_point_position<Phi_in, Phi_0_sign, Phi_grad_out>(grid); +} + +/**@brief Computes the magnitude of the gradient (L2-norm of gradient vector). + * + * @tparam Phi_grad_in Size_t index of property that contains the gradient. + * @tparam Phi_magnOfGrad_out Size_t index of property where the magnitude of gradient should be stored. + * @tparam gridtype Type of input grid. + * @param grid Grid, on which the magnitude of gradient should be computed. + */ +template <size_t Phi_grad_in, size_t Phi_magnOfGrad_out, typename gridtype> +void get_gradient_magnitude(gridtype & grid) +{ + grid.template ghost_get<Phi_grad_in>(); + auto dom = grid.getDomainGhostIterator(); + while(dom.isNext()) + { + double sum = 0; + auto key = dom.get(); + for(size_t d = 0; d < gridtype::dims; d++) + { + sum += grid.template get<Phi_grad_in> (key)[d] * grid.template get<Phi_grad_in> (key)[d]; + } + grid.template get<Phi_magnOfGrad_out> (key) = sqrt(sum); + ++dom; + } +} + + + +#endif //REDISTANCING_SUSSMAN_COMPUTEGRADIENT_HPP diff --git a/src/level_set/redistancing_Sussman/GaussFilter.hpp b/src/level_set/redistancing_Sussman/GaussFilter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4121f634ed924aaf32c2731081c9cc6ba2f3dc4d --- /dev/null +++ b/src/level_set/redistancing_Sussman/GaussFilter.hpp @@ -0,0 +1,81 @@ +// +// Created by jstark on 2019-11-11. +// +/** + * @file GaussFilter.hpp + * + * @brief Header file containing function which applies Gaussian smoothing (Gaussian blur) to an n-dim. grid. + * + * @details In cases of very steep gradients of the initial (pre-redistancing) phi at the interface, it can be + * beneficial to smoothen phi prior to redistancing. If the initial function is a heaviside-like function, Gaussian + * smoothing transforms it into a rather sigmoid-like functions. + * + * @author Justina Stark + * @date November 2019 + */ +#ifndef REDISTANCING_SUSSMAN_GAUSSFILTER_HPP +#define REDISTANCING_SUSSMAN_GAUSSFILTER_HPP + + +#include <iostream> +#include <typeinfo> +#include <cmath> + +#include "Grid/grid_dist_id.hpp" +#include "HelpFunctions.hpp" +#include "HelpFunctionsForGrid.hpp" + +/**@brief Discrete Gaussian filter with sigma=1. + * + * @details If Gaussian blur of sigma>1 is desired, just repeat smoothing. + * + * @tparam Phi_0_in Index of property that contains the values which should be Gaussian blurred. + * @tparam Phi_smoothed_out Index of property where the output of smoothing should be stored. + * @tparam grid_in_type Type of input grid. + * @param grid_in Input-OpenFPM-grid on which Gaussian blur should be applied. + */ +template <size_t Phi_0_in, size_t Phi_smoothed_out, typename grid_in_type> +void gauss_filter(grid_in_type & grid_in) +{ + grid_in.template ghost_get<Phi_0_in>(); + + // create a temporary grid with same size on which smoothing is perfomed to not override Phi_0 of the input grid + typedef aggregate<double, double> props_temp; + typedef grid_dist_id<grid_in_type::dims, typename grid_in_type::stype, props_temp> g_temp_type; + g_temp_type g_temp(grid_in.getDecomposition(), grid_in.getGridInfoVoid().getSize(), Ghost<grid_in_type::dims, long int>(3)); + // Some indices for better readability + static constexpr size_t Phi_0_temp = 0; + static constexpr size_t Phi_smoothed_temp = 1; + + copy_gridTogrid<Phi_0_in, Phi_0_temp>(grid_in, g_temp, true); // copy incl. ghost + + double gauss1d[] = {0.006, 0.061, 0.242, 0.383}; // 1D Gauss kernel for sigma = 1.0 + +// loop over grid points. Gaussian kernel separated in its dimensions and first applied in x, then in y and z + for (int d = 0; d < g_temp_type::dims; d++) + { + g_temp.template ghost_get<Phi_0_temp, Phi_smoothed_temp>(); + auto dom1 = g_temp.getDomainIterator(); + while (dom1.isNext()) + { + auto key = dom1.get(); + auto key_g = g_temp.getGKey(key); + + g_temp.template get<Phi_smoothed_temp>(key) = + gauss1d[0] * g_temp.template get<Phi_0_temp>(key.move(d, -3)) + + gauss1d[1] * g_temp.template get<Phi_0_temp>(key.move(d, -2)) + + gauss1d[2] * g_temp.template get<Phi_0_temp>(key.move(d, -1)) + + gauss1d[3] * g_temp.template get<Phi_0_temp>(key) + + gauss1d[2] * g_temp.template get<Phi_0_temp>(key.move(d, +1)) + + gauss1d[1] * g_temp.template get<Phi_0_temp>(key.move(d, +2)) + + gauss1d[0] * g_temp.template get<Phi_0_temp>(key.move(d, +3)); + ++dom1; + } + copy_gridTogrid<Phi_smoothed_temp, Phi_0_temp>(g_temp, g_temp); + g_temp.template ghost_get<Phi_0_temp, Phi_smoothed_temp>(); + } + copy_gridTogrid<Phi_smoothed_temp, Phi_smoothed_out>(g_temp, grid_in, true); // copy incl. ghost +} + + +#endif //REDISTANCING_SUSSMAN_GAUSSFILTER_HPP diff --git a/src/level_set/redistancing_Sussman/HelpFunctions.hpp b/src/level_set/redistancing_Sussman/HelpFunctions.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cd7d36730ecd32d94fc271efee3991340bda5f91 --- /dev/null +++ b/src/level_set/redistancing_Sussman/HelpFunctions.hpp @@ -0,0 +1,84 @@ +// +// Created by jstark on 2019-11-08. +// +/** + * @file HelpFunctions.hpp + * + * @brief Header file containing small mathematical help-functions that are needed e.g. for the Sussman redistancing. + * + * @author Justina Stark + * @date November 2019 + */ +#ifndef REDISTANCING_SUSSMAN_HELPFUNCTIONS_HPP +#define REDISTANCING_SUSSMAN_HELPFUNCTIONS_HPP + +#include <iostream> +/**@brief Gets the sign of a variable. + * + * @tparam T Inferred type of input variable. + * @param val Scalar variable of arbitrary type for which the sign should be determined. + * @return Integer variable that contains a -1, if argument negative, 0 if argument=0, and +1 if argument positive. + */ +template <class T> +int sgn(T val) +{ + return (T(0) < val) - (val < T(0)); +} +/**@brief Gets the smoothed sign of a variable. + * + * @details Sign function with smoothing effect on numerical solution (see: <a href="https://www.math.uci + * .edu/~zhao/publication/mypapers/ps/local_levelset.ps">Peng \a et \a al. "A PDE-Based Fast Local + * Level Set Method"</a>, equation (36). Peng \a et \a al.: "The choice of approximation to S(d) by (36) solves the + * problem of the changing of sign of Phi (thus moving the interface across the cell boundary) in the reinitialization + * step when Phi is steep and speeds up the convergence when Phi is flat at the interface." + * + * @f[ sgn_{\epsilon}(\phi) = \frac{\phi}{ \sqrt{\phi^2 + |\nabla\phi|^2 \Delta x^2} }@f] + * + * where the \a φ is the approximation of the SDF from the last iteration. + * + * @tparam T Inferred type of the variable for which the smooth sign should be determined. + * @param val Scalar variable for which the smooth sign should be determined. + * @param epsilon Scalar variable containing the smoothing factor. In case of Peng: @f[\epsilon = + * |\nabla\phi|^2 \Delta x^2@f] + * @return Smooth sign of the argument variable: @f[sgn_{\epsilon}(\phi)@f]. + */ +template <typename T> +T smooth_S(T val, T epsilon) +{ + return (val / sqrt(val * val + epsilon * epsilon)); +} + + +/**@brief Checks, if two values are sufficiently close to each other within a given tolerance range, as to be + * considered as approximately equal. + * + * @tparam T Inferred type of the two variables, for which it should be checked, if they are sufficiently close. + * @param val1 Variable that contains the first value. + * @param val2 Variable that contains the second value. + * @param tolerance Tolerance (or error) by which values are allowed to deviate while still being considered as equal. + * @return True, if \p val1 and \p val2 are the same +/- \p tolerance. False, if they differ by more + * than \p tolerance. + */ +template <class T> +bool isApproxEqual(T val1, T val2, T tolerance) +{ + return (val1 <= val2 + tolerance && val1 >= val2 - tolerance); +} + +/**@brief Appends the value of a given variable of any type to a textfile as string. + * + * @tparam T Inferred type of the input variable, whose value should be appended to textfile as string. + * @param textfile Std::string that contains the path and filename of the textfile, to which \p value should be added. + * @param value Variable that contains the value which should be appended to the \p textfile. + */ +template <typename T> +void append_value_to_textfile(std::string & textfile, T value) +{ + std::ofstream out(textfile); + out << value; +} + + + + +#endif //REDISTANCING_SUSSMAN_HELPFUNCTIONS_HPP diff --git a/src/level_set/redistancing_Sussman/HelpFunctionsForGrid.hpp b/src/level_set/redistancing_Sussman/HelpFunctionsForGrid.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ed4a02bee2e7040e27b7ec8ae51990a12f2e6cb5 --- /dev/null +++ b/src/level_set/redistancing_Sussman/HelpFunctionsForGrid.hpp @@ -0,0 +1,231 @@ +// +// Created by jstark on 2020-05-12. +// +/** + * @file HelpFunctionsForGrid.hpp + * + * @brief Header file containing help-functions that perform on OpenFPM-grids. + * + * @author Justina Stark + * @date May 2020 + */ +#ifndef REDISTANCING_SUSSMAN_HELPFUNCTIONSFORGRID_HPP +#define REDISTANCING_SUSSMAN_HELPFUNCTIONSFORGRID_HPP + +#include <iostream> +#include <limits> + +#include "HelpFunctions.hpp" + +/**@brief Computes the time step for the iterative re-distancing fulfilling CFL condition. + * + * @tparam grid_type Inferred type of the input grid. + * @param grid Input OpenFPM grid. + * @return Time step. + */ +template <typename grid_type> +double get_time_step_CFL(grid_type &grid) +{ + double divisor = 0; + for (size_t d = 0; d < grid_type::dims; d++) + { + divisor += 1.0 / (grid.spacing(d) * grid.spacing(d)); + } + return 1.0 / (2.0 * divisor); +} + +/**@brief Initializes given property \p Prop of an OpenFPM grid including ghost layer with a given value from \p + * init_value. + * + * @tparam Prop Index of property that should be initialized with the value from \p init_value. + * @tparam grid_type Inferred type of input OpenFPM grid. + * @tparam T Inferred type of the variable containing the initialization value \p init_value. + * @param grid OpenFPM grid whose property \p Prop should be initialized, including its ghost layer. + * @param init_value Variable that contains the value that should be copied to \p Prop. + */ +template <size_t Prop, typename grid_type, typename T> +void init_grid_and_ghost(grid_type & grid, T init_value) +{ + auto dom_ghost = grid.getDomainGhostIterator(); + while(dom_ghost.isNext()) + { + auto key = dom_ghost.get(); + grid.template get<Prop>(key) = init_value; + ++dom_ghost; + } +} + +/**@brief Copies the value stored in a given property from a given source grid to a given destination grid. + * + * @tparam attr_sc Index of property that contains the value that should be copied. + * @tparam attr_ds Index of property to which the value should be copied to. + * @tparam grid_source_type Inferred type of the source grid \p grid_sc. + * @tparam grid_dest_type Inferred type of the destination grid \p grid_ds. + * @param grid_sc OpenFPM grid from which value is copied (source). + * @param grid_ds OpenFPM grid to which value is copied to (destination). + * @param include_ghost Bool variable that defines if the copying should include the ghost layer. False, if not; true, + * if yes. Default: false. + */ +template <size_t attr_sc, size_t attr_ds, typename grid_source_type, typename grid_dest_type> +void copy_gridTogrid(const grid_source_type & grid_sc, grid_dest_type & grid_ds, bool include_ghost=false) +{ + assert(grid_source_type::dims == grid_dest_type::dims); + assert(grid_sc.size() == grid_ds.size()); + if (include_ghost) + { + auto dom_sc = grid_sc.getDomainGhostIterator(); + auto dom_ds = grid_ds.getDomainGhostIterator(); + while (dom_sc.isNext()) + { + auto key_sc = dom_sc.get(); + auto key_ds = dom_ds.get(); + grid_ds.template get<attr_ds>(key_ds) = grid_sc.template get<attr_sc>(key_sc); + ++dom_sc; + ++dom_ds; + } + } + else + { + auto dom_sc = grid_sc.getDomainIterator(); + auto dom_ds = grid_ds.getDomainIterator(); + while (dom_sc.isNext()) + { + auto key_sc = dom_sc.get(); + auto key_ds = dom_ds.get(); + grid_ds.template get<attr_ds>(key_ds) = grid_sc.template get<attr_sc>(key_sc); + ++dom_sc; + ++dom_ds; + } + } +} + +/**@brief Determines the biggest spacing of a grid which is potentially anisotropic when comparing x, y (and z) + * coordinate axis. + * + * @tparam grid_type Inferred type of input grid. + * @param grid Input OpenFPM grid. + * @return Double variable that contains the value of the biggest spacing. + */ +template <typename grid_type> +double get_biggest_spacing(grid_type & grid) +{ + double h_max = 0; + for (size_t d = 0; d < grid_type::dims; d++) + { + if (grid.spacing(d) > h_max) h_max = grid.spacing(d); + } + return h_max; +} + +/**@brief Determines the smallest spacing of a grid which is potentially anisotropic when comparing x, y (and z) + * coordinate axis. + * + * @tparam grid_type Inferred type of input grid. + * @param grid Input OpenFPM grid. + * @return Double variable that contains the value of the smallest spacing. + */ +template <typename grid_type> +double get_smallest_spacing(grid_type & grid) +{ + double spacing [grid_type::dims]; + for (size_t d = 0; d < grid_type::dims; d++) + { + spacing[d] = grid.spacing(d); + } + std::sort(std::begin(spacing), std::end(spacing)); + return spacing[0]; +} + +/**@brief Computes the average difference between the values stored at two different properties of the same grid, that + * is, the total difference of these values summed over all the grid nodes divided by the gridsize. + * + * @tparam Prop1 Index of the first property. + * @tparam Prop2 Index of the second property. + * @tparam grid_type Inferred type of the input grid. + * @param grid Input OpenFPM grid. + * @return Double variable that contains the sum over all grid nodes of the difference between the value stored at \p + * Prop1 and the value stored at \p Prop2. + */ +template <size_t Prop1, size_t Prop2, typename grid_type> +double average_difference(grid_type & grid) +{ + double total_diff = 0; + auto dom = grid.getDomainIterator(); + while (dom.isNext()) + { + auto key = dom.get(); + total_diff += abs( grid.template get<Prop1>(key) - grid.template get<Prop2>(key) ); + ++dom; + } + return total_diff / grid.size(); +} + +/**@brief Determines the maximum value stored on a given grid at a given property. + * + * @tparam Prop Index of property for which maximum should be found. + * @tparam grid_type Inferred type of the input grid. + * @param grid Input OpenFPM grid. + * @return Double variable that contains the maximum value of property \p Prop in \p grid. + */ +template <size_t Prop, typename grid_type> +double get_max_val(grid_type & grid) +{ + double max_value = std::numeric_limits<double>::lowest(); + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + if (grid.template get<Prop>(key) > max_value) max_value = grid.template get<Prop>(key); + ++dom; + } + auto & v_cl = create_vcluster(); + v_cl.max(max_value); + v_cl.execute(); + return max_value; +} + +/**@brief Determines the minimum value stored on a given grid at a given property. + * + * @tparam Prop Index of property for which minimum should be found. + * @tparam grid_type Inferred type of the input grid. + * @param grid Input OpenFPM grid. + * @return Double variable that contains the minimum value of property \p Prop in \p grid. + */ +template <size_t Prop, typename grid_type> +double get_min_val(grid_type & grid) +{ + double min_value = std::numeric_limits<double>::max(); + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + if (grid.template get<Prop>(key) < min_value) min_value = grid.template get<Prop>(key); + ++dom; + } + auto & v_cl = create_vcluster(); + v_cl.min(min_value); + v_cl.execute(); + return min_value; +} + +/**@brief Determines the sign of a value stored at a given property and stores it in another property. + * + * @tparam Prop_in Index of property that contains the value whose sign should be determined. + * @tparam Prop_out Index of property where the sign should be written to. + * @tparam grid_type Inferred type of the input grid. + * @param grid Input OpenFPM grid. + */ +template <size_t Prop_in, size_t Prop_out, typename grid_type> +void init_sign_prop(grid_type & grid) +{ + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + grid.template get<Prop_out>(key) = sgn(grid.template get<Prop_in>(key)); + ++dom; + } +} + + +#endif //REDISTANCING_SUSSMAN_HELPFUNCTIONSFORGRID_HPP diff --git a/src/level_set/redistancing_Sussman/NarrowBand.hpp b/src/level_set/redistancing_Sussman/NarrowBand.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a6532defc6b7b91a88ae271bd7af6f798fe34969 --- /dev/null +++ b/src/level_set/redistancing_Sussman/NarrowBand.hpp @@ -0,0 +1,397 @@ +// +// Created by jstark on 2020-05-16. +// +/** + * @file NarrowBand.hpp + * @class NarrowBand + * + * @brief Class for getting the narrow band around the interface. + * + * @details Places particle at those grid point positions, which lay inside a narrow band of user-defined width + * around the interface. The respective SDF or arbitrary other property value is then also copied from the grid node + * to that particle occupying the same position. + * + * + * @author Justina Stark + * @date May 2020 + */ + +#ifndef REDISTANCING_SUSSMAN_NARROWBAND_HPP +#define REDISTANCING_SUSSMAN_NARROWBAND_HPP + +// Include standard library header files +#include <iostream> + +// Include OpenFPM header files +#include "Vector/vector_dist.hpp" +#include "Grid/grid_dist_id.hpp" +#include "data_type/aggregate.hpp" +#include "Decomposition/CartDecomposition.hpp" + +// Include level-set-method related header files +#include "ComputeGradient.hpp" + +/**@brief Class for getting the narrow band around the interface + * @file NarrowBand.hpp + * @class NarrowBand + * @tparam grid_in_type Inferred type of input grid that stores the signed distance function Phi_SDF. + */ +template <typename grid_in_type> +class NarrowBand +{ +public: + /** @brief Constructor taking the thickness of the narrow band as #grid points in order to initialize the + * lower and upper bound for the narrow band. Initializes the temporary internal grid. + * + * @param grid_in Input grid with min. 1 property: Phi_SDF. + * @param thickness Width of narrow band in # grid points. + */ + NarrowBand(const grid_in_type & grid_in, + size_t thickness) // thickness in # grid points + : g_temp(grid_in.getDecomposition(), grid_in.getGridInfoVoid().getSize(), Ghost<grid_in_type::dims, long int>(3)) + { + set_bounds(thickness, grid_in); + } + /** @brief Constructor taking the thickness of the narrow band as physical width in space in order to initialize the + * lower and upper bound for the narrow band. Initializes the temporary internal grid. + * + * @param grid_in Input grid with min. 1 property: Phi_SDF. + * @param thickness Width of narrow band as physical width. + */ + NarrowBand(const grid_in_type & grid_in, + double thickness) // thickness as physical width + : g_temp(grid_in.getDecomposition(), grid_in.getGridInfoVoid().getSize(), Ghost<grid_in_type::dims, long int>(3)) + { + set_bounds(thickness, grid_in); + } + /** @brief Constructor taking the inside and outside physical width of the narrow band in order to initialize the + * lower and upper bound for the narrow band. Initializes the temporary internal grid. + * + * @param grid_in Input grid with min. 1 property: Phi_SDF. + * @param width_outside Extension of narrow band as physical width inside of object (Phi > 0). + * @param width_inside Extension of narrow band as physical width outside of object (Phi < 0). + */ + NarrowBand(const grid_in_type & grid_in, + double width_outside, // physical width of nb inside of object -> Phi > 0 + double width_inside) // physical width nb outside of object -> Phi < 0 + : g_temp(grid_in.getDecomposition(), grid_in.getGridInfoVoid().getSize(), Ghost<grid_in_type::dims, long int>(3)) + { + set_bounds(width_outside, width_inside, grid_in); + } + + + /**@brief Aggregated properties for the temporary grid. + * + * @details The properties are Phi_SDF (result from redistancing), gradient of Phi, L2 norm of gradient of Phi + * (=gradient magnitude) and sign of Phi_SDF (for the upwinding). + * */ + typedef aggregate<double, double[grid_in_type::dims], double, int> props_temp; + /** @brief Type definition for the temporary grid. + */ + typedef grid_dist_id<grid_in_type::dims, typename grid_in_type::stype, props_temp> g_temp_type; + /** + * @brief Create temporary grid, which is only used inside the class to get the gradients. + * + * @details The grid is needed, when the user wants a narrow band which also contains the upwind gradient of phi. + * It contains the following 5 properties: Phi_SDF (result from redistancing), gradient of Phi, L2 norm of gradient + * of Phi (=gradient magnitude) and sign of Phi_SDF (for the upwinding). + * */ + g_temp_type g_temp; + + /** @brief Calls NarrowBand::get_narrow_band_phi_only which fills the empty vector passed as argument with particles + * by placing these within a narrow band around the interface. Only the SDF is copied from the grid properties to + * the respective particle. + * + * @tparam Phi_SDF_grid Index of property storing the signed distance function in the input grid. + * @tparam Phi_SDF_vd Index of property that should store the SDF in the narrow band particle vector. + * @tparam vector_type Inferred type of the particle vector. + * @tparam grid_type Inferred type of the grid storing the SDF. + * + * @param grid Grid of arb. dims. storing the SDF (result of redistancing). + * @param vd Empty vector with same spatial scaling (box) as the grid. + */ + template <size_t Phi_SDF_grid, size_t Phi_SDF_vd, typename vector_type, typename grid_type> + void get_narrow_band(grid_type & grid, vector_type & vd) + { + get_narrow_band_phi_only<Phi_SDF_grid, Phi_SDF_vd>(grid, vd); + } + /** @brief Calls NarrowBand::get_narrow_band_with_gradients which fills the empty vector passed as argument with particles by placing these within a narrow band around the + * interface. SDF and Phi_grad_temp are copied from the temp. grid to the respective particle. + * + * @tparam Phi_SDF_grid Index of property storing the signed distance function in the input grid. + * @tparam Phi_SDF_vd Index of property that should store the SDF in the narrow band particle vector. + * @tparam Phi_grad Index of property that should store the gradient of phi in the narrow band particle vector. + * @tparam vector_type Inferred type of the particle vector. + * @tparam grid_type Inferred type of the grid storing the SDF. + * + * @param grid Grid of arb. dims. storing the SDF (result of redistancing). + * @param vd Empty vector with same spatial scaling (box) as the grid. + */ + template <size_t Phi_SDF_grid, size_t Phi_SDF_vd, size_t Phi_grad, typename vector_type, typename grid_type> + void get_narrow_band(grid_type & grid, vector_type & vd) + { + get_narrow_band_with_gradients<Phi_SDF_grid, Phi_SDF_vd, Phi_grad>(grid, vd); + } + /** @brief Calls NarrowBand::get_narrow_band_with_gradients which fills the empty vector passed as argument with + * particles by placing these within a narrow band around the interface. SDF, Phi_grad_temp and + * Phi_magnOfGrad_temp are copied from the temp. grid to the respective particle. + * + * @tparam Phi_SDF_grid Index of property storing the signed distance function in the input grid. + * @tparam Phi_SDF_vd Index of property that should store the SDF in the narrow band particle vector. + * @tparam Phi_grad Index of property that should store the gradient of phi in the narrow band particle vector. + * @tparam Phi_magnOfGrad Index of property that should store the gradient magnitude of phi in the narrow band particles. + * @tparam vector_type Inferred type of the particle vector. + * @tparam grid_type Inferred type of the grid storing the SDF. + * + * @param grid Grid of arb. dims. storing the SDF (result of redistancing). + * @param vd Empty vector with same spatial scaling (box) as the grid. + */ + template <size_t Phi_SDF_grid, size_t Phi_SDF_vd, size_t Phi_grad, size_t Phi_magnOfGrad, typename vector_type, typename grid_type> + void get_narrow_band(grid_type & grid, vector_type & vd) + { + get_narrow_band_with_gradients<Phi_SDF_grid, Phi_SDF_vd, Phi_grad, Phi_magnOfGrad>(grid, vd); + } + /** @brief Calls NarrowBand::get_narrow_band_one_prop which fills the empty vector passed as argument with + * particles by placing these within a narrow band around the interface. An arbitrary property is copied from the + * grid to the respective particle. + * + * @tparam Phi_SDF_grid Index of property storing the signed distance function in the input grid. + * @tparam Prop1_grid Index of arbitrary grid property that should be copied to the particles (prop. value must + * be a scalar). + * @tparam Prop1_vd Index of particle property, where grid property should be copied to (prop. value must be a + * scalar). + * @tparam vector_type Inferred type of the particle vector. + * @tparam grid_type Inferred type of the grid storing the SDF. + * + * @param grid Grid of arb. dims. storing the SDF (result of redistancing) and any arbitrary other (scalar) + * property. + * @param vd Empty vector with same spatial scaling (box) as the grid. + */ + template <size_t Phi_SDF_grid, size_t Prop1_grid, size_t Prop1_vd, typename vector_type, typename grid_type> + void get_narrow_band_copy_specific_property(grid_type & grid, vector_type & vd) + { + get_narrow_band_one_prop<Phi_SDF_grid, Prop1_grid, Prop1_vd>(grid, vd); + } + +private: + // Some indices for better readability + static const size_t Phi_SDF_temp = 0; ///< Property index of Phi_SDF on the temporary grid. + static const size_t Phi_grad_temp = 1; ///< Property index of gradient of Phi on the temporary grid. + static const size_t Phi_magnOfGrad_temp = 2; ///< Property index of gradient magnitude of Phi on the temp. grid. + static const size_t Phi_sign_temp = 3; ///< Property index of sign of Phi on the temporary grid. + + double b_low; ///< Narrow band extension towards the object outside. + double b_up; ///< Narrow band extension towards the object inside. + + /** @brief Set the member variable NarrowBand::b_low and NarrowBand::b_up. + * + * @param thickness Thickness of narrow band in number of grid points. + * @param grid_in Input grid storing the signed distance function Phi_SDF (redistancing output). + */ + void set_bounds(size_t thickness, const grid_in_type & grid_in) + { + b_low = - ceil(thickness / 2.0) * get_biggest_spacing(grid_in); + b_up = ceil(thickness / 2.0) * get_biggest_spacing(grid_in); + } + /** @brief Set the member variable NarrowBand::b_low and NarrowBand::b_up. + * + * @param thickness Thickness of narrow band as physical width. + * @param grid_in Input grid storing the signed distance function Phi_SDF (redistancing output). + */ + void set_bounds(double thickness, const grid_in_type & grid_in) + { + b_low = - thickness / 2.0; + b_up = thickness / 2.0; + } + /** @brief Set the member variable NarrowBand::b_low and NarrowBand::b_up. + * + * @param lower_bound Extension of narrow band as physical width inside of object (Phi > 0). + * @param upper_bound Extension of narrow band as physical width outside of object (Phi < 0). + * @param grid_in Input grid storing the signed distance function Phi_SDF (redistancing output). + */ + void set_bounds(double lower_bound, double upper_bound, const grid_in_type & grid_in) + { + b_low = lower_bound; + b_up = upper_bound; + } + + /**@brief Initialize the internal temporary grid. + * + * @details Copies Phi_SDF from the input grid to the temorary grid. + * + * @tparam Phi_SDF Index of property storing the signed distance function in the input grid. + * @param grid_in Input grid storing the signed distance function Phi_SDF (redistancing output). + */ + template <size_t Phi_SDF> + void initialize_temporary_grid(const grid_in_type & grid_in) + { + copy_gridTogrid<Phi_SDF, Phi_SDF_temp>(grid_in, g_temp); // Copy Phi_SDF from the input grid to the temorary grid + init_sign_prop<Phi_SDF_temp, Phi_sign_temp>(g_temp); // initialize Phi_sign_temp with the sign of the + // input Phi_SDF + get_upwind_gradient<Phi_SDF_temp, Phi_sign_temp, Phi_grad_temp>(g_temp); // Get initial gradients + get_gradient_magnitude<Phi_grad_temp, Phi_magnOfGrad_temp>(g_temp); // Get initial magnitude of gradients + } + /**@brief Checks if a value for Phi_SDF lays within the narrow band. + * + * @param Phi Value of the signed distance function Phi_SDF. + * @return True, if point lays within the narrow band; False if not. + */ + bool within_narrow_band(double Phi) + { + return (Phi >= b_low && Phi <= b_up); + } + /** @brief Fills the empty vector passed as argument with particles by placing these within a narrow band around the + * interface. Only the SDF is copied from the grid properties to the respective particle. + * + * @tparam Phi_SDF_grid Index of property storing the signed distance function in the input grid. + * @tparam Phi_SDF_vd Index of property that should store the SDF in the narrow band particle vector. + * @tparam vector_type Inferred type of the particle vector. + * @tparam grid_type Inferred type of the grid storing the SDF. + * + * @param[in] grid Grid of arb. dims. storing the SDF (result of redistancing). + * @param[in] vd Empty vector with same spatial scaling (box) as the grid. + * @param[out] vd Particle vector containing the narrow-band-particles that store the SDF provided by the grid. + * + */ + template <size_t Phi_SDF_grid, size_t Phi_SDF_vd, typename grid_type, typename vector_type> + void get_narrow_band_phi_only(grid_type & grid, vector_type & vd) + { + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + auto key_g = grid.getGKey(key); + if(within_narrow_band(grid.template get<Phi_SDF_grid>(key))) + { + // add particle to vd_narrow_band + vd.add(); + // assign coordinates and properties from respective grid point to particle + for(size_t d = 0; d < grid_type::dims; d++) + { + vd.getLastPos()[d] = key_g.get(d) * grid.getSpacing()[d]; + vd.template getLastProp<Phi_SDF_vd>() = grid.template get<Phi_SDF_grid>(key); + } + } + ++dom; + } + } + /** @brief Fills the empty vector passed as argument with particles by placing these within a narrow band around the + * interface. SDF and Phi_grad_temp are copied from the temp. grid to the respective particle. + * + * @tparam Phi_SDF_grid Index of property storing the signed distance function in the input grid. + * @tparam Phi_SDF_vd Index of property that should store the SDF in the narrow band particle vector. + * @tparam Phi_grad Index of property that should store the gradient of phi in the narrow band particle vector. + * @tparam vector_type Inferred type of the particle vector. + * @tparam grid_type Inferred type of the grid storing the SDF. + * + * @param[in] grid Grid of arb. dims. storing the SDF (result of redistancing). + * @param[in] vd Empty vector with same spatial scaling (box) as the grid. + * @param[out] vd Particle vector containing the narrow-band-particles that store the SDF and Phi_grad provided + * by the grid. + */ + template <size_t Phi_SDF_grid, size_t Phi_SDF_vd, size_t Phi_grad, typename grid_type, typename vector_type> + void get_narrow_band_with_gradients(grid_type & grid, vector_type & vd) + { + initialize_temporary_grid<Phi_SDF_grid>(grid); + auto dom = g_temp.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + auto key_g = g_temp.getGKey(key); + if(within_narrow_band(g_temp.template get<Phi_SDF_temp>(key))) + { + // add particle to vd_narrow_band + vd.add(); + // assign coordinates and properties from respective grid point to particle + for(size_t d = 0; d < grid_type::dims; d++) + { + vd.getLastPos()[d] = key_g.get(d) * g_temp.getSpacing()[d]; + vd.template getLastProp<Phi_SDF_vd>() = g_temp.template get<Phi_SDF_temp>(key); + vd.template getLastProp<Phi_grad>()[d] = g_temp.template get<Phi_grad_temp>(key)[d]; + } + } + ++dom; + } + } + /** @brief Fills the empty vector passed as argument with particles by placing these within a narrow band around the + * interface. SDF, Phi_grad_temp and Phi_magnOfGrad_temp are copied from the temp. grid to the respective particle. + * + * @tparam Phi_SDF_grid Index of property storing the signed distance function in the input grid. + * @tparam Phi_SDF_vd Index of property that should store the SDF in the narrow band particle vector. + * @tparam Phi_grad Index of property that should store the gradient of phi in the narrow band particle vector. + * @tparam Phi_magnOfGrad Index of property that should store the gradient magnitude of phi in the narrow band particles. + * @tparam vector_type Inferred type of the particle vector. + * @tparam grid_type Inferred type of the grid storing the SDF. + * + * @param[in] grid Grid of arb. dims. storing the SDF (result of redistancing). + * @param[in] vd Empty vector with same spatial scaling (box) as the grid. + * @param[out] vd Particle vector containing the narrow-band-particles that store the SDF, Phi_grad and + * Phi_magnOfGrad provided by the grid. + */ + template <size_t Phi_SDF_grid, size_t Phi_SDF_vd, size_t Phi_grad, size_t Phi_magnOfGrad, typename grid_type, typename vector_type> + void get_narrow_band_with_gradients(grid_type & grid, vector_type & vd) + { + initialize_temporary_grid<Phi_SDF_grid>(grid); + auto dom = g_temp.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + auto key_g = g_temp.getGKey(key); + if(within_narrow_band(g_temp.template get<Phi_SDF_temp>(key))) + { + // add particle to vd_narrow_band + vd.add(); + // assign coordinates and properties from respective grid point to particle + for(size_t d = 0; d < grid_type::dims; d++) + { + vd.getLastPos()[d] = key_g.get(d) * g_temp.getSpacing()[d]; + vd.template getLastProp<Phi_SDF_vd>() = g_temp.template get<Phi_SDF_temp>(key); + vd.template getLastProp<Phi_grad>()[d] = g_temp.template get<Phi_grad_temp>(key)[d]; + vd.template getLastProp<Phi_magnOfGrad>() = g_temp.template get<Phi_magnOfGrad_temp>(key); + } + } + ++dom; + } + } + /** @brief Fills the empty vector passed as argument with particles by placing these within a narrow band around the + * interface. An arbitrary property is copied from the grid to the respective particle. + * + * @tparam Phi_SDF_grid Index of property storing the signed distance function in the input grid. + * @tparam Prop1_grid Index of arbitrary grid property that should be copied to the particles (prop. value must + * be a scalar). + * @tparam Prop1_vd Index of particle property, where grid property should be copied to (prop. value must be a + * scalar). + * @tparam vector_type Inferred type of the particle vector. + * @tparam grid_type Inferred type of the grid storing the SDF. + * + * @param[in] grid Grid of arb. dims. storing the SDF (result of redistancing). + * @param[in] vd Empty vector with same spatial scaling (box) as the grid. + * @param[out] vd Particle vector containing the narrow-band-particles that store an arbitrary property provided by + * the grid. + */ + template <size_t Phi_SDF_grid, size_t Prop1_grid, size_t Prop1_vd, typename grid_type, typename vector_type> + void get_narrow_band_one_prop(grid_type & grid, vector_type & vd) + { + auto dom = grid.getDomainIterator(); + while(dom.isNext()) + { + auto key = dom.get(); + auto key_g = grid.getGKey(key); + if(within_narrow_band(grid.template get<Phi_SDF_grid>(key))) + { + // add particle to vd_narrow_band + vd.add(); + // assign coordinates and properties from respective grid point to particle + for(size_t d = 0; d < grid_type::dims; d++) + { + vd.getLastPos()[d] = key_g.get(d) * grid.getSpacing()[d]; + vd.template getLastProp<Prop1_vd>() = grid.template get<Prop1_grid>(key); + } + } + ++dom; + } + } +}; + + +#endif //REDISTANCING_SUSSMAN_NARROWBAND_HPP diff --git a/src/level_set/redistancing_Sussman/RedistancingSussman.hpp b/src/level_set/redistancing_Sussman/RedistancingSussman.hpp new file mode 100644 index 0000000000000000000000000000000000000000..397e84013dcd6474477c1fbcf02522bee30159a5 --- /dev/null +++ b/src/level_set/redistancing_Sussman/RedistancingSussman.hpp @@ -0,0 +1,545 @@ +// +// Created by jstark on 2020-04-06. +// +/** + * @file RedistancingSussman.hpp + * @class RedistancingSussman + * + * @brief Class for reinitializing a level-set function into a signed distance function using Sussman redistancing. + * + * @details First-time-only redistancing step (see: <a href="https://www.researchgate + * .net/publication/2642502_An_Efficient_Interface_Preserving_Level_Set_Re + * -Distancing_Algorithm_And_Its_Application_To_Interfacial_Incompressible_Fluid_Flow.html">M. Sussman and E. Fatemi, + * “Efficient, interface-preserving level set redistancing algorithm and its application to interfacial + * incompressible fluid flow†(1999)</a> ). In the Sussman-redistancing, a level-set-function Phi_0, which can be a + * simple step-function having positive values inside the object and negative values outside, is reinitialized to a + * signed distance function Phi_SDF by finding the steady-state solution of the following PDE: + * @f[ \phi_{t} + sgn_{\epsilon}(\phi)(|\nabla\phi| - 1) = 0 @f] + * + * The signed distance function is defined as: + * + * @f[ \phi_{\text{SDF}} = \begin{cases} + * +d & \text{orthogonal distance to the closest surface of a point lying inside the object} \\ + * 0 & \text{object surface} \\ + * -d & \text{orthogonal distance to the closest surface of a point lying outside the object} \\ + * \end{cases} @f] + * + * @author Justina Stark + * @date April 2020 + */ + +#ifndef REDISTANCING_SUSSMAN_REDISTANCINGSUSSMAN_HPP +#define REDISTANCING_SUSSMAN_REDISTANCINGSUSSMAN_HPP + +// Include standard library header files +#include <iostream> +#include <string> +#include <fstream> +// Include OpenFPM header files +#include "Vector/vector_dist.hpp" +#include "Grid/grid_dist_id.hpp" +#include "data_type/aggregate.hpp" +#include "Decomposition/CartDecomposition.hpp" + +// Include self-written header files +#include "HelpFunctions.hpp" +#include "HelpFunctionsForGrid.hpp" +#include "GaussFilter.hpp" +#include "ComputeGradient.hpp" + +/** @brief Optional convergence criterium checking the total change. + * + * @details The change between the current and the previous iterations is computed as sum over all the If + * Conv_tol_change + * .check = true, the total change of Phi + * between the iterations is considered and the + * redistancing finishes when the Conv_tol_change.value (or the max-iteration) is reached. + */ +struct Conv_tol_change +{ + bool check = true; ///< If true, the total change of Phi (see DistFromSol::change) + ///< between the iterations is considered and the redistancing finishes when the Conv_tol_change.value (or the + ///< max-iteration) is reached. If false, the change is not considered as steady-state + ///< criterion. Default: true. + double value = 1e-6; ///< Double variable that stores the tolerance value for the change at which the iterative + ///< redistancing process is considered as steady-state. (see also DistFromSol::change) +}; + +/** @brief Optional convergence criterium checking the residual. + * + * @details If Conv_tol_residual.check = true, the residual, that is abs(magnitude gradient of phi - 1), is considered + * and the redistancing finishes when the Conv_tol_residual.value (or the max-iteration) is reached. + */ +struct Conv_tol_residual +{ + bool check = false; ///< If true, the residual of Phi (see DistFromSol::residual) is considered and the + ///< redistancing finishes when the Conv_tol_residual.value (or the + ///< max-iteration) is reached. If false, the change is not considered as steady-state criterion. Default: false. + double value = 1e-1; ///< Double variable that stores the tolerance value for the residual at which the + ///< iterative redistancing process is considered as steady-state. (see also + ///< DistFromSol::residual) +}; + +/** @brief Structure to bundle options for redistancing. + * @struct Redist_options + * @details For the redistancing, we can choose some options. These options will then be passed bundled as a structure to + * the redistancing function. Setting these options is optional, since they all have a Default value as well. + * + * @param sigma: Sigma of the gaussian kernel, which is used for gaussian smooting Phi_0. If the + * initial gradient of + * phi_0 at the interface is too large and no sigma is chosen or chosen too small, gauss smoothing will + * automatically be applied until phi gradient magnitude <= 12, regardless of which sigma is chosen by + * the user. Default = 0. + * @param min_iter: Minimum number of iterations before steady state in narrow band will be checked (Default: 100). + * @param max_iter: Maximum number of iterations you want to run the redistancing, even if steady state might not yet + * have been reached (Default: 1e6). + * @param convTolChange.value: Convolution tolerance for the normalized total change of Phi in the narrow band between + * two consecutive iterations (Default: 1e-6). + * @param convTolChange.check: Set true, if you want to use the normalized total change between two iterations as + * measure of how close you are to the steady state solution. Redistancing will then stop + * if convTolChange.value is reached or if the current iteration is bigger than max_iter. + * @param convTolResidual.value: Convolution tolerance for the residual, that is abs(magnitude gradient of phi - 1) of + * Phi in the narrow band (Default 1e-1). + * @param convTolResidual.check: Set true, if you want to use the residual of the current iteration as measure of how + * close you are to the steady state solution. Redistancing will then stop if + * convTolResidual.value is reached or if the current iteration is bigger than max_iter. + * @param interval_check_convergence: Interval of number of iterations at which convergence to steady state is checked + * (Default: 100). + * @param width_NB_in_grid_points: Width of narrow band in number of grid points. Must be at least 4, in order to + * have at least 2 grid points on each side of the interface. Is automatically set + * to 4, if a value smaller than 4 is chosen (Default: 4). + * @param print_current_iterChangeResidual: If true, the number of the current iteration, the corresponding change + * w.r.t the previous iteration and the residual is printed (Default: false). + * @param print_steadyState_iter: If true, the number of the steady-state-iteration, the corresponding change + * w.r.t the previous iteration and the residual is printed (Default: false). + */ +struct Redist_options +{ + size_t sigma = 0; // if you want to apply Gaussian smoothing prior to the redistancing. Smoothing is applied + // automatically if h_min <= 1e-6 (otherwise gradient at interface too big) + size_t min_iter = 1000; + size_t max_iter = 1e6; + + Conv_tol_change convTolChange; + Conv_tol_residual convTolResidual; + + size_t interval_check_convergence = 100; + size_t width_NB_in_grid_points = 4; + bool print_current_iterChangeResidual = false; + bool print_steadyState_iter = true; +}; + +/** @brief Bundles total residual and total change over all the grid points. + * + * @details In order to evaluate, how close the current solution is away from the convergence criterium. + * + * @see RedistancingSussman::get_residual_and_change_NB() + */ +struct DistFromSol +{ + double residual; ///< Double variable that contains the absolute value of how far the gradient magnitude of + ///< the current \a φ of iteration number \a i is away from being equal to 1: @f[ abs(|\nabla\phi_i| - 1 ) @f] + ///< It is computed for all grid points that lie within the narrow band and + ///< normalized to the number of grid points that lie in that narrow band. + double change; ///< Double variable that contains the absolute value of the change of \a φ between the + ///< current + ///< iteration \a i and the previous iteration \a i-1: @f[ abs( \phi_i - \phi_{i-1} ) @f] It is + ///< computed for all grid + ///< points that lie within the narrow band and normalized to the number of grid points that + ///< lie in that narrow band. +}; + + +/**@brief Class for reinitializing a level-set function into a signed distance function using Sussman redistancing. + * @file RedistancingSussman.hpp + * @class RedistancingSussman + * @tparam grid_in_type Inferred type of input grid, which stores the initial level-set function Phi_0. + */ +template <typename grid_in_type> +class RedistancingSussman +{ +public: + /** @brief Constructor initializing the redistancing options, the temporary internal grid and reference variable + * to the input grid. + * + * @param grid_in Input grid with min. 2 properties: 1.) Phi_0, 2.) Phi_SDF <- will be overwritten with + * re-distancing result + * @param redistOptions User defined options for the Sussman redistancing process + * + */ + RedistancingSussman(grid_in_type &grid_in, Redist_options &redistOptions) : redistOptions(redistOptions), + r_grid_in(grid_in), + g_temp(grid_in.getDecomposition(), + grid_in.getGridInfoVoid().getSize(), + Ghost<grid_in_type::dims, long int>( + 3)) + { + time_step = get_time_step_CFL(g_temp); + assure_minimal_thickness_of_NB(); + } + + /**@brief Aggregated properties for the temporary grid. + * + * @details The initial (input) Phi_0 (will be updated by Phi_{n+1} after each redistancing step), + * Phi_{n+1} (received from redistancing), + * gradient of Phi_{n+1}, + * L2 norm of gradient of Phi_{n+1} (=gradient magnitude), + * sign of the original input Phi_0 (for the upwinding). + */ + typedef aggregate<double, double, double[grid_in_type::dims], double, int> props_temp; + /** @brief Type definition for the temporary grid. + */ + typedef grid_dist_id<grid_in_type::dims, typename grid_in_type::stype, props_temp> g_temp_type; + /** + * @brief Create temporary grid, which is only used inside the class for the redistancing. + * + * @details The temporary grid stores the following 5 properties: + * the initial (input) Phi_0 (will be updated by Phi_{n+1} after each redistancing step), + * Phi_{n+1}(received from redistancing), + * gradient of Phi_{n+1}, + * L2 norm of gradient of Phi_{n+1} (=gradient magnitude), + * sign of the original input Phi_0 (for the upwinding). + */ + g_temp_type g_temp; + + /**@brief Runs the Sussman-redistancing. + * + * @details Copies Phi_0 from input grid to an internal temporary grid which allows + * having more properties. Computes the gradients. Applies gauss smoothing if sigma set in redistOptions or if + * initial gradients to steep. Runs the redistancing on the internal tenporary grid. Copies resulting signed + * distance function to the Phi_SDF_out property of the input grid. + */ + template<size_t Phi_0_in, size_t Phi_SDF_out> void run_redistancing() + { + init_temp_grid<Phi_0_in>(); + if (redistOptions.sigma > 0) + { + run_gauss_filter(redistOptions.sigma); + } + init_sign_prop<Phi_0_temp, Phi_0_sign_temp>( + g_temp); // initialize Phi_0_sign_temp with the sign of the initial (pre-redistancing) Phi_0 + get_upwind_gradient<Phi_0_temp, Phi_0_sign_temp, Phi_grad_temp>(g_temp); // Get initial gradients + get_gradient_magnitude<Phi_grad_temp, Phi_magnOfGrad_temp>(g_temp); // Get initial magnitude of gradients + + smoothing_if_grad_steep(); + + iterative_redistancing( + g_temp); // Do the redistancing on the temporary grid + copy_gridTogrid<Phi_nplus1_temp, Phi_SDF_out>(g_temp, r_grid_in); // Copy resulting SDF function to + // input grid + } + + /** @brief Overwrite the time_step found via CFL condition with an individual time_step. + * + * @details If the user wants to overwrite the time_step found via CFL condition with an individual time_step. + * Should be only used carefully, time_step must not be too large (jump over solution) nor too small (extremely + * slow). + * + * @param dt Artificial time step by which re-distancing should be performed. + * + */ + template<typename T> + void set_user_time_step(T dt) + { + time_step = dt; + } + /** @brief Access the artificial timestep (private member) which will be used for the iterative redistancing. + * @see get_time_step_CFL(g_temp_type &grid), set_user_time_step() + */ + double get_time_step() + { + /// This timestep is computed according to the grid spacing fulfilling the CFL condition. + return time_step; + } + +private: + // Some indices for better readability + static constexpr size_t Phi_0_temp = 0; ///< Property index of Phi_0 on the temporary grid. + static constexpr size_t Phi_nplus1_temp = 1; ///< Property index of Phi_n+1 on the temporary grid. + static constexpr size_t Phi_grad_temp = 2; ///< Property index of gradient of Phi_n on the temporary grid. + static constexpr size_t Phi_magnOfGrad_temp = 3; ///< Property index of gradient magn. of Phi_n (temporary grid). + static constexpr size_t Phi_0_sign_temp = 4; ///< Property index of sign of initial (input) Phi_0 (temp. grid). + + + // Member variables + Redist_options redistOptions; ///< Instantiate redistancing options. + grid_in_type &r_grid_in; ///< Define reference to input grid. + + double h_max = get_biggest_spacing(g_temp); ///< Grid spacing in less resolved direction. + double h_min = get_smallest_spacing(g_temp); ///< Grid spacing in higher resolved direction. + /// Transform the half-bandwidth in no_of_grid_points into physical half-bandwidth kappa. + double kappa = ceil(redistOptions.width_NB_in_grid_points / 2.0) * h_max; + /**@brief Artificial timestep for the redistancing iterations. + * @see get_time_step_CFL(g_temp_type &grid), get_time_step(), set_user_time_step() + */ + double time_step; + + // Member functions + /** @brief Copies values from input grid to internal temporary grid and initializes ghost layer with minimum + * value of input grid. + */ + template<size_t Phi_0_in> + void init_temp_grid() + { + double min_value = get_min_val<Phi_0_in>(r_grid_in); // get minimum Phi_0 value on the input grid + init_grid_and_ghost<Phi_0_temp>(g_temp, min_value); // init. Phi_0_temp (incl. ghost) with min. Phi_0 + init_grid_and_ghost<Phi_nplus1_temp>(g_temp, min_value); // init. Phi_nplus1_temp (incl. ghost) with min. Phi_0 + copy_gridTogrid<Phi_0_in, Phi_0_temp>(r_grid_in, g_temp); // Copy Phi_0 from the input grid to Phi_0_temp + } + + /** @brief Checks if narrow band thickness >= 4 grid points. Else, sets it to 4 grid points. + * + * @details Makes sure, that the narrow band within which the convergence criteria are checked during the + * redistancing, is thick enough. + */ + void assure_minimal_thickness_of_NB() + { + if (redistOptions.width_NB_in_grid_points < 4) + { + redistOptions.width_NB_in_grid_points = 4; + } // overwrite kappa if set too small by user + } + + /** @brief Convolves with a Gauss kernel if initial gradient at the interface too steep. + */ + void smoothing_if_grad_steep() + { + double max_grad = get_max_val<Phi_magnOfGrad_temp>(g_temp); + while (max_grad > 12) + { + run_gauss_filter(1); + // Update gradient + get_upwind_gradient<Phi_0_temp, Phi_0_sign_temp, Phi_grad_temp>(g_temp); + get_gradient_magnitude<Phi_grad_temp, Phi_magnOfGrad_temp>(g_temp); + // Update steepest gradient + max_grad = get_max_val<Phi_magnOfGrad_temp>(g_temp); + } + } + /** @brief Finds number of iterations a gauss kernel of sigma=1 has to be applied for getting an equivalent to + * convolution with a sigma=input sigma. + * + * @param sigma Sigma of gauss kernel with which grid should be smoothed prior to re-distancing. + * + * @return Number of iterations required with gauss of sigma = 1. + * + */ + inline size_t count_gauss_repitition(const size_t sigma) + { + return sigma * sigma; + } + + /** @brief Convolves the grid with a gauss filter. + * + * @param sigma Sigma of gauss kernel with which grid should be smoothed prior to re-distancing. + */ + void run_gauss_filter(const size_t sigma) + { + size_t n = count_gauss_repitition(sigma); + for (size_t i = 0; i < n; i++) + { + gauss_filter<Phi_0_temp, Phi_nplus1_temp>(g_temp); + copy_gridTogrid<Phi_nplus1_temp, Phi_0_temp>(g_temp, g_temp); + } + } + + /** @brief Run one timestep of re-distancing and compute Phi_n+1. + * + * @param phi_n Phi value on current node and current time. + * @param phi_n_magnOfGrad Gradient magnitude of current Phi from upwinding FD. + * @param dt Time step. + * @param sign_phi_n Sign of the current Phi, should be the smooth sign. + * + * @return Phi_n+1 which is the Phi of the next time step on current node. + * + */ + double get_phi_nplus1(double phi_n, double phi_n_magnOfGrad, double dt, double sign_phi_n) + { + double step = dt * sign_phi_n * (1 - phi_n_magnOfGrad); // <- original Sussman + if (step > 10) + { + std::cout << "phi_n_magnOfGrad = " << phi_n_magnOfGrad << ", step = " << step + << ", skip to prevent exploding peaks." << std::endl; + step = 0; + } + return phi_n + step; + } + + /** @brief Go one re-distancing time-step on the whole grid. + * + * @param grid Internal temporary grid. + */ + void go_one_redistancing_step_whole_grid(g_temp_type &grid) + { + grid.template ghost_get<Phi_0_temp, Phi_nplus1_temp, Phi_grad_temp, Phi_magnOfGrad_temp>(); + double spacing_x = grid.getSpacing()[0]; + auto dom = grid.getDomainIterator(); + while (dom.isNext()) + { + auto key = dom.get(); + const double phi_n = grid.template get<Phi_0_temp>(key); + const double phi_n_magnOfGrad = grid.template get<Phi_magnOfGrad_temp>(key); + double epsilon = phi_n_magnOfGrad * spacing_x; + grid.template get<Phi_nplus1_temp>(key) = get_phi_nplus1(phi_n, phi_n_magnOfGrad, time_step, + smooth_S(phi_n, epsilon)); + ++dom; + } + } + + /** @brief Updates Phi_n with the new Phi_n+1 and recomputes the gradients. + * + * @param grid Internal temporary grid. + */ + void update_grid(g_temp_type &grid) + { + copy_gridTogrid<Phi_nplus1_temp, Phi_0_temp>(grid, grid); // Update Phi_0 + get_upwind_gradient<Phi_0_temp, Phi_0_sign_temp, Phi_grad_temp>(grid); + get_gradient_magnitude<Phi_grad_temp, Phi_magnOfGrad_temp>(grid); + } + + /** @brief Checks if a node lays within the narrow band around the interface. + * + * @param Phi Value of Phi at that specific node. + * + * @return True, if node lays within nb., false, if the distance to the interface is > kappa. + */ + bool lays_inside_NB(double Phi) + { + return (abs(Phi) <= kappa); + } + + /** @brief Checks how far current solution is from fulfilling the user-defined convergence criteria. + * + * @param grid Internal temporary grid. + * + * @return Total residual (1 - phi_gradient_magnitude) and total change from the last time step, + * both normalized by the number of grid nodes in the narrow band. + */ + DistFromSol get_residual_and_change_NB(g_temp_type &grid) + { + double total_residual = 0; + double total_change = 0; + double total_points_in_nb = 0; + auto dom = grid.getDomainIterator(); + while (dom.isNext()) + { + auto key = dom.get(); + if (lays_inside_NB(grid.template get<Phi_nplus1_temp>(key))) + { + total_points_in_nb += 1.0; + total_residual += abs(grid.template get<Phi_magnOfGrad_temp>(key) - 1); + total_change += abs(grid.template get<Phi_nplus1_temp>(key) - grid.template get<Phi_0_temp>(key)); + } + ++dom; + } + auto &v_cl = create_vcluster(); + v_cl.sum(total_points_in_nb); + v_cl.sum(total_residual); + v_cl.sum(total_change); + v_cl.execute(); + return {total_residual / total_points_in_nb, total_change / total_points_in_nb}; + } + + /** @brief Prints out the iteration number, residual and change of the current re-distancing iteration + * + * @param grid Internal temporary grid. + * @param iter Current re-distancing iteration. + * + * @return Total residual (1 - phi_gradient_magnitude) and total change from the last time step, + * both normalized by the number of grid nodes in the narrow band. + */ + void print_out_iteration_change_residual(g_temp_type &grid, size_t iter) + { + DistFromSol distFromSol = get_residual_and_change_NB(grid); + auto &v_cl = create_vcluster(); + if (v_cl.rank() == 0) + { + if (iter == 0) + { + std::cout << "Iteration,Change,Residual" << std::endl; + } + std::cout << iter << "," << distFromSol.change << "," << distFromSol.residual << std::endl; + } + } + + /** @brief Checks steady-state is reached in the narrow band. + * + * @details Checks if change and/or residual between 2 iterations smaller than user-defined convolution tolerance. + * + * @param grid Internal temporary grid. + * + * @return True, if steady-state reached, else false. + * + * @see Conv_tol_change, Conv_tol_residual + */ + bool steady_state_NB(g_temp_type &grid) + { + bool steady_state = false; + DistFromSol distFromSol = get_residual_and_change_NB(grid); + if (redistOptions.convTolChange.check && redistOptions.convTolResidual.check) + { + steady_state = (distFromSol.change <= redistOptions.convTolChange.value && + distFromSol.residual <= redistOptions.convTolResidual.value); + } + else + { + if (redistOptions.convTolChange.check) + { + steady_state = (distFromSol.change <= redistOptions.convTolChange.value); + } // Use the normalized total change between two iterations in the narrow bands steady-state criterion + if (redistOptions.convTolResidual.check) + { + steady_state = (distFromSol.residual <= redistOptions.convTolResidual.value); + } // Use the normalized total residual of phi compared to SDF in the narrow bands steady-state criterion + } + return steady_state; + } + + /** @brief Runs Sussman re-distancing on the internal temporary grid. + * + * @details The number of iterations is minimum redistOptions.min_iter iterations and finishes when either + * steady-state or redist_options.max_iter is reached. The steady-state convergence accuracy depends on the + * user defined redist_options.convTolChange.value and redist_options.convTolResidual.value, respectively. + * + * @param grid Internal temporary grid. + * + */ + void iterative_redistancing(g_temp_type &grid) + { + for (size_t i = 0; i <= redistOptions.max_iter; i++) + { + go_one_redistancing_step_whole_grid(grid); + + if (redistOptions.print_current_iterChangeResidual) + { + print_out_iteration_change_residual(grid, i); + } + + if (i >= redistOptions.min_iter) + { + if (i % redistOptions.interval_check_convergence == + 0) // after some iterations check if steady state is reached in the narrow band + { + if (steady_state_NB(grid)) + { + if (redistOptions.print_steadyState_iter) + { + auto &v_cl = create_vcluster(); + if (v_cl.rank() == 0) + { + std::cout << "Steady state reached at iteration: " << i << std::endl; + std::cout << "Final_Iteration,Change,Residual" << std::endl; + } + print_out_iteration_change_residual(grid, i); + } + update_grid(grid); // Update Phi + break; + } + auto &v_cl = create_vcluster(); + } + } + update_grid(grid); + } + } +}; + +#endif //REDISTANCING_SUSSMAN_REDISTANCINGSUSSMAN_HPP