diff --git a/cmake/AkantuTestsMacros.cmake b/cmake/AkantuTestsMacros.cmake index 27e4e1be0..85bc9cdea 100644 --- a/cmake/AkantuTestsMacros.cmake +++ b/cmake/AkantuTestsMacros.cmake @@ -1,473 +1,474 @@ #=============================================================================== # @file AkantuTestsMacros.cmake # # @author Nicolas Richart <nicolas.richart@epfl.ch> # # @date creation: Fri Sep 03 2010 # @date last modification: Fri Jan 22 2016 # # @brief macros for tests # # @section LICENSE # # Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de # Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des # Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with Akantu. If not, see <http://www.gnu.org/licenses/>. # #=============================================================================== #[=======================================================================[.rst: AkantuTestsMacros ----------------- This modules provides the functions to helper to declare tests and folders containing tests in akantu .. command:: add_test_tree add_test_tree(<test_direcotry>) ``<test_directory>`` is the entry direcroty of the full structure of subfolders containing tests .. command:: add_akantu_test add_akantu_test(<dir> <desc>) This function add a subdirectory ``<dir>`` of tests that will be conditionnaly activable and will be visible only if the parent folder as been activated An option ``AKANTU_BUILD_TEST_<dir>`` will appear in ccmake with the description ``<desc>``. The compilation of all tests can be forced with the option ``AKANTU_BUILD_ALL_TESTS`` .. command:: register_test register_test(<test_name> SOURCES <sources>... PACKAGE <akantu_packages>... SCRIPT <scirpt> [FILES_TO_COPY <filenames>...] [DEPENDS <targets>...] [DIRECTORIES_TO_CREATE <directories>...] [COMPILE_OPTIONS <flags>...] [EXTRA_FILES <filnames>...] [LINK_LIBRARIES <libraries>...] [UNSABLE] [PARALLEL] [PARALLEL_LEVEL <procs>...] ) This function defines a test ``<test_name>_run`` this test could be of different nature depending on the context. If Just sources are provided the test consist of running the executable generated. If a file ``<test_name>.sh`` is present the test will execute the script. And if a ``<test_name>.verified`` exists the output of the test will be compared to this reference file The options are: ``SOURCES <sources>...`` The list of source files to compile to generate the executable of the test ``PACKAGE <akantu_packages>...`` The list of package to which this test belongs. The test will be activable only of all the packages listed are activated ``SCRIPT <script>`` The script to execute instead of the executable ``FILES_TO_COPY <filenames>...`` List of files to copy from the source directory to the build directory ``DEPENDS <targets>...`` List of targets the test depends on, for example if a mesh as to be generated ``DIRECTORIES_TO_CREATE <directories>...`` Obsolete. This specifies a list of directories that have to be created in the build folder ``COMPILE_OPTIONS <flags>...`` List of extra compilations options to pass to the compiler ``EXTRA_FILES <filnames>...`` Files to consider when generating a package_source ``UNSABLE`` If this option is specified the test can be unacitivated by the glocal option ``AKANTU_BUILD_UNSTABLE_TESTS``, this is mainly intendeed to remove test under developement from the continious integration ``PARALLEL`` This specifies that this test should be run in parallel. It will generate a series of test for different number of processors. This automaticaly adds a dependency to the package ``AKANTU_PARALLEL`` ``PARALLEL_LEVEL`` This defines the different processor numbers to use, if not defined the macro tries to determine it in a "clever" way ]=======================================================================] set(AKANTU_DRIVER_SCRIPT ${AKANTU_CMAKE_DIR}/akantu_test_driver.sh) # ============================================================================== macro(add_test_tree dir) if(AKANTU_TESTS) enable_testing() include(CTest) mark_as_advanced(BUILD_TESTING) - + set(_akantu_current_parent_test ${dir} CACHE INTERNAL "Current test folder" FORCE) set(_akantu_${dir}_tests_count 0 CACHE INTERNAL "" FORCE) string(TOUPPER ${dir} _u_dir) set(AKANTU_BUILD_${_u_dir} ON CACHE INTERNAL "${desc}" FORCE) package_get_all_test_folders(_test_dirs) foreach(_dir ${_test_dirs}) add_subdirectory(${_dir}) endforeach() endif() endmacro() # ============================================================================== function(add_akantu_test dir desc) if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}) return() endif() set(_my_parent_dir ${_akantu_current_parent_test}) - + # initialize variables set(_akantu_current_parent_test ${dir} CACHE INTERNAL "Current test folder" FORCE) set(_akantu_${dir}_tests_count 0 CACHE INTERNAL "" FORCE) # set the option for this directory string(TOUPPER ${dir} _u_dir) option(AKANTU_BUILD_${_u_dir} "${desc}") mark_as_advanced(AKANTU_BUILD_${_u_dir}) set(AKANTU_${_u_dir}_GTEST_SOURCE "" CACHE INTERNAL "") # add the sub-directory add_subdirectory(${dir}) if(AKANTU_${_u_dir}_GTEST_SOURCE) set(_srcs) foreach(_src ${AKANTU_${_u_dir}_GTEST_SOURCE}) list(APPEND _srcs ${dir}/${_src}) endforeach() add_executable(${dir}_gtest_all ${PROJECT_SOURCE_DIR}/test/test_gtest_main.cc ${_srcs}) target_link_libraries(${dir}_gtest_all akantu GTest::GTest GTest::Main) + target_include_directories(${dir}_gtest_all PRIVATE ${PROJECT_SOURCE_DIR}/test) set_property(TARGET ${dir}_gtest_all PROPERTY OUTPUT_NAME ${dir}/gtest_all) add_test(NAME ${dir}_gtest COMMAND ${AKANTU_DRIVER_SCRIPT} -n "${dir}" -e "gtest_all" -w "${CMAKE_CURRENT_BINARY_DIR}/${dir}") endif() # if no test can be activated make the option disappear set(_force_deactivate_count FALSE) if(${_akantu_${dir}_tests_count} EQUAL 0) set(_force_deactivate_count TRUE) endif() # if parent off make the option disappear set(_force_deactivate_parent FALSE) string(TOUPPER ${_my_parent_dir} _u_parent_dir) if(NOT AKANTU_BUILD_${_u_parent_dir}) set(_force_deactivate_parent TRUE) endif() if(_force_deactivate_parent OR _force_deactivate_count OR AKANTU_BUILD_ALL_TESTS) if(NOT DEFINED _AKANTU_BUILD_${_u_dir}_SAVE) set(_AKANTU_BUILD_${_u_dir}_SAVE ${AKANTU_BUILD_${_u_dir}} CACHE INTERNAL "" FORCE) endif() unset(AKANTU_BUILD_${_u_dir} CACHE) if(AKANTU_BUILD_ALL_TESTS AND NOT _force_deactivate_count) set(AKANTU_BUILD_${_u_dir} ON CACHE INTERNAL "${desc}" FORCE) else() set(AKANTU_BUILD_${_u_dir} OFF CACHE INTERNAL "${desc}" FORCE) endif() else() if(DEFINED _AKANTU_BUILD_${_u_dir}_SAVE) unset(AKANTU_BUILD_${_u_dir} CACHE) set(AKANTU_BUILD_${_u_dir} ${_AKANTU_BUILD_${_u_dir}_SAVE} CACHE BOOL "${desc}") unset(_AKANTU_BUILD_${_u_dir}_SAVE CACHE) endif() endif() # adding up to the parent count math(EXPR _tmp_parent_count "${_akantu_${dir}_tests_count} + ${_akantu_${_my_parent_dir}_tests_count}") set(_akantu_${_my_parent_dir}_tests_count ${_tmp_parent_count} CACHE INTERNAL "" FORCE) # restoring the parent current dir set(_akantu_current_parent_test ${_my_parent_dir} CACHE INTERNAL "Current test folder" FORCE) endfunction() # ============================================================================== function(register_test test_name) set(flags GTEST UNSTABLE PARALLEL ) set(one_variables POSTPROCESS SCRIPT ) set(multi_variables SOURCES FILES_TO_COPY DEPENDS DIRECTORIES_TO_CREATE COMPILE_OPTIONS EXTRA_FILES LINK_LIBRARIES PACKAGE PARALLEL_LEVEL ) cmake_parse_arguments(_register_test "${flags}" "${one_variables}" "${multi_variables}" ${ARGN} ) if(NOT _register_test_PACKAGE) message(FATAL_ERROR "No reference package was defined for the test" " ${test_name} in folder ${CMAKE_CURRENT_SOURCE_DIR}") endif() set(_test_act TRUE) # Activate the test anly if all packages associated to the test are activated foreach(_package ${_register_test_PACKAGE}) package_is_activated(${_package} _act) if(NOT _act) set(_test_act FALSE) endif() endforeach() # check if the test is marked unstable and if the unstable test should be run if(_register_test_UNSTABLE AND NOT AKANTU_BUILD_UNSTABLE_TESTS) set(_test_act FALSE) endif() # check that the sources are files that need to be compiled if(_register_test_SOURCES} OR _register_test_UNPARSED_ARGUMENTS) set(_need_to_compile TRUE) else() set(_need_to_compile FALSE) endif() set(_compile_source) foreach(_file ${_register_test_SOURCES} ${_register_test_UNPARSED_ARGUMENTS}) if(_file MATCHES "\\.cc$" OR _file MATCHES "\\.hh$") list(APPEND _compile_source ${_file}) endif() endforeach() # todo this should be checked for the build package_sources since the file will not be listed. if(_test_act) math(EXPR _tmp_parent_count "${_akantu_${_akantu_current_parent_test}_tests_count} + 1") set(_akantu_${_akantu_current_parent_test}_tests_count ${_tmp_parent_count} CACHE INTERNAL "" FORCE) string(TOUPPER ${_akantu_current_parent_test} _u_parent) if(AKANTU_BUILD_${_u_parent} OR AKANTU_BUILD_ALL_TESTS) if(_compile_source) # get the include directories for sources in activated directories package_get_all_include_directories( AKANTU_LIBRARY_INCLUDE_DIRS ) # get the external packages compilation and linking informations package_get_all_external_informations( AKANTU_EXTERNAL_INCLUDE_DIR AKANTU_EXTERNAL_LIBRARIES ) if(NOT _register_test_GTEST) # Register the executable to compile add_executable(${test_name} ${_compile_source}) # set the proper includes to build most of the tests target_include_directories(${test_name} PRIVATE ${AKANTU_LIBRARY_INCLUDE_DIRS} ${AKANTU_EXTERNAL_INCLUDE_DIR} ${PROJECT_BINARY_DIR}/src) target_link_libraries(${test_name} akantu ${_register_test_LINK_LIBRARIES}) if(_register_test_DEPENDS) add_dependencies(${test_name} ${_register_test_DEPENDS}) endif() # add the extra compilation options if(_register_test_COMPILE_OPTIONS) set_target_properties(${test_name} PROPERTIES COMPILE_DEFINITIONS "${_register_test_COMPILE_OPTIONS}") endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_BUILD_TYPE STREQUAL "Debug") set(AKANTU_EXTRA_CXX_FLAGS "${AKANTU_EXTRA_CXX_FLAGS} -no-pie") endif() if(AKANTU_EXTRA_CXX_FLAGS) set_target_properties(${test_name} PROPERTIES COMPILE_FLAGS "${AKANTU_EXTRA_CXX_FLAGS}") endif() else() set(_list ${AKANTU_${_u_parent}_GTEST_SOURCE}) list(APPEND _list ${_compile_source}) set(AKANTU_${_u_parent}_GTEST_SOURCE ${_list} CACHE INTERNAL "") endif() else() if(_register_test_UNPARSED_ARGUMENTS AND NOT _register_test_SCRIPT) set(_register_test_SCRIPT ${_register_test_UNPARSED_ARGUMENTS}) endif() endif() # copy the needed files to the build folder if(_register_test_FILES_TO_COPY) foreach(_file ${_register_test_FILES_TO_COPY}) file(COPY "${_file}" DESTINATION .) endforeach() endif() # create the needed folders in the build folder if(_register_test_DIRECTORIES_TO_CREATE) foreach(_dir ${_register_test_DIRECTORIES_TO_CREATE}) if(IS_ABSOLUTE ${dir}) file(MAKE_DIRECTORY "${_dir}") else() file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${_dir}") endif() endforeach() endif() # register the test for ctest set(_arguments -n "${test_name}") if(_register_test_SCRIPT) file(COPY ${_register_test_SCRIPT} FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE DESTINATION .) list(APPEND _arguments -e "${_register_test_SCRIPT}") elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${test_name}.sh") file(COPY ${test_name}.sh DESTINATION .) list(APPEND _arguments -e "${test_name}.sh") else() list(APPEND _arguments -e "${test_name}") endif() list(APPEND _arguments -E "${PROJECT_BINARY_DIR}/akantu_environement.sh") package_is_activated(parallel _is_parallel) if(_is_parallel AND AKANTU_TESTS_ALWAYS_USE_MPI AND NOT _register_test_PARALLEL) set(_register_test_PARALLEL TRUE) set(_register_test_PARALLEL_LEVEL 1) endif() if(_register_test_PARALLEL) list(APPEND _arguments -p "${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG}") if(_register_test_PARALLEL_LEVEL) set(_procs "${_register_test_PARALLEL_LEVEL}") elseif(CMAKE_VERSION VERSION_GREATER "3.0") set(_procs) include(ProcessorCount) ProcessorCount(N) while(N GREATER 1) list(APPEND _procs ${N}) math(EXPR N "${N} / 2") endwhile() endif() if(NOT _procs) set(_procs 2) endif() endif() if(_register_test_POSTPROCESS) list(APPEND _arguments -s "${_register_test_POSTPROCESS}") file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/${_register_test_POSTPROCESS} FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) endif() list(APPEND _arguments -w "${CMAKE_CURRENT_BINARY_DIR}") if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${test_name}.verified") list(APPEND _arguments -r "${CMAKE_CURRENT_SOURCE_DIR}/${test_name}.verified") endif() string(REPLACE ";" " " _command "${_arguments}") if(NOT _register_test_GTEST) # register them test if(_procs) foreach(p ${_procs}) add_test(NAME ${test_name}_${p} COMMAND ${AKANTU_DRIVER_SCRIPT} ${_arguments} -N ${p}) endforeach() else() add_test(NAME ${test_name} COMMAND ${AKANTU_DRIVER_SCRIPT} ${_arguments}) endif() endif() endif() endif() set(_test_all_files) # add the source files in the list of all files foreach(_file ${_register_test_SOURCES} ${_register_test_UNPARSED_ARGUMENTS} ${_register_test_EXTRA_FILES} ${_register_test_SOURCES} ${_register_test_SCRIPT} ${_register_test_POSTPROCESS} ${_register_test_FILES_TO_COPY}) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_file}) list(APPEND _test_all_files "${_file}") else() message("The file \"${_file}\" registred by the test \"${test_name}\" does not exists") endif() endforeach() # add the different dependencies files (meshes, local libraries, ...) foreach(_dep ${_register_test_DEPENDS}) get_target_list_of_associated_files(${_dep} _dep_ressources) if(_dep_ressources) list(APPEND _test_all_files "${_dep_ressources}") endif() endforeach() # add estra files to the list of files referenced by a given test if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${test_name}.sh") list(APPEND _test_all_files "${test_name}.sh") endif() if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${test_name}.verified") list(APPEND _test_all_files "${test_name}.verified") endif() if(_register_test_SCRIPT) list(APPEND _test_all_files "${_register_test_SCRIPT}") endif() # clean the list of all files for this test and add them in the total list foreach(_file ${_test_all_files}) get_filename_component(_full ${_file} ABSOLUTE) file(RELATIVE_PATH __file ${PROJECT_SOURCE_DIR} ${_full}) list(APPEND _tmp "${__file}") endforeach() foreach(_pkg ${_register_test_PACKAGE}) package_get_name(${_pkg} _pkg_name) _package_add_to_variable(TESTS_FILES ${_pkg_name} ${_tmp}) endforeach() endfunction() diff --git a/src/common/aka_compatibilty_with_cpp_standard.hh b/src/common/aka_compatibilty_with_cpp_standard.hh index 154f339f1..9aa4fb8af 100644 --- a/src/common/aka_compatibilty_with_cpp_standard.hh +++ b/src/common/aka_compatibilty_with_cpp_standard.hh @@ -1,132 +1,143 @@ /** * @file aka_compatibilty_with_cpp_standard.hh * * @author Nicolas Richart * * @date creation Wed Oct 25 2017 * * @brief The content of this file is taken from the possible implementations on * http://en.cppreference.com * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include <type_traits> #include <utility> #include <tuple> /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_AKA_COMPATIBILTY_WITH_CPP_STANDARD_HH__ #define __AKANTU_AKA_COMPATIBILTY_WITH_CPP_STANDARD_HH__ namespace aka { // Part taken from C++14 #if __cplusplus < 201402L template <bool B, class T = void> using enable_if_t = typename enable_if<B, T>::type; #else template <bool B, class T = void> using enable_if_t = std::enable_if_t<B, T>; #endif // Part taken from C++17 #if __cplusplus < 201703L // bool_constant template <bool B> using bool_constant = std::integral_constant<bool, B>; namespace { template <bool B> constexpr bool bool_constant_v = bool_constant<B>::value; } +/* -------------------------------------------------------------------------- */ // conjunction template <class...> struct conjunction : std::true_type {}; template <class B1> struct conjunction<B1> : B1 {}; template <class B1, class... Bn> struct conjunction<B1, Bn...> : std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {}; +/* -------------------------------------------------------------------------- */ + +// negations +template<class B> +struct negation : bool_constant<!bool(B::value)> { }; + + +/* -------------------------------------------------------------------------- */ + + namespace detail { // template <class T> // struct is_reference_wrapper : std::false_type {}; // template <class U> // struct is_reference_wrapper<std::reference_wrapper<U>> : std::true_type {}; // template <class T> // constexpr bool is_reference_wrapper_v = is_reference_wrapper<T>::value; template <class T, class Type, class T1, class... Args> decltype(auto) INVOKE(Type T::*f, T1 && t1, Args &&... args) { static_assert(std::is_member_function_pointer<decltype(f)>{} and std::is_base_of<T, std::decay_t<T1>>{}, "Does not know what to do with this types"); return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...); } // template <class T, class Type, class T1, class... Args> // decltype(auto) INVOKE(Type T::*f, T1 && t1, Args &&... args) { // if constexpr (std::is_member_function_pointer_v<decltype(f)>) { // if constexpr (std::is_base_of_v<T, std::decay_t<T1>>) // return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...); // else if constexpr (is_reference_wrapper_v<std::decay_t<T1>>) // return (t1.get().*f)(std::forward<Args>(args)...); // else // return ((*std::forward<T1>(t1)).*f)(std::forward<Args>(args)...); // } else { // static_assert(std::is_member_object_pointer_v<decltype(f)>); // static_assert(sizeof...(args) == 0); // if constexpr (std::is_base_of_v<T, std::decay_t<T1>>) // return std::forward<T1>(t1).*f; // else if constexpr (is_reference_wrapper_v<std::decay_t<T1>>) // return t1.get().*f; // else // return (*std::forward<T1>(t1)).*f; // } // } template <class F, class... Args> decltype(auto) INVOKE(F && f, Args &&... args) { return std::forward<F>(f)(std::forward<Args>(args)...); } } // namespace detail template <class F, class... Args> decltype(auto) invoke(F && f, Args &&... args) { return detail::INVOKE(std::forward<F>(f), std::forward<Args>(args)...); } namespace detail { template <class F, class Tuple, std::size_t... Is> constexpr decltype(auto) apply_impl(F && f, Tuple && t, std::index_sequence<Is...>) { return invoke(std::forward<F>(f), std::get<Is>(std::forward<Tuple>(t))...); } } // namespace detail template <class F, class Tuple> constexpr decltype(auto) apply(F && f, Tuple && t) { return detail::apply_impl( std::forward<F>(f), std::forward<Tuple>(t), std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{}); } #endif } // namespace aka #endif /* __AKANTU_AKA_COMPATIBILTY_WITH_CPP_STANDARD_HH__ */ diff --git a/test/test_fe_engine/CMakeLists.txt b/test/test_fe_engine/CMakeLists.txt index 8c53658bb..57a63303a 100644 --- a/test/test_fe_engine/CMakeLists.txt +++ b/test/test_fe_engine/CMakeLists.txt @@ -1,94 +1,113 @@ #=============================================================================== # @file CMakeLists.txt # # @author Guillaume Anciaux <guillaume.anciaux@epfl.ch> # # @date creation: Fri Sep 03 2010 # @date last modification: Mon Dec 07 2015 # # @brief configuration for FEM tests # # @section LICENSE # # Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de # Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des # Solides) # # Akantu is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Akantu is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with Akantu. If not, see <http://www.gnu.org/licenses/>. # # @section DESCRIPTION # #=============================================================================== #=============================================================================== function(register_fem_test operation type) set(_target test_${operation}${type}) register_test(${_target} SOURCES test_${operation}.cc FILES_TO_COPY ${type}.msh COMPILE_OPTIONS TYPE=${type} PACKAGE core ) endfunction() #=============================================================================== package_get_element_types(core _types) foreach(_type ${_types}) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_type}.msh) register_fem_test(fe_engine_precomputation ${_type}) - register_fem_test(fe_engine_gauss_integration ${_type}) - register_fem_test(interpolate ${_type}) - register_fem_test(gradient ${_type}) - register_fem_test(integrate ${_type}) - register_fem_test(inverse_map ${_type}) else() if(NOT ${_type} STREQUAL _point_1) message("The mesh ${_type}.msh is missing, the fe_engine test cannot be activated without it") endif() endif() endforeach() #register_test(test_interpolate_bernoulli_beam_2 test_interpolate_bernoulli_beam_2.cc) #add_mesh(test_fem_circle_1_mesh circle.geo 2 1 OUTPUT circle1.msh) #add_mesh(test_fem_circle_2_mesh circle.geo 2 2 OUTPUT circle2.msh) # Tests for class MeshData macro(register_typed_test test_name type value1 value2) set(target test_${test_name}_${type}) register_test(${target} SOURCES test_${test_name}.cc COMPILE_OPTIONS "TYPE=${type};VALUE1=${value1};VALUE2=${value2}" PACKAGE core ) endmacro() register_typed_test(mesh_data string \"5\" \"10\") register_typed_test(mesh_data UInt 5 10) add_mesh(test_boundary_msh cube.geo 3 1) add_mesh(test_boundary_msh_physical_names cube_physical_names.geo 3 1) register_test(test_mesh_boundary SOURCES test_mesh_boundary.cc DEPENDS test_boundary_msh test_boundary_msh_physical_names PACKAGE core) register_test(test_facet_element_mapping SOURCES test_facet_element_mapping.cc DEPENDS test_boundary_msh_physical_names PACKAGE core) + +register_test(test_fe_engine_gauss_integration + SOURCES test_fe_engine_gauss_integration.cc + PACKAGE core + GTEST + ) + +register_test(test_gradient + SOURCES test_gradient.cc + PACKAGE core + GTEST + ) + +register_test(test_integrate + SOURCES test_integrate.cc + PACKAGE core + GTEST + ) + +register_test(test_inverse_map + SOURCES test_inverse_map.cc + PACKAGE core + GTEST + ) diff --git a/test/test_fe_engine/test_fe_engine_fixture.hh b/test/test_fe_engine/test_fe_engine_fixture.hh new file mode 100644 index 000000000..78d18a7c2 --- /dev/null +++ b/test/test_fe_engine/test_fe_engine_fixture.hh @@ -0,0 +1,59 @@ +/* -------------------------------------------------------------------------- */ +#include "element_class.hh" +#include "fe_engine.hh" +#include "integrator_gauss.hh" +#include "shape_lagrange.hh" +#include "test_gtest_utils.hh" +/* -------------------------------------------------------------------------- */ +#include <gtest/gtest.h> +/* -------------------------------------------------------------------------- */ + +using namespace akantu; + +template <typename type_> class TestFEMFixture : public ::testing::Test { +public: + static constexpr const ElementType type = type_::value; + static constexpr const size_t dim = ElementClass<type>::getSpatialDimension(); + using FEM = FEEngineTemplate<IntegratorGauss, ShapeLagrange>; + + void SetUp() override { + const auto dim = this->dim; + const auto type = this->type; + mesh = std::make_unique<Mesh>(dim); + + std::stringstream meshfilename; + meshfilename << type << ".msh"; + mesh->read(meshfilename.str()); + + lower = mesh->getLowerBounds(); + upper = mesh->getUpperBounds(); + + nb_element = this->mesh->getNbElement(type); + + fem = std::make_unique<FEM>(*mesh, dim, "my_fem"); + fem->initShapeFunctions(); + + nb_quadrature_points_total = fem->getNbIntegrationPoints(type) * nb_element; + + std::stringstream sstr; + sstr << type; + SCOPED_TRACE(sstr.str().c_str()); + } + + void TearDown() override { + fem.reset(nullptr); + mesh.reset(nullptr); + } + +protected: + std::unique_ptr<FEM> fem; + std::unique_ptr<Mesh> mesh; + UInt nb_element; + UInt nb_quadrature_points_total; + Vector<Real> lower; + Vector<Real> upper; +}; + +using types = gtest_list_t<TestElementTypes>; + +TYPED_TEST_CASE(TestFEMFixture, types); diff --git a/test/test_fe_engine/test_fe_engine_gauss_integration.cc b/test/test_fe_engine/test_fe_engine_gauss_integration.cc index 815cc9ae8..4edf29f8e 100644 --- a/test/test_fe_engine/test_fe_engine_gauss_integration.cc +++ b/test/test_fe_engine/test_fe_engine_gauss_integration.cc @@ -1,203 +1,215 @@ /** * @file test_fe_engine_precomputation.cc * * @author Nicolas Richart <nicolas.richart@epfl.ch> * * @date creation: Mon Jun 14 2010 * @date last modification: Mon Jul 13 2015 * * @brief test integration on elements, this test consider that mesh is a cube * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ -#include "fe_engine.hh" -#include "shape_lagrange.hh" -#include "integrator_gauss.hh" +#include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ +#include <gtest/gtest.h> #include <iostream> /* -------------------------------------------------------------------------- */ using namespace akantu; -typedef FEEngineTemplate<IntegratorGauss, ShapeLagrange> FEM; -const ElementType type = TYPE; - -// cst x x^2 x^3 -// x^4 x^5 -Real alpha[3][6] = {{0.40062394, 0.13703225, 0.51731446, 0.87830084, 0.5410543, - 0.71842292}, // x - {0.41861835, 0.11080576, 0.49874043, 0.49077504, 0.85073835, - 0.66259755}, // y - {0.92620845, 0.7503478, 0.62962232, 0.31662719, 0.64069644, - 0.30878135}}; // z - -static Vector<Real> eval_poly(UInt degree, const Vector<Real> & x) { - Vector<Real> res(x.size()); - for (UInt i = 0; i < degree + 1; ++i) { - for (UInt d = 0; d < x.size(); ++d) { - res(d) += std::pow(x(d), i) * alpha[d][i]; - } +namespace { +/* -------------------------------------------------------------------------- */ +template <size_t t> using degree_t = std::integral_constant<size_t, t>; + +/* -------------------------------------------------------------------------- */ + +template <size_t degree> class Polynomial { +public: + Polynomial() = default; + + Polynomial(std::initializer_list<double> && init) { + for (auto && pair : zip(init, constants)) + std::get<1>(pair) = std::get<0>(pair); } - return res; -} -static Vector<Real> eval_int(UInt degree, const Vector<Real> & a, - const Vector<Real> & b) { - Vector<Real> res(a.size()); - for (UInt i = 0; i < degree + 1; ++i) { - for (UInt d = 0; d < a.size(); ++d) { - res(d) += (std::pow(b(d), i + 1) - std::pow(a(d), i + 1)) * alpha[d][i] / Real(i + 1); + double operator()(double x) { + double res = 0.; + for (auto && vals : enumerate(constants)) { + double a; + int k; + std::tie(k, a) = vals; + res += a * std::pow(x, k); } + return res; } - if (a.size() == 3) { - res(_x) *= std::abs(b(_y) - a(_y)) * std::abs(b(_z) - a(_z)); - res(_y) *= std::abs(b(_x) - a(_x)) * std::abs(b(_z) - a(_z)); - res(_z) *= std::abs(b(_y) - a(_y)) * std::abs(b(_x) - a(_x)); - } else if (a.size() == 2) { - res(_x) *= std::abs(b(_y) - a(_y)); - res(_y) *= std::abs(b(_x) - a(_x)); + Polynomial extract(size_t pdegree) { + Polynomial<degree> extract(*this); + for (size_t d = pdegree + 1; d < degree + 1; ++d) + extract.constants[d] = 0; + return extract; } - return res; -} - -template <UInt degree> -static Vector<Real> integrate_poly(UInt poly_degree, FEM & fem) { - Mesh & mesh = fem.getMesh(); - UInt dim = mesh.getSpatialDimension(); - - Matrix<Real> integration_points = - fem.getIntegrator().getIntegrationPoints<type, degree>(); - - // Vector<Real> integration_weights = - // fem.getIntegrator().getIntegrationWeights<type, degree>(); - - // for (UInt i = 0; i < integration_points.cols(); ++i) { - // std::cout << "q(" << i << ") = " << Vector<Real>(integration_points(i)) - // << " - w(" << i << ") = "<< integration_weights[i] << std::endl; - // } + auto integral() { + Polynomial<degree + 1> integral_; + integral_.set(0, 0.); + ; + for (size_t d = 0; d < degree + 1; ++d) { + integral_.set(1 + d, get(d) / double(d + 1)); + } + return integral_; + } - UInt nb_integration_points = integration_points.cols(); - UInt nb_element = mesh.getNbElement(type); + auto integrate(double a, double b) { + auto primitive = integral(); + return (primitive(b) - primitive(a)); + } - UInt shapes_size = ElementClass<type>::getShapeSize(); - Array<Real> shapes(0, shapes_size); - fem.getShapeFunctions().computeShapesOnIntegrationPoints<type>( - mesh.getNodes(), integration_points, shapes, _not_ghost); + double get(int i) const { return constants[i]; } - UInt vect_size = nb_integration_points * nb_element; - Array<Real> integration_points_pos(vect_size, dim); - fem.getShapeFunctions().interpolateOnIntegrationPoints<type>( - mesh.getNodes(), integration_points_pos, dim, shapes); + void set(int i, double a) { constants[i] = a; } - Array<Real> polynomial(vect_size, dim); +protected: + std::array<double, degree + 1> constants; +}; - Array<Real>::vector_iterator P_it = polynomial.begin(dim); - Array<Real>::vector_iterator P_end = polynomial.end(dim); - Array<Real>::const_vector_iterator x_it = integration_points_pos.begin(dim); +template <size_t degree> +std::ostream & operator<<(std::ostream & stream, const Polynomial<degree> & p) { + for (size_t d = 0; d < degree + 1; ++d) { + if (d != 0) + stream << " + "; - for (; P_it != P_end; ++P_it, ++x_it) { - *P_it = eval_poly(poly_degree, *x_it); - // std::cout << "Q = " << *x_it << std::endl; - // std::cout << "P(Q) = " << *P_it << std::endl; + stream << p.get(degree - d); + if (d != degree) + stream << "x ^ " << degree - d; } + return stream; +} - Vector<Real> res(dim); +/* -------------------------------------------------------------------------- */ +using TestDegreeTypes = std::tuple<degree_t<0>, degree_t<1>, degree_t<2>, degree_t<3>, degree_t<4>, degree_t<5>>; + +std::array<Polynomial<5>, 3> global_polys{ + {{0.40062394, 0.13703225, 0.51731446, 0.87830084, 0.5410543, 0.71842292}, + {0.41861835, 0.11080576, 0.49874043, 0.49077504, 0.85073835, 0.66259755}, + {0.92620845, 0.7503478, 0.62962232, 0.31662719, 0.64069644, 0.30878135}}}; + +template <typename T> +class TestGaussIntegrationFixture + : public TestFEMFixture<std::tuple_element_t<0, T>> { +protected: + using parent = TestFEMFixture<std::tuple_element_t<0, T>>; + static constexpr size_t degree{std::tuple_element_t<1, T>{}}; + +public: + TestGaussIntegrationFixture() : integration_points_pos(0, parent::dim) {} + + void SetUp() override { + parent::SetUp(); + auto integration_points = + this->fem->getIntegrator().template getIntegrationPoints < + parent::type, + degree == 0 ? 1 : degree > (); + + nb_integration_points = integration_points.cols(); + + auto shapes_size = ElementClass<parent::type>::getShapeSize(); + Array<Real> shapes(0, shapes_size); + this->fem->getShapeFunctions() + .template computeShapesOnIntegrationPoints<parent::type>( + this->mesh->getNodes(), integration_points, shapes, _not_ghost); + + auto vect_size = this->nb_integration_points * this->nb_element; + integration_points_pos.resize(vect_size); + this->fem->getShapeFunctions() + .template interpolateOnIntegrationPoints<parent::type>( + this->mesh->getNodes(), integration_points_pos, this->dim, shapes); + + for (size_t d = 0; d < this->dim; ++d) { + polys[d] = global_polys[d].extract(degree); + } + } - Array<Real> polynomial_1d(vect_size, 1); - for (UInt d = 0; d < dim; ++d) { - Array<Real>::const_vector_iterator P_it = polynomial.begin(dim); - Array<Real>::const_vector_iterator P_end = polynomial.end(dim); - Array<Real>::scalar_iterator P1_it = polynomial_1d.begin(); - for (; P_it != P_end; ++P_it, ++P1_it) { - *P1_it = (*P_it)(d); - // std::cout << "P(Q, d) = " << *P1_it << std::endl; + void testIntegrate() { + std::stringstream sstr; + sstr << this->type << ":" << this->degree; + SCOPED_TRACE(sstr.str().c_str()); + + auto vect_size = this->nb_integration_points * this->nb_element; + Array<Real> polynomial(vect_size); + size_t dim = parent::dim; + + for (size_t d = 0; d < dim; ++d) { + auto poly = this->polys[d]; + for (auto && pair : + zip(polynomial, make_view(this->integration_points_pos, dim))) { + auto & p = std::get<0>(pair); + auto & x = std::get<1>(pair); + p = poly(x(d)); + } + + auto res = + this->fem->getIntegrator() + .template integrate<parent::type, this->degree == 0 ? 1 : this->degree>(polynomial); + auto expect = poly.integrate(this->lower(d), this->upper(d)); + + for (size_t o = 0; o < dim; ++o) { + if (o == d) + continue; + expect *= this->upper(d) - this->lower(d); + } + + EXPECT_NEAR(expect, res, 5e-14); } - res(d) = fem.getIntegrator().integrate<type, degree>(polynomial_1d); } - return res; +protected: + UInt nb_integration_points; + std::array<Array<Real>, parent::dim> polynomial; + Array<Real> integration_points_pos; + std::array<Polynomial<5>, 3> polys; +}; + +/* -------------------------------------------------------------------------- */ +/* Tests */ +/* -------------------------------------------------------------------------- */ +TYPED_TEST_CASE_P(TestGaussIntegrationFixture); + +TYPED_TEST_P(TestGaussIntegrationFixture, ArbitraryOrder) { + this->testIntegrate(); } -int main(int argc, char * argv[]) { - akantu::initialize(argc, argv); - - const UInt dim = ElementClass<type>::getSpatialDimension(); - Mesh mesh(dim); - - std::stringstream meshfilename; - meshfilename << type << ".msh"; - mesh.read(meshfilename.str()); - - FEM fem(mesh, dim, "my_fem"); - - const Vector<Real> & lower = mesh.getLowerBounds(); - const Vector<Real> & upper = mesh.getUpperBounds(); - - bool ok = true; - - for (UInt d = 0; d < 6; ++d) { - Vector<Real> res(dim); - switch (d) { - case 0: - res = integrate_poly<1>(d, fem); - break; - case 1: - res = integrate_poly<1>(d, fem); - break; - case 2: - res = integrate_poly<2>(d, fem); - break; - case 3: - res = integrate_poly<3>(d, fem); - break; - case 4: - res = integrate_poly<4>(d, fem); - break; - case 5: - res = integrate_poly<5>(d, fem); - break; - } +REGISTER_TYPED_TEST_CASE_P(TestGaussIntegrationFixture, ArbitraryOrder); - Vector<Real> exact(dim); - exact = eval_int(d, lower, upper); - Vector<Real> error(dim); - error = exact - res; +using TestTypes = + gtest_list_t<tuple_split_t<50, cross_product_t<TestElementTypes, TestDegreeTypes>>>; - Real error_n = error.norm<L_inf>(); - if (error_n > 5e-14) { - std::cout << d << " -> Resultat " << res << " - "; - std::cout << "Exact" << exact << " -- "; - std::cout << error << " {" << error_n << "}" << std::endl; - ok = false; - } - } +INSTANTIATE_TYPED_TEST_CASE_P(Split1, TestGaussIntegrationFixture, TestTypes); + +using TestTypesTail = + gtest_list_t<tuple_split_tail_t<50, cross_product_t<TestElementTypes, TestDegreeTypes>>>; - finalize(); +INSTANTIATE_TYPED_TEST_CASE_P(Split2, TestGaussIntegrationFixture, TestTypesTail); - if (ok) - return 0; - else - return 1; } diff --git a/test/test_fe_engine/test_gradient.cc b/test/test_fe_engine/test_gradient.cc index bba6656c4..edd30e6a9 100644 --- a/test/test_fe_engine/test_gradient.cc +++ b/test/test_fe_engine/test_gradient.cc @@ -1,144 +1,103 @@ /** * @file test_gradient.cc * * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Peter Spijker <peter.spijker@epfl.ch> * * @date creation: Fri Sep 03 2010 * @date last modification: Thu Oct 15 2015 * * @brief test of the fem class * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * * @section DESCRIPTION * * This code is computing the gradient of a linear field and check that it gives * a constant result. It also compute the gradient the coordinates of the mesh * and check that it gives the identity * */ - /* -------------------------------------------------------------------------- */ -#include "fe_engine.hh" -#include "shape_lagrange.hh" -#include "integrator_gauss.hh" +#include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ #include <cstdlib> #include <iostream> /* -------------------------------------------------------------------------- */ using namespace akantu; -int main(int argc, char * argv[]) { - akantu::initialize(argc, argv); - debug::setDebugLevel(dblTest); - - const ElementType type = TYPE; - UInt dim = ElementClass<type>::getSpatialDimension(); - - Real eps = 1e-12; - std::cout << "Epsilon : " << eps << std::endl; - - Mesh my_mesh(dim); - - std::stringstream meshfilename; - meshfilename << type << ".msh"; - my_mesh.read(meshfilename.str()); - - FEEngine * fem = new FEEngineTemplate<IntegratorGauss, ShapeLagrange>( - my_mesh, dim, "my_fem"); - - fem->initShapeFunctions(); +namespace { +TYPED_TEST(TestFEMFixture, GradientPoly) { Real alpha[2][3] = {{13, 23, 31}, {11, 7, 5}}; - /// create the 2 component field - const Array<Real> & position = fem->getMesh().getNodes(); - Array<Real> const_val(fem->getMesh().getNbNodes(), 2, "const_val"); + const auto dim = this->dim; + const auto type = this->type; - UInt nb_element = my_mesh.getNbElement(type); - UInt nb_quadrature_points = fem->getNbIntegrationPoints(type) * nb_element; + const auto & position = this->fem->getMesh().getNodes(); + Array<Real> const_val(this->fem->getMesh().getNbNodes(), 2, "const_val"); + for(auto && pair : zip(make_view(position, dim), make_view(const_val, 2))) { + auto & pos = std::get<0>(pair); + auto & const_ = std::get<1>(pair); - Array<Real> grad_on_quad(nb_quadrature_points, 2 * dim, "grad_on_quad"); - for (UInt i = 0; i < const_val.size(); ++i) { - const_val(i, 0) = 0; - const_val(i, 1) = 0; + const_.set(0.); for (UInt d = 0; d < dim; ++d) { - const_val(i, 0) += alpha[0][d] * position(i, d); - const_val(i, 1) += alpha[1][d] * position(i, d); + const_(0) += alpha[0][d] * pos(d); + const_(1) += alpha[1][d] * pos(d); } } /// compute the gradient - fem->gradientOnIntegrationPoints(const_val, grad_on_quad, 2, type); - - // std::cout << "Linear array on nodes : " << const_val << std::endl; - // std::cout << "Gradient on quad : " << grad_on_quad << std::endl; + Array<Real> grad_on_quad(this->nb_quadrature_points_total, 2 * dim, "grad_on_quad"); + this->fem->gradientOnIntegrationPoints(const_val, grad_on_quad, 2, type); /// check the results - Array<Real>::matrix_iterator it = grad_on_quad.begin(2, dim); - Array<Real>::matrix_iterator it_end = grad_on_quad.end(2, dim); - for (; it != it_end; ++it) { + for(auto && grad : make_view(grad_on_quad, 2, dim)) { for (UInt d = 0; d < dim; ++d) { - Matrix<Real> & grad = *it; - if (!(std::abs(grad(0, d) - alpha[0][d]) < eps) || - !(std::abs(grad(1, d) - alpha[1][d]) < eps)) { - std::cout << "Error gradient is not correct " << (*it)(0, d) << " " - << alpha[0][d] << " (" << std::abs((*it)(0, d) - alpha[0][d]) - << ")" - << " - " << (*it)(1, d) << " " << alpha[1][d] << " (" - << std::abs((*it)(1, d) - alpha[1][d]) << ")" - << " - " << d << std::endl; - std::cout << *it << std::endl; - exit(EXIT_FAILURE); - } + EXPECT_NEAR(grad(0, d), alpha[0][d], 5e-13); + EXPECT_NEAR(grad(1, d), alpha[1][d], 5e-13); } } +} + +TYPED_TEST(TestFEMFixture, GradientPositions) { + const auto dim = this->dim; + const auto type = this->type; - // compute gradient of coordinates + UInt nb_quadrature_points = this->fem->getNbIntegrationPoints(type) * this->nb_element; Array<Real> grad_coord_on_quad(nb_quadrature_points, dim * dim, "grad_coord_on_quad"); - fem->gradientOnIntegrationPoints(my_mesh.getNodes(), grad_coord_on_quad, - my_mesh.getSpatialDimension(), type); - - // std::cout << "Node positions : " << my_mesh.getNodes() << std::endl; - // std::cout << "Gradient of nodes : " << grad_coord_on_quad << std::endl; - - Array<Real>::matrix_iterator itp = grad_coord_on_quad.begin(dim, dim); - Array<Real>::matrix_iterator itp_end = grad_coord_on_quad.end(dim, dim); - - for (; itp != itp_end; ++itp) { - for (UInt i = 0; i < dim; ++i) { - for (UInt j = 0; j < dim; ++j) { - if (!(std::abs((*itp)(i, j) - (i == j)) < eps)) { - std::cout << *itp << std::endl; - exit(EXIT_FAILURE); - } - } - } - } - delete fem; - finalize(); + const auto & position = this->mesh->getNodes(); + this->fem->gradientOnIntegrationPoints(position, grad_coord_on_quad, + dim, type); + + auto I = Matrix<Real>::eye(UInt(dim)); + + for(auto && grad : make_view(grad_coord_on_quad, dim, dim)) { + auto diff = (I - grad).template norm<L_inf>(); + + EXPECT_NEAR(0., diff, 2e-14); + } +} - return EXIT_SUCCESS; } diff --git a/test/test_fe_engine/test_integrate.cc b/test/test_fe_engine/test_integrate.cc index f9ca9251d..40200579e 100644 --- a/test/test_fe_engine/test_integrate.cc +++ b/test/test_fe_engine/test_integrate.cc @@ -1,105 +1,75 @@ /** * @file test_integrate.cc * * @author Guillaume Anciaux <guillaume.anciaux@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Peter Spijker <peter.spijker@epfl.ch> * * @date creation: Fri Sep 03 2010 * @date last modification: Thu Oct 15 2015 * * @brief test of the fem class * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ - /* -------------------------------------------------------------------------- */ -#include "fe_engine.hh" -#include "shape_lagrange.hh" -#include "integrator_gauss.hh" +#include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ #include <cstdlib> #include <iostream> /* -------------------------------------------------------------------------- */ using namespace akantu; -int main(int argc, char *argv[]) { - akantu::initialize(argc, argv); - - debug::setDebugLevel(dblTest); - - const ElementType type = TYPE; - UInt dim = ElementClass<type>::getSpatialDimension(); - - Real eps = 3e-13; - std::cout << "Epsilon : " << eps << std::endl; - - Mesh my_mesh(dim); - - std::stringstream meshfilename; meshfilename << type << ".msh"; - my_mesh.read(meshfilename.str()); +namespace { - FEEngine *fem = new FEEngineTemplate<IntegratorGauss,ShapeLagrange>(my_mesh, dim, "my_fem"); +TYPED_TEST(TestFEMFixture, IntegrateConstant) { + const auto type = this->type; - fem->initShapeFunctions(); + const auto & position = this->fem->getMesh().getNodes(); - UInt nb_element = my_mesh.getNbElement(type); - UInt nb_quadrature_points = fem->getNbIntegrationPoints(type) * nb_element; + Array<Real> const_val(position.size(), 2, "const_val"); + Array<Real> val_on_quad(this->nb_quadrature_points_total, 2, "val_on_quad"); - Array<Real> const_val(fem->getMesh().getNbNodes(), 2, "const_val"); - Array<Real> val_on_quad(nb_quadrature_points, 2 , "val_on_quad"); - - for (UInt i = 0; i < const_val.size(); ++i) { - const_val.storage()[i * 2 + 0] = 1.; - const_val.storage()[i * 2 + 1] = 2.; + Vector<Real> value{1, 2}; + for (auto && const_ : make_view(const_val, 2)) { + const_ = value; } - //interpolate function on quadrature points - fem->interpolateOnIntegrationPoints(const_val, val_on_quad, 2, type); + // interpolate function on quadrature points + this->fem->interpolateOnIntegrationPoints(const_val, val_on_quad, 2, type); - //integrate function on elements - akantu::Array<akantu::Real> int_val_on_elem(nb_element, 2, "int_val_on_elem"); - fem->integrate(val_on_quad, int_val_on_elem, 2, type); + // integrate function on elements + Array<Real> int_val_on_elem(this->nb_element, 2, "int_val_on_elem"); + this->fem->integrate(val_on_quad, int_val_on_elem, 2, type); // get global integration value - Real value[2] = {0,0}; - std::cout << "Val on quads : " << val_on_quad << std::endl; - std::cout << "Integral on elements : " << int_val_on_elem << std::endl; - - for (UInt i = 0; i < fem->getMesh().getNbElement(type); ++i) { - value[0] += int_val_on_elem.storage()[2*i]; - value[1] += int_val_on_elem.storage()[2*i+1]; - } + Vector<Real> sum{0., 0.}; - std::cout << "integral on the mesh of 1 is " << value[0] << " and of 2 is " << value[1] << std::endl; - - delete fem; - finalize(); - - if(!(std::abs(value[0] - 1.) < eps && std::abs(value[1] - 2.) < eps)) { - std::cout << "|1 - " << value[0] << "| = " << std::abs(value[0] - 1.) << std::endl - << "|2 - " << value[1] << "| = " << std::abs(value[1] - 2.) << std::endl; - return EXIT_FAILURE; + for (auto && int_ : make_view(int_val_on_elem, 2)) { + sum += int_; } - return EXIT_SUCCESS; + auto diff = (value - sum).template norm<L_inf>(); + EXPECT_NEAR(0, diff, 1e-14); } + +} // namespace diff --git a/test/test_fe_engine/test_interpolate.cc b/test/test_fe_engine/test_interpolate.cc index 9b8d626fd..e01b59ec9 100644 --- a/test/test_fe_engine/test_interpolate.cc +++ b/test/test_fe_engine/test_interpolate.cc @@ -1,92 +1,72 @@ /** * @file test_interpolate.cc * * @author Nicolas Richart <nicolas.richart@epfl.ch> * * @date creation: Fri Sep 03 2010 * @date last modification: Thu Oct 15 2015 * * @brief test of the fem class * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ -#include "fe_engine.hh" -#include "shape_lagrange.hh" -#include "integrator_gauss.hh" -/* -------------------------------------------------------------------------- */ -#include <cstdlib> -#include <iostream> +#include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ using namespace akantu; -int main(int argc, char *argv[]) { - akantu::initialize(argc, argv); - - debug::setDebugLevel(dblTest); - const ElementType type = TYPE; - UInt dim = ElementClass<type>::getSpatialDimension(); - - Real eps = 3e-13; - std::cout << "Epsilon : " << eps << std::endl; - - Mesh my_mesh(dim); - - std::stringstream meshfilename; meshfilename << type << ".msh"; - my_mesh.read(meshfilename.str()); +namespace { - FEEngine *fem = new FEEngineTemplate<IntegratorGauss,ShapeLagrange>(my_mesh, dim, "my_fem"); +TYPED_TEST(TestFEMFixture, InterpolateConstant) { + const auto type = this->type; - fem->initShapeFunctions(); + const auto & position = this->fem->getMesh().getNodes(); - Array<Real> const_val(fem->getMesh().getNbNodes(), 2, "const_val"); + Array<Real> const_val(position.size(), 2, "const_val"); + Array<Real> val_on_quad(this->nb_quadrature_points_total, 2, "val_on_quad"); - UInt nb_element = my_mesh.getNbElement(type); - UInt nb_quadrature_points = fem->getNbIntegrationPoints(type) * nb_element; - - Array<Real> val_on_quad(nb_quadrature_points, 2, "val_on_quad"); - - for (UInt i = 0; i < const_val.size(); ++i) { - const_val.storage()[i * 2 + 0] = 1.; - const_val.storage()[i * 2 + 1] = 2.; + Vector<Real> value{1, 2}; + for (auto && const_ : make_view(const_val, 2)) { + const_ = value; } - fem->interpolateOnIntegrationPoints(const_val, val_on_quad, 2, type); + // interpolate function on quadrature points + this->fem->interpolateOnIntegrationPoints(const_val, val_on_quad, 2, type); - std::cout << "Interpolation of array : " << const_val << std::endl; - std::cout << "Gives on quads : " << val_on_quad << std::endl; + for (auto && int_ : make_view(val_on_quad, 2)) { + auto diff = (value - int_).template norm<L_inf>(); + EXPECT_NEAR(0, diff, 1e-14); + } +} - // interpolate coordinates - Array<Real> coord_on_quad(nb_quadrature_points, my_mesh.getSpatialDimension(), "coord_on_quad"); +// TYPED_TEST(TestFEMFixture, InterpolatePosition) { +// const auto dim = this->dim; +// const auto type = this->type; +// const auto & position = this->fem->getMesh().getNodes(); - fem->interpolateOnIntegrationPoints(my_mesh.getNodes(), - coord_on_quad, - my_mesh.getSpatialDimension(), - type); - std::cout << "Interpolations of node coordinates : " << my_mesh.getNodes() << std::endl; - std::cout << "Gives : " << coord_on_quad << std::endl; +// Array<Real> coord_on_quad(this->nb_quadrature_points_total, dim, +// "coord_on_quad"); - delete fem; - finalize(); +// this->fem->interpolateOnIntegrationPoints(position, coord_on_quad, dim, type); +// } - return EXIT_SUCCESS; -} +} // namespace diff --git a/test/test_fe_engine/test_inverse_map.cc b/test/test_fe_engine/test_inverse_map.cc index daeac8134..34b2496c3 100644 --- a/test/test_fe_engine/test_inverse_map.cc +++ b/test/test_fe_engine/test_inverse_map.cc @@ -1,110 +1,72 @@ /** * @file test_inverse_map.cc * * @author Guillaume Anciaux <guillaume.anciaux@epfl.ch> * * @date creation: Fri Sep 03 2010 * @date last modification: Thu Oct 15 2015 * * @brief test of the fem class * * @section LICENSE * * Copyright (©) 2010-2012, 2014, 2015 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ - -/* -------------------------------------------------------------------------- */ -#include "aka_common.hh" -#include "aka_iterators.hh" -#include "fe_engine.hh" -#include "integrator_gauss.hh" -#include "shape_lagrange.hh" /* -------------------------------------------------------------------------- */ -#include <iostream> +#include "test_fe_engine_fixture.hh" /* -------------------------------------------------------------------------- */ using namespace akantu; -int main(int argc, char * argv[]) { - akantu::initialize(argc, argv); - - const auto type = TYPE; - auto dim = ElementClass<type>::getSpatialDimension(); - - Mesh my_mesh(dim); - std::stringstream meshfilename; - meshfilename << type << ".msh"; - - my_mesh.read(meshfilename.str()); - - const auto & lower = my_mesh.getLowerBounds(); - const auto & upper = my_mesh.getUpperBounds(); - UInt nb_elements = my_mesh.getNbElement(type); +namespace { - auto fem = std::make_unique<FEEngineTemplate<IntegratorGauss, ShapeLagrange>>( - my_mesh, dim, "my_fem"); +TYPED_TEST(TestFEMFixture, InverseMap) { + const auto type = this->type; + const auto dim = this->dim; - fem->initShapeFunctions(); Matrix<Real> quad = GaussIntegrationElement<type>::getQuadraturePoints(); + const auto & position = this->fem->getMesh().getNodes(); + /// get the quadrature points coordinates - Array<Real> coord_on_quad(quad.cols() * nb_elements, - my_mesh.getSpatialDimension(), "coord_on_quad"); + Array<Real> coord_on_quad(quad.cols() * this->nb_element, dim, + "coord_on_quad"); - fem->interpolateOnIntegrationPoints(my_mesh.getNodes(), coord_on_quad, - my_mesh.getSpatialDimension(), type); + this->fem->interpolateOnIntegrationPoints(position, coord_on_quad, dim, type); Vector<Real> natural_coords(dim); - /// loop over the quadrature points - auto it = coord_on_quad.begin_reinterpret(dim, quad.cols(), nb_elements); - auto end = coord_on_quad.end_reinterpret(dim, quad.cols(), nb_elements); + auto length = (this->upper - this->lower).template norm<L_inf>(); - auto length = (upper - lower).norm<L_inf>(); + for(auto && enum_ : enumerate(make_view(coord_on_quad, dim, quad.cols()))) { + auto el = std::get<0>(enum_); + const auto & quads_coords = std::get<1>(enum_); - auto eps = 5e-12; - UInt el = 0; - for (; it != end; ++it, ++el) { - const auto & quads_coords = *it; for (auto q : arange(quad.cols())) { Vector<Real> quad_coord = quads_coords(q); Vector<Real> ref_quad_coord = quad(q); - fem->inverseMap(quad_coord, el, type, natural_coords); + this->fem->inverseMap(quad_coord, el, type, natural_coords); auto dis_normalized = ref_quad_coord.distance(natural_coords) / length; - if (not(dis_normalized < eps)) { - std::cout << "Real coordinates inversion test failed : " - << std::scientific << natural_coords << " - " - << ref_quad_coord << " / " << length << " = " << dis_normalized << std::endl; - return 1; - } - - // std::cout << "Real coordinates inversion : " << std::scientific - // << natural_coords << " = " << ref_quad_coord << " (" - // << dis_normalized << ")" << std::endl; + EXPECT_NEAR(0., dis_normalized, 5e-12); } } - - std::cout << "inverse completed over " << nb_elements << " elements" - << std::endl; - - finalize(); - - return 0; } + +} // namespace diff --git a/test/test_gtest_utils.hh b/test/test_gtest_utils.hh new file mode 100644 index 000000000..c2cbe0de6 --- /dev/null +++ b/test/test_gtest_utils.hh @@ -0,0 +1,110 @@ +/* -------------------------------------------------------------------------- */ +#include "aka_common.hh" +/* -------------------------------------------------------------------------- */ +#include <boost/preprocessor.hpp> +#include <gtest/gtest.h> +#include <tuple> +/* -------------------------------------------------------------------------- */ + +namespace { + +/* -------------------------------------------------------------------------- */ +template <::akantu::ElementType t> +using element_type_t = std::integral_constant<::akantu::ElementType, t>; + +/* -------------------------------------------------------------------------- */ +template <typename... T> struct gtest_list {}; + +template <typename... Ts> struct gtest_list<std::tuple<Ts...>> { + using type = ::testing::Types<Ts...>; +}; + +template <typename... T> using gtest_list_t = typename gtest_list<T...>::type; + +/* -------------------------------------------------------------------------- */ +template <typename... T> struct tuple_concat {}; + +template <typename... T1s, typename... T2s> +struct tuple_concat<std::tuple<T1s...>, std::tuple<T2s...>> { + using type = std::tuple<T1s..., T2s...>; +}; + +template <typename... T> +using tuple_concat_t = typename tuple_concat<T...>::type; + +/* -------------------------------------------------------------------------- */ +template <template <typename> class Pred, typename... Ts> struct tuple_filter {}; + +template <template <typename> class Pred, typename T> +struct tuple_filter<Pred, std::tuple<T>> { + using type = std::conditional_t<Pred<T>::value, std::tuple<T>, std::tuple<>>; +}; + +template <template <typename> class Pred, typename T, typename... Ts> +struct tuple_filter<Pred, std::tuple<T, Ts...>> { + using type = tuple_concat_t< + typename tuple_filter<Pred, std::tuple<T>>::type, + typename tuple_filter<Pred, std::tuple<Ts...>>::type>; +}; + +template <template <typename> class Pred, typename... Ts> +using tuple_filter_t = typename tuple_filter<Pred, Ts...>::type; + +/* -------------------------------------------------------------------------- */ +template <size_t N, typename... Ts> struct tuple_split {}; + +template <size_t N, typename T, typename... Ts> +struct tuple_split<N, std::tuple<T, Ts...>> { +protected: + using split = tuple_split<N - 1, std::tuple<Ts...>>; + +public: + using type = tuple_concat_t<std::tuple<T>, typename split::type>; + using type_tail = typename split::type_tail; +}; + +template <typename T, typename... Ts> +struct tuple_split<1, std::tuple<T, Ts...>> { + using type = std::tuple<T>; + using type_tail = std::tuple<Ts...>; +}; + +template <size_t N, typename... T> +using tuple_split_t = typename tuple_split<N, T...>::type; + +template <size_t N, typename... T> +using tuple_split_tail_t = typename tuple_split<N, T...>::type_tail; + +/* -------------------------------------------------------------------------- */ +template <typename... T> struct cross_product {}; + +template <typename... T2s> +struct cross_product<std::tuple<>, std::tuple<T2s...>> { + using type = std::tuple<>; +}; + +template <typename T1, typename... T1s, typename... T2s> +struct cross_product<std::tuple<T1, T1s...>, std::tuple<T2s...>> { + using type = tuple_concat_t< + std::tuple<std::tuple<T1, T2s>...>, + typename cross_product<std::tuple<T1s...>, std::tuple<T2s...>>::type>; +}; + +template <typename... T> +using cross_product_t = typename cross_product<T...>::type; +/* -------------------------------------------------------------------------- */ + +#define OP_CAT(s, data, elem) element_type_t<::akantu::elem> + +using TestElementTypesAll = std::tuple<BOOST_PP_SEQ_ENUM( + BOOST_PP_SEQ_TRANSFORM(OP_CAT, _, AKANTU_ek_regular_ELEMENT_TYPE))>; +} // namespace + +template <typename T> +using is_point_1 = std::is_same<T, element_type_t<::akantu::_point_1>>; + +template <typename T> +using not_is_point_1 = + aka::negation<std::is_same<T, element_type_t<::akantu::_point_1>>>; + +using TestElementTypes = tuple_filter_t<not_is_point_1, TestElementTypesAll>;