#include <catch2/catch_test_macros.hpp> #include <catch2/matchers/catch_matchers_all.hpp> #include <algebra/TinyVector.hpp> #include <utils/Array.hpp> #include <utils/Messenger.hpp> #include <utils/pugs_config.hpp> #include <fstream> #ifdef PUGS_HAS_MPI #include <mpi.h> #define IF_MPI(INSTRUCTION) INSTRUCTION #else #define IF_MPI(INSTRUCTION) #endif // PUGS_HAS_MPI namespace mpi_check { struct integer { int m_int; operator int&() { return m_int; } operator const int&() const { return m_int; } integer& operator=(int i) { m_int = i; return *this; } template <typename T> bool operator==(const T& t) const { return m_int == static_cast<int>(t); } }; struct tri_int { int m_int_0; int m_int_1; int m_int_2; bool operator==(tri_int t) const { return ((m_int_0 == t.m_int_0) and (m_int_1 == t.m_int_1) and (m_int_2 == t.m_int_2)); } }; template <typename T> void test_allToAll() { Array<T> data_array(parallel::size()); for (size_t i = 0; i < data_array.size(); ++i) { data_array[i] = parallel::rank(); } auto exchanged_array = parallel::allToAll(data_array); for (size_t i = 0; i < data_array.size(); ++i) { const size_t value = exchanged_array[i]; REQUIRE(value == i); } } template <> void test_allToAll<bool>() { Array<bool> data_array(parallel::size()); for (size_t i = 0; i < data_array.size(); ++i) { data_array[i] = ((parallel::rank() % 2) == 0); } auto exchanged_array = parallel::allToAll(data_array); for (size_t i = 0; i < data_array.size(); ++i) { REQUIRE(exchanged_array[i] == ((i % 2) == 0)); } } template <> void test_allToAll<tri_int>() { Array<tri_int> data_array(parallel::size()); for (size_t i = 0; i < data_array.size(); ++i) { const int val = 1 + parallel::rank(); data_array[i] = tri_int{val, 2 * val, val + 3}; } auto exchanged_array = parallel::allToAll(data_array); for (size_t i = 0; i < data_array.size(); ++i) { const int val = 1 + i; REQUIRE(exchanged_array[i] == tri_int{val, 2 * val, val + 3}); } } } // namespace mpi_check // clazy:excludeall=non-pod-global-static TEST_CASE("Messenger", "[mpi]") { SECTION("communication info") { int rank = 0; IF_MPI(MPI_Comm_rank(MPI_COMM_WORLD, &rank)); REQUIRE(rank == static_cast<int>(parallel::rank())); int size = 1; IF_MPI(MPI_Comm_size(MPI_COMM_WORLD, &size)); REQUIRE(size == static_cast<int>(parallel::size())); } SECTION("reduction") { const int min_value = parallel::allReduceMin(parallel::rank() + 3); REQUIRE(min_value == 3); const int max_value = parallel::allReduceMax(parallel::rank() + 3); REQUIRE(max_value == static_cast<int>((parallel::size() - 1) + 3)); const bool and_value = parallel::allReduceAnd(true); REQUIRE(and_value == true); const bool and_value_2 = parallel::allReduceAnd(parallel::rank() > 0); REQUIRE(and_value_2 == false); const bool or_value = parallel::allReduceOr(false); REQUIRE(or_value == false); const bool or_value_2 = parallel::allReduceOr(parallel::rank() > 0); REQUIRE(or_value_2 == (parallel::size() > 1)); const size_t sum_value = parallel::allReduceSum(parallel::rank() + 1); REQUIRE(sum_value == parallel::size() * (parallel::size() + 1) / 2); const TinyVector<2, size_t> sum_tiny_vector = parallel::allReduceSum(TinyVector<2, size_t>(parallel::rank() + 1, 1)); REQUIRE( (sum_tiny_vector == TinyVector<2, size_t>{parallel::size() * (parallel::size() + 1) / 2, parallel::size()})); } SECTION("all to all") { // chars mpi_check::test_allToAll<char>(); mpi_check::test_allToAll<wchar_t>(); // integers mpi_check::test_allToAll<int8_t>(); mpi_check::test_allToAll<int16_t>(); mpi_check::test_allToAll<int32_t>(); mpi_check::test_allToAll<int64_t>(); mpi_check::test_allToAll<uint8_t>(); mpi_check::test_allToAll<uint16_t>(); mpi_check::test_allToAll<uint32_t>(); mpi_check::test_allToAll<uint64_t>(); mpi_check::test_allToAll<signed long long int>(); mpi_check::test_allToAll<unsigned long long int>(); // floats mpi_check::test_allToAll<float>(); mpi_check::test_allToAll<double>(); mpi_check::test_allToAll<long double>(); // bools mpi_check::test_allToAll<bool>(); // trivial simple type mpi_check::test_allToAll<mpi_check::integer>(); // compound trivial type mpi_check::test_allToAll<mpi_check::tri_int>(); #ifndef NDEBUG SECTION("checking invalid all to all") { if (parallel::size() > 1) { Array<int> invalid_all_to_all(parallel::size() + 1); REQUIRE_THROWS_AS(parallel::allToAll(invalid_all_to_all), AssertError); Array<int> different_size_all_to_all(parallel::size() * (parallel::rank() + 1)); REQUIRE_THROWS_AS(parallel::allToAll(different_size_all_to_all), AssertError); } } #endif // NDEBUG } SECTION("broadcast value") { { // simple type size_t value{(3 + parallel::rank()) * 2}; parallel::broadcast(value, 0); REQUIRE(value == 6); } { // trivial simple type mpi_check::integer value{static_cast<int>((3 + parallel::rank()) * 2)}; parallel::broadcast(value, 0); REQUIRE((value == 6)); } { // compound trivial type mpi_check::tri_int value{static_cast<int>((3 + parallel::rank()) * 2), static_cast<int>(2 + parallel::rank()), static_cast<int>(4 - parallel::rank())}; parallel::broadcast(value, 0); REQUIRE((value == mpi_check::tri_int{6, 2, 4})); } } SECTION("broadcast array") { { // simple type Array<size_t> array(3); array[0] = (3 + parallel::rank()) * 2; array[1] = 2 + parallel::rank(); array[2] = 4 - parallel::rank(); parallel::broadcast(array, 0); REQUIRE(((array[0] == 6) and (array[1] == 2) and (array[2] == 4))); } { // trivial simple type Array<mpi_check::integer> array(3); array[0] = static_cast<int>((3 + parallel::rank()) * 2); array[1] = static_cast<int>(2 + parallel::rank()); array[2] = static_cast<int>(4 - parallel::rank()); parallel::broadcast(array, 0); REQUIRE(((array[0] == 6) and (array[1] == 2) and (array[2] == 4))); } { // compound trivial type Array<mpi_check::tri_int> array(3); array[0] = mpi_check::tri_int{static_cast<int>((3 + parallel::rank()) * 2), static_cast<int>(2 + parallel::rank()), static_cast<int>(4 - parallel::rank())}; array[1] = mpi_check::tri_int{static_cast<int>((2 + parallel::rank()) * 4), static_cast<int>(3 + parallel::rank()), static_cast<int>(1 - parallel::rank())}; array[2] = mpi_check::tri_int{static_cast<int>((5 + parallel::rank())), static_cast<int>(-3 + parallel::rank()), static_cast<int>(parallel::rank())}; parallel::broadcast(array, 0); REQUIRE(((array[0] == mpi_check::tri_int{6, 2, 4}) and (array[1] == mpi_check::tri_int{8, 3, 1}) and (array[2] == mpi_check::tri_int{5, -3, 0}))); } } SECTION("gather value to") { { const size_t target_rank = 10 % parallel::size(); // simple type size_t value{(3 + parallel::rank()) * 2}; Array<size_t> gather_array = parallel::gather(value, target_rank); if (parallel::rank() == target_rank) { REQUIRE(gather_array.size() == parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { REQUIRE((gather_array[i] == (3 + i) * 2)); } } else { REQUIRE(gather_array.size() == 0); } } { const size_t target_rank = 7 % parallel::size(); // trivial simple type mpi_check::integer value{static_cast<int>((3 + parallel::rank()) * 2)}; Array<mpi_check::integer> gather_array = parallel::gather(value, target_rank); if (parallel::rank() == target_rank) { REQUIRE(gather_array.size() == parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { const int expected_value = (3 + i) * 2; REQUIRE(gather_array[i] == expected_value); } } else { REQUIRE(gather_array.size() == 0); } } { const size_t target_rank = 5 % parallel::size(); // compound trivial type mpi_check::tri_int value{static_cast<int>((3 + parallel::rank()) * 2), static_cast<int>(2 + parallel::rank()), static_cast<int>(4 - parallel::rank())}; Array<mpi_check::tri_int> gather_array = parallel::gather(value, target_rank); if (parallel::rank() == target_rank) { REQUIRE(gather_array.size() == parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { mpi_check::tri_int expected_value{static_cast<int>((3 + i) * 2), static_cast<int>(2 + i), static_cast<int>(4 - i)}; REQUIRE((gather_array[i] == expected_value)); } } else { REQUIRE(gather_array.size() == 0); } } } SECTION("gather array to") { { const size_t target_rank = 17 % parallel::size(); // simple type Array<int> array(3); for (size_t i = 0; i < array.size(); ++i) { array[i] = (3 + parallel::rank()) * 2 + i; } Array<int> gather_array = parallel::gather(array, target_rank); if (parallel::rank() == target_rank) { REQUIRE(gather_array.size() == array.size() * parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { const int expected_value = (3 + i / array.size()) * 2 + (i % array.size()); REQUIRE((gather_array[i] == expected_value)); } } else { REQUIRE(gather_array.size() == 0); } } { const size_t target_rank = 13 % parallel::size(); // trivial simple type Array<mpi_check::integer> array(3); for (size_t i = 0; i < array.size(); ++i) { array[i] = (3 + parallel::rank()) * 2 + i; } Array<mpi_check::integer> gather_array = parallel::gather(array, target_rank); if (parallel::rank() == target_rank) { REQUIRE(gather_array.size() == array.size() * parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { const int expected_value = (3 + i / array.size()) * 2 + (i % array.size()); REQUIRE((gather_array[i] == expected_value)); } } else { REQUIRE(gather_array.size() == 0); } } { const size_t target_rank = 11 % parallel::size(); // compound trivial type Array<mpi_check::tri_int> array(3); for (size_t i = 0; i < array.size(); ++i) { array[i] = mpi_check::tri_int{static_cast<int>((3 + parallel::rank()) * 2), static_cast<int>(2 + parallel::rank() + i), static_cast<int>(4 - parallel::rank() - i)}; } Array<mpi_check::tri_int> gather_array = parallel::gather(array, target_rank); if (parallel::rank() == target_rank) { REQUIRE(gather_array.size() == array.size() * parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { mpi_check::tri_int expected_value{static_cast<int>((3 + i / array.size()) * 2), static_cast<int>(2 + i / array.size() + (i % array.size())), static_cast<int>(4 - i / array.size() - (i % array.size()))}; REQUIRE((gather_array[i] == expected_value)); } } else { REQUIRE(gather_array.size() == 0); } } } SECTION("gather variable array to") { { const size_t target_rank = 17 % parallel::size(); // simple type Array<size_t> array(3 + parallel::rank()); for (size_t i = 0; i < array.size(); ++i) { array[i] = (parallel::rank() + i) * 2 + i; } Array<size_t> gather_array = parallel::gatherVariable(array, target_rank); if (parallel::rank() == target_rank) { const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (3 + i_rank); } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < i_rank + 3; ++j) { REQUIRE(gather_array[i++] == (i_rank + j) * 2 + j); } } } } else { REQUIRE(gather_array.size() == 0); } } { const size_t target_rank = 0; // trivial simple type Array<mpi_check::integer> array(2 + 2 * parallel::rank()); for (size_t i = 0; i < array.size(); ++i) { array[i] = (3 + parallel::rank()) * 2 + i; } Array<mpi_check::integer> gather_array = parallel::gatherVariable(array, target_rank); if (parallel::rank() == target_rank) { const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (2 + 2 * i_rank); } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < 2 + 2 * i_rank; ++j) { REQUIRE(gather_array[i++] == (3 + i_rank) * 2 + j); } } } } else { REQUIRE(gather_array.size() == 0); } } { const size_t target_rank = 13 % parallel::size(); // compound trivial type Array<mpi_check::tri_int> array(3 * parallel::rank() + 1); for (size_t i = 0; i < array.size(); ++i) { array[i] = mpi_check::tri_int{static_cast<int>((3 + parallel::rank()) * 2), static_cast<int>(2 + parallel::rank() + i), static_cast<int>(4 - parallel::rank() - i)}; } Array<mpi_check::tri_int> gather_array = parallel::gatherVariable(array, target_rank); if (parallel::rank() == target_rank) { const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (3 * i_rank + 1); } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < 3 * i_rank + 1; ++j) { auto value = mpi_check::tri_int{static_cast<int>((3 + i_rank) * 2), static_cast<int>(2 + i_rank + j), static_cast<int>(4 - i_rank - j)}; REQUIRE(gather_array[i++] == value); } } } } else { REQUIRE(gather_array.size() == 0); } } } SECTION("gather variable array to (with some empty)") { { const size_t target_rank = 17 % parallel::size(); // simple type Array<size_t> array(3 * parallel::rank()); for (size_t i = 0; i < array.size(); ++i) { array[i] = (parallel::rank() + i) * 2 + i; } Array<size_t> gather_array = parallel::gatherVariable(array, target_rank); if (parallel::rank() == target_rank) { const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (3 * i_rank); } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < i_rank * 3; ++j) { REQUIRE(gather_array[i++] == (i_rank + j) * 2 + j); } } } } else { REQUIRE(gather_array.size() == 0); } } { const size_t target_rank = 0; // trivial simple type Array<mpi_check::integer> array((parallel::rank() < parallel::size() - 1) ? (2 + 2 * parallel::rank()) : 0); for (size_t i = 0; i < array.size(); ++i) { array[i] = (3 + parallel::rank()) * 2 + i; } Array<mpi_check::integer> gather_array = parallel::gatherVariable(array, target_rank); if (parallel::rank() == target_rank) { const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (i_rank < parallel::size() - 1) ? (2 + 2 * i_rank) : 0; } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < ((i_rank < parallel::size() - 1) ? (2 + 2 * i_rank) : 0); ++j) { REQUIRE(gather_array[i++] == (3 + i_rank) * 2 + j); } } } } else { REQUIRE(gather_array.size() == 0); } } { const size_t target_rank = 13 % parallel::size(); // compound trivial type Array<mpi_check::tri_int> array(3 * parallel::rank()); for (size_t i = 0; i < array.size(); ++i) { array[i] = mpi_check::tri_int{static_cast<int>((3 + parallel::rank()) * 2), static_cast<int>(2 + parallel::rank() + i), static_cast<int>(4 - parallel::rank() - i)}; } Array<mpi_check::tri_int> gather_array = parallel::gatherVariable(array, target_rank); if (parallel::rank() == target_rank) { const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (3 * i_rank); } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < 3 * i_rank; ++j) { auto value = mpi_check::tri_int{static_cast<int>((3 + i_rank) * 2), static_cast<int>(2 + i_rank + j), static_cast<int>(4 - i_rank - j)}; REQUIRE(gather_array[i++] == value); } } } } else { REQUIRE(gather_array.size() == 0); } } } SECTION("gather variable array to (with all empty arrays)") { { const size_t target_rank = 7 % parallel::size(); // simple type [empty arrays] Array<size_t> array; Array<size_t> gather_array = parallel::gatherVariable(array, target_rank); REQUIRE(gather_array.size() == 0); } { const size_t target_rank = 0; // trivial simple type Array<mpi_check::integer> array; Array<mpi_check::integer> gather_array = parallel::gatherVariable(array, target_rank); REQUIRE(gather_array.size() == 0); } { const size_t target_rank = 13 % parallel::size(); // compound trivial type Array<mpi_check::tri_int> array; Array<mpi_check::tri_int> gather_array = parallel::gatherVariable(array, target_rank); REQUIRE(gather_array.size() == 0); } } SECTION("all gather value") { { // simple type size_t value{(3 + parallel::rank()) * 2}; Array<size_t> gather_array = parallel::allGather(value); REQUIRE(gather_array.size() == parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { REQUIRE((gather_array[i] == (3 + i) * 2)); } } { // trivial simple type mpi_check::integer value{static_cast<int>((3 + parallel::rank()) * 2)}; Array<mpi_check::integer> gather_array = parallel::allGather(value); REQUIRE(gather_array.size() == parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { const int expected_value = (3 + i) * 2; REQUIRE(gather_array[i] == expected_value); } } { // compound trivial type mpi_check::tri_int value{static_cast<int>((3 + parallel::rank()) * 2), static_cast<int>(2 + parallel::rank()), static_cast<int>(4 - parallel::rank())}; Array<mpi_check::tri_int> gather_array = parallel::allGather(value); REQUIRE(gather_array.size() == parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { mpi_check::tri_int expected_value{static_cast<int>((3 + i) * 2), static_cast<int>(2 + i), static_cast<int>(4 - i)}; REQUIRE((gather_array[i] == expected_value)); } } } SECTION("all gather array") { { // simple type Array<int> array(3); for (size_t i = 0; i < array.size(); ++i) { array[i] = (3 + parallel::rank()) * 2 + i; } Array<int> gather_array = parallel::allGather(array); REQUIRE(gather_array.size() == array.size() * parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { const int expected_value = (3 + i / array.size()) * 2 + (i % array.size()); REQUIRE((gather_array[i] == expected_value)); } } { // trivial simple type Array<mpi_check::integer> array(3); for (size_t i = 0; i < array.size(); ++i) { array[i] = (3 + parallel::rank()) * 2 + i; } Array<mpi_check::integer> gather_array = parallel::allGather(array); REQUIRE(gather_array.size() == array.size() * parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { const int expected_value = (3 + i / array.size()) * 2 + (i % array.size()); REQUIRE((gather_array[i] == expected_value)); } } { // compound trivial type Array<mpi_check::tri_int> array(3); for (size_t i = 0; i < array.size(); ++i) { array[i] = mpi_check::tri_int{static_cast<int>((3 + parallel::rank()) * 2), static_cast<int>(2 + parallel::rank() + i), static_cast<int>(4 - parallel::rank() - i)}; } Array<mpi_check::tri_int> gather_array = parallel::allGather(array); REQUIRE(gather_array.size() == array.size() * parallel::size()); for (size_t i = 0; i < gather_array.size(); ++i) { mpi_check::tri_int expected_value{static_cast<int>((3 + i / array.size()) * 2), static_cast<int>(2 + i / array.size() + (i % array.size())), static_cast<int>(4 - i / array.size() - (i % array.size()))}; REQUIRE((gather_array[i] == expected_value)); } } } SECTION("all gather empty array") { { // simple type Array<int> array; Array<int> gather_array = parallel::allGather(array); REQUIRE(gather_array.size() == 0); } { // trivial simple type Array<mpi_check::integer> array; Array<mpi_check::integer> gather_array = parallel::allGather(array); REQUIRE(gather_array.size() == 0); } { // compound trivial type Array<mpi_check::tri_int> array; Array<mpi_check::tri_int> gather_array = parallel::allGather(array); REQUIRE(gather_array.size() == 0); } } SECTION("all gather variable array") { { // simple type Array<size_t> array(3 + parallel::rank()); for (size_t i = 0; i < array.size(); ++i) { array[i] = (parallel::rank() + i) * 2 + i; } Array<size_t> gather_array = parallel::allGatherVariable(array); const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (3 + i_rank); } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < i_rank + 3; ++j) { REQUIRE(gather_array[i++] == (i_rank + j) * 2 + j); } } } } { // trivial simple type Array<mpi_check::integer> array(2 + 2 * parallel::rank()); for (size_t i = 0; i < array.size(); ++i) { array[i] = (3 + parallel::rank()) * 2 + i; } Array<mpi_check::integer> gather_array = parallel::allGatherVariable(array); const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (2 + 2 * i_rank); } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < 2 + 2 * i_rank; ++j) { REQUIRE(gather_array[i++] == (3 + i_rank) * 2 + j); } } } } { // compound trivial type Array<mpi_check::tri_int> array(3 * parallel::rank() + 1); for (size_t i = 0; i < array.size(); ++i) { array[i] = mpi_check::tri_int{static_cast<int>((3 + parallel::rank()) * 2), static_cast<int>(2 + parallel::rank() + i), static_cast<int>(4 - parallel::rank() - i)}; } Array<mpi_check::tri_int> gather_array = parallel::allGatherVariable(array); const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (3 * i_rank + 1); } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < 3 * i_rank + 1; ++j) { auto value = mpi_check::tri_int{static_cast<int>((3 + i_rank) * 2), static_cast<int>(2 + i_rank + j), static_cast<int>(4 - i_rank - j)}; REQUIRE(gather_array[i++] == value); } } } } } SECTION("all gather variable array to (with some empty)") { { // simple type Array<size_t> array(3 * parallel::rank()); for (size_t i = 0; i < array.size(); ++i) { array[i] = (parallel::rank() + i) * 2 + i; } Array<size_t> gather_array = parallel::allGatherVariable(array); const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (3 * i_rank); } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < i_rank * 3; ++j) { REQUIRE(gather_array[i++] == (i_rank + j) * 2 + j); } } } } { // trivial simple type Array<mpi_check::integer> array((parallel::rank() < parallel::size() - 1) ? (2 + 2 * parallel::rank()) : 0); for (size_t i = 0; i < array.size(); ++i) { array[i] = (3 + parallel::rank()) * 2 + i; } Array<mpi_check::integer> gather_array = parallel::allGatherVariable(array); const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (i_rank < parallel::size() - 1) ? (2 + 2 * i_rank) : 0; } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < ((i_rank < parallel::size() - 1) ? (2 + 2 * i_rank) : 0); ++j) { REQUIRE(gather_array[i++] == (3 + i_rank) * 2 + j); } } } } { // compound trivial type Array<mpi_check::tri_int> array(3 * parallel::rank()); for (size_t i = 0; i < array.size(); ++i) { array[i] = mpi_check::tri_int{static_cast<int>((3 + parallel::rank()) * 2), static_cast<int>(2 + parallel::rank() + i), static_cast<int>(4 - parallel::rank() - i)}; } Array<mpi_check::tri_int> gather_array = parallel::allGatherVariable(array); const size_t gather_array_size = [&]() { size_t size = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { size += (3 * i_rank); } return size; }(); REQUIRE(gather_array.size() == gather_array_size); { size_t i = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { for (size_t j = 0; j < 3 * i_rank; ++j) { auto value = mpi_check::tri_int{static_cast<int>((3 + i_rank) * 2), static_cast<int>(2 + i_rank + j), static_cast<int>(4 - i_rank - j)}; REQUIRE(gather_array[i++] == value); } } } } } SECTION("gather variable array to (with all empty arrays)") { { // simple type [empty arrays] Array<size_t> array; Array<size_t> gather_array = parallel::allGatherVariable(array); REQUIRE(gather_array.size() == 0); } { // trivial simple type Array<mpi_check::integer> array; Array<mpi_check::integer> gather_array = parallel::allGatherVariable(array); REQUIRE(gather_array.size() == 0); } { // compound trivial type Array<mpi_check::tri_int> array; Array<mpi_check::tri_int> gather_array = parallel::allGatherVariable(array); REQUIRE(gather_array.size() == 0); } } SECTION("all array exchanges") { { // simple type std::vector<Array<const size_t>> send_array_list(parallel::size()); for (size_t i = 0; i < send_array_list.size(); ++i) { Array<size_t> send_array(i + 1); for (size_t j = 0; j < send_array.size(); ++j) { send_array[j] = (parallel::rank() + 1) * j; } send_array_list[i] = send_array; } std::vector<Array<size_t>> recv_array_list(parallel::size()); for (size_t i = 0; i < recv_array_list.size(); ++i) { recv_array_list[i] = Array<size_t>(parallel::rank() + 1); } parallel::exchange(send_array_list, recv_array_list); for (size_t i = 0; i < parallel::size(); ++i) { const Array<const size_t> recv_array = recv_array_list[i]; for (size_t j = 0; j < recv_array.size(); ++j) { REQUIRE(recv_array[j] == (i + 1) * j); } } } { // trivial simple type std::vector<Array<mpi_check::integer>> send_array_list(parallel::size()); for (size_t i = 0; i < send_array_list.size(); ++i) { Array<mpi_check::integer> send_array(i + 1); for (size_t j = 0; j < send_array.size(); ++j) { send_array[j] = static_cast<int>((parallel::rank() + 1) * j); } send_array_list[i] = send_array; } std::vector<Array<mpi_check::integer>> recv_array_list(parallel::size()); for (size_t i = 0; i < recv_array_list.size(); ++i) { recv_array_list[i] = Array<mpi_check::integer>(parallel::rank() + 1); } parallel::exchange(send_array_list, recv_array_list); for (size_t i = 0; i < parallel::size(); ++i) { const Array<const mpi_check::integer> recv_array = recv_array_list[i]; for (size_t j = 0; j < recv_array.size(); ++j) { REQUIRE(recv_array[j] == (i + 1) * j); } } } { // compound trivial type std::vector<Array<mpi_check::tri_int>> send_array_list(parallel::size()); for (size_t i = 0; i < send_array_list.size(); ++i) { Array<mpi_check::tri_int> send_array(i + 1); for (size_t j = 0; j < send_array.size(); ++j) { send_array[j] = mpi_check::tri_int{static_cast<int>((parallel::rank() + 1) * j), static_cast<int>(parallel::rank()), static_cast<int>(j)}; } send_array_list[i] = send_array; } std::vector<Array<mpi_check::tri_int>> recv_array_list(parallel::size()); for (size_t i = 0; i < recv_array_list.size(); ++i) { recv_array_list[i] = Array<mpi_check::tri_int>(parallel::rank() + 1); } parallel::exchange(send_array_list, recv_array_list); for (size_t i = 0; i < parallel::size(); ++i) { const Array<const mpi_check::tri_int> recv_array = recv_array_list[i]; for (size_t j = 0; j < recv_array.size(); ++j) { mpi_check::tri_int expected_value{static_cast<int>((i + 1) * j), static_cast<int>(i), static_cast<int>(j)}; REQUIRE((recv_array[j] == expected_value)); } } } } #ifndef NDEBUG SECTION("checking all array exchange invalid sizes") { std::vector<Array<const int>> send_array_list(parallel::size()); for (size_t i = 0; i < send_array_list.size(); ++i) { Array<int> send_array(i + 1); send_array.fill(parallel::rank()); send_array_list[i] = send_array; } std::vector<Array<int>> recv_array_list(parallel::size()); REQUIRE_THROWS_AS(parallel::exchange(send_array_list, recv_array_list), AssertError); } #endif // NDEBUG SECTION("checking barrier") { for (size_t i = 0; i < parallel::size(); ++i) { if (i == parallel::rank()) { std::ofstream file; if (i == 0) { file.open("barrier_test", std::ios_base::out); } else { file.open("barrier_test", std::ios_base::app); } file << i << "\n" << std::flush; } parallel::barrier(); } { // reading produced file std::ifstream file("barrier_test"); std::vector<size_t> number_list; while (file) { size_t value; file >> value; if (file) { number_list.push_back(value); } } REQUIRE(number_list.size() == parallel::size()); for (size_t i = 0; i < number_list.size(); ++i) { REQUIRE(number_list[i] == i); } } parallel::barrier(); std::remove("barrier_test"); } SECTION("errors") { int argc = 0; char** argv = nullptr; REQUIRE_THROWS_WITH((parallel::Messenger::create(argc, argv)), "unexpected error: Messenger already created"); } }