diff --git a/c++/mpi/mpi.hpp b/c++/mpi/mpi.hpp index 96ec39f5..6603be65 100644 --- a/c++/mpi/mpi.hpp +++ b/c++/mpi/mpi.hpp @@ -31,6 +31,17 @@ namespace mpi { // ------------------------------------------------------------ + /* helper function to check for MPI runtime environment + * covers at the moment OpenMPI, MPICH, and intelmpi + * as cray uses MPICH under the hood it should work as well + */ + static const bool has_env = []() { + if (std::getenv("OMPI_COMM_WORLD_RANK") != nullptr or std::getenv("PMI_RANK") != nullptr) + return true; + else + return false; + }(); + /// Environment must be initialized in C++ struct environment { @@ -56,24 +67,39 @@ namespace mpi { [[nodiscard]] MPI_Comm get() const noexcept { return _com; } [[nodiscard]] int rank() const { - int num; - MPI_Comm_rank(_com, &num); - return num; + if (has_env) { + int num; + MPI_Comm_rank(_com, &num); + return num; + } else + return 0; } [[nodiscard]] int size() const { - int num; - MPI_Comm_size(_com, &num); - return num; + if (has_env) { + int num; + MPI_Comm_size(_com, &num); + return num; + } else + return 1; } [[nodiscard]] communicator split(int color, int key = 0) const { - communicator c; - MPI_Comm_split(_com, color, key, &c._com); - return c; + if (has_env) { + communicator c; + MPI_Comm_split(_com, color, key, &c._com); + return c; + } else + //TODO split should not be done without MPI? + return 0; } - void abort(int error_code) { MPI_Abort(_com, error_code); } + void abort(int error_code) { + if (has_env) + MPI_Abort(_com, error_code); + else + std::abort(); + } #ifdef BOOST_MPI_HPP // Conversion to and from boost communicator, Keep for backward compatibility @@ -81,7 +107,9 @@ namespace mpi { inline communicator(boost::mpi::communicator c) : _com(c) {} #endif - void barrier() const { MPI_Barrier(_com); } + void barrier() const { + if (has_env) { MPI_Barrier(_com); } + } }; // ---------------------------------------- @@ -104,53 +132,113 @@ namespace mpi { MPI_Op op{}; }; - template - inline constexpr bool is_mpi_lazy = false; - - template - inline constexpr bool is_mpi_lazy> = true; - // ---------------------------------------- // ------- general functions ------- // ---------------------------------------- template - [[gnu::always_inline]] inline decltype(auto) broadcast(T &&x, communicator c = {}, int root = 0) { - return mpi_broadcast(std::forward(x), c, root); + [[gnu::always_inline]] inline void broadcast(T &x, communicator c = {}, int root = 0) { + static_assert(not std::is_const_v, "mpi::broadcast cannot be called on const objects"); + if (has_env) mpi_broadcast(x, c, root); } + + namespace details { + + template + inline constexpr bool is_mpi_lazy = false; + + template + inline constexpr bool is_mpi_lazy> = true; + + template + inline constexpr bool is_std_vector = false; + + template + inline constexpr bool is_std_vector> = true; + + template + T convert(V v) { + if constexpr (is_std_vector) { + T res; + res.reserve(v.size()); + for (auto &x : v) res.emplace_back(convert(std::move(x))); + return res; + } else + return T{std::move(v)}; + } + } // namespace details + template [[gnu::always_inline]] inline decltype(auto) reduce(T &&x, communicator c = {}, int root = 0, bool all = false, MPI_Op op = MPI_SUM) { - return mpi_reduce(std::forward(x), c, root, all, op); + using r_t = decltype(mpi_reduce(std::forward(x), c, root, all, op)); + + if constexpr (details::is_mpi_lazy) { + return mpi_reduce(std::forward(x), c, root, all, op); + } else { + if (has_env) + return mpi_reduce(std::forward(x), c, root, all, op); + else + return details::convert(std::forward(x)); + } } + template - [[gnu::always_inline]] inline void reduce_in_place(T &&x, communicator c = {}, int root = 0, bool all = false, MPI_Op op = MPI_SUM) { - return mpi_reduce_in_place(std::forward(x), c, root, all, op); + [[gnu::always_inline]] inline void reduce_in_place(T &x, communicator c = {}, int root = 0, bool all = false, MPI_Op op = MPI_SUM) { + static_assert(not std::is_const_v, "In-place mpi functions cannot be called on const objects"); + if (has_env) mpi_reduce_in_place(x, c, root, all, op); } + template [[gnu::always_inline]] inline decltype(auto) scatter(T &&x, mpi::communicator c = {}, int root = 0) { - return mpi_scatter(std::forward(x), c, root); + using r_t = decltype(mpi_scatter(std::forward(x), c, root)); + + if constexpr (details::is_mpi_lazy) { + return mpi_scatter(std::forward(x), c, root); + } else { + // if it does not have a mpi lazy type, check manually if triqs is run with MPI + if (has_env) + return mpi_scatter(std::forward(x), c, root); + else + return details::convert(std::forward(x)); + } } + template [[gnu::always_inline]] inline decltype(auto) gather(T &&x, mpi::communicator c = {}, int root = 0, bool all = false) { - return mpi_gather(std::forward(x), c, root, all); + using r_t = decltype(mpi_gather(std::forward(x), c, root, all)); + + if constexpr (details::is_mpi_lazy) { + return mpi_gather(std::forward(x), c, root, all); + } else { + // if it does not have a mpi lazy type, check manually if triqs is run with MPI + if (has_env) + return mpi_gather(std::forward(x), c, root, all); + else + return details::convert(std::forward(x)); + } } + template [[gnu::always_inline]] inline decltype(auto) all_reduce(T &&x, communicator c = {}, MPI_Op op = MPI_SUM) { return reduce(std::forward(x), c, 0, true, op); } + template [[gnu::always_inline]] inline void all_reduce_in_place(T &&x, communicator c = {}, MPI_Op op = MPI_SUM) { - return reduce_in_place(std::forward(x), c, 0, true, op); + reduce_in_place(std::forward(x), c, 0, true, op); } + template [[gnu::always_inline]] inline decltype(auto) all_gather(T &&x, communicator c = {}) { return gather(std::forward(x), c, 0, true); } + template [[gnu::always_inline]] [[deprecated("mpi_all_reduce is deprecated, please use mpi::all_reduce instead")]] inline decltype(auto) mpi_all_reduce(T &&x, communicator c = {}, MPI_Op op = MPI_SUM) { return reduce(std::forward(x), c, 0, true, op); } + template [[gnu::always_inline]] [[deprecated("mpi_all_gather is deprecated, please use mpi::all_gather instead")]] inline decltype(auto) mpi_all_gather(T &&x, communicator c = {}) { @@ -332,9 +420,13 @@ namespace mpi { #define MPI_TEST_MAIN \ int main(int argc, char **argv) { \ - mpi::environment env(argc, argv); \ ::testing::InitGoogleTest(&argc, argv); \ - return RUN_ALL_TESTS(); \ + if (mpi::has_env) { \ + mpi::environment env(argc, argv); \ + std::cout << "MPI environment detected\n"; \ + return RUN_ALL_TESTS(); \ + } else \ + return RUN_ALL_TESTS(); \ } } // namespace mpi diff --git a/test/c++/CMakeLists.txt b/test/c++/CMakeLists.txt index 0304b2f9..8074ffb7 100644 --- a/test/c++/CMakeLists.txt +++ b/test/c++/CMakeLists.txt @@ -7,6 +7,11 @@ endforeach() # List of all tests file(GLOB_RECURSE all_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp) +# List of all no mpi tests +file(GLOB_RECURSE nompi_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp) +# remove custom mpi test, as this one explicitly uses MPI +list(REMOVE_ITEM nompi_tests mpi_custom.cpp mpi_monitor.cpp) + # ========= OpenMP Dependency ========== find_package(OpenMP REQUIRED COMPONENTS CXX) @@ -23,8 +28,7 @@ foreach(test ${all_tests}) target_link_libraries(${test_name} ${PROJECT_NAME}::${PROJECT_NAME}_c openmp ${PROJECT_NAME}_warnings gtest_main) set_property(TARGET ${test_name} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${test_dir}) set(test_bin ${CMAKE_CURRENT_BINARY_DIR}/${test_dir}/${test_name}) - add_test(NAME ${test_name}_np1 COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 1 ${MPIEXEC_PREFLAGS} ${test_bin} ${MPIEXEC_POSTFLAGS} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${test_dir}) - add_test(NAME ${test_name}_np2 COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 2 ${MPIEXEC_PREFLAGS} ${test_bin} ${MPIEXEC_POSTFLAGS} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${test_dir}) + add_test(NAME ${test_name}_np2 COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 4 ${MPIEXEC_PREFLAGS} ${test_bin} ${MPIEXEC_POSTFLAGS} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${test_dir}) add_test(NAME ${test_name}_np4 COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 4 ${MPIEXEC_PREFLAGS} ${test_bin} ${MPIEXEC_POSTFLAGS} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${test_dir}) # Run clang-tidy if found if(CLANG_TIDY_EXECUTABLE) @@ -45,3 +49,29 @@ foreach(test ${all_tests}) ) endif() endforeach() + +# now the no mpi tests +foreach(test ${nompi_tests}) + get_filename_component(test_name ${test} NAME_WE) + get_filename_component(test_dir ${test} DIRECTORY) + set(test_bin ${CMAKE_CURRENT_BINARY_DIR}/${test_dir}/${test_name}) + add_test(NAME ${test_name}_nompi COMMAND ${test_bin} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${test_dir}) + # Run clang-tidy if found + if(CLANG_TIDY_EXECUTABLE) + set_target_properties(${test_name} PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_EXECUTABLE}") + endif() + # Run cppcheck if found + if(CPPCHECK_EXECUTABLE) + add_custom_command( + TARGET ${test_name} + COMMAND ${CPPCHECK_EXECUTABLE} + --enable=warning,style,performance,portability + --std=c++17 + --template=gcc + --verbose + --force + --quiet + ${CMAKE_CURRENT_SOURCE_DIR}/${test} + ) + endif() +endforeach()