diff --git a/post-process/PythonScripts/geometryXYVxVy/plot_electric_field.py b/post-process/PythonScripts/geometryXYVxVy/plot_electric_field.py index 4c4bc205c..79a6a2df1 100644 --- a/post-process/PythonScripts/geometryXYVxVy/plot_electric_field.py +++ b/post-process/PythonScripts/geometryXYVxVy/plot_electric_field.py @@ -134,16 +134,17 @@ def plot_electric_energy(): Emax = np.array(Emaxval) tm = np.array(tm) - res = stats.linregress(tm,np.log(np.sqrt(Emax))) - print(f'relative error on growth rate {(theo_rate-res[0]) /abs(res[0]):0.2f}') - + if Emax.size>0: + res = stats.linregress(tm,np.log(np.sqrt(Emax))) + print(f'relative error on growth rate {(theo_rate-res[0]) /abs(res[0]):0.2f}') plt.clf() plt.plot(epot.coords['time'].values,np.log(np.sqrt(Energy))) plt.scatter(tm ,np.log(np.sqrt(Emax)),c='red') - plt.plot(tm ,tm*res[0]+res[1] ,c='green') + if Emax.size>0: + plt.plot(tm ,tm*res[0]+res[1] ,c='green') + plt.title(f'growthrate: {res[0]:0.3f} (theory: {theo_rate:0.3f})', fontsize=16) plt.ylabel('Electric energy', fontsize=12) plt.xlabel('time', fontsize=12) - plt.title(f'growthrate: {res[0]:0.3f} (theory: {theo_rate:0.3f})', fontsize=16) plt.savefig('Electric_energy.png') def plot_fdist(itime,Vx,Vy): diff --git a/src/README.md b/src/README.md index 2d0271fed..6f3982130 100644 --- a/src/README.md +++ b/src/README.md @@ -10,3 +10,4 @@ The `src/` folder contains all the code necessary to build a gyrokinetic semi-La - [quadrature](./quadrature/README.md) - Code describing different quadrature methods. +- [utils](./utils/README.md) - Code describing utility functions. diff --git a/src/utils/README.md b/src/utils/README.md new file mode 100644 index 000000000..cebd30c56 --- /dev/null +++ b/src/utils/README.md @@ -0,0 +1,9 @@ +# Utility Functions + +This folder contains classes and functions which facilitate the writing of the rest of the code. For the most part these objects are required to fill gaps in DDC which will hopefully be filled in the future. + +The class ddcHelper exists to provide functionalities which are currently missing from DDC. + +The class NDTag exists to provide a way to group directional tags together. This is notably useful in order to create a vector field. + +Finally the classes VectorField and VectorFieldSpan provide a way to represent a vector field. diff --git a/src/utils/ddc_helper.hpp b/src/utils/ddc_helper.hpp index 8089f98b8..c4ef7097f 100644 --- a/src/utils/ddc_helper.hpp +++ b/src/utils/ddc_helper.hpp @@ -4,28 +4,38 @@ #include +namespace ddcHelper { +//TODO: this should be directly handled by ddc::Discretization really, +// in the meantime, we do it ourselves /** * Computes fluid moments of the distribution function * Density, mean velocity and temperature. + * + * @param dom The domain on which the length should be calculated. + * + * @return The length of the domain. */ -class ddcHelper +template +constexpr std::enable_if_t +total_interval_length(ddc::DiscreteDomain const& dom) { -public: - //TODO: this should be directly handled by ddc::Discretization really, - // in the meantime, we do it ourselves - template - static constexpr std::enable_if_t - total_interval_length(ddc::DiscreteDomain const& dom) - { - return std::fabs(ddc::rlength(dom)); - } + return std::fabs(ddc::rlength(dom)); +} - //TODO: this should be directly handled by ddc::Discretization really, - // in the meantime, we do it ourselves - template - static constexpr std::enable_if_t total_interval_length( - ddc::DiscreteDomain> const& dom) - { - return std::fabs(ddc::rlength(dom) + ddc::step>()); - } -}; +//TODO: this should be directly handled by ddc::Discretization really, +// in the meantime, we do it ourselves +/** + * Computes fluid moments of the distribution function + * Density, mean velocity and temperature. + * + * @param dom The domain on which the length should be calculated. + * + * @return The length of the domain. + */ +template +constexpr std::enable_if_t total_interval_length( + ddc::DiscreteDomain> const& dom) +{ + return std::fabs(ddc::rlength(dom) + ddc::step>()); +} +}; // namespace ddcHelper diff --git a/src/utils/directional_tag.hpp b/src/utils/directional_tag.hpp new file mode 100644 index 000000000..c2ee1124c --- /dev/null +++ b/src/utils/directional_tag.hpp @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +template +using NDTag = ddc::detail::TypeSeq; diff --git a/src/utils/vector_field.hpp b/src/utils/vector_field.hpp new file mode 100644 index 000000000..a5cf9ece1 --- /dev/null +++ b/src/utils/vector_field.hpp @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "vector_field_common.hpp" + +/** + * @brief Pre-declaration of VectorField. + */ +template > +class VectorField; + + +template +inline constexpr bool enable_field> = true; + +/** + * @brief Pre-declaration of VectorFieldSpan. + */ +template +class VectorFieldSpan; + +/** + * @brief A class which describes a vector field. + * + * A class which describes a vector field. In other words a class which maps a position on a domain + * to a vector (x,y,z,...). This is done by storing the values at the positions in individual + * Chunks. + */ +template +class VectorField : public VectorFieldCommon, NDTag> +{ +public: + /** + * @brief Type describing the object which can be extracted from this VectorField using the get<> function. + */ + using chunk_type = ddc::Chunk; + +private: + using base_type = VectorFieldCommon; + + using mdomain_type = typename base_type::mdomain_type; + +public: + /** + * @brief A type which can hold a reference to this VectorField. + */ + using span_type = VectorFieldSpan< + ElementType, + Domain, + NDTag, + std::experimental::layout_right, + typename Allocator::memory_space>; + + /** + * @brief A type which can hold a constant reference to this VectorField. + */ + using view_type = VectorFieldSpan< + const ElementType, + Domain, + NDTag, + std::experimental::layout_right, + typename Allocator::memory_space>; + +private: + /// Construct a VectorField on a domain with uninitialized values + template + explicit VectorField( + mdomain_type const& domain, + Allocator allocator, + std::index_sequence const&) + : base_type(((void)Is, chunk_type(domain, allocator))...) + { + } + + template + VectorField(VectorField&& other, std::index_sequence const&) + : base_type(chunk_type( + std::move(ddcHelpers::get>(other)))...) + { + } + + /** + * Construct a VectorField from a deepcopy of a VectorFieldSpan + * + * @param[inout] field_span + */ + template + explicit VectorField( + VectorFieldSpan field_span, + std::index_sequence const&) + : base_type(chunk_type(ddcHelpers::get>(field_span))...) + { + } + +public: + /// Empty VectorField + VectorField() = default; + + /** + * Construct a VectorField on a domain with uninitialized values + * + * @param[in] domain The domain on which the chunk will be defined. + * @param[in] allocator An optional allocator used to create the chunks. + */ + explicit VectorField(mdomain_type const& domain, Allocator allocator = Allocator()) + : VectorField(domain, allocator, std::make_index_sequence {}) + { + } + + /** + * Construct a VectorField from a deepcopy of a VectorFieldSpan + * + * @param[inout] field_span A reference to another vector field. + */ + template + explicit VectorField( + VectorFieldSpan field_span) + : VectorField(field_span, std::make_index_sequence {}) + { + } + + /// Deleted: use deepcopy instead + VectorField(VectorField const& other) = delete; + + /** + * Constructs a new VectorField by move + * @param other the VectorField to move + */ + VectorField(VectorField&& other) + : VectorField(std::move(other), std::make_index_sequence {}) + { + } + + /** + * Copy-assigns a new value to this VectorFieldSpan, yields a new view to the same data + * @param other the VectorFieldSpan to copy + * @return *this + */ + VectorField& operator=(VectorField const& other) = default; + + /** + * Move-assigns a new value to this VectorFieldSpan + * @param other the VectorFieldSpan to move + * @return *this + */ + VectorField& operator=(VectorField&& other) = default; + + ~VectorField() = default; + + /** + * Get a constant reference to this vector field. + * + * @return A constant reference to this vector field. + */ + view_type span_cview() const + { + return view_type(*this); + } + + /** + * Get a constant reference to this vector field. + * + * @return A constant reference to this vector field. + */ + view_type span_view() const + { + return view_type(*this); + } + + /** + * Get a modifiable reference to this vector field. + * + * @return A modifiable reference to this vector field. + */ + span_type span_view() + { + return span_type(*this); + } + + /** + * @brief Slice out some dimensions. + * + * Get the VectorField on the reduced domain which is obtained by indexing + * the dimensions QueryDDims at the position slice_spec. + * + * @param[in] slice_spec The slice describing the domain of interest. + * + * @return A constant reference to the vector field on the sliced domain. + */ + template + auto operator[](ddc::DiscreteElement const& slice_spec) const + { + return span_cview()[slice_spec]; + } + + /** + * @brief Slice out some dimensions. + * + * Get the VectorField on the reduced domain which is obtained by indexing + * the dimensions QueryDDims at the position slice_spec. + * + * @param[in] slice_spec The slice describing the domain of interest. + * + * @return A modifiable reference to the vector field on the sliced domain. + */ + template + auto operator[](ddc::DiscreteElement const& slice_spec) + { + return span_view()[slice_spec]; + } + + /** + * @brief Slice out some dimensions. + * + * Get the VectorField on the reduced domain passed as an argument. + * + * @param[in] odomain The domain of interest. + * + * @return A modifiable reference to the vector field on the sliced domain. + */ + template + auto operator[](ddc::DiscreteDomain const& odomain) const + { + return span_cview()[odomain]; + } + + /** + * @brief Slice out some dimensions. + * + * Get the VectorField on the reduced domain passed as an argument. + * + * @param[in] odomain The domain of interest. + * + * @return A modifiable reference to the vector field on the sliced domain. + */ + template + auto operator[](ddc::DiscreteDomain const& odomain) + { + return span_view()[odomain]; + } +}; diff --git a/src/utils/vector_field_common.hpp b/src/utils/vector_field_common.hpp new file mode 100644 index 000000000..2509bd7a1 --- /dev/null +++ b/src/utils/vector_field_common.hpp @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +template +class VectorFieldCommon; + +template +inline constexpr bool enable_field = false; + +template +inline constexpr bool enable_borrowed_field = false; + +template +inline constexpr bool is_field_v = enable_field>>; + +template +inline constexpr bool is_borrowed_field_v + = is_field_v< + T> && (std::is_lvalue_reference_v || enable_borrowed_field>>); + +namespace ddcHelpers { + +template +auto get_domain(FieldType const& field) noexcept +{ + static_assert(is_field_v, "Not a field span type"); + return field.template domain(); +} + +template +auto deepcopy(FieldDst&& dst, FieldSrc&& src) +{ + static_assert(is_borrowed_field_v); + static_assert(is_borrowed_field_v); + static_assert(std::is_same_v< + typename std::remove_reference_t::NDTypeTag, + typename std::remove_reference_t::NDTypeTag>); + + assert(dst.domain().extents() == src.domain().extents()); + + dst.deepcopy(src); + return dst.span_view(); +} + +template +inline constexpr typename ChunkType::span_type get( + VectorFieldCommon& field) noexcept +{ + return field.template get(); +} + +template +inline constexpr typename ChunkType::view_type get( + VectorFieldCommon const& field) noexcept +{ + return field.template get(); +} + + +}; // namespace ddcHelpers + +template +class VectorFieldCommon> +{ + static_assert(ddc::is_chunk_v); + using data_type = typename ChunkType::element_type; + +public: + using mdomain_type = typename ChunkType::mdomain_type; + using element_type = typename ddc::detail::TaggedVector; + using element_ref_type = typename ddc::detail::TaggedVector; + using NDTypeTag = ddc::detail::TypeSeq; + + using chunk_span_type = typename ChunkType::span_type; + using chunk_view_type = typename ChunkType::view_type; + +protected: + static constexpr std::size_t NDims = sizeof...(DDims); + +private: + std::array m_values; + +protected: + /// Empty Chunk + VectorFieldCommon() = default; + + /// Construct a Chunk on a domain with uninitialized values + template < + class... Chunks, + class = std::enable_if_t...>>> + explicit VectorFieldCommon(Chunks&&... chunks) : m_values {std::move(chunks)...} + { + } + +public: + /** Element access using a list of DiscreteElement + * @param delems 1D discrete coordinates + * @return copy of this element + */ + template + element_type operator()(ddc::DiscreteElement const&... delems) const noexcept + { + return element_type((this->get()(delems...))...); + } + + /** Element access using a multi-dimensional DiscreteElement + * @param delems discrete coordinates + * @return copy of this element + */ + template > + element_type operator()(ddc::DiscreteElement const& delems) const noexcept + { + return element_type((this->get()(delems))...); + } + + constexpr mdomain_type domain() const noexcept + { + return m_values[0].domain(); + } + + /** Provide access to the domain on which this chunk is defined + * @return the domain on which this chunk is defined + */ + template + constexpr ddc::DiscreteDomain domain() const noexcept + { + return ddc::select(domain()); + } + + static constexpr int rank() noexcept + { + return ChunkType::rank(); + } + + template + void deepcopy(FieldSrc const& src) + { + ((ddc::deepcopy(this->get(), src.template get())), ...); + } + + template + inline constexpr chunk_span_type get() noexcept + { + static_assert( + ddc::in_tags_v, + "requested Tag absent from TaggedVector"); + return m_values[ddc::type_seq_rank_v].span_view(); + } + + template + inline constexpr chunk_view_type get() const noexcept + { + static_assert( + ddc::in_tags_v, + "requested Tag absent from TaggedVector"); + return m_values[ddc::type_seq_rank_v].span_cview(); + } +}; diff --git a/src/utils/vector_field_span.hpp b/src/utils/vector_field_span.hpp new file mode 100644 index 000000000..32bcab344 --- /dev/null +++ b/src/utils/vector_field_span.hpp @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: MIT + +#pragma once + +#include "vector_field.hpp" + + +template < + class ElementType, + class Domain, + class NDTag, + class LayoutStridedPolicy = std::experimental::layout_right, + class MemorySpace = Kokkos::DefaultHostExecutionSpace::memory_space> +class VectorFieldSpan; + +template < + class ElementType, + class Domain, + class NDTag, + class LayoutStridedPolicy, + class MemorySpace> +inline constexpr bool enable_field< + VectorFieldSpan> = true; + +template < + class ElementType, + class Domain, + class NDTag, + class LayoutStridedPolicy, + class MemorySpace> +inline constexpr bool enable_borrowed_field< + VectorFieldSpan> = true; + + +template < + class ElementType, + class Domain, + class NDTag, + class LayoutStridedPolicy, + class MemorySpace> +class VectorFieldSpan + : public VectorFieldCommon< + ddc::ChunkSpan, + NDTag> +{ +public: + /** + * @brief Type describing the object which can be extracted from this VectorFieldSpan using the get<> function. + */ + using chunk_type = ddc::ChunkSpan; + +private: + using base_type = VectorFieldCommon; + + using mdomain_type = typename base_type::mdomain_type; + +public: + /** + * @brief A type which can hold a modifiable reference to a VectorField. + * + * A type which can hold a reference to a VectorField. If this object is modifiable then + * so is the span type. + */ + using span_type = VectorFieldSpan; + /** + * @brief A type which can hold a constant reference to a VectorField. + */ + using view_type + = VectorFieldSpan; + + /** + * @brief Type describing the way in which the data is laid out in the Chunk span memory. + * + * Type describing the way in which the data is laid out in the Chunk span memory. + * I.e. it describes whether it is contiguous or not. + */ + using layout_type = LayoutStridedPolicy; + +private: + template + friend class VectorFieldSpan; + + template + constexpr VectorFieldSpan( + VectorField& other, + std::index_sequence const&) noexcept + : base_type((chunk_type(ddcHelpers::get>(other)))...) + { + } + + /** Constructs a new VectorFieldSpan from a VectorField, yields a new view to the same data + * @param other the VectorField to view + */ + // Disabled by SFINAE in the case of `ElementType` is not `const` to avoid write access + template < + class OElementType, + std::size_t... Is, + class SFINAEElementType = ElementType, + class = std::enable_if_t>, + class Allocator> + constexpr VectorFieldSpan( + VectorField const& other, + std::index_sequence const&) noexcept + : base_type((chunk_type(ddcHelpers::get>(other)))...) + { + } + + template < + class... Chunks, + class = std::enable_if_t...>>> + constexpr VectorFieldSpan(Chunks&&... chunks) : base_type(std::move(chunks)...) + { + } + + /** Constructs a new VectorFieldSpan by copy of a chunk, yields a new view to the same data + * @param other the VectorFieldSpan to move + */ + template + constexpr VectorFieldSpan( + VectorFieldSpan const& + other, + std::index_sequence const&) noexcept + : base_type((ddcHelpers::get>(other))...) + { + } + + template + constexpr auto get_slice(SliceType const& slice_spec, std::index_sequence const&) + { + auto chunk_slices = std::make_tuple( + this->template get>()[slice_spec]...); + using ChunkType = std::tuple_element_t<0, decltype(chunk_slices)>; + return VectorFieldSpan< + ElementType, + typename ChunkType::mdomain_type, + NDTag, + typename ChunkType::layout_type, + typename ChunkType::memory_space>(std::move(std::get(chunk_slices))...); + } + +public: + /// Empty VectorFieldSpan + constexpr VectorFieldSpan() = default; + + /** Constructs a new VectorFieldSpan by copy, yields a new view to the same data + * @param other the VectorFieldSpan to copy + */ + constexpr VectorFieldSpan(VectorFieldSpan const& other) = default; + + /** Constructs a new VectorFieldSpan by move + * @param other the VectorFieldSpan to move + */ + constexpr VectorFieldSpan(VectorFieldSpan&& other) = default; + + /** Constructs a new VectorFieldSpan from a VectorField, yields a new view to the same data + * @param other the VectorField to view + */ + template + constexpr VectorFieldSpan(VectorField& other) noexcept + : VectorFieldSpan(other, std::make_index_sequence {}) + { + } + + /** Constructs a new VectorFieldSpan from a VectorField, yields a new view to the same data + * @param other the VectorField to view + */ + // Disabled by SFINAE in the case of `ElementType` is not `const` to avoid write access + template < + class OElementType, + class SFINAEElementType = ElementType, + class = std::enable_if_t>, + class Allocator> + constexpr VectorFieldSpan( + VectorField const& other) noexcept + : VectorFieldSpan(other, std::make_index_sequence {}) + { + } + + /** Constructs a new VectorFieldSpan by copy of a chunk, yields a new view to the same data + * @param other the VectorFieldSpan to move + */ + template + constexpr VectorFieldSpan( + VectorFieldSpan const& + other) noexcept + : VectorFieldSpan(other, std::make_index_sequence {}) + { + } + + /** Constructs a new VectorFieldSpan from scratch + * @param ptr the allocation pointer to the data + * @param domain the domain that sustains the view + */ + template < + class... OElementType, + class = std::enable_if_t< + std::conjunction_v...>>, + class = std::enable_if_t> + KOKKOS_FUNCTION VectorFieldSpan(mdomain_type const& domain, OElementType*... ptr) + : base_type((chunk_type(ptr, domain))...) + { + } + + /** Copy-assigns a new value to this VectorFieldSpan, yields a new view to the same data + * @param other the VectorFieldSpan to copy + * @return *this + */ + constexpr VectorFieldSpan& operator=(VectorFieldSpan const& other) = default; + + /** Move-assigns a new value to this VectorFieldSpan + * @param other the VectorFieldSpan to move + * @return *this + */ + constexpr VectorFieldSpan& operator=(VectorFieldSpan&& other) = default; + + /** + * Get a constant reference to the vector field referred to by this vector field span. + * + * @return A constant reference to the vector field. + */ + constexpr view_type span_cview() const + { + return view_type(*this); + } + + /** + * Get a modifiable reference to the vector field referred to by this vector field span. + * + * @return A constant reference to the vector field. + */ + constexpr span_type span_view() const + { + return *this; + } + + /** + * @brief Slice out some dimensions. + * + * Get the VectorField on the reduced domain which is obtained by indexing + * the dimensions QueryDDims at the position slice_spec. + * + * @param[in] slice_spec The slice describing the domain of interest. + * + * @return A reference to the vector field on the sliced domain. + */ + template + constexpr auto operator[](ddc::DiscreteElement const& slice_spec) + { + return get_slice(slice_spec, std::make_index_sequence {}); + } + + /** + * @brief Slice out some dimensions. + * + * Get the VectorField on the reduced domain passed as an argument. + * + * @param[in] odomain The domain of interest. + * + * @return A reference to the vector field on the sliced domain. + */ + template + constexpr auto operator[](ddc::DiscreteDomain const& odomain) + { + return get_slice(odomain, std::make_index_sequence {}); + } +}; + +template < + class ElementType, + class Domain, + class NDTag, + class LayoutStridedPolicy = std::experimental::layout_right, + class MemorySpace = Kokkos::DefaultHostExecutionSpace::memory_space> +using VectorFieldView + = VectorFieldSpan; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d935ecf2d..10d355b42 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,3 +18,4 @@ gtest_discover_tests(unit_tests_common) add_subdirectory(geometryXVx) add_subdirectory(geometryXYVxVy) add_subdirectory(geometryRTheta) +add_subdirectory(utils) diff --git a/tests/utils/CMakeLists.txt b/tests/utils/CMakeLists.txt new file mode 100644 index 000000000..79ce4f9f8 --- /dev/null +++ b/tests/utils/CMakeLists.txt @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.15) + +include(GoogleTest) + +add_executable(unit_tests_utils + field.cpp + ../main.cpp +) +target_compile_features(unit_tests_utils PUBLIC cxx_std_17) +target_link_libraries(unit_tests_utils + PUBLIC + DDC::PDI_Wrapper + GTest::gtest + GTest::gmock + paraconf::paraconf + vcx::utils +) + +gtest_discover_tests(unit_tests_utils) diff --git a/tests/utils/field.cpp b/tests/utils/field.cpp new file mode 100644 index 000000000..e1d3f6396 --- /dev/null +++ b/tests/utils/field.cpp @@ -0,0 +1,640 @@ +// SPDX-License-Identifier: MIT +#include + +#include + +#include +#include +#include + +namespace { + +class Tag1 +{ +}; +class Tag2 +{ +}; + +using Direction = NDTag; + +using Coord = ddc::Coordinate; + +using DElem0D = ddc::DiscreteElement<>; +using DVect0D = ddc::DiscreteVector<>; +using DDom0D = ddc::DiscreteDomain<>; + +template +using VectorField0D = VectorField; +template +using VectorFieldSpan0D = VectorFieldSpan; + +struct DDimX; +using DElemX = ddc::DiscreteElement; +using DVectX = ddc::DiscreteVector; +using DDomX = ddc::DiscreteDomain; + +template +using VectorFieldX = VectorField; +template +using VectorFieldSpanX = VectorFieldSpan; + + +struct DDimY; +using DElemY = ddc::DiscreteElement; +using DVectY = ddc::DiscreteVector; +using DDomY = ddc::DiscreteDomain; + +template +using VectorFieldY = VectorField; +template +using VectorFieldSpanY = VectorFieldSpan; + + +struct DDimZ; +using DElemZ = ddc::DiscreteElement; +using DVectZ = ddc::DiscreteVector; +using DDomZ = ddc::DiscreteDomain; + + +using DElemXY = ddc::DiscreteElement; +using DVectXY = ddc::DiscreteVector; +using DDomXY = ddc::DiscreteDomain; + +template +using VectorFieldXY = VectorField; + + +using DElemYX = ddc::DiscreteElement; +using DVectYX = ddc::DiscreteVector; +using DDomYX = ddc::DiscreteDomain; + +template +using VectorFieldYX = VectorField; +template +using VectorFieldSpanYX = VectorFieldSpan; + + +static DElem0D constexpr lbound_0d {}; +static DVect0D constexpr nelems_0d {}; +static DDom0D constexpr dom_0d(lbound_0d, nelems_0d); + +static DElemX constexpr lbound_x(50); +static DVectX constexpr nelems_x(3); +static DDomX constexpr dom_x(lbound_x, nelems_x); + +static DElemY constexpr lbound_y(4); +static DVectY constexpr nelems_y(12); + +static DElemXY constexpr lbound_x_y {lbound_x, lbound_y}; +static DVectXY constexpr nelems_x_y(nelems_x, nelems_y); +static DDomXY constexpr dom_x_y(lbound_x_y, nelems_x_y); + +} // namespace + +// Member types of VectorField 1D \{ + +TEST(VectorField0DTest, LayoutType) +{ + EXPECT_TRUE((std::is_same_v< + VectorField0D::chunk_type::layout_type, + std::experimental::layout_right>)); +} + +TEST(VectorField1DTest, LayoutType) +{ + VectorFieldX chunk(dom_x); + + EXPECT_TRUE((std::is_same_v< + std::decay_t::layout_type, + std::experimental::layout_right>)); +} + +// TODO: many missing types + +// \} +// Functions implemented in VectorField 1D (and free functions specific to it) \{ + +TEST(VectorField0DTest, VectorFieldSpanConversionConstructor) +{ + Coord constexpr factor(1.391, 2.444); + VectorField0D chunk(dom_0d); + ddcHelpers::get(chunk)() = ddc::get(factor); + ddcHelpers::get(chunk)() = ddc::get(factor); + + VectorField0D chunk2(chunk.span_view()); + EXPECT_EQ(chunk2.domain(), dom_0d); + EXPECT_DOUBLE_EQ(factor.get(), chunk2().get()); + EXPECT_DOUBLE_EQ(factor.get(), chunk2().get()); +} + +TEST(VectorField1DTest, VectorFieldSpanConversionConstructor) +{ + Coord constexpr factor(1.391, 2.444); + VectorFieldX chunk(dom_x); + for (auto&& ix : chunk.domain()) { + Coord val = double(ix.uid()) * factor; + ddcHelpers::get(chunk)(ix) = ddc::get(val); + ddcHelpers::get(chunk)(ix) = ddc::get(val); + } + + VectorFieldX chunk2(chunk.span_view()); + EXPECT_EQ(chunk2.domain(), dom_x); + for (auto&& ix : chunk2.domain()) { + EXPECT_DOUBLE_EQ(ddc::get(factor) * ix.uid(), ddc::get(chunk2(ix))); + EXPECT_DOUBLE_EQ(ddc::get(factor) * ix.uid(), ddc::get(chunk2(ix))); + } +} + +TEST(VectorField0DTest, MoveConstructor) +{ + Coord constexpr factor(1.391, 2.444); + VectorField0D chunk(dom_0d); + ddcHelpers::get(chunk)() = ddc::get(factor); + ddcHelpers::get(chunk)() = ddc::get(factor); + + VectorField0D chunk2(std::move(chunk)); + EXPECT_EQ(chunk2.domain(), dom_0d); + EXPECT_DOUBLE_EQ(ddc::get(factor), ddc::get(chunk2())); + EXPECT_DOUBLE_EQ(ddc::get(factor), ddc::get(chunk2())); +} + +TEST(VectorField1DTest, MoveConstructor) +{ + Coord constexpr factor(1.391, 2.444); + VectorFieldX chunk(dom_x); + for (auto&& ix : chunk.domain()) { + Coord val = double(ix.uid()) * factor; + ddcHelpers::get(chunk)(ix) = ddc::get(val); + ddcHelpers::get(chunk)(ix) = ddc::get(val); + } + + VectorFieldX chunk2(std::move(chunk)); + EXPECT_EQ(chunk2.domain(), dom_x); + for (auto&& ix : chunk2.domain()) { + // we expect exact equality, not EXPECT_DOUBLE_EQ: this is the same ref twice + Coord val = double(ix.uid()) * factor; + EXPECT_EQ(ddc::get(val), ddc::get(chunk2(ix))); + EXPECT_EQ(ddc::get(val), ddc::get(chunk2(ix))); + } +} + +TEST(VectorField0DTest, MoveAssignment) +{ + Coord constexpr factor(1.391, 2.444); + VectorField0D chunk(dom_0d); + ddcHelpers::get(chunk)() = ddc::get(factor); + ddcHelpers::get(chunk)() = ddc::get(factor); + + VectorField0D chunk2(DDom0D(lbound_0d, DVect0D())); + chunk2 = std::move(chunk); + EXPECT_EQ(chunk2.domain(), dom_0d); + EXPECT_DOUBLE_EQ(ddc::get(factor), ddc::get(chunk2())); + EXPECT_DOUBLE_EQ(ddc::get(factor), ddc::get(chunk2())); +} + +TEST(VectorField1DTest, MoveAssignment) +{ + Coord constexpr factor(1.391, 2.444); + VectorFieldX chunk(dom_x); + for (auto&& ix : chunk.domain()) { + Coord val = double(ix.uid()) * factor; + ddcHelpers::get(chunk)(ix) = ddc::get(val); + ddcHelpers::get(chunk)(ix) = ddc::get(val); + } + + VectorFieldX chunk2(DDomX(lbound_x, DVectX(0))); + chunk2 = std::move(chunk); + EXPECT_EQ(chunk2.domain(), dom_x); + for (auto&& ix : chunk2.domain()) { + // we expect exact equality, not EXPECT_DOUBLE_EQ: this is the same ref twice + Coord val = double(ix.uid()) * factor; + EXPECT_EQ(ddc::get(val), ddc::get(chunk2(ix))); + EXPECT_EQ(ddc::get(val), ddc::get(chunk2(ix))); + } +} + +TEST(VectorField0DTest, Swap) +{ + Coord constexpr factor(1.391, 2.444); + VectorField0D chunk(dom_0d); + ddcHelpers::get(chunk)() = ddc::get(factor); + ddcHelpers::get(chunk)() = ddc::get(factor); + + DDom0D empty_domain(lbound_0d, DVect0D()); + VectorField0D chunk2(empty_domain); + + std::swap(chunk2, chunk); + EXPECT_EQ(chunk.domain(), empty_domain); + EXPECT_EQ(chunk2.domain(), dom_0d); + EXPECT_DOUBLE_EQ(ddc::get(factor), ddc::get(chunk2())); + EXPECT_DOUBLE_EQ(ddc::get(factor), ddc::get(chunk2())); +} + +TEST(VectorField1DTest, Swap) +{ + Coord constexpr factor(1.391, 2.444); + VectorFieldX chunk(dom_x); + for (auto&& ix : chunk.domain()) { + Coord val = double(ix.uid()) * factor; + ddcHelpers::get(chunk)(ix) = ddc::get(val); + ddcHelpers::get(chunk)(ix) = ddc::get(val); + } + + DDomX empty_domain(lbound_x, DVectX(0)); + VectorFieldX chunk2(empty_domain); + + std::swap(chunk2, chunk); + EXPECT_EQ(chunk.domain(), empty_domain); + EXPECT_EQ(chunk2.domain(), dom_x); + for (auto&& ix : chunk2.domain()) { + // we expect exact equality, not EXPECT_DOUBLE_EQ: this is the same ref twice + Coord val = double(ix.uid()) * factor; + EXPECT_EQ(ddc::get(val), ddc::get(chunk2(ix))); + EXPECT_EQ(ddc::get(val), ddc::get(chunk2(ix))); + } +} + +// no dim subset access in 1D + +TEST(VectorField1DTest, AccessConst) +{ + Coord constexpr factor(1.391, 2.444); + VectorFieldX chunk(dom_x); + VectorFieldX const& chunk_cref = chunk; + for (auto&& ix : chunk.domain()) { + Coord val = double(ix.uid()) * factor; + ddcHelpers::get(chunk)(ix) = ddc::get(val); + ddcHelpers::get(chunk)(ix) = ddc::get(val); + // we expect exact equality, not EXPECT_DOUBLE_EQ: this is the same ref twice + EXPECT_EQ(ddc::get(val), ddc::get(chunk_cref(ix))); + EXPECT_EQ(ddc::get(val), ddc::get(chunk_cref(ix))); + } +} + +TEST(VectorField1DTest, Access) +{ + Coord constexpr factor(1.391, 2.444); + VectorFieldX chunk(dom_x); + for (auto&& ix : chunk.domain()) { + Coord val = double(ix.uid()) * factor; + ddcHelpers::get(chunk)(ix) = ddc::get(val); + ddcHelpers::get(chunk)(ix) = ddc::get(val); + // we expect exact equality, not EXPECT_DOUBLE_EQ: this is the same ref twice + EXPECT_EQ(ddc::get(val), ddc::get(chunk(ix))); + EXPECT_EQ(ddc::get(val), ddc::get(chunk(ix))); + } +} + +TEST(VectorField1DTest, SpanCview) +{ + Coord constexpr factor(1.391, 2.444); + VectorFieldX chunk(dom_x); + for (auto&& ix : chunk.domain()) { + Coord val = double(ix.uid()) * factor; + ddcHelpers::get(chunk)(ix) = ddc::get(val); + ddcHelpers::get(chunk)(ix) = ddc::get(val); + // we expect exact equality, not EXPECT_DOUBLE_EQ: this is the same ref twice + EXPECT_EQ(ddc::get(val), ddc::get(chunk.span_cview()(ix))); + EXPECT_EQ(ddc::get(val), ddc::get(chunk.span_cview()(ix))); + } +} + +TEST(VectorField1DTest, ViewConst) +{ + Coord constexpr factor(1.391, 2.444); + VectorFieldX chunk(dom_x); + VectorFieldX const& chunk_cref = chunk; + for (auto&& ix : chunk.domain()) { + Coord val = double(ix.uid()) * factor; + ddcHelpers::get(chunk)(ix) = ddc::get(val); + ddcHelpers::get(chunk)(ix) = ddc::get(val); + // we expect exact equality, not EXPECT_DOUBLE_EQ: this is the same ref twice + EXPECT_EQ(ddc::get(val), ddc::get(chunk_cref.span_view()(ix))); + EXPECT_EQ(ddc::get(val), ddc::get(chunk_cref.span_view()(ix))); + } +} + +TEST(VectorField1DTest, View) +{ + Coord constexpr factor(1.391, 2.444); + VectorFieldX chunk(dom_x); + VectorFieldSpanX chunk_span = chunk.span_view(); + for (auto&& ix : chunk.domain()) { + Coord val = double(ix.uid()) * factor; + ddcHelpers::get(chunk_span)(ix) = ddc::get(val); + ddcHelpers::get(chunk_span)(ix) = ddc::get(val); + // we expect exact equality, not EXPECT_DOUBLE_EQ: this is the same ref twice + EXPECT_EQ(ddc::get(val), ddc::get(chunk(ix))); + EXPECT_EQ(ddc::get(val), ddc::get(chunk(ix))); + } +} + +// \} +// Functions inherited from VectorFieldCommon (and free functions implemented for it) \{ + +// constructors are hidden + +// assignment operators are hidden + +// no slicing (operator[]) in 1D + +// access (operator()) operators are hidden + +// TODO: accessor + +TEST(VectorField1DTest, Rank) +{ + VectorFieldX chunk(dom_x); + EXPECT_EQ(VectorFieldX::rank(), 1); +} + +// TODO: stride + +// swap is hidden + +TEST(VectorField1DTest, Domain) +{ + VectorFieldX chunk(dom_x); + EXPECT_EQ(dom_x, chunk.domain()); +} + +TEST(VectorField1DTest, DomainX) +{ + VectorFieldX chunk(dom_x); + EXPECT_EQ(dom_x, chunk.domain()); +} + +// TODO: data_handle() + +// TODO: internal_mdspan + +// TODO: allocation_mdspan + +TEST(VectorField1DTest, GetDomainX) +{ + VectorFieldX chunk(dom_x); + EXPECT_EQ(dom_x, ddcHelpers::get_domain(chunk)); +} + + +TEST(VectorField1DTest, Deepcopy) +{ + VectorFieldX chunk(dom_x); + for (auto&& ix : chunk.domain()) { + ddcHelpers::get(chunk)(ix) = 1.001 * ix.uid(); + ddcHelpers::get(chunk)(ix) = 0.0; + } + VectorFieldX chunk2(chunk.domain()); + ddcHelpers::deepcopy(chunk2, chunk); + for (auto&& ix : chunk.domain()) { + // we expect exact equality, not EXPECT_DOUBLE_EQ: these are copy + EXPECT_EQ(ddc::get(chunk2(ix)), ddc::get(chunk(ix))); + EXPECT_EQ(ddc::get(chunk2(ix)), ddc::get(chunk(ix))); + } +} + +// \} +// Functions implemented in VectorField 2D (and free functions specific to it) \{ + +// TODO: lots to do still! + +TEST(VectorField2DTest, Access) +{ + VectorFieldXY chunk(dom_x_y); + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + ddcHelpers::get(chunk)(ix, iy) = 1.357 * ix.uid(); + ddcHelpers::get(chunk)(ix, iy) = 1.159 * iy.uid(); + // we expect exact equality, not EXPECT_DOUBLE_EQ: this is the same ref twice + EXPECT_EQ(ddc::get(chunk(ix, iy)), ddc::get(chunk(ix, iy))); + EXPECT_EQ(ddc::get(chunk(ix, iy)), ddc::get(chunk(ix, iy))); + } + } +} + +TEST(VectorField2DTest, AccessReordered) +{ + VectorFieldXY chunk(dom_x_y); + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + ddcHelpers::get(chunk)(ix, iy) = 1.455 * ix.uid(); + ddcHelpers::get(chunk)(ix, iy) = 1.522 * iy.uid(); + // we expect exact equality, not EXPECT_DOUBLE_EQ: this is the same ref twice + EXPECT_EQ(ddc::get(chunk(iy, ix)), ddc::get(chunk(ix, iy))); + EXPECT_EQ(ddc::get(chunk(iy, ix)), ddc::get(chunk(ix, iy))); + } + } +} + +TEST(VectorField2DTest, Cview) +{ + VectorFieldXY chunk(dom_x_y); + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + ddcHelpers::get(chunk)(ix, iy) = 1. * ix.uid(); + ddcHelpers::get(chunk)(ix, iy) = .001 * iy.uid(); + } + } + auto cview = chunk.span_cview(); + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + // we expect complete equality, not EXPECT_DOUBLE_EQ: these are copy + EXPECT_EQ(ddc::get(cview(ix, iy)), ddc::get(chunk(ix, iy))); + EXPECT_EQ(ddc::get(cview(ix, iy)), ddc::get(chunk(ix, iy))); + } + } +} + +TEST(VectorField2DTest, SliceCoordX) +{ + DElemX constexpr slice_x_val = DElemX(lbound_x + 1); + + VectorFieldXY chunk(dom_x_y); + VectorFieldXY const& chunk_cref = chunk; + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + ddcHelpers::get(chunk)(ix, iy) = 1. * ix.uid(); + ddcHelpers::get(chunk)(ix, iy) = .001 * iy.uid(); + } + } + + auto&& chunk_y = chunk_cref[slice_x_val]; + EXPECT_TRUE((std::is_same_v< + std::decay_t::layout_type, + std::experimental::layout_right>)); + EXPECT_EQ( + ddcHelpers::get(chunk_y).extent(), + ddcHelpers::get(chunk).extent()); + EXPECT_EQ( + ddcHelpers::get(chunk_y).extent(), + ddcHelpers::get(chunk).extent()); + for (auto&& ix : chunk_cref.domain()) { + // we expect complete equality, not EXPECT_DOUBLE_EQ: these are copy + EXPECT_EQ(ddc::get(chunk_y(ix)), ddc::get(chunk_cref(slice_x_val, ix))); + EXPECT_EQ(ddc::get(chunk_y(ix)), ddc::get(chunk_cref(slice_x_val, ix))); + } +} + +TEST(VectorField2DTest, SliceCoordY) +{ + DElemY constexpr slice_y_val = DElemY(lbound_y + 1); + + VectorFieldXY chunk(dom_x_y); + VectorFieldXY const& chunk_cref = chunk; + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + ddcHelpers::get(chunk)(ix, iy) = 1. * ix.uid(); + ddcHelpers::get(chunk)(ix, iy) = .001 * iy.uid(); + } + } + + auto&& chunk_x = chunk_cref[slice_y_val]; + EXPECT_TRUE((std::is_same_v< + std::decay_t::layout_type, + std::experimental::layout_stride>)); + EXPECT_EQ( + ddcHelpers::get(chunk_x).extent(), + ddcHelpers::get(chunk).extent()); + EXPECT_EQ( + ddcHelpers::get(chunk_x).extent(), + ddcHelpers::get(chunk).extent()); + for (auto&& ix : chunk_cref.domain()) { + // we expect complete equality, not EXPECT_DOUBLE_EQ: these are copy + EXPECT_EQ(ddc::get(chunk_x(ix)), ddc::get(chunk_cref(ix, slice_y_val))); + EXPECT_EQ(ddc::get(chunk_x(ix)), ddc::get(chunk_cref(ix, slice_y_val))); + } +} + +TEST(VectorField2DTest, SliceDomainX) +{ + DDomX constexpr subdomain_x = DDomX(DElemX(lbound_x + 1), DVectX(nelems_x - 2)); + + VectorFieldXY chunk(dom_x_y); + VectorFieldXY const& chunk_cref = chunk; + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + ddcHelpers::get(chunk)(ix, iy) = 1. * ix.uid(); + ddcHelpers::get(chunk)(ix, iy) = .001 * iy.uid(); + } + } + + auto&& subchunk_x = chunk_cref[subdomain_x]; + EXPECT_TRUE((std::is_same_v< + std::decay_t::layout_type, + std::experimental::layout_right>)); + + EXPECT_EQ(ddcHelpers::get(subchunk_x).extent(), subdomain_x.size()); + EXPECT_EQ(ddcHelpers::get(subchunk_x).extent(), chunk.domain().size()); + EXPECT_EQ(ddcHelpers::get(subchunk_x).extent(), subdomain_x.size()); + EXPECT_EQ(ddcHelpers::get(subchunk_x).extent(), chunk.domain().size()); + for (auto&& ix : subchunk_x.domain()) { + for (auto&& iy : subchunk_x.domain()) { + // we expect complete equality, not EXPECT_DOUBLE_EQ: these are copy + EXPECT_EQ(ddc::get(subchunk_x(ix, iy)), ddc::get(chunk_cref(ix, iy))); + EXPECT_EQ(ddc::get(subchunk_x(ix, iy)), ddc::get(chunk_cref(ix, iy))); + } + } +} + +TEST(VectorField2DTest, SliceDomainXTooearly) +{ + [[maybe_unused]] DDomX constexpr subdomain_x = DDomX(DElemX(lbound_x - 1), nelems_x); + + VectorFieldXY chunk(dom_x_y); +#ifndef NDEBUG // The assertion is only checked if NDEBUG isn't defined + // the error message is checked with clang & gcc only + EXPECT_DEATH( + chunk[subdomain_x], + R"rgx([Aa]ssert.*uid\(m_element_begin\).*uid\(odomain\.m_element_begin\))rgx"); +#endif +} + +TEST(VectorField2DTest, SliceDomainXToolate) +{ + [[maybe_unused]] DDomX constexpr subdomain_x = DDomX(lbound_x, DVectX(nelems_x + 1)); + + VectorFieldXY chunk(dom_x_y); +#ifndef NDEBUG // The assertion is only checked if NDEBUG isn't defined + // the error message is checked with clang & gcc only + EXPECT_DEATH( + chunk[subdomain_x], + R"rgx([Aa]ssert.*uid\(m_element_end\).*uid\(odomain\.m_element_end\).*)rgx"); +#endif +} + +TEST(VectorField2DTest, SliceDomainY) +{ + DDomY constexpr subdomain_y = DDomY(DElemY(lbound_y + 1), DVectY(nelems_y - 2)); + + VectorFieldXY chunk(dom_x_y); + VectorFieldXY const& chunk_cref = chunk; + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + ddcHelpers::get(chunk)(ix, iy) = 1. * ix.uid(); + ddcHelpers::get(chunk)(ix, iy) = .001 * iy.uid(); + } + } + auto&& subchunk_y = chunk_cref[subdomain_y]; + EXPECT_TRUE((std::is_same_v< + std::decay_t::layout_type, + std::experimental::layout_stride>)); + + EXPECT_EQ(ddcHelpers::get(subchunk_y).extent(), chunk.domain().size()); + EXPECT_EQ(ddcHelpers::get(subchunk_y).extent(), subdomain_y.size()); + EXPECT_EQ(ddcHelpers::get(subchunk_y).extent(), chunk.domain().size()); + EXPECT_EQ(ddcHelpers::get(subchunk_y).extent(), subdomain_y.size()); + for (auto&& ix : subchunk_y.domain()) { + for (auto&& iy : subchunk_y.domain()) { + // we expect complete equality, not EXPECT_DOUBLE_EQ: these are copy + EXPECT_EQ(ddc::get(subchunk_y(ix, iy)), ddc::get(chunk_cref(ix, iy))); + EXPECT_EQ(ddc::get(subchunk_y(ix, iy)), ddc::get(chunk_cref(ix, iy))); + } + } +} + +TEST(VectorField2DTest, Deepcopy) +{ + VectorFieldXY chunk(dom_x_y); + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + ddcHelpers::get(chunk)(ix, iy) = 1.739 * ix.uid(); + ddcHelpers::get(chunk)(ix, iy) = 1.412 * iy.uid(); + } + } + VectorFieldXY chunk2(chunk.domain()); + ddcHelpers::deepcopy(chunk2, chunk); + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + // we expect complete equality, not EXPECT_DOUBLE_EQ: these are copy + EXPECT_EQ(ddc::get(chunk2(ix, iy)), ddc::get(chunk(ix, iy))); + EXPECT_EQ(ddc::get(chunk2(ix, iy)), ddc::get(chunk(ix, iy))); + } + } +} + +TEST(VectorField2DTest, DeepcopyReordered) +{ + VectorFieldXY chunk(dom_x_y); + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + ddcHelpers::get(chunk)(ix, iy) = 1.739 * ix.uid(); + ddcHelpers::get(chunk)(ix, iy) = 1.412 * iy.uid(); + } + } + VectorFieldYX chunk2(ddc::select(chunk.domain())); + VectorFieldSpan chunk2_view( + chunk.domain(), + chunk2.get().data_handle(), + chunk2.get().data_handle()); + ddcHelpers::deepcopy(chunk2_view, chunk); + for (auto&& ix : chunk.domain()) { + for (auto&& iy : chunk.domain()) { + // we expect complete equality, not EXPECT_DOUBLE_EQ: these are copy + EXPECT_EQ(ddc::get(chunk2(ix, iy)), ddc::get(chunk(ix, iy))); + EXPECT_EQ(ddc::get(chunk2(ix, iy)), ddc::get(chunk(ix, iy))); + EXPECT_EQ(ddc::get(chunk2(ix, iy)), ddc::get(chunk(iy, ix))); + EXPECT_EQ(ddc::get(chunk2(ix, iy)), ddc::get(chunk(iy, ix))); + } + } +}