OpenFPM  5.2.0
Project that contain the implementation of distributed structures
closest_point_unit_tests.cpp
1 /* Unit tests for the closest point methods
2  * Date : 29 May 2021
3  * Author : sachin
4  */
5 
6 #include<iostream>
7 #include <boost/test/unit_test_log.hpp>
8 #include <cmath>
9 #define BOOST_TEST_DYN_LINK
10 #include <boost/test/unit_test.hpp>
11 #include <iostream>
12 #include "Grid/grid_dist_id.hpp"
13 #include "data_type/aggregate.hpp"
14 #include "VCluster/VCluster.hpp"
15 #include "Vector/Vector.hpp"
16 #include "FiniteDifference/util/common.hpp"
17 #include "FiniteDifference/eq.hpp"
18 #include "FiniteDifference/FD_op.hpp"
19 #include "closest_point.hpp"
20 
21 constexpr int narrow_band_half_width = 5;
22 
23 typedef struct EllipseParameters{
24  double origin[3];
25  double radiusA;
26  double radiusB;
27  double radiusC;
28  double eccentricity;
30 
31 // Generate an ellipsoid initial levelset signed distance function
32 template<size_t phi_field, typename grid_type>
33 void initializeLSEllipsoid(grid_type &gd, const EllipseParams &params)
34 {
35  auto it = gd.getDomainIterator();
36  while(it.isNext())
37  {
38  auto key = it.get();
39  Point<grid_type::dims, double> coords = gd.getPos(key);
40  double posx = coords.get(0);
41  double posy = coords.get(1);
42  double posz = coords.get(2);
43 
44  // NOTE: Except for a sphere, this is not the SDF. It is just an implicit function whose zero contour is an ellipsoid.
45  double phi_val = 1.0 - sqrt(((posx - params.origin[0])/params.radiusA)*((posx - params.origin[0])/params.radiusA) + ((posy - params.origin[1])/params.radiusB)*((posy - params.origin[1])/params.radiusB) + ((posz - params.origin[2])/params.radiusC)*((posz - params.origin[2])/params.radiusC));
46  gd.template get<phi_field>(key) = phi_val;
47  ++it;
48  }
49 }
50 
51 // Initialize a scalar field or grid points near the interface
52 template<const unsigned int phi, const unsigned int field, typename grid_type>
53 void initializeScalarField3D(grid_type &gd, double init_width)
54 {
55  auto it = gd.getDomainIterator();
56 
57  // Trying with a L_1 and L_2 spherical harmonics as initial condition for scalar_field
58  double prefactor_l1 = std::sqrt(2.0/(4.0*M_PI));
59  //double prefactor_l2 = std::sqrt(5.0/(16.0*M_PI));
60 
61  while(it.isNext())
62  {
63  auto key = it.get();
64  if(gd.template get<phi>(key) < init_width)
65  {
66  auto coords = gd.getPos(key);
67  double posx = coords.get(0);
68  double posy = coords.get(1);
69  double posz = coords.get(2);
70 
71  double theta = std::atan2(std::sqrt(posx*posx + posy*posy), posz);
72 
73  gd.template get<field>(key) = prefactor_l1 * std::cos(theta);
74  //gd.template get<field>(key) = prefactor_l2 * (3.0 * std::cos(theta) * std::cos(theta) - 1.0);
75  }
76  ++it;
77  }
78 
79 }
80 
81 BOOST_AUTO_TEST_SUITE( closest_point_test )
82 
83 
84 BOOST_AUTO_TEST_CASE( closest_point_unit_sphere )
85 {
86 
87  constexpr int SIM_DIM = 3; //> Spatial dimension
88  constexpr int POLY_ORDER = 5; //> Order of the polynomial for stencil interpolation
89  constexpr int SIM_GRID_SIZE = 128; //> Size of the simulation grid along each dimension
90 
91  // Properties - phi, cp
93  using GridKey = grid_dist_key_dx<SIM_DIM>;
94 
95  // Grid size on each dimension
96  const long int sz[SIM_DIM] = {SIM_GRID_SIZE, SIM_GRID_SIZE, SIM_GRID_SIZE};
97  const size_t szu[SIM_DIM] = {(size_t) sz[0], (size_t) sz[1], (size_t) sz[2]};
98 
99  // 3D physical domain
100  Box<SIM_DIM,double> domain({-1.5,-1.5,-1.5},{1.5,1.5,1.5});
101 
102  constexpr int x = 0;
103  constexpr int y = 1;
104  constexpr int z = 2;
105 
106  // Alias for properties on the grid
107  constexpr int phi = 0;
108  constexpr int cp = 1;
109 
110  double nb_gamma = 0.0;
111 
112  periodicity<SIM_DIM> grid_bc = {NON_PERIODIC, NON_PERIODIC, NON_PERIODIC};
113  // Ghost in grid units.
114  Ghost <SIM_DIM, long int> grid_ghost(2*narrow_band_half_width);
115  GridDist gdist(szu, domain, grid_ghost, grid_bc);
116 
117  // Parameters for initializing Ellipsoid level set function
118  EllipseParams params;
119  params.origin[x] = 0.0;
120  params.origin[y] = 0.0;
121  params.origin[z] = 0.0;
122  params.radiusA = 1.0;
123  params.radiusB = 1.0;
124  params.radiusC = 1.0;
125 
126  // Width of the narrowband on one side of the interface
127  nb_gamma = narrow_band_half_width * gdist.spacing(0);
128 
129  // Initializes the grid property 'phi' whose zero contour represents the ellipsoid
130  initializeLSEllipsoid<phi>(gdist, params);
131 
132  // Updates the property 'cp' of the grid to the closest point coords (only done in the narrowband).
133  estimateClosestPoint<phi, cp, POLY_ORDER>(gdist, nb_gamma);
134 
135  // Estimate error in closest point estimation
136  auto &patches = gdist.getLocalGridsInfo();
137  double max_error_cp = -1.0;
138  for(int i = 0; i < patches.size();i++)
139  {
140  // The coordinates of the two bounding points of grid patches.
141  auto p_xlo = patches.get(i).Dbox.getLow(0) + patches.get(i).origin[0];
142  auto p_xhi = patches.get(i).Dbox.getHigh(0) + patches.get(i).origin[0];
143  auto p_ylo = patches.get(i).Dbox.getLow(1) + patches.get(i).origin[1];
144  auto p_yhi = patches.get(i).Dbox.getHigh(1) + patches.get(i).origin[1];
145  auto p_zlo = patches.get(i).Dbox.getLow(2) + patches.get(i).origin[2];
146  auto p_zhi = patches.get(i).Dbox.getHigh(2) + patches.get(i).origin[2];
147 
148  auto it = gdist.getSubDomainIterator({p_xlo, p_ylo, p_zlo}, {p_xhi, p_yhi, p_zhi});
149  while(it.isNext())
150  {
151  auto key = it.get();
152 
153  if(std::abs(gdist.template get<phi>(key)) < nb_gamma)
154  {
155  // Computed closest point coordinates.
156  // Note: This is patch coordinates not the real one.
157  double cpx = gdist.template get<cp>(key)[x];
158  double cpy = gdist.template get<cp>(key)[y];
159  double cpz = gdist.template get<cp>(key)[z];
160 
161  if(cpx == -100.0 && cpy == -100.0 && cpz == -100.0)
162  std::cout<<"ERROR: Requesting closest point where it was not computed."<<std::endl;
163 
164  // Convert patch coords to real coordinates.
165  double estim_px = domain.getLow(x) + (p_xlo - algoim_padding)*gdist.spacing(x) + cpx;
166  double estim_py = domain.getLow(y) + (p_ylo - algoim_padding)*gdist.spacing(y) + cpy;
167  double estim_pz = domain.getLow(z) + (p_zlo - algoim_padding)*gdist.spacing(z) + cpz;
168 
169  // Global coordinate of the selected grid point.
170  Point<GridDist::dims, double> coords = gdist.getPos(key);
171  double posx = coords.get(0);
172  double posy = coords.get(1);
173  double posz = coords.get(2);
174 
175  double norm = sqrt(posx*posx + posy*posy + posz*posz);
176  // Analytically known closest point coordinate for unit sphere.
177  double exact_px = posx / norm;
178  double exact_py = posy / norm;
179  double exact_pz = posz / norm;
180 
181  max_error_cp = std::max({std::abs(estim_px - exact_px), std::abs(estim_py - exact_py), std::abs(estim_pz - exact_pz), max_error_cp});
182  }
183  ++it;
184  }
185  }
186  std::cout<<"Unit sphere closest_point error : "<<max_error_cp<<std::endl;
187  double tolerance = 1e-5;
188  bool check;
189  if (std::abs(max_error_cp) < tolerance)
190  check = true;
191  else
192  check = false;
193 
194  BOOST_TEST( check );
195 
196 }
197 
198 BOOST_AUTO_TEST_CASE( reinitialization_unit_sphere )
199 {
200 
201  constexpr int SIM_DIM = 3;
202  constexpr int POLY_ORDER = 5;
203  constexpr int SIM_GRID_SIZE = 128;
204 
205  // Fields - phi, cp
207  using GridKey = grid_dist_key_dx<SIM_DIM>;
208 
209  // Grid size on each dimension
210  const long int sz[SIM_DIM] = {SIM_GRID_SIZE, SIM_GRID_SIZE, SIM_GRID_SIZE};
211  const size_t szu[SIM_DIM] = {(size_t) sz[0], (size_t) sz[1], (size_t) sz[2]};
212 
213  // 3D physical domain
214  Box<SIM_DIM,double> domain({-1.5,-1.5,-1.5},{1.5,1.5,1.5});
215 
216  constexpr int x = 0;
217  constexpr int y = 1;
218  constexpr int z = 2;
219 
220  // Alias for properties on the grid
221  constexpr int phi = 0;
222  constexpr int cp = 1;
223 
224  double nb_gamma = 0.0;
225 
226  periodicity<SIM_DIM> grid_bc = {NON_PERIODIC, NON_PERIODIC, NON_PERIODIC};
227  // Ghost in grid units
228  Ghost <SIM_DIM, long int> grid_ghost(2*narrow_band_half_width);
229  GridDist gdist(szu, domain, grid_ghost, grid_bc);
230 
231  EllipseParams params;
232  params.origin[x] = 0.0;
233  params.origin[y] = 0.0;
234  params.origin[z] = 0.0;
235  params.radiusA = 1.0;
236  params.radiusB = 1.0;
237  params.radiusC = 1.0;
238 
239  nb_gamma = narrow_band_half_width * gdist.spacing(0);
240 
241  initializeLSEllipsoid<phi>(gdist, params);
242 
243  estimateClosestPoint<phi, cp, POLY_ORDER>(gdist, nb_gamma);
244 
245  // Reinitialize the level set function stored in property 'phi' based on closest points in 'cp'
246  reinitializeLS<phi, cp>(gdist, nb_gamma);
247 
248  // Estimate error in closest point estimation
249  auto &patches = gdist.getLocalGridsInfo();
250  double max_error = -1.0;
251  for(int i = 0; i < patches.size();i++)
252  {
253  auto p_xlo = patches.get(i).Dbox.getLow(0) + patches.get(i).origin[0];
254  auto p_xhi = patches.get(i).Dbox.getHigh(0) + patches.get(i).origin[0];
255  auto p_ylo = patches.get(i).Dbox.getLow(1) + patches.get(i).origin[1];
256  auto p_yhi = patches.get(i).Dbox.getHigh(1) + patches.get(i).origin[1];
257  auto p_zlo = patches.get(i).Dbox.getLow(2) + patches.get(i).origin[2];
258  auto p_zhi = patches.get(i).Dbox.getHigh(2) + patches.get(i).origin[2];
259 
260  auto it = gdist.getSubDomainIterator({p_xlo, p_ylo, p_zlo}, {p_xhi, p_yhi, p_zhi});
261  while(it.isNext())
262  {
263  auto key = it.get();
264 
265  if(std::abs(gdist.template get<phi>(key)) < nb_gamma)
266  {
267  // Global grid coordinate
268  Point<GridDist::dims, double> coords = gdist.getPos(key);
269  double posx = coords.get(0);
270  double posy = coords.get(1);
271  double posz = coords.get(2);
272 
273  // Analytically computed signed distance
274  // NOTE: SDF convention here is positive inside and negative outside the sphere
275  double distance = 1.0 - sqrt(posx*posx + posy*posy + posz*posz);
276 
277  max_error = std::max({std::abs(distance - gdist.template get<phi>(key)), max_error});
278  }
279  ++it;
280  }
281  }
282  std::cout<<"Reinitialization error : "<<max_error<<std::endl;
283  double tolerance = 1e-5;
284  bool check;
285  if (std::abs(max_error) < tolerance)
286  check = true;
287  else
288  check = false;
289 
290  BOOST_TEST( check );
291 
292 }
293 
294 
295 BOOST_AUTO_TEST_CASE( extension_unit_sphere )
296 {
297 
298  constexpr int SIM_DIM = 3;
299  constexpr int POLY_ORDER = 5;
300  constexpr int SIM_GRID_SIZE = 128;
301 
302  // Fields - phi, cp, scalar_field, scalar_field_temp
304  using GridKey = grid_dist_key_dx<SIM_DIM>;
305 
306  // Grid size on each dimension
307  const long int sz[SIM_DIM] = {SIM_GRID_SIZE, SIM_GRID_SIZE, SIM_GRID_SIZE};
308  const size_t szu[SIM_DIM] = {(size_t) sz[0], (size_t) sz[1], (size_t) sz[2]};
309 
310  // 3D physical domain
311  Box<SIM_DIM,double> domain({-1.5,-1.5,-1.5},{1.5,1.5,1.5});
312 
313  constexpr int x = 0;
314  constexpr int y = 1;
315  constexpr int z = 2;
316 
317  // Alias for properties on the grid
318  constexpr int phi = 0;
319  constexpr int cp = 1;
320  constexpr int scalar_field = 2;
321  constexpr int scalar_field_temp = 3;
322 
323  double nb_gamma = 0.0;
324 
325  periodicity<SIM_DIM> grid_bc = {NON_PERIODIC, NON_PERIODIC, NON_PERIODIC};
326  // Ghost in grid units
327  Ghost <SIM_DIM, long int> grid_ghost(2*narrow_band_half_width);
328  GridDist gdist(szu, domain, grid_ghost, grid_bc);
329 
330  EllipseParams params;
331  params.origin[x] = 0.0;
332  params.origin[y] = 0.0;
333  params.origin[z] = 0.0;
334  params.radiusA = 1.0;
335  params.radiusB = 1.0;
336  params.radiusC = 1.0;
337 
338  nb_gamma = narrow_band_half_width * gdist.spacing(0);
339 
340  initializeLSEllipsoid<phi>(gdist, params);
341 
342  estimateClosestPoint<phi, cp, POLY_ORDER>(gdist, nb_gamma);
343 
344  // Reinitialize the level set function stored in property 'phi' based on closest points in 'cp'
345  reinitializeLS<phi, cp>(gdist, nb_gamma);
346 
347  // Initialize a scalar field close to interface
348  initializeScalarField3D<phi,scalar_field>(gdist, 4*gdist.spacing(0));
349 
350  // Extension to the full narrow band
351  extendLSField<phi, cp, scalar_field, scalar_field_temp, -1>(gdist, nb_gamma);
352  double prefactor_l1 = std::sqrt(2.0/(4.0*M_PI));
353 
354  // Estimate error in closest point estimation
355  auto &patches = gdist.getLocalGridsInfo();
356  double max_error = -1.0;
357  for(int i = 0; i < patches.size();i++)
358  {
359  auto p_xlo = patches.get(i).Dbox.getLow(0) + patches.get(i).origin[0];
360  auto p_xhi = patches.get(i).Dbox.getHigh(0) + patches.get(i).origin[0];
361  auto p_ylo = patches.get(i).Dbox.getLow(1) + patches.get(i).origin[1];
362  auto p_yhi = patches.get(i).Dbox.getHigh(1) + patches.get(i).origin[1];
363  auto p_zlo = patches.get(i).Dbox.getLow(2) + patches.get(i).origin[2];
364  auto p_zhi = patches.get(i).Dbox.getHigh(2) + patches.get(i).origin[2];
365 
366  auto it = gdist.getSubDomainIterator({p_xlo, p_ylo, p_zlo}, {p_xhi, p_yhi, p_zhi});
367  while(it.isNext())
368  {
369  auto key = it.get();
370 
371  if(std::abs(gdist.template get<phi>(key)) < nb_gamma)
372  {
373  // Global grid coordinate
374  auto coords = gdist.getPos(key);
375  double posx = coords.get(0);
376  double posy = coords.get(1);
377  double posz = coords.get(2);
378 
379  double theta = std::atan2(std::sqrt(posx*posx + posy*posy), posz);
380  // Analytically computed signed distance
381  // NOTE: SDF convention here is positive inside and negative outside the sphere
382  double exact_val = prefactor_l1 * std::cos(theta);
383 
384  max_error = std::max({std::abs(exact_val - gdist.template get<scalar_field>(key)), max_error});
385  }
386  ++it;
387  }
388  }
389  std::cout<<"Extension error : "<<max_error<<std::endl;
390  double tolerance = 1e-5;
391  bool check;
392  if (std::abs(max_error) < tolerance)
393  check = true;
394  else
395  check = false;
396 
397  BOOST_TEST( check );
398 
399 }
400 
401 BOOST_AUTO_TEST_SUITE_END()
This class represent an N-dimensional box.
Definition: Box.hpp:60
Definition: Ghost.hpp:40
This class implement the point shape in an N-dimensional space.
Definition: Point.hpp:28
__device__ __host__ const T & get(unsigned int i) const
Get coordinate.
Definition: Point.hpp:172
This is a distributed grid.
Point< dim, St > getPos(const grid_dist_key_dx< dim, bg_key > &v1)
Get the reference of the selected element.
grid_dist_iterator< dim, device_grid, decltype(device_grid::type_of_subiterator()), FREE > getDomainIterator() const
It return an iterator that span the full grid domain (each processor span its local domain)
Grid key for a distributed grid.
Functions for level set reinitialization and extension on OpenFPM grids based on closest point method...
void extendLSField(grid_type &gd, const double nb_gamma)
Extends a (scalar) field to within nb_gamma from interface. The grid should have level set SDF and cl...
Boundary conditions.
Definition: common.hpp:22