From 7d56741852e5f4d511cfff22724f99f1f5f64620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 1 Apr 2021 12:54:00 +0200 Subject: [PATCH] Add ItemArray class It consists in a collection of arrays of the SAME size associated to items. The implementation return a SubArray associate with each item. It mimics the API of ItemValue's. The array size itself is dynamic. --- src/mesh/ItemArray.hpp | 219 +++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/test_ItemArray.cpp | 256 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 476 insertions(+) create mode 100644 src/mesh/ItemArray.hpp create mode 100644 tests/test_ItemArray.cpp diff --git a/src/mesh/ItemArray.hpp b/src/mesh/ItemArray.hpp new file mode 100644 index 000000000..f47835b7c --- /dev/null +++ b/src/mesh/ItemArray.hpp @@ -0,0 +1,219 @@ +#ifndef ITEM_ARRAY_HPP +#define ITEM_ARRAY_HPP + +#include <mesh/IConnectivity.hpp> +#include <mesh/ItemId.hpp> +#include <mesh/ItemType.hpp> +#include <utils/Array.hpp> +#include <utils/PugsAssert.hpp> +#include <utils/SubArray.hpp> + +#include <memory> + +template <typename DataType, ItemType item_type, typename ConnectivityPtr = std::shared_ptr<const IConnectivity>> +class ItemArray +{ + public: + static constexpr ItemType item_t{item_type}; + using data_type = DataType; + + using ItemId = ItemIdT<item_type>; + using index_type = ItemId; + + private: + using ConnectivitySharedPtr = std::shared_ptr<const IConnectivity>; + using ConnectivityWeakPtr = std::weak_ptr<const IConnectivity>; + + static_assert(std::is_same_v<ConnectivityPtr, ConnectivitySharedPtr> or + std::is_same_v<ConnectivityPtr, ConnectivityWeakPtr>); + + ConnectivityPtr m_connectivity_ptr; + + Array<DataType> m_arrays_values; + + size_t m_size_of_arrays; + + public: + // Allow const std:shared_ptr version to access our data + friend ItemArray<std::add_const_t<DataType>, item_type, ConnectivitySharedPtr>; + + // Allow const std:weak_ptr version to access our data + friend ItemArray<std::add_const_t<DataType>, item_type, ConnectivityWeakPtr>; + + friend PUGS_INLINE ItemArray<std::remove_const_t<DataType>, item_type, ConnectivityPtr> + copy(const ItemArray<DataType, item_type, ConnectivityPtr>& source) + { + ItemArray<std::remove_const_t<DataType>, item_type, ConnectivityPtr> image; + + image.m_connectivity_ptr = source.m_connectivity_ptr; + image.m_arrays_values = copy(source.m_arrays_values); + image.m_size_of_arrays = source.m_size_of_arrays; + return image; + } + + PUGS_INLINE + bool + isBuilt() const noexcept + { + return m_connectivity_ptr.use_count() != 0; + } + + PUGS_INLINE + std::shared_ptr<const IConnectivity> + connectivity_ptr() const noexcept + { + if constexpr (std::is_same_v<ConnectivityPtr, ConnectivitySharedPtr>) { + return m_connectivity_ptr; + } else { + return m_connectivity_ptr.lock(); + } + } + + PUGS_INLINE + size_t + numberOfValues() const noexcept(NO_ASSERT) + { + Assert(this->isBuilt()); + return m_arrays_values.size(); + } + + PUGS_INLINE + void + fill(const DataType& data) const noexcept + { + static_assert(not std::is_const_v<DataType>, "Cannot modify ItemArray of const"); + m_arrays_values.fill(data); + } + + // Following Kokkos logic, these classes are view and const view does allow + // changes in data + PUGS_FORCEINLINE + SubArray<DataType> + operator[](const ItemId& i) const noexcept(NO_ASSERT) + { + Assert(this->isBuilt()); + return SubArray{m_arrays_values, i * m_size_of_arrays, m_size_of_arrays}; + } + + template <typename IndexType> + SubArray<DataType> + operator[](const IndexType&) const noexcept(NO_ASSERT) + { + static_assert(std::is_same_v<IndexType, ItemId>, "ItemArray must be indexed by ItemId"); + } + + PUGS_INLINE + size_t + numberOfItems() const noexcept(NO_ASSERT) + { + Assert(this->isBuilt()); + return m_connectivity_ptr->template numberOf<item_type>(); + } + + PUGS_INLINE + size_t + sizeOfArrays() const + { + Assert(this->isBuilt()); + return m_size_of_arrays; + } + + template <typename DataType2> + PUGS_INLINE ItemArray& + operator=(const Array<DataType2>& arrays) noexcept(NO_ASSERT) + { + // ensures that DataType is the same as source DataType2 + static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>, + "Cannot assign ItemArray of different type"); + // ensures that const is not lost through copy + static_assert(((std::is_const_v<DataType2> and std::is_const_v<DataType>) or not std::is_const_v<DataType2>), + "Cannot assign ItemArray of const to ItemArray of non-const"); + + Assert((arrays.size() == 0) or this->isBuilt(), "Cannot assign array of arrays to a non-built ItemArray\n"); + + Assert(m_arrays_values.size() == arrays.size(), "Cannot assign an array of arrays of a different size\n"); + + m_arrays_values = arrays; + + return *this; + } + + template <typename DataType2, typename ConnectivityPtr2> + PUGS_INLINE ItemArray& + operator=(const ItemArray<DataType2, item_type, ConnectivityPtr2>& array_per_item) noexcept + { + // ensures that DataType is the same as source DataType2 + static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>, + "Cannot assign ItemArray of different type"); + // ensures that const is not lost through copy + static_assert(((std::is_const_v<DataType2> and std::is_const_v<DataType>) or not std::is_const_v<DataType2>), + "Cannot assign ItemArray of const to ItemArray of non-const"); + + m_arrays_values = array_per_item.m_arrays_values; + m_size_of_arrays = array_per_item.m_size_of_arrays; + + if constexpr (std::is_same_v<ConnectivityPtr, ConnectivitySharedPtr> and + std::is_same_v<ConnectivityPtr2, ConnectivityWeakPtr>) { + m_connectivity_ptr = array_per_item.m_connectivity_ptr.lock(); + } else { + m_connectivity_ptr = array_per_item.m_connectivity_ptr; + } + + return *this; + } + + template <typename DataType2, typename ConnectivityPtr2> + PUGS_INLINE + ItemArray(const ItemArray<DataType2, item_type, ConnectivityPtr2>& array_per_item) noexcept + { + this->operator=(array_per_item); + } + + PUGS_INLINE + ItemArray() = default; + + PUGS_INLINE + ItemArray(const IConnectivity& connectivity, size_t size_of_array) noexcept + : m_connectivity_ptr{connectivity.shared_ptr()}, + m_arrays_values{connectivity.numberOf<item_type>() * size_of_array}, + m_size_of_arrays{size_of_array} + { + static_assert(not std::is_const_v<DataType>, "Cannot allocate ItemArray of const data: only view is " + "supported"); + ; + } + + PUGS_INLINE + ~ItemArray() = default; +}; + +template <typename DataType> +using NodeArray = ItemArray<DataType, ItemType::node>; + +template <typename DataType> +using EdgeArray = ItemArray<DataType, ItemType::edge>; + +template <typename DataType> +using FaceArray = ItemArray<DataType, ItemType::face>; + +template <typename DataType> +using CellArray = ItemArray<DataType, ItemType::cell>; + +// Weak versions: should not be used outside of Connectivity + +template <typename DataType, ItemType item_type> +using WeakItemArray = ItemArray<DataType, item_type, std::weak_ptr<const IConnectivity>>; + +template <typename DataType> +using WeakNodeArray = WeakItemArray<DataType, ItemType::node>; + +template <typename DataType> +using WeakEdgeArray = WeakItemArray<DataType, ItemType::edge>; + +template <typename DataType> +using WeakFaceArray = WeakItemArray<DataType, ItemType::face>; + +template <typename DataType> +using WeakCellArray = WeakItemArray<DataType, ItemType::cell>; + +#endif // ITEM_ARRAY_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d6fff1695..976258f59 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -100,6 +100,7 @@ add_executable (mpi_unit_tests mpi_test_main.cpp test_Messenger.cpp test_Partitioner.cpp + test_ItemArray.cpp test_ItemValue.cpp test_ItemValueUtils.cpp test_SubItemValuePerItem.cpp diff --git a/tests/test_ItemArray.cpp b/tests/test_ItemArray.cpp new file mode 100644 index 000000000..2177d83c2 --- /dev/null +++ b/tests/test_ItemArray.cpp @@ -0,0 +1,256 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/ItemArray.hpp> +#include <mesh/Mesh.hpp> +#include <utils/Messenger.hpp> + +template class ItemArray<int, ItemType::node>; + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("ItemArray", "[mesh]") +{ + SECTION("default constructors") + { + REQUIRE_NOTHROW(NodeArray<int>{}); + REQUIRE_NOTHROW(EdgeArray<int>{}); + REQUIRE_NOTHROW(FaceArray<int>{}); + REQUIRE_NOTHROW(CellArray<int>{}); + + REQUIRE(not NodeArray<int>{}.isBuilt()); + REQUIRE(not EdgeArray<int>{}.isBuilt()); + REQUIRE(not FaceArray<int>{}.isBuilt()); + REQUIRE(not CellArray<int>{}.isBuilt()); + } + + SECTION("1D") + { + const Mesh<Connectivity<1>>& mesh_1d = MeshDataBaseForTests::get().cartesianMesh<1>(); + const Connectivity<1>& connectivity = mesh_1d.connectivity(); + + REQUIRE_NOTHROW(NodeArray<int>{connectivity, 3}); + REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 3}); + REQUIRE_NOTHROW(FaceArray<int>{connectivity, 3}); + REQUIRE_NOTHROW(CellArray<int>{connectivity, 3}); + + REQUIRE(NodeArray<int>{connectivity, 3}.isBuilt()); + REQUIRE(EdgeArray<int>{connectivity, 3}.isBuilt()); + REQUIRE(FaceArray<int>{connectivity, 3}.isBuilt()); + REQUIRE(CellArray<int>{connectivity, 3}.isBuilt()); + + NodeArray<int> node_value{connectivity, 3}; + EdgeArray<int> edge_value{connectivity, 3}; + FaceArray<int> face_value{connectivity, 3}; + CellArray<int> cell_value{connectivity, 3}; + + REQUIRE(edge_value.numberOfItems() == node_value.numberOfItems()); + REQUIRE(face_value.numberOfItems() == node_value.numberOfItems()); + REQUIRE(cell_value.numberOfItems() + 1 == node_value.numberOfItems()); + + REQUIRE(node_value.numberOfValues() == 3 * node_value.numberOfItems()); + REQUIRE(edge_value.numberOfValues() == 3 * edge_value.numberOfItems()); + REQUIRE(face_value.numberOfValues() == 3 * face_value.numberOfItems()); + REQUIRE(cell_value.numberOfValues() == 3 * cell_value.numberOfItems()); + + REQUIRE(node_value.sizeOfArrays() == 3); + REQUIRE(edge_value.sizeOfArrays() == 3); + REQUIRE(face_value.sizeOfArrays() == 3); + REQUIRE(cell_value.sizeOfArrays() == 3); + } + + SECTION("2D") + { + const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>(); + const Connectivity<2>& connectivity = mesh_2d.connectivity(); + + REQUIRE_NOTHROW(NodeArray<int>{connectivity, 2}); + REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 2}); + REQUIRE_NOTHROW(FaceArray<int>{connectivity, 2}); + REQUIRE_NOTHROW(CellArray<int>{connectivity, 2}); + + REQUIRE(NodeArray<int>{connectivity, 2}.isBuilt()); + REQUIRE(EdgeArray<int>{connectivity, 2}.isBuilt()); + REQUIRE(FaceArray<int>{connectivity, 2}.isBuilt()); + REQUIRE(CellArray<int>{connectivity, 2}.isBuilt()); + + NodeArray<int> node_value{connectivity, 2}; + EdgeArray<int> edge_value{connectivity, 2}; + FaceArray<int> face_value{connectivity, 2}; + CellArray<int> cell_value{connectivity, 2}; + + REQUIRE(edge_value.numberOfItems() == face_value.numberOfItems()); + + REQUIRE(node_value.numberOfValues() == 2 * node_value.numberOfItems()); + REQUIRE(edge_value.numberOfValues() == 2 * edge_value.numberOfItems()); + REQUIRE(face_value.numberOfValues() == 2 * face_value.numberOfItems()); + REQUIRE(cell_value.numberOfValues() == 2 * cell_value.numberOfItems()); + + REQUIRE(node_value.sizeOfArrays() == 2); + REQUIRE(edge_value.sizeOfArrays() == 2); + REQUIRE(face_value.sizeOfArrays() == 2); + REQUIRE(cell_value.sizeOfArrays() == 2); + } + + SECTION("3D") + { + const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>(); + const Connectivity<3>& connectivity = mesh_3d.connectivity(); + + REQUIRE_NOTHROW(NodeArray<int>{connectivity, 3}); + REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 3}); + REQUIRE_NOTHROW(FaceArray<int>{connectivity, 3}); + REQUIRE_NOTHROW(CellArray<int>{connectivity, 3}); + + REQUIRE(NodeArray<int>{connectivity, 3}.isBuilt()); + REQUIRE(EdgeArray<int>{connectivity, 3}.isBuilt()); + REQUIRE(FaceArray<int>{connectivity, 3}.isBuilt()); + REQUIRE(CellArray<int>{connectivity, 3}.isBuilt()); + + NodeArray<int> node_value{connectivity, 3}; + EdgeArray<int> edge_value{connectivity, 3}; + FaceArray<int> face_value{connectivity, 3}; + CellArray<int> cell_value{connectivity, 3}; + + REQUIRE(node_value.numberOfValues() == 3 * node_value.numberOfItems()); + REQUIRE(edge_value.numberOfValues() == 3 * edge_value.numberOfItems()); + REQUIRE(face_value.numberOfValues() == 3 * face_value.numberOfItems()); + REQUIRE(cell_value.numberOfValues() == 3 * cell_value.numberOfItems()); + + REQUIRE(node_value.sizeOfArrays() == 3); + REQUIRE(edge_value.sizeOfArrays() == 3); + REQUIRE(face_value.sizeOfArrays() == 3); + REQUIRE(cell_value.sizeOfArrays() == 3); + } + + SECTION("set values from array") + { + const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>(); + const Connectivity<3>& connectivity = mesh_3d.connectivity(); + + CellArray<size_t> cell_array{connectivity, 3}; + + Array<size_t> array{cell_array.numberOfValues()}; + for (size_t i = 0; i < array.size(); ++i) { + array[i] = i; + } + + cell_array = array; + + auto is_same = [](const CellArray<size_t>& cell_array, const Array<size_t>& array) { + bool is_same = true; + size_t k = 0; + for (CellId cell_id = 0; cell_id < cell_array.numberOfItems(); ++cell_id) { + SubArray sub_array = cell_array[cell_id]; + for (size_t i = 0; i < sub_array.size(); ++i, ++k) { + is_same &= (sub_array[i] == array[k]); + } + } + return is_same; + }; + + REQUIRE(is_same(cell_array, array)); + } + + SECTION("copy") + { + auto is_same = [](const auto& cell_array, int value) { + bool is_same = true; + for (CellId cell_id = 0; cell_id < cell_array.numberOfItems(); ++cell_id) { + SubArray sub_array = cell_array[cell_id]; + for (size_t i = 0; i < sub_array.size(); ++i) { + is_same &= (sub_array[i] == value); + } + } + return is_same; + }; + + const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>(); + const Connectivity<3>& connectivity = mesh_3d.connectivity(); + + CellArray<int> cell_array{connectivity, 4}; + cell_array.fill(parallel::rank()); + + CellArray<const int> cell_array_const_view{cell_array}; + REQUIRE(cell_array.numberOfValues() == cell_array_const_view.numberOfValues()); + REQUIRE(is_same(cell_array_const_view, static_cast<std::int64_t>(parallel::rank()))); + + CellArray<const int> const_cell_array; + const_cell_array = copy(cell_array); + + cell_array.fill(0); + + REQUIRE(is_same(cell_array, 0)); + REQUIRE(is_same(cell_array_const_view, 0)); + REQUIRE(is_same(const_cell_array, static_cast<std::int64_t>(parallel::rank()))); + } + + SECTION("WeakItemArray") + { + const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>(); + const Connectivity<2>& connectivity = mesh_2d.connectivity(); + + WeakFaceArray<int> weak_face_array{connectivity, 5}; + + weak_face_array.fill(parallel::rank()); + + FaceArray<const int> face_array{weak_face_array}; + + REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr()); + } + +#ifndef NDEBUG + SECTION("error") + { + SECTION("checking for build ItemArray") + { + CellArray<int> cell_array; + REQUIRE_THROWS_AS(cell_array[CellId{0}], AssertError); + + FaceArray<int> face_array; + REQUIRE_THROWS_AS(face_array[FaceId{0}], AssertError); + + EdgeArray<int> edge_array; + REQUIRE_THROWS_AS(edge_array[EdgeId{0}], AssertError); + + NodeArray<int> node_array; + REQUIRE_THROWS_AS(node_array[NodeId{0}], AssertError); + } + + SECTION("checking for bounds violation") + { + const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>(); + const Connectivity<3>& connectivity = mesh_3d.connectivity(); + + CellArray<int> cell_array{connectivity, 1}; + CellId invalid_cell_id = connectivity.numberOfCells(); + REQUIRE_THROWS_AS(cell_array[invalid_cell_id], AssertError); + + FaceArray<int> face_array{connectivity, 2}; + FaceId invalid_face_id = connectivity.numberOfFaces(); + REQUIRE_THROWS_AS(face_array[invalid_face_id], AssertError); + + EdgeArray<int> edge_array{connectivity, 1}; + EdgeId invalid_edge_id = connectivity.numberOfEdges(); + REQUIRE_THROWS_AS(edge_array[invalid_edge_id], AssertError); + + NodeArray<int> node_array{connectivity, 0}; + NodeId invalid_node_id = connectivity.numberOfNodes(); + REQUIRE_THROWS_AS(node_array[invalid_node_id], AssertError); + } + + SECTION("set values from invalid array size") + { + const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>(); + const Connectivity<3>& connectivity = mesh_3d.connectivity(); + + CellArray<size_t> cell_array{connectivity, 2}; + + Array<size_t> values{3 + cell_array.numberOfValues()}; + REQUIRE_THROWS_AS(cell_array = values, AssertError); + } + } +#endif // NDEBUG +} -- GitLab