Skip to content
Snippets Groups Projects
Commit 8179cc80 authored by sachin_krishnan_tv's avatar sachin_krishnan_tv
Browse files

Merge branch 'closest-point' into develop

parents 2fe3ef4b 4ed7de0c
Branches
1 merge request!12Closest point
......@@ -19,6 +19,8 @@ set(SUITESPARSE_ROOT CACHE PATH "The suitesparse root path")
set(TINYOBJLOADER_ROOT CACHE PATH "TinyObjLoader library path")
set(SE_CLASS1 CACHE BOOL "Activate compilation with SE_CLASS1")
set(SE_CLASS3 CACHE BOOL "Activate compilation with SE_CLASS3")
set(BLITZ_ROOT CACHE PATH "Blitz root directory")
set(ALGOIM_ROOT CACHE PATH "Algoim root directory")
set(PROFILE_WITH_SCOREP CACHE BOOL "Enable profiling with scorep")
set(ENV{PETSC_DIR} ${PETSC_ROOT})
set(ENV{HDF5_ROOT} ${HDF5_ROOT})
......
......@@ -41,9 +41,11 @@ add_executable(numerics ${OPENFPM_INIT_FILE} ${CUDA_SOURCES}
FiniteDifference/eq_unit_test.cpp
Operators/Vector/vector_dist_operators_unit_tests.cpp
Operators/Vector/vector_dist_operators_apply_kernel_unit_tests.cpp
../../src/lib/pdata.cpp)
../../src/lib/pdata.cpp
# level_set/redistancing_Sussman/tests/redistancingSussman_unit_test.cpp
# level_set/redistancing_Sussman/tests/convergence_test.cpp)
# level_set/redistancing_Sussman/tests/convergence_test.cpp
level_set/closest_point/closest_point_unit_tests.cpp)
add_dependencies(numerics ofpmmemory)
add_dependencies(numerics vcluster)
......@@ -84,6 +86,8 @@ target_include_directories (numerics PUBLIC ${HDF5_ROOT}/include)
target_include_directories (numerics PUBLIC ${LIBHILBERT_INCLUDE_DIRS})
target_include_directories (numerics PUBLIC ${Boost_INCLUDE_DIRS})
target_include_directories (numerics PUBLIC ${Vc_INCLUDE_DIR})
target_include_directories (numerics PUBLIC ${BLITZ_ROOT}/include)
target_include_directories (numerics PUBLIC ${ALGOIM_ROOT}/include)
target_include_directories (numerics PUBLIC ${ALPAKA_ROOT}/include)
target_include_directories (numerics PUBLIC ${MPI_C_INCLUDE_DIRS})
if(EIGEN3_FOUND)
......@@ -257,6 +261,10 @@ install(FILES DMatrix/EMatrix.hpp
DESTINATION openfpm_numerics/include/DMatrix
COMPONENT OpenFPM)
install(FILES level_set/closest_point/closest_point.hpp
DESTINATION openfpm_numerics/include/level_set/closest_point
COMPONENT OpenFPM)
#if(BUILD_TESTING)
# add_executable(particle_test test.cu)
......
/**
* @file closest_point.hpp
*
*
* @brief Functions for level set reinitialization and extension on OpenFPM grids based
* on closest point method.
*
* @details Depends on Algoim library for higher order closest point calculations and constructing
* stencil interpolating polynomials.
*
*
* @author Sachin Krishnan T V
* @date May 2021
*/
#ifndef __CLOSEST_POINT_HPP__
#define __CLOSEST_POINT_HPP__
#include "algoim_hocp.hpp"
// Width of extra padding around each grid patch needed to correctly construct kDTree in Algoim.
constexpr int algoim_padding = 4;
/**@brief Wrapping container to pass OpenFPM grid property values to Algoim library.
*
* @file closest_point.hpp
* @struct AlgoimWrapper
* @tparam grid_type Type of the grid container
* @tparam grid_key_type Type of the key for the grid container
* @tparam dim Dimension of the space
* @tparam wrapping_field Property id on the grid for the field to be wrapped
*/
template<typename grid_type, typename grid_key_type, unsigned int dim, size_t wrapping_field>
struct AlgoimWrapper
{
grid_type &gd;
int patch_id;
AlgoimWrapper(grid_type& ls_grid, const int pid) : gd(ls_grid), patch_id(pid) {}
//! Call operator for the wrapper.
double operator() (const blitz::TinyVector<int,dim> idx) const
{
long int local_key[dim];
auto ghost_offset = gd.getLocalGridsInfo().get(patch_id).Dbox.getKP1();
for (int d = 0; d < dim; ++d)
local_key[d] = idx(d) - algoim_padding;
// Generate OpenFPM grid_key object from local grid indices
grid_key_type grid_key(patch_id, grid_key_dx<dim> (local_key) + ghost_offset);
return gd.template get<wrapping_field>(grid_key);
}
};
/**@brief Computes the closest point coordinate for each grid point within nb_gamma from interface.
*
* @tparam grid_type Type of the grid container
* @tparam grid_key_type Type of the key for the grid container
* @tparam dim Dimension of the space
* @tparam poly_order Order of the polynomial for stencil interpolation (orders between 2 to 5 is supported)
* @tparam phi_field Property id on grid for the level set SDF
* @tparam cp_field Property id on grid for storing closest point coordinates
*
* @param gd The distributed grid containing at least level set SDF field and placeholder for closest point coordinates
* @param nb_gamma The width of the narrow band within which closest point estimation is to be done
*/
template<typename grid_type, typename grid_key_type, unsigned int poly_order, size_t phi_field, size_t cp_field>
void estimateClosestPoint(grid_type &gd, const double nb_gamma)
{
const unsigned int dim = grid_type::dims;
// Stencil polynomial type
using Poly = typename Algoim::StencilPoly<dim, poly_order>::T_Poly;
// Grid spacing along each dimension
blitz::TinyVector<double,dim> dx;
for(int d = 0; d < dim; ++d)
dx(d) = gd.spacing(d);
auto &patches = gd.getLocalGridsInfo();
grid_key_dx<dim> p_lo;
grid_key_dx<dim> p_hi;
for(int i = 0; i < patches.size();i++)
{
for(int d = 0; d < dim; ++d)
{
p_lo.set_d(d, patches.get(i).Dbox.getLow(d) + patches.get(i).origin[d]);
p_hi.set_d(d, patches.get(i).Dbox.getHigh(d) + patches.get(i).origin[d]);
}
AlgoimWrapper<grid_type, grid_key_type, dim, phi_field> phiwrap(gd, i);
// Find all cells containing the interface and construct the high-order polynomials
std::vector<Algoim::detail::CellPoly<dim,Poly>> cells;
blitz::TinyVector<int,dim> ext;
for(int d = 0; d < dim; ++d)
ext(d) = static_cast<int>(p_hi.get(d) - p_lo.get(d) + 1 + 2*algoim_padding);
Algoim::detail::createCellPolynomials(ext, phiwrap, dx, false, cells);
std::vector<blitz::TinyVector<double,dim>> points;
std::vector<int> pointcells;
Algoim::detail::samplePolynomials<dim,Poly>(cells, 2, dx, 0.0, points, pointcells);
Algoim::KDTree<double,dim> kdtree(points);
// Pass everything to the closest point computation engine
Algoim::ComputeHighOrderCP<dim,Poly> hocp(nb_gamma < std::numeric_limits<double>::max() ? nb_gamma*nb_gamma : std::numeric_limits<double>::max(), // squared bandradius
0.5*blitz::max(dx), // amount that each polynomial overlaps / size of the bounding ball in Newton's method
Algoim::sqr(std::max(1.0e-14, std::pow(blitz::max(dx), Poly::order))), // tolerance to determine convergence
cells, kdtree, points, pointcells, dx, 0.0);
auto it = gd.getSubDomainIterator(p_lo, p_hi);
while(it.isNext())
{
auto key = it.get();
if(std::abs(gd.template get<phi_field>(key)) <= nb_gamma)
{
auto key_g = gd.getGKey(key);
// NOTE: This is not the real grid coordinates, but internal coordinates for algoim
blitz::TinyVector<double,dim> patch_pos, cp;
for(int d = 0; d < dim; ++d)
patch_pos(d) = (key_g.get(d) - p_lo.get(d) + algoim_padding) * dx(d);
if (hocp.compute(patch_pos, cp))
{
for(int d = 0; d < dim; ++d)
gd.template get<cp_field>(key)[d] = cp(d);
}
else
{
for(int d = 0; d < dim; ++d)
gd.template get<cp_field>(key)[d] = -100.0;
}
}
++it;
}
}
return;
}
/**@brief Extends a (scalar) field to within nb_gamma from interface. The grid should have level set SDF and closest point field.
*
* @tparam grid_type Type of the grid container
* @tparam grid_key_type Type of the key for the grid container
* @tparam dim Dimension of the space
* @tparam poly_order Order of the polynomial for stencil interpolation
* @tparam phi_field Property id on grid for the level set SDF
* @tparam cp_field Property id on grid for storing closest point coordinates
* @tparam extend_field Property id on grid where the field to be extended resides
* @tparam extend_field_temp Property id on grid for storing temporary intermediate values
*
* @param gd The distributed grid containing atleast level set SDF field and closest point coordinates
* @param nb_gamma The width of the narrow band within which extension is required
*/
template<typename grid_type, typename grid_key_type, unsigned int poly_order, size_t phi_field, size_t cp_field, size_t extend_field, size_t extend_field_temp>
void extendLSField(grid_type &gd, const double nb_gamma)
{
const unsigned int dim = grid_type::dims;
// Stencil polynomial object
using Poly = typename Algoim::StencilPoly<dim, poly_order>::T_Poly;
auto &patches = gd.getLocalGridsInfo();
blitz::TinyVector<double,dim> dx;
for(int d = 0; d < dim; ++d)
dx(d) = gd.spacing(d);
grid_key_dx<dim> p_lo;
grid_key_dx<dim> p_hi;
for(int i = 0; i < patches.size();i++)
{
for(int d = 0; d < dim; ++d)
{
p_lo.set_d(d, patches.get(i).Dbox.getLow(d) + patches.get(i).origin[d]);
p_hi.set_d(d, patches.get(i).Dbox.getHigh(d) + patches.get(i).origin[d]);
}
auto it = gd.getSubDomainIterator(p_lo, p_hi);
while(it.isNext())
{
auto key = it.get();
if(std::abs(gd.template get<phi_field>(key)) < nb_gamma)
{
blitz::TinyVector<int,dim> coord;
blitz::TinyVector<double,dim> pos;
for(int d = 0; d < dim; ++d)
{
double cp_d = gd.template get<cp_field>(key)[d];
coord(d) = static_cast<int>(floor(cp_d / gd.spacing(d)));
pos(d) = cp_d - coord(d)*gd.spacing(d);
}
AlgoimWrapper<grid_type, grid_key_type, dim, extend_field> fieldwrap(gd,i);
Poly field_poly = Poly(coord, fieldwrap, dx);
// Extension is first done to the temporary field. Otherwise interpolation will be affected.
gd.template get<extend_field_temp>(key) = field_poly(pos);
}
++it;
}
}
// Copy the results to the actual variable
auto it = gd.getDomainIterator();
while(it.isNext())
{
auto key = it.get();
if(std::abs(gd.template get<phi_field>(key)) < nb_gamma)
gd.template get<extend_field>(key) = gd.template get<extend_field_temp>(key);
++it;
}
}
/**@brief Reinitializes the level set Phi field on a grid. The grid should have level set SDF and closest point field.
*
* @tparam grid_type Type of the grid container
* @tparam grid_key_type Type of the key for the grid container
* @tparam dim Dimension of the space
* @tparam poly_order Order of the polynomial for stencil interpolation
* @tparam phi_field Property id on grid for the level set SDF
* @tparam cp_field Property id on grid for storing closest point coordinates
*
* @param gd The distributed grid containing atleast level set SDF field and closest point coordinates
* @param nb_gamma The width of the narrow band for reinitialization
*/
template<typename grid_type, typename grid_key_type, unsigned int poly_order, size_t phi_field, size_t cp_field>
void reinitializeLS(grid_type &gd, const double nb_gamma)
{
const unsigned int dim = grid_type::dims;
// Stencil polynomial object
using Poly = typename Algoim::StencilPoly<dim, poly_order>::T_Poly;
auto &patches = gd.getLocalGridsInfo();
blitz::TinyVector<double,dim> dx;
for(int d = 0; d < dim; ++d)
dx(d) = gd.spacing(d);
grid_key_dx<dim> p_lo;
grid_key_dx<dim> p_hi;
for(int i = 0; i < patches.size();i++)
{
for(int d = 0; d < dim; ++d)
{
p_lo.set_d(d, patches.get(i).Dbox.getLow(d) + patches.get(i).origin[d]);
p_hi.set_d(d, patches.get(i).Dbox.getHigh(d) + patches.get(i).origin[d]);
}
auto it = gd.getSubDomainIterator(p_lo, p_hi);
while(it.isNext())
{
auto key = it.get();
if(std::abs(gd.template get<phi_field>(key)) < nb_gamma)
{
// Preserve the current sign of the SDF
double sign_fn = (gd.template get<phi_field>(key) >= 0.0)?1.0:-1.0;
auto key_g = gd.getGKey(key);
// Compute the Euclidean distance from gird coordinate to closest point coordinate
double distance = 0.0;
for(int d = 0; d < dim; ++d)
{
// NOTE: This is not the real grid coordinates, but internal coordinates used for algoim
double patch_pos = (key_g.get(d) - p_lo.get(d) + algoim_padding) * gd.spacing(d);
double cp_d = gd.template get<cp_field>(key)[d];
distance += ((patch_pos - cp_d)*(patch_pos - cp_d));
}
distance = sqrt(distance);
gd.template get<phi_field>(key) = sign_fn*distance;
}
++it;
}
}
}
#endif //__CLOSEST_POINT_HPP__
/* Unit tests for the closest point methods
* Date : 29 May 2021
* Author : sachin
*/
#include<iostream>
#include <boost/test/unit_test_log.hpp>
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include <iostream>
#include "Grid/grid_dist_id.hpp"
#include "data_type/aggregate.hpp"
#include "VCluster/VCluster.hpp"
#include "Vector/Vector.hpp"
#include "FiniteDifference/util/common.hpp"
#include "FiniteDifference/eq.hpp"
#include "FiniteDifference/FD_op.hpp"
#include "closest_point.hpp"
constexpr int narrow_band_half_width = 5;
typedef struct EllipseParameters{
double origin[3];
double radiusA;
double radiusB;
double radiusC;
double eccentricity;
} EllipseParams;
// Generate an ellipsoid initial levelset signed distance function
template<typename grid_type, typename domain_type, size_t phi_field>
void initializeLSEllipsoid(grid_type &gd, const domain_type &domain, const EllipseParams &params)
{
auto it = gd.getDomainIterator();
double dx = gd.getSpacing()[0];
double dy = gd.getSpacing()[1];
double dz = gd.getSpacing()[2];
while(it.isNext())
{
auto key = it.get();
auto key_g = gd.getGKey(key);
double posx = key_g.get(0)*dx + domain.getLow(0);
double posy = key_g.get(1)*dy + domain.getLow(1);
double posz = key_g.get(2)*dz + domain.getLow(2);
// NOTE: Except for a sphere, this is not the SDF. It is just an implicit function whose zero contour is an ellipsoid.
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));
gd.template get<phi_field>(key) = phi_val;
++it;
}
}
BOOST_AUTO_TEST_SUITE( closest_point_test )
BOOST_AUTO_TEST_CASE( closest_point_unit_sphere )
{
constexpr int SIM_DIM = 3; //> Spatial dimension
constexpr int POLY_ORDER = 5; //> Order of the polynomial for stencil interpolation
constexpr int SIM_GRID_SIZE = 128; //> Size of the simulation grid along each dimension
// Properties - phi, cp
using GridDist = grid_dist_id<SIM_DIM,double,aggregate<double,double[SIM_DIM]>>;
using GridKey = grid_dist_key_dx<SIM_DIM>;
// Grid size on each dimension
const long int sz[SIM_DIM] = {SIM_GRID_SIZE, SIM_GRID_SIZE, SIM_GRID_SIZE};
const size_t szu[SIM_DIM] = {(size_t) sz[0], (size_t) sz[1], (size_t) sz[2]};
// 3D physical domain
Box<SIM_DIM,double> domain({-1.5,-1.5,-1.5},{1.5,1.5,1.5});
constexpr int x = 0;
constexpr int y = 1;
constexpr int z = 2;
// Alias for properties on the grid
constexpr int phi = 0;
constexpr int cp = 1;
double nb_gamma = 0.0;
periodicity<SIM_DIM> grid_bc = {NON_PERIODIC, NON_PERIODIC, NON_PERIODIC};
// Ghost in grid units.
Ghost <SIM_DIM, long int> grid_ghost(2*narrow_band_half_width);
GridDist gdist(szu, domain, grid_ghost, grid_bc);
// Parameters for initializing Ellipsoid level set function
EllipseParams params;
params.origin[x] = 0.0;
params.origin[y] = 0.0;
params.origin[z] = 0.0;
params.radiusA = 1.0;
params.radiusB = 1.0;
params.radiusC = 1.0;
// Width of the narrowband on one side of the interface
nb_gamma = narrow_band_half_width * gdist.spacing(0);
// Initializes the grid property 'phi' whose zero contour represents the ellipsoid
initializeLSEllipsoid<GridDist, Box<SIM_DIM,double>, phi>(gdist, domain, params);
gdist.template ghost_get<phi>();
// Updates the property 'cp' of the grid to the closest point coords (only done in the narrowband).
estimateClosestPoint<GridDist, GridKey, POLY_ORDER, phi, cp>(gdist, nb_gamma);
gdist.template ghost_get<cp>();
// Estimate error in closest point estimation
auto &patches = gdist.getLocalGridsInfo();
double max_error_cp = -1.0;
for(int i = 0; i < patches.size();i++)
{
// The coordinates of the two bounding points of grid patches.
auto p_xlo = patches.get(i).Dbox.getLow(0) + patches.get(i).origin[0];
auto p_xhi = patches.get(i).Dbox.getHigh(0) + patches.get(i).origin[0];
auto p_ylo = patches.get(i).Dbox.getLow(1) + patches.get(i).origin[1];
auto p_yhi = patches.get(i).Dbox.getHigh(1) + patches.get(i).origin[1];
auto p_zlo = patches.get(i).Dbox.getLow(2) + patches.get(i).origin[2];
auto p_zhi = patches.get(i).Dbox.getHigh(2) + patches.get(i).origin[2];
auto it = gdist.getSubDomainIterator({p_xlo, p_ylo, p_zlo}, {p_xhi, p_yhi, p_zhi});
while(it.isNext())
{
auto key = it.get();
if(std::abs(gdist.template get<phi>(key)) < nb_gamma)
{
auto key_g = gdist.getGKey(key);
// Computed closest point coordinates.
// Note: This is patch coordinates not the real one.
double cpx = gdist.template get<cp>(key)[x];
double cpy = gdist.template get<cp>(key)[y];
double cpz = gdist.template get<cp>(key)[z];
if(cpx == -100.0 && cpy == -100.0 && cpz == -100.0)
std::cout<<"ERROR: Requesting closest point where it was not computed."<<std::endl;
// Convert patch coords to real coordinates.
double estim_px = domain.getLow(x) + (p_xlo - algoim_padding)*gdist.spacing(x) + cpx;
double estim_py = domain.getLow(y) + (p_ylo - algoim_padding)*gdist.spacing(y) + cpy;
double estim_pz = domain.getLow(z) + (p_zlo - algoim_padding)*gdist.spacing(z) + cpz;
// Global coordinate of the selected grid point.
double posx = key_g.get(0)*gdist.spacing(0) + domain.getLow(0);
double posy = key_g.get(1)*gdist.spacing(1) + domain.getLow(1);
double posz = key_g.get(2)*gdist.spacing(2) + domain.getLow(2);
double norm = sqrt(posx*posx + posy*posy + posz*posz);
// Analytically known closest point coordinate for unit sphere.
double exact_px = posx / norm;
double exact_py = posy / norm;
double exact_pz = posz / norm;
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});
}
++it;
}
}
std::cout<<"Unit sphere closest_point error : "<<max_error_cp<<std::endl;
double tolerance = 1e-5;
bool check;
if (std::abs(max_error_cp) < tolerance)
check = true;
else
check = false;
BOOST_TEST( check );
}
BOOST_AUTO_TEST_CASE( reinitialization_unit_sphere )
{
constexpr int SIM_DIM = 3;
constexpr int POLY_ORDER = 5;
constexpr int SIM_GRID_SIZE = 128;
// Fields - phi, cp
using GridDist = grid_dist_id<SIM_DIM,double,aggregate<double,double[SIM_DIM]>>;
using GridKey = grid_dist_key_dx<SIM_DIM>;
// Grid size on each dimension
const long int sz[SIM_DIM] = {SIM_GRID_SIZE, SIM_GRID_SIZE, SIM_GRID_SIZE};
const size_t szu[SIM_DIM] = {(size_t) sz[0], (size_t) sz[1], (size_t) sz[2]};
// 3D physical domain
Box<SIM_DIM,double> domain({-1.5,-1.5,-1.5},{1.5,1.5,1.5});
constexpr int x = 0;
constexpr int y = 1;
constexpr int z = 2;
// Alias for properties on the grid
constexpr int phi = 0;
constexpr int cp = 1;
double nb_gamma = 0.0;
periodicity<SIM_DIM> grid_bc = {NON_PERIODIC, NON_PERIODIC, NON_PERIODIC};
// Ghost in grid units
Ghost <SIM_DIM, long int> grid_ghost(2*narrow_band_half_width);
GridDist gdist(szu, domain, grid_ghost, grid_bc);
EllipseParams params;
params.origin[x] = 0.0;
params.origin[y] = 0.0;
params.origin[z] = 0.0;
params.radiusA = 1.0;
params.radiusB = 1.0;
params.radiusC = 1.0;
nb_gamma = narrow_band_half_width * gdist.spacing(0);
initializeLSEllipsoid<GridDist, Box<SIM_DIM,double>, phi>(gdist, domain, params);
gdist.template ghost_get<phi>();
estimateClosestPoint<GridDist, GridKey, POLY_ORDER, phi, cp>(gdist, nb_gamma);
gdist.template ghost_get<cp>();
// Reinitialize the level set function stored in property 'phi' based on closest points in 'cp'
reinitializeLS<GridDist, GridKey, POLY_ORDER, phi, cp>(gdist, nb_gamma);
gdist.template ghost_get<phi>();
// Estimate error in closest point estimation
auto &patches = gdist.getLocalGridsInfo();
double max_error = -1.0;
for(int i = 0; i < patches.size();i++)
{
auto p_xlo = patches.get(i).Dbox.getLow(0) + patches.get(i).origin[0];
auto p_xhi = patches.get(i).Dbox.getHigh(0) + patches.get(i).origin[0];
auto p_ylo = patches.get(i).Dbox.getLow(1) + patches.get(i).origin[1];
auto p_yhi = patches.get(i).Dbox.getHigh(1) + patches.get(i).origin[1];
auto p_zlo = patches.get(i).Dbox.getLow(2) + patches.get(i).origin[2];
auto p_zhi = patches.get(i).Dbox.getHigh(2) + patches.get(i).origin[2];
auto it = gdist.getSubDomainIterator({p_xlo, p_ylo, p_zlo}, {p_xhi, p_yhi, p_zhi});
while(it.isNext())
{
auto key = it.get();
if(std::abs(gdist.template get<phi>(key)) < nb_gamma)
{
auto key_g = gdist.getGKey(key);
// Global grid coordinate
double posx = key_g.get(0)*gdist.spacing(0) + domain.getLow(0);
double posy = key_g.get(1)*gdist.spacing(1) + domain.getLow(1);
double posz = key_g.get(2)*gdist.spacing(2) + domain.getLow(2);
// Analytically computed signed distance
// NOTE: SDF convention here is positive inside and negative outside the sphere
double distance = 1.0 - sqrt(posx*posx + posy*posy + posz*posz);
max_error = std::max({std::abs(distance - gdist.template get<phi>(key)), max_error});
}
++it;
}
}
std::cout<<"Reinitialization error : "<<max_error<<std::endl;
double tolerance = 1e-5;
bool check;
if (std::abs(max_error) < tolerance)
check = true;
else
check = false;
BOOST_TEST( check );
}
BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment