--- /dev/null
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2021 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii_ndarray_h
+#define dealii_ndarray_h
+
+#include <deal.II/base/config.h>
+
+#include <deal.II/base/exceptions.h>
+
+#include <array>
+
+DEAL_II_NAMESPACE_OPEN
+
+#ifndef DOXYGEN
+namespace internal
+{
+ namespace ndarray
+ {
+ // clang-format off
+ /**
+ * A variadic template helper class to recursively "unroll" the size
+ * information of the ndarray. This is best explained on an example:
+ * @code
+ * HelperArray<double, 1, 2, 3, 4>::type
+ * == std::array<HelperArray<double, 2, 3, 4>::type, 1>
+ * == std::array<std::array<HelperArray<double, 3, 4>::type, 2>, 1>
+ * == std::array<std::array<std::array<HelperArray<double, 4>::type, 3>, 2>, 1>
+ * == std::array<std::array<std::array<std::array<HelperArray<double>::type, 4>, 3>, 2>, 1>
+ * == std::array<std::array<std::array<std::array<double, 4>, 3>, 2>, 1>
+ * @endcode
+ */
+ template <typename T, std::size_t... Ns>
+ struct HelperArray;
+ // clang-format on
+
+ /**
+ * Recursively define the type alias "type" of HelperArray<T, N, ...Ns>
+ * by wrapping a std::array around HelperArray<T, Ns...>::type
+ */
+ template <typename T, std::size_t N, std::size_t... Ns>
+ struct HelperArray<T, N, Ns...>
+ {
+ using type = std::array<typename HelperArray<T, Ns...>::type, N>;
+ };
+
+ /**
+ * End recursion once no std::size_t template parameters are left and
+ * simply set the type alias to type T
+ */
+ template <typename T>
+ struct HelperArray<T>
+ {
+ using type = T;
+ };
+ } // namespace ndarray
+} // namespace internal
+#endif // DOXYGEN
+
+/**
+ * A (variadic template) type alias for conveniently defining multidimensional
+ * <a href="https://en.cppreference.com/w/cpp/container/array">std::array</a>s
+ *
+ * The problem we try to address with the type alias is the following.
+ * Suppose you want to create a multdimensional array of doubles of, for
+ * example, rank 3, with sizes 2, 3, 4 for the first, middle, and last
+ * index. Then using C-style arrays you could simply write
+ * @code
+ * double my_array[2][3][4] = { ... };
+ * @endcode
+ * Nowadays, there are a number of good reasons why using C-style arrays is
+ * usually discouraged (ranging from incompatibilities with STL functions
+ * requiring awkward wrappers, surprises when comparing for equality, etc.)
+ * If you want to do the same, however, using the more modern (and
+ * encouraged) `std::array` class, then you would have to declare
+ * @code
+ * std::array<std::array<std::array<double, 4>, 3>, 2> = { ... };
+ * @endcode
+ * The repetitions of `std::array` look awkward and, worse, the index
+ * ranges have reversed: the leftmost index has range [0,2), the middle
+ * index has rnage [0,3) and the rightmost index has range [0,4).
+ * We address this issue by providing a class ndarray that allows to you
+ * declare the above stacked `std::array` type by simply writing:
+ * @code
+ * dealii::ndarray<double, 2, 3, 4> my_array = { ... };
+ * @endcode
+ *
+ * @note dealii::ndarray is merely syntactic sugar in form of a
+ * <a href="https://en.cppreference.com/w/cpp/language/type_alias">type
+ * alias</a>
+ * (`using` declaration). It is not a deal.II specific class, but merely a
+ * helper to cleanly define multidimensional arrays realized by "stacked"
+ * `std::array` classes.
+ */
+template <typename T, std::size_t... Ns>
+using ndarray = typename internal::ndarray::HelperArray<T, Ns...>::type;
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif