Skip to content
Snippets Groups Projects
Commit 7d567418 authored by Stéphane Del Pino's avatar Stéphane Del Pino
Browse files

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.
parent b60d6b7f
Branches
Tags
1 merge request!84Add SubArray class
#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
...@@ -100,6 +100,7 @@ add_executable (mpi_unit_tests ...@@ -100,6 +100,7 @@ add_executable (mpi_unit_tests
mpi_test_main.cpp mpi_test_main.cpp
test_Messenger.cpp test_Messenger.cpp
test_Partitioner.cpp test_Partitioner.cpp
test_ItemArray.cpp
test_ItemValue.cpp test_ItemValue.cpp
test_ItemValueUtils.cpp test_ItemValueUtils.cpp
test_SubItemValuePerItem.cpp test_SubItemValuePerItem.cpp
......
#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
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment