diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 02726edb..7a5ff747 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,7 @@ jobs: run: | mkdir build cd build - cmake -DENABLE_COVERAGE=ON -DENABLE_ARRAY_INDEX_CHECKING=ON .. + cmake -DENABLE_COVERAGE=ON -DENABLE_ARRAY_INDEX_CHECKING=ON -DENABLE_UNIT_TEST=ON .. make -j2 - name: Install test dependencies run: | @@ -39,6 +39,10 @@ jobs: git lfs pull cd tests conda run -n svfsiplus pytest -rPv --durations=0 + - name: Run unit tests + run: | + cd build/svFSI-build/Source/svFSI + ctest --verbose - name: Generate code coverage if: startsWith(matrix.os, 'ubuntu-22.04') run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 30e2209a..20105156 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,7 @@ set(SV_PETSC_DIR "" CACHE STRING "Path to a local install of the PETSc linear al set(ENABLE_COVERAGE OFF CACHE BOOL "Enable code coverage") set(ENABLE_ARRAY_INDEX_CHECKING OFF CACHE BOOL "Enable Array index checking") set(SV_LOCAL_VTK_PATH "" CACHE STRING "Path to a local build of VTK.") +set(ENABLE_UNIT_TEST OFF CACHE BOOL "Enable Unit Test by Google Test") #----------------------------------------------------------------------------- # RPATH handling @@ -147,6 +148,7 @@ ExternalProject_Add(svFSI #-DSV_USE_PETSC:BOOL=${SV_USE_PETSC} -DSV_PETSC_DIR:STRING=${SV_PETSC_DIR} -DENABLE_COVERAGE:BOOL=${ENABLE_COVERAGE} + -DENABLE_UNIT_TEST:BOOL=${ENABLE_UNIT_TEST} -DENABLE_ARRAY_INDEX_CHECKING:BOOL=${ENABLE_ARRAY_INDEX_CHECKING} -DSV_LOCAL_VTK_PATH:STRING=${SV_LOCAL_VTK_PATH} ${SV_APPLE_CMAKE_ARGS} diff --git a/Code/Source/svFSI/CMakeLists.txt b/Code/Source/svFSI/CMakeLists.txt index d2a0a9a5..00e7ccdc 100644 --- a/Code/Source/svFSI/CMakeLists.txt +++ b/Code/Source/svFSI/CMakeLists.txt @@ -260,4 +260,56 @@ if(ENABLE_COVERAGE) WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) endif() +# unit tests and Google Test +if(ENABLE_UNIT_TEST) + + # link pthread on ubuntu20 + find_package(Threads REQUIRED) + + # install Google Test + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip + ) + FetchContent_MakeAvailable(googletest) + enable_testing() + include(GoogleTest) + + # add test.cpp for unit test + + # remove the main.cpp and add test.cpp + list(APPEND CSRCS "../../../tests/unitTests/test.cpp") + list(REMOVE_ITEM CSRCS "main.cpp") + + # include source files (same as what svFSI does except for main.cpp) + add_executable(run_all_unit_tests ${CSRCS}) + + # libraries + target_link_libraries(run_all_unit_tests + ${GLOBAL_LIBRARIES} + ${INTELRUNTIME_LIBRARIES} + ${ZLIB_LIBRARY} + ${BLAS_LIBRARIES} + ${LAPACK_LIBRARIES} + ${METIS_SVFSI_LIBRARY_NAME} + ${PARMETIS_SVFSI_LIBRARY_NAME} + ${TETGEN_LIBRARY_NAME} + ${TINYXML_LIBRARY_NAME} + ${SV_LIB_SVFSILS_NAME}${SV_MPI_NAME_EXT} + ${VTK_LIBRARIES} + ) + + # link Google Test + target_link_libraries( + run_all_unit_tests + gtest + GTest::gtest_main + pthread # link pthread on ubuntu20 + ) + + # gtest_discover_tests(runUnitTest) + add_test(NAME all_unit_tests COMMAND run_all_unit_tests) + +endif() diff --git a/tests/unitTests/test.cpp b/tests/unitTests/test.cpp new file mode 100644 index 00000000..7f24916a --- /dev/null +++ b/tests/unitTests/test.cpp @@ -0,0 +1,86 @@ +/* Copyright (c) Stanford University, The Regents of the University of California, and others. + * + * All Rights Reserved. + * + * See Copyright-SimVascular.txt for additional details. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test.h" + +using namespace mat_fun; +using namespace std; + + +TEST(UnitTestIso_1, nHK) { + // Step 1: define parameters + auto matType = consts::ConstitutiveModelType::stIso_nHook; // Material_model: options refer to consts.h + auto volType = consts::ConstitutiveModelType::stVol_ST91; // Dilational_penalty_model + double E = 1e6; // Elasticity_modulus + double nu = 0.5; // Poisson_ratio + double pen = 4e9; // Penalty_parameter + double C01; // additional parameter to C10 (optional) + + // Step 2: construct test object + UnitTestIso nHK(matType, E, nu, volType, pen, C01); + + // Step 3: define the input + double F[3][3] = {}; + F[0][0] = 1.0; F[1][1] = 1.0; F[2][2] = 1.0; // set to Identity + + // Step 4: define the reference output + double S_ref[3][3] = {}; + double Dm_ref[6][6] = {}; + + // Step 5: run unit test + nHK.runUnitTest(F, S_ref, Dm_ref); + +} + +TEST(UnitTestIso_2, MR) { + // Step 1: define parameters + auto matType = consts::ConstitutiveModelType::stIso_MR; // Material_model: options refer to consts.h + auto volType = consts::ConstitutiveModelType::stVol_ST91; // Dilational_penalty_model + double E = 1e6; // Elasticity_modulus + double nu = 0.495; // Poisson_ratio + double pen = 4e9; // Penalty_parameter + double C01 = 0.1; // additional parameter to C10 (optional) + + // Step 2: construct test object + UnitTestIso MR(matType, E, nu, volType, pen, C01); + + // Step 3: define the input + double F[3][3] = {}; + F[0][0] = 1.0; F[1][1] = 1.0; F[2][2] = 1.0; // set to Identity + + // Step 4: define the reference output + double S_ref[3][3] = {}; + double Dm_ref[6][6] = {}; + + // Step 5: run unit test + MR.runUnitTest(F, S_ref, Dm_ref); + +} + diff --git a/tests/unitTests/test.h b/tests/unitTests/test.h new file mode 100644 index 00000000..d6aabdd6 --- /dev/null +++ b/tests/unitTests/test.h @@ -0,0 +1,119 @@ +/* Copyright (c) Stanford University, The Regents of the University of California, and others. + * + * All Rights Reserved. + * + * See Copyright-SimVascular.txt for additional details. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "gtest/gtest.h" // include GoogleTest +#include "mat_fun.h" +#include "mat_fun_carray.h" +#include "mat_models.h" +#include "mat_models_carray.h" + +class MockCepMod : public CepMod { +public: + MockCepMod() { + // initialize if needed + } + // Mock methods if needed +}; +class MockdmnType : public dmnType { +public: + MockdmnType() { + // initialize if needed + } + // MockstModelType mockStM; + // Mock methods if needed +}; +class MockmshType : public mshType { +public: + MockmshType() { + // initialize if needed + } + // Mock methods if needed +}; +class MockeqType : public eqType { +public: + MockeqType() { + // initialize if needed + } + MockdmnType mockDmn; + // Mock methods if needed +}; +class MockComMod : public ComMod { +public: + MockComMod() { + // initialize if needed + nsd = 3; + } + MockeqType mockEq; + MockmshType mockMsh; + // Mock methods if needed +}; + +// Class for unit test of isotropic material model (currently only introduced two +// parameters C10 and C01) +class UnitTestIso { +public: + MockComMod com_mod; + MockCepMod cep_mod; + + UnitTestIso(consts::ConstitutiveModelType matType, double E, double nu, + consts::ConstitutiveModelType penType, double pen, double C01 = 0.0) { + int nsd = com_mod.nsd; + auto &dmn = com_mod.mockEq.mockDmn; + mat_fun_carray::ten_init(nsd); // initialize tensor index pointer + dmn.stM.isoType = matType; // Mat_model + double mu = 0.5 * E / (1.0 + nu); // Shear_modulus + dmn.stM.C10 = 0.5 * mu - C01; // set_material_props.h + dmn.stM.C01 = C01; + dmn.stM.volType = penType; // Dilational_penalty_model + dmn.stM.Kpen = pen; // Penalty_parameter + } + + void runUnitTest(double F[3][3], double S_ref[3][3], double Dm_ref[6][6]) { + int nsd = com_mod.nsd; + auto &dmn = com_mod.mockEq.mockDmn; + // hard code for nHK + int nFn = 1; + Array fN(nsd, nFn); + double ya_g = 0.0; + double S[3][3], Dm[6][6]; + + mat_models_carray::get_pk2cc(com_mod, cep_mod, dmn, F, nFn, fN, ya_g, S, Dm); + double tol = 1e-12; // tolerance + + // Compare with reference solution + for (int i = 0; i < 3; i++){ + for (int j = 0; j < 3; j++){ + EXPECT_NEAR(S[i][j], S_ref[i][j], tol); + } + } + } +}; \ No newline at end of file