From b67f1e658fb754bf4065975dd223cfe4378cdf93 Mon Sep 17 00:00:00 2001 From: Sam Reeve Date: Fri, 3 Apr 2020 15:35:06 -0700 Subject: [PATCH] Initial unit testing; add integrator test --- .travis.yml | 2 + CMakeLists.txt | 23 ++++- unit_test/CMakeLists.txt | 49 ++++++++++ unit_test/TestCUDA_Category.hpp | 19 ++++ unit_test/TestCUDA_UVM_Category.hpp | 23 +++++ unit_test/TestOPENMP_Category.hpp | 19 ++++ unit_test/TestSERIAL_Category.hpp | 19 ++++ unit_test/tstIntegrator.hpp | 144 ++++++++++++++++++++++++++++ unit_test/unit_test_main.cpp | 28 ++++++ 9 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 unit_test/CMakeLists.txt create mode 100644 unit_test/TestCUDA_Category.hpp create mode 100644 unit_test/TestCUDA_UVM_Category.hpp create mode 100644 unit_test/TestOPENMP_Category.hpp create mode 100644 unit_test/TestSERIAL_Category.hpp create mode 100644 unit_test/tstIntegrator.hpp create mode 100644 unit_test/unit_test_main.cpp diff --git a/.travis.yml b/.travis.yml index dd2fb7ea..5adc7ba2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,12 +67,14 @@ script: - mkdir build && pushd build && cmake -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_PREFIX_PATH="$HOME/Cabana;$HOME/kokkos" + -DCabanaMD_ENABLE_TESTING=ON -DCabanaMD_ENABLE_Serial=OFF -DCabanaMD_ENABLE_NNP=${NNP} -DN2P2_DIR=$HOME/build/n2p2 ${CABANAMD_OPTS[@]} .. && make -j4 VERBOSE=1 && + make test CTEST_OUTPUT_ON_FAILURE=1 && make format && git diff --exit-code && popd diff --git a/CMakeLists.txt b/CMakeLists.txt index 4eb7750a..2881257a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,25 @@ include(GNUInstallDirs) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +##---------------------------------------------------------------------------## +# Download and unpack googletest +##---------------------------------------------------------------------------## +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.10.0 +) +FetchContent_GetProperties(googletest) +if(NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) +endif() + +# Prevent GoogleTest from overriding our compiler/linker options +# when building with Visual Studio +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + ##---------------------------------------------------------------------------## # Set up main options (inherit from Kokkos and Cabana CMake) ##---------------------------------------------------------------------------## @@ -67,7 +86,7 @@ add_subdirectory(src) add_subdirectory(bin) ##---------------------------------------------------------------------------## -## Enable Tests? +## Unit tests ##---------------------------------------------------------------------------## option(CabanaMD_ENABLE_TESTING "Build tests" OFF) if(CabanaMD_ENABLE_TESTING) @@ -80,7 +99,7 @@ endif() ##---------------------------------------------------------------------------## find_package(CLANG_FORMAT) if(CLANG_FORMAT_FOUND) - file(GLOB_RECURSE FORMAT_SOURCES src/*.cpp src/*.h bin/*.cpp) + file(GLOB_RECURSE FORMAT_SOURCES src/*.cpp src/*.h bin/*.cpp unit_test/*.cpp unit_test/*.hpp) add_custom_target(format COMMAND ${CLANG_FORMAT_EXECUTABLE} -i -style=file ${FORMAT_SOURCES} DEPENDS ${FORMAT_SOURCES}) diff --git a/unit_test/CMakeLists.txt b/unit_test/CMakeLists.txt new file mode 100644 index 00000000..be614951 --- /dev/null +++ b/unit_test/CMakeLists.txt @@ -0,0 +1,49 @@ +set(gtest_args --gtest_color=yes) + +##--------------------------------------------------------------------------## +## On-node tests +##--------------------------------------------------------------------------## +macro(CabanaMD_add_tests) + cmake_parse_arguments(CABANAMD_UNIT_TEST "MPI" "" "NAMES" ${ARGN}) + set(CABANAMD_UNIT_TEST_MPIEXEC_NUMPROCS 1 2) + if(MPIEXEC_MAX_NUMPROCS GREATER 2) + list(APPEND CABANAMD_UNIT_TEST_MPIEXEC_NUMPROCS ${MPIEXEC_MAX_NUMPROCS}) + endif() + set(CABANAMD_UNIT_TEST_NUMTHREADS 1 2) + set(CABANAMD_UNIT_TEST_MAIN unit_test_main.cpp) + + foreach(_device Serial OpenMP Cuda) + if(CabanaMD_ENABLE_${_device}) + string(TOUPPER ${_device} _uppercase_device) + set(_dir ${CMAKE_CURRENT_BINARY_DIR}/${_uppercase_device}) + file(MAKE_DIRECTORY ${_dir}) + foreach(_test ${CABANAMD_UNIT_TEST_NAMES}) + set(_file ${_dir}/tst${_test}_${_uppercase_device}.cpp) + file(WRITE ${_file} + "#include \n" + "#include \n" + ) + set(_target ${_test}_test_${_uppercase_device}) + add_executable(${_target} ${_file} ${CABANAMD_UNIT_TEST_MAIN}) + target_include_directories(${_target} PRIVATE ${_dir} ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(${_target} PRIVATE CabanaMD gtest CabanaMD) + + foreach(_np ${CABANAMD_UNIT_TEST_MPIEXEC_NUMPROCS}) + if(_device STREQUAL THREADS OR _device STREQUAL OpenMP) + foreach(_thread ${CABANAMD_UNIT_TEST_NUMTHREADS}) + add_test(NAME ${_target}_${_np}_${_thread} COMMAND + ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${_np} ${MPIEXEC_PREFLAGS} + ${_target} ${MPIEXEC_POSTFLAGS} ${gtest_args} --kokkos-threads=${_thread}) + endforeach() + else() + add_test(NAME ${_target}_${_np} COMMAND + ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${_np} ${MPIEXEC_PREFLAGS} + ${_target} ${MPIEXEC_POSTFLAGsS} ${gtest_args}) + endif() + endforeach() + endforeach() + endif() + endforeach() +endmacro() + +CabanaMD_add_tests(MPI NAMES Integrator) diff --git a/unit_test/TestCUDA_Category.hpp b/unit_test/TestCUDA_Category.hpp new file mode 100644 index 00000000..13bae5ed --- /dev/null +++ b/unit_test/TestCUDA_Category.hpp @@ -0,0 +1,19 @@ +/**************************************************************************** + * Copyright (c) 2018-2019 by the Cabana authors * + * All rights reserved. * + * * + * This file is part of the Cabana library. Cabana is distributed under a * + * BSD 3-clause license. For the licensing terms see the LICENSE file in * + * the top-level directory. * + * * + * SPDX-License-Identifier: BSD-3-Clause * + ****************************************************************************/ + +#ifndef CABANA_TEST_CUDA_CATEGORY_HPP +#define CABANA_TEST_CUDA_CATEGORY_HPP + +#define TEST_CATEGORY cuda +#define TEST_EXECSPACE Kokkos::Cuda +#define TEST_MEMSPACE Kokkos::CudaSpace + +#endif // end CABANA_TEST_CUDA_CATEGORY_HPP diff --git a/unit_test/TestCUDA_UVM_Category.hpp b/unit_test/TestCUDA_UVM_Category.hpp new file mode 100644 index 00000000..0858b81b --- /dev/null +++ b/unit_test/TestCUDA_UVM_Category.hpp @@ -0,0 +1,23 @@ +/**************************************************************************** + * Copyright (c) 2018-2019 by the Cabana authors * + * All rights reserved. * + * * + * This file is part of the Cabana library. Cabana is distributed under a * + * BSD 3-clause license. For the licensing terms see the LICENSE file in * + * the top-level directory. * + * * + * SPDX-License-Identifier: BSD-3-Clause * + ****************************************************************************/ + +#ifndef CABANA_TEST_CUDAUVM_CATEGORY_HPP +#define CABANA_TEST_CUDAUVM_CATEGORY_HPP + +#include + +#include + +#define TEST_CATEGORY cuda_uvm +#define TEST_EXECSPACE Kokkos::Cuda +#define TEST_MEMSPACE Kokkos::CudaUVMSpace + +#endif // end CABANA_TEST_CUDAUVM_CATEGORY_HPP diff --git a/unit_test/TestOPENMP_Category.hpp b/unit_test/TestOPENMP_Category.hpp new file mode 100644 index 00000000..b7d5c008 --- /dev/null +++ b/unit_test/TestOPENMP_Category.hpp @@ -0,0 +1,19 @@ +/**************************************************************************** + * Copyright (c) 2018-2019 by the Cabana authors * + * All rights reserved. * + * * + * This file is part of the Cabana library. Cabana is distributed under a * + * BSD 3-clause license. For the licensing terms see the LICENSE file in * + * the top-level directory. * + * * + * SPDX-License-Identifier: BSD-3-Clause * + ****************************************************************************/ + +#ifndef CABANA_TEST_OPENMP_CATEGORY_HPP +#define CABANA_TEST_OPENMP_CATEGORY_HPP + +#define TEST_CATEGORY openmp +#define TEST_EXECSPACE Kokkos::OpenMP +#define TEST_MEMSPACE Kokkos::HostSpace + +#endif // end CABANA_TEST_OPENMP_CATEGORY_HPP diff --git a/unit_test/TestSERIAL_Category.hpp b/unit_test/TestSERIAL_Category.hpp new file mode 100644 index 00000000..e96803b0 --- /dev/null +++ b/unit_test/TestSERIAL_Category.hpp @@ -0,0 +1,19 @@ +/**************************************************************************** + * Copyright (c) 2018-2019 by the Cabana authors * + * All rights reserved. * + * * + * This file is part of the Cabana library. Cabana is distributed under a * + * BSD 3-clause license. For the licensing terms see the LICENSE file in * + * the top-level directory. * + * * + * SPDX-License-Identifier: BSD-3-Clause * + ****************************************************************************/ + +#ifndef CABANA_TEST_SERIAL_CATEGORY_HPP +#define CABANA_TEST_SERIAL_CATEGORY_HPP + +#define TEST_CATEGORY serial +#define TEST_EXECSPACE Kokkos::Serial +#define TEST_MEMSPACE Kokkos::HostSpace + +#endif // end CABANA_TEST_SERIAL_CATEGORY_HPP diff --git a/unit_test/tstIntegrator.hpp b/unit_test/tstIntegrator.hpp new file mode 100644 index 00000000..0bbbb90d --- /dev/null +++ b/unit_test/tstIntegrator.hpp @@ -0,0 +1,144 @@ +/**************************************************************************** + * Copyright (c) 2018-2019 by the Cabana authors * + * All rights reserved. * + * * + * This file is part of the Cabana library. Cabana is distributed under a * + * BSD 3-clause license. For the licensing terms see the LICENSE file in * + * the top-level directory. * + * * + * SPDX-License-Identifier: BSD-3-Clause * + ****************************************************************************/ + +#include +#include + +#include +#include +#include + +#include + +#include +#include + +namespace Test +{ +//---------------------------------------------------------------------------// +// Create particles. +template +t_System createParticles( const int num_particle, const int num_ghost, + const double box_min, const double box_max ) +{ + t_System system; + system.init(); + + // Manually setup what would be done in input + system.dt = 0.005; + system.mvv2e = 1.0; + system.mass( 0 ) = 1.0; + + system.resize( num_particle ); + system.N_local = num_particle - num_ghost; + system.N_ghost = num_ghost; + + auto box = box_max - box_min; + system.domain_x = system.domain_y = system.domain_z = box; + system.domain_lo_x = system.domain_lo_y = system.domain_lo_z = box_min; + system.domain_hi_x = system.domain_hi_y = system.domain_hi_z = box_max; + + system.slice_integrate(); + auto x = system.x; + auto v = system.v; + auto f = system.f; + auto type = system.type; + + using PoolType = Kokkos::Random_XorShift64_Pool; + using RandomType = Kokkos::Random_XorShift64; + PoolType pool( 342343901 ); + auto random_coord_op = KOKKOS_LAMBDA( const int p ) + { + auto gen = pool.get_state(); + for ( int d = 0; d < 3; ++d ) + { + x( p, d ) = + Kokkos::rand::draw( gen, box_min, box_max ); + v( p, d ) = + Kokkos::rand::draw( gen, -1.0, 1.0 ); + f( p, d ) = + Kokkos::rand::draw( gen, -1.0, 1.0 ); + type( p ) = 0; + } + pool.free_state( gen ); + }; + Kokkos::RangePolicy exec_policy( 0, num_particle ); + Kokkos::parallel_for( exec_policy, random_coord_op ); + Kokkos::fence(); + + return system; +} + +//---------------------------------------------------------------------------// +template +void testIntegratorReversibility( int steps ) +{ + // Create the AoSoA and fill with random particle positions + int num_particle = 1e3; + int num_ghost = 200; + double test_radius = 2.32; + double box_min = -5.3 * test_radius; + double box_max = 4.7 * test_radius; + + t_System system = + createParticles( num_particle, num_ghost, box_min, box_max ); + Integrator integrator( &system ); + + // Keep a copy of initial positions on the host + using DataTypes = Cabana::MemberTypes; + using AoSoA_t = Cabana::AoSoA; + AoSoA_t x_aosoa_init( "x_init_host", num_particle ); + auto x_init = Cabana::slice<0>( x_aosoa_init ); + system.slice_x(); + Cabana::deep_copy( x_init, system.x ); + + // Integrate one step + for ( int s = 0; s < steps; ++s ) + { + integrator.initial_integrate( &system ); + integrator.final_integrate( &system ); + } + + // Reverse system + system.slice_v(); + for ( int p = 0; p < num_particle; ++p ) + for ( int d = 0; d < 3; ++d ) + system.v( p, d ) *= -1.0; + + // Integrate back + for ( int s = 0; s < steps; ++s ) + { + integrator.initial_integrate( &system ); + integrator.final_integrate( &system ); + } + + // Make a copy of final results on the host + AoSoA_t x_aosoa_final( "x_final_host", num_particle ); + auto x_final = Cabana::slice<0>( x_aosoa_final ); + Cabana::deep_copy( x_final, system.x ); + + // Check the results + for ( int p = 0; p < num_particle; ++p ) + for ( int d = 0; d < 3; ++d ) + EXPECT_FLOAT_EQ( x_final( p, d ), x_init( p, d ) ); +} + +//---------------------------------------------------------------------------// +// TESTS +//---------------------------------------------------------------------------// +TEST( TEST_CATEGORY, reversibility_test ) +{ + testIntegratorReversibility>( 100 ); +} + +//---------------------------------------------------------------------------// + +} // end namespace Test diff --git a/unit_test/unit_test_main.cpp b/unit_test/unit_test_main.cpp new file mode 100644 index 00000000..1708b0d4 --- /dev/null +++ b/unit_test/unit_test_main.cpp @@ -0,0 +1,28 @@ +/**************************************************************************** + * Copyright (c) 2018-2019 by the Cabana authors * + * All rights reserved. * + * * + * This file is part of the Cabana library. Cabana is distributed under a * + * BSD 3-clause license. For the licensing terms see the LICENSE file in * + * the top-level directory. * + * * + * SPDX-License-Identifier: BSD-3-Clause * + ****************************************************************************/ + +#include + +#include +#include + +#include + +int main( int argc, char *argv[] ) +{ + MPI_Init( &argc, &argv ); + Kokkos::initialize( argc, argv ); + ::testing::InitGoogleTest( &argc, argv ); + int return_val = RUN_ALL_TESTS(); + Kokkos::finalize(); + MPI_Finalize(); + return return_val; +}