Line data Source code
1 1 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : /// \file 5 : /// Defines class template Index. 6 : 7 : #pragma once 8 : 9 : #include <array> 10 : #include <concepts> 11 : #include <cstddef> 12 : #include <limits> 13 : #include <ostream> 14 : 15 : #include "Utilities/ErrorHandling/Assert.hpp" 16 : #include "Utilities/ForceInline.hpp" 17 : #include "Utilities/Gsl.hpp" 18 : #include "Utilities/MakeArray.hpp" 19 : 20 : namespace PUP { 21 : class er; 22 : } // namespace PUP 23 : 24 : /// \ingroup DataStructuresGroup 25 : /// An integer multi-index. 26 : /// 27 : /// \tparam Dim the number of integers in the Index. 28 : template <size_t Dim> 29 1 : class Index { 30 : public: 31 : /// Construct with each element set to the same value. 32 1 : explicit Index(const size_t i0 = std::numeric_limits<size_t>::max()) 33 : : indices_(make_array<Dim>(i0)) {} 34 : 35 : /// Construct specifying value in each dimension 36 : template <std::integral... I> 37 : requires(sizeof...(I) == Dim and sizeof...(I) > 1) 38 1 : explicit Index(I... i) : indices_(make_array(static_cast<size_t>(i)...)) {} 39 : 40 0 : explicit Index(std::array<size_t, Dim> i) : indices_(std::move(i)) {} 41 : 42 0 : size_t operator[](const size_t d) const { return gsl::at(indices_, d); } 43 0 : size_t& operator[](const size_t d) { return gsl::at(indices_, d); } 44 : 45 0 : typename std::array<size_t, Dim>::iterator begin() { 46 : return indices_.begin(); 47 : } 48 0 : typename std::array<size_t, Dim>::const_iterator begin() const { 49 : return indices_.begin(); 50 : } 51 : 52 0 : typename std::array<size_t, Dim>::iterator end() { return indices_.end(); } 53 0 : typename std::array<size_t, Dim>::const_iterator end() const { 54 : return indices_.end(); 55 : } 56 : 57 0 : size_t size() const { return Dim; } 58 : 59 : /// The product of the indices. 60 : /// If Dim = 0, the product is defined as 1. 61 : template <size_t N = Dim> 62 : requires(N > 0) 63 1 : constexpr size_t product() const { 64 : return indices_[N - 1] * product<N - 1>(); 65 : } 66 : /// \cond 67 : // Specialization for N = 0 to stop recursion 68 : template <size_t N = Dim> 69 : requires(N == 0) 70 : constexpr size_t product() const { 71 : return 1; 72 : } 73 : /// \endcond 74 : 75 : /// Return a smaller Index with the d-th element removed. 76 : /// 77 : /// \param d the element to remove. 78 : template <size_t N = Dim> 79 : requires(N > 0) 80 1 : Index<Dim - 1> slice_away(const size_t d) const { 81 : ASSERT(d < Dim, 82 : "Can't slice dimension " << d << " from an Index<" << Dim << ">"); 83 : std::array<size_t, Dim - 1> t{}; 84 : for (size_t i = 0; i < d; ++i) { 85 : gsl::at(t, i) = gsl::at(indices_, i); 86 : } 87 : for (size_t i = d + 1; i < Dim; ++i) { 88 : gsl::at(t, i - 1) = gsl::at(indices_, i); 89 : } 90 : return Index<Dim - 1>(t); 91 : } 92 : 93 : /// \cond 94 : // NOLINTNEXTLINE(google-runtime-references) 95 : void pup(PUP::er& p); 96 : /// \endcond 97 : 98 : template <size_t N> 99 0 : friend std::ostream& operator<<(std::ostream& os, // NOLINT 100 : const Index<N>& i); 101 : 102 0 : const size_t* data() const { return indices_.data(); } 103 0 : size_t* data() { return indices_.data(); } 104 : 105 0 : const std::array<size_t, Dim>& indices() const { return indices_; } 106 : 107 : private: 108 0 : std::array<size_t, Dim> indices_; 109 : }; 110 : 111 : /// \ingroup DataStructuresGroup 112 : /// Get the collapsed index into a 1D array of the data corresponding to this 113 : /// Index. Note that the first dimension of the Index varies fastest when 114 : /// computing the collapsed index. 115 : template <size_t N> 116 1 : size_t collapsed_index(const Index<N>& index, const Index<N>& extents); 117 : 118 : /// \ingroup DataStructuresGroup 119 : /// This is the inverse function of collapsed_index. Given a collapsed (1D) 120 : /// index and the extents of the array, return the Index corresponding to that 121 : /// collapsed index. Note that the first dimension of the Index varies fastest 122 : /// when computing the collapsed index. 123 : template <size_t Dim> 124 1 : Index<Dim> expanded_index(size_t index, const Index<Dim>& extents); 125 : 126 : template <size_t N> 127 0 : std::ostream& operator<<(std::ostream& os, const Index<N>& i); 128 : 129 : /// \cond HIDDEN_SYMBOLS 130 : #ifdef SPECTRE_DEBUG 131 : namespace Index_detail { 132 : template <size_t Dim> 133 : void collapsed_index_check(const Index<Dim>& index, const Index<Dim>& extents) { 134 : for (size_t d = 0; d < Dim; ++d) { 135 : ASSERT(index[d] < extents[d], "The requested index in the dimension " 136 : << d << " with value " << index[d] 137 : << " exceeds the number of grid " 138 : "points " 139 : << extents[d]); 140 : } 141 : } 142 : } // namespace Index_detail 143 : #endif 144 : 145 : // the specializations are in the header file so they can be inlined. We use 146 : // specializations to avoid having loops since this computation is very 147 : // straightforward. 148 : template <> 149 : SPECTRE_ALWAYS_INLINE size_t collapsed_index(const Index<0>& /*index*/, 150 : const Index<0>& /*extents*/) { 151 : return 0; 152 : } 153 : 154 : template <> 155 : SPECTRE_ALWAYS_INLINE size_t collapsed_index(const Index<1>& index, 156 : const Index<1>& extents) { 157 : (void)extents; 158 : #ifdef SPECTRE_DEBUG 159 : Index_detail::collapsed_index_check(index, extents); 160 : #endif 161 : return index[0]; 162 : } 163 : 164 : template <> 165 : SPECTRE_ALWAYS_INLINE size_t collapsed_index(const Index<2>& index, 166 : const Index<2>& extents) { 167 : #ifdef SPECTRE_DEBUG 168 : Index_detail::collapsed_index_check(index, extents); 169 : #endif 170 : return index[0] + extents[0] * index[1]; 171 : } 172 : 173 : template <> 174 : SPECTRE_ALWAYS_INLINE size_t collapsed_index(const Index<3>& index, 175 : const Index<3>& extents) { 176 : #ifdef SPECTRE_DEBUG 177 : Index_detail::collapsed_index_check(index, extents); 178 : #endif 179 : return index[0] + extents[0] * (index[1] + extents[1] * index[2]); 180 : } 181 : 182 : template <> 183 : SPECTRE_ALWAYS_INLINE size_t collapsed_index(const Index<4>& index, 184 : const Index<4>& extents) { 185 : #ifdef SPECTRE_DEBUG 186 : Index_detail::collapsed_index_check(index, extents); 187 : #endif 188 : return index[0] + 189 : extents[0] * 190 : (index[1] + extents[1] * (index[2] + extents[2] * index[3])); 191 : } 192 : 193 : template <> 194 : SPECTRE_ALWAYS_INLINE Index<1> expanded_index(size_t index, 195 : const Index<1>& extents) { 196 : (void)extents; 197 : return Index<1>{index}; 198 : } 199 : 200 : template <> 201 : SPECTRE_ALWAYS_INLINE Index<2> expanded_index(size_t index, 202 : const Index<2>& extents) { 203 : const size_t ix = index % extents[0]; 204 : const size_t iy = index / extents[0]; 205 : return Index<2>{{ix, iy}}; 206 : } 207 : 208 : template <> 209 : SPECTRE_ALWAYS_INLINE Index<3> expanded_index(size_t index, 210 : const Index<3>& extents) { 211 : const size_t ix = index % extents[0]; 212 : const size_t iy = (index / extents[0]) % extents[1]; 213 : const size_t iz = (index / extents[0]) / extents[1]; 214 : return Index<3>{{ix, iy, iz}}; 215 : } 216 : 217 : template <size_t N> 218 : bool operator==(const Index<N>& lhs, const Index<N>& rhs); 219 : 220 : template <size_t N> 221 : bool operator!=(const Index<N>& lhs, const Index<N>& rhs); 222 : /// \endcond