LCOV - code coverage report
Current view: top level - include/kokkos/base - KokkosArray.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 354 419 84.5 %
Date: 2026-05-29 20:35:17 Functions: 1801 2279 79.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://www.mooseframework.org
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #pragma once
      11             : 
      12             : #ifdef MOOSE_KOKKOS_SCOPE
      13             : #include "KokkosHeader.h"
      14             : #endif
      15             : 
      16             : #include "Conversion.h"
      17             : #include "DataIO.h"
      18             : 
      19             : #define usingKokkosArrayBaseMembers(T, dimension, index_type)                                      \
      20             : private:                                                                                           \
      21             :   using ArrayBase<T, dimension, index_type>::_n;                                                   \
      22             :   using ArrayBase<T, dimension, index_type>::_s;                                                   \
      23             :   using ArrayBase<T, dimension, index_type>::_d;                                                   \
      24             :   using ArrayBase<T, dimension, index_type>::_is_offset;                                           \
      25             :                                                                                                    \
      26             : public:                                                                                            \
      27             :   using typename ArrayBase<T, dimension, index_type>::signed_index_type;                           \
      28             :   using ArrayBase<T, dimension, index_type>::operator=
      29             : 
      30             : namespace Moose::Kokkos
      31             : {
      32             : 
      33             : // This function simply calls ::Kokkos::kokkos_free, but it is separately defined in KokkosArray.K
      34             : // because the Kokkos function cannot be directly seen by the host compiler
      35             : void free(void * ptr);
      36             : 
      37             : /**
      38             :  * The enumerator that dictates the memory copy direction
      39             :  */
      40             : enum class MemcpyType
      41             : {
      42             :   HOST_TO_HOST,
      43             :   HOST_TO_DEVICE,
      44             :   DEVICE_TO_HOST,
      45             :   DEVICE_TO_DEVICE
      46             : };
      47             : 
      48             : /**
      49             :  * The enumerator that dictates the memory layout
      50             :  */
      51             : enum class LayoutType
      52             : {
      53             :   LEFT,
      54             :   RIGHT
      55             : };
      56             : 
      57             : /**
      58             :  * The Kokkos array class
      59             :  */
      60             : template <typename T,
      61             :           unsigned int dimension = 1,
      62             :           typename index_type = dof_id_type,
      63             :           LayoutType layout = LayoutType::LEFT>
      64             : class Array;
      65             : 
      66             : /**
      67             :  * The type trait that determines if a template type is Kokkos array
      68             :  */
      69             : ///@{
      70             : template <typename>
      71             : struct is_kokkos_array : std::false_type
      72             : {
      73             : };
      74             : 
      75             : template <typename T, unsigned int dimension, typename index_type, LayoutType layout>
      76             : struct is_kokkos_array<Array<T, dimension, index_type, layout>> : std::true_type
      77             : {
      78             : };
      79             : ///@}
      80             : 
      81             : /**
      82             :  * The type trait that determines the default behavior of copy constructor and deepCopy()
      83             :  * If this type trait is set to true, the copy constructor will call deepCopy(),
      84             :  * and the deepCopy() method will copy-construct each entry.
      85             :  * If this type trait is set to false, the copy constructor will call shallowCopy(),
      86             :  * and the deepCopy() method will do a memory copy.
      87             :  */
      88             : ///@{
      89             : template <typename T>
      90             : struct ArrayDeepCopy
      91             : {
      92             :   static constexpr bool value = false;
      93             : };
      94             : 
      95             : template <typename T, unsigned int dimension, typename index_type, LayoutType layout>
      96             : struct ArrayDeepCopy<Array<T, dimension, index_type, layout>>
      97             : {
      98             :   static constexpr bool value = ArrayDeepCopy<T>::value;
      99             : };
     100             : ///@}
     101             : 
     102             : /**
     103             :  * The base class for Kokkos arrays
     104             :  */
     105             : template <typename T, unsigned int dimension, typename index_type>
     106             : class ArrayBase
     107             : {
     108             :   static_assert(std::is_integral_v<index_type>, "Kokkos array index type must be an integral type");
     109             :   static_assert(std::is_unsigned_v<index_type>, "Kokkos array index type must be unsigned");
     110             :   static_assert(!std::is_same_v<bool, index_type>, "Kokkos array index type must not be bool");
     111             : 
     112             : public:
     113             :   using signed_index_type = typename std::make_signed<index_type>::type;
     114             : 
     115             :   /**
     116             :    * Constructor
     117             :    * @param layout The memory layout type
     118             :    */
     119     9391238 :   ArrayBase(const LayoutType layout) : _layout(layout) {}
     120             : 
     121             : #ifdef MOOSE_KOKKOS_SCOPE
     122             :   /**
     123             :    * Constructor
     124             :    * Initialize and allocate array with given dimensions
     125             :    * This allocates both host and device data
     126             :    * @param layout The memory layout type
     127             :    * @param n The size of each dimension
     128             :    */
     129             :   template <typename... size_type>
     130          84 :   ArrayBase(const LayoutType layout, size_type... n) : _layout(layout)
     131             :   {
     132          84 :     create(n...);
     133          84 :   }
     134             : #endif
     135             : 
     136             :   /**
     137             :    * Copy constructor
     138             :    */
     139    48508245 :   ArrayBase(const ArrayBase<T, dimension, index_type> & array) : _layout(array._layout)
     140             :   {
     141             : #ifndef MOOSE_KOKKOS_SCOPE
     142             :     static_assert(!ArrayDeepCopy<T>::value,
     143             :                   "Kokkos array cannot be deep copied outside the Kokkos compilation scope");
     144             : #endif
     145             : 
     146             :     if constexpr (ArrayDeepCopy<T>::value)
     147       92279 :       deepCopy(array);
     148             :     else
     149    48415966 :       shallowCopy(array);
     150    48508245 :   }
     151             : 
     152             :   /**
     153             :    * Destructor
     154             :    */
     155    57688728 :   ~ArrayBase() { destroy(); }
     156             : 
     157             :   /**
     158             :    * Free all data and reset
     159             :    */
     160             :   void destroy();
     161             : 
     162             :   /**
     163             :    * Shallow copy another Kokkos array
     164             :    * @param array The Kokkos array to be shallow copied
     165             :    */
     166             :   void shallowCopy(const ArrayBase<T, dimension, index_type> & array);
     167             : 
     168             :   /**
     169             :    * Get the reference count
     170             :    * @returns The reference count
     171             :    */
     172             :   unsigned int useCount() const { return _counter.use_count(); }
     173             : 
     174             : #ifdef MOOSE_KOKKOS_SCOPE
     175             :   /**
     176             :    * Get whether the array was allocated either on host or device
     177             :    * @returns Whether the array was allocated either on host or device
     178             :    */
     179    13414220 :   KOKKOS_FUNCTION bool isAlloc() const { return _is_host_alloc || _is_device_alloc; }
     180             :   /**
     181             :    * Get whether the array was allocated on host
     182             :    * @returns Whether the array was allocated on host
     183             :    */
     184         531 :   KOKKOS_FUNCTION bool isHostAlloc() const { return _is_host_alloc; }
     185             :   /**
     186             :    * Get whether the array was allocated on device
     187             :    * @returns Whether the array was allocated on device
     188             :    */
     189        1472 :   KOKKOS_FUNCTION bool isDeviceAlloc() const { return _is_device_alloc; }
     190             :   /**
     191             :    * Get whether the host array was aliased
     192             :    * @returns Whether the host array was aliased
     193             :    */
     194             :   KOKKOS_FUNCTION bool isHostAlias() const { return _is_host_alias; }
     195             :   /**
     196             :    * Get whether the device array was aliased
     197             :    * @returns Whether the device array was aliased
     198             :    */
     199             :   KOKKOS_FUNCTION bool isDeviceAlias() const { return _is_device_alias; }
     200             :   /**
     201             :    * Get the total array size
     202             :    * @returns The total array size
     203             :    */
     204   470835140 :   KOKKOS_FUNCTION index_type size() const { return _size; }
     205             :   /**
     206             :    * Get the size of a dimension
     207             :    * @param dim The dimension index
     208             :    * @returns The size of the dimension
     209             :    */
     210        3530 :   KOKKOS_FUNCTION index_type n(unsigned int dim) const { return _n[dim]; }
     211             :   /**
     212             :    * Get the data pointer
     213             :    * @returns The pointer to the underlying data depending on the architecture this function is
     214             :    * being called on
     215             :    */
     216    41385190 :   KOKKOS_FUNCTION T * data() const
     217             :   {
     218    41385190 :     KOKKOS_IF_ON_HOST(return _host_data;)
     219             : 
     220    41008074 :     return _device_data;
     221             :   }
     222             :   /**
     223             :    * Get the first element
     224             :    * @returns The reference of the first element depending on the architecture this function is
     225             :    * being called on
     226             :    */
     227             :   KOKKOS_FUNCTION T & first() const
     228             :   {
     229             :     KOKKOS_IF_ON_HOST(return _host_data[0];)
     230             : 
     231             :     return _device_data[0];
     232             :   }
     233             :   /**
     234             :    * Get the last element
     235             :    * @returns The reference of the last element depending on the architecture this function is being
     236             :    * called on
     237             :    */
     238          56 :   KOKKOS_FUNCTION T & last() const
     239             :   {
     240          56 :     KOKKOS_IF_ON_HOST(return _host_data[_size - 1];)
     241             : 
     242           0 :     return _device_data[_size - 1];
     243             :   }
     244             :   /**
     245             :    * Get an array entry
     246             :    * @param i The dimensionless index
     247             :    * @returns The reference of the entry depending on the architecture this function is being called
     248             :    * on
     249             :    */
     250  6181230226 :   KOKKOS_FUNCTION T & operator[](index_type i) const
     251             :   {
     252             :     KOKKOS_ASSERT(i < _size);
     253             : 
     254  6181230226 :     KOKKOS_IF_ON_HOST(return _host_data[i];)
     255             : 
     256  6123480098 :     return _device_data[i];
     257             :   }
     258             : 
     259             :   /**
     260             :    * Get the host data pointer
     261             :    * @returns The pointer to the underlying host data
     262             :    */
     263     1478921 :   T * hostData() const { return _host_data; }
     264             :   /**
     265             :    * Get the device data pointer
     266             :    * @returns The pointer to the underlying device data
     267             :    */
     268     1471861 :   T * deviceData() const { return _device_data; }
     269             :   /**
     270             :    * Get the host unmanaged view
     271             :    * @returns The host unmanaged view
     272             :    */
     273             :   auto hostView() const
     274             :   {
     275             :     return ::Kokkos::View<T *, ::Kokkos::HostSpace, ::Kokkos::MemoryTraits<::Kokkos::Unmanaged>>(
     276             :         _host_data, _size);
     277             :   }
     278             :   /**
     279             :    * Get the device unmanaged view
     280             :    * @returns The device unmanaged view
     281             :    */
     282      589645 :   auto deviceView() const
     283             :   {
     284      589645 :     return ::Kokkos::View<T *, MemSpace, ::Kokkos::MemoryTraits<::Kokkos::Unmanaged>>(_device_data,
     285      338827 :                                                                                       _size);
     286             :   }
     287             :   /**
     288             :    * Initialize array with given dimensions but do not allocate
     289             :    * @param n The size of each dimension
     290             :    */
     291             :   template <typename... size_type>
     292         200 :   void init(size_type... n)
     293             :   {
     294         200 :     createInternal<false, false, false>(n...);
     295         200 :   }
     296             :   /**
     297             :    * Allocate array on host and device
     298             :    * @tparam initialize Whether to initialize host data (calls default constructor)
     299             :    * @param n The vector containing the size of each dimension
     300             :    */
     301             :   template <bool initialize = true>
     302             :   void create(const std::vector<index_type> & n)
     303             :   {
     304             :     createInternal<true, true, initialize>(n);
     305             :   }
     306             :   /**
     307             :    * Allocate array on host and device
     308             :    * @tparam initialize Whether to initialize host data (calls default constructor)
     309             :    * @param n The size of each dimension
     310             :    */
     311             :   template <bool initialize = true, typename... size_type>
     312      592435 :   void create(size_type... n)
     313             :   {
     314      592435 :     createInternal<true, true, initialize>(n...);
     315      592435 :   }
     316             :   /**
     317             :    * Allocate array on host only
     318             :    * @tparam initialize Whether to initialize host data (calls default constructor)
     319             :    * @param n The vector containing the size of each dimension
     320             :    */
     321             :   template <bool initialize = true>
     322             :   void createHost(const std::vector<index_type> & n)
     323             :   {
     324             :     createInternal<true, false, initialize>(n);
     325             :   }
     326             :   /**
     327             :    * Allocate array on host only
     328             :    * @tparam initialize Whether to initialize host data (calls default constructor)
     329             :    * @param n The size of each dimension
     330             :    */
     331             :   template <bool initialize = true, typename... size_type>
     332       23766 :   void createHost(size_type... n)
     333             :   {
     334       23766 :     createInternal<true, false, initialize>(n...);
     335       23766 :   }
     336             :   /**
     337             :    * Allocate array on device only
     338             :    * @param n The vector containing the size of each dimension
     339             :    */
     340        5120 :   void createDevice(const std::vector<index_type> & n) { createInternal<false, true, false>(n); }
     341             :   /**
     342             :    * Allocate array on device only
     343             :    * @param n The size of each dimension
     344             :    */
     345             :   template <typename... size_type>
     346       34588 :   void createDevice(size_type... n)
     347             :   {
     348       34588 :     createInternal<false, true, false>(n...);
     349       34588 :   }
     350             :   /**
     351             :    * Point the host data to an external data instead of allocating it
     352             :    * @param ptr The pointer to the external host data
     353             :    */
     354             :   void aliasHost(T * ptr);
     355             :   /**
     356             :    * Point the device data to an external data instead of allocating it
     357             :    * @param ptr The pointer to the external device data
     358             :    */
     359             :   void aliasDevice(T * ptr);
     360             :   /**
     361             :    * Apply starting index offsets to each dimension
     362             :    * @param d The vector containing the offset of each dimension
     363             :    */
     364             :   void offset(const std::vector<signed_index_type> & d);
     365             :   /**
     366             :    * Apply starting index offsets to each dimension
     367             :    * @param d The offset of each dimension
     368             :    */
     369             :   template <typename... offset_type>
     370             :   void offset(offset_type... d);
     371             :   /**
     372             :    * Copy data from host to device
     373             :    */
     374             :   void copyToDevice();
     375             :   /**
     376             :    * Copy data from device to host
     377             :    */
     378             :   void copyToHost();
     379             :   /**
     380             :    * Copy data from an external data to this array
     381             :    * @param ptr The pointer to the external data
     382             :    * @param dir The copy direction
     383             :    * @param n The number of entries to copy
     384             :    * @param offset The starting offset of this array
     385             :    */
     386             :   void copyIn(const T * ptr, MemcpyType dir, index_type n, index_type offset = 0);
     387             :   /**
     388             :    * Copy data to an external data from this array
     389             :    * @param ptr The pointer to the external data
     390             :    * @param dir The copy direction
     391             :    * @param n The number of entries to copy
     392             :    * @param offset The starting offset of this array
     393             :    */
     394             :   void copyOut(T * ptr, MemcpyType dir, index_type n, index_type offset = 0);
     395             :   /**
     396             :    * Copy all the nested Kokkos arrays including self from host to device
     397             :    */
     398             :   void copyToDeviceNested();
     399             :   /**
     400             :    * Copy data from host to device and deallocate host
     401             :    * @param should_free_host Whether the host memory should be freed.
     402             :    * Host memory cannot be freed when there are shallow copies of this array that are still alive.
     403             :    * If \p should_free_host is true, and we cannot free for above reason, it will error.
     404             :    */
     405             :   void moveToDevice(bool should_free_host = true);
     406             :   /**
     407             :    * Copy data from device to host and deallocate device
     408             :    * @param should_free_device Whether the device memory should be freed.
     409             :    * Device memory cannot be freed when there are shallow copies of this array that are still alive.
     410             :    * If \p should_free_device is true, and we cannot free for above reason, it will error.
     411             :    */
     412             :   void moveToHost(bool should_free_device = true);
     413             :   /**
     414             :    * Deep copy another Kokkos array
     415             :    * If ArrayDeepCopy<T>::value is true, it will copy-construct each entry
     416             :    * If ArrayDeepCopy<T>::value is false, it will do a memory copy
     417             :    * @param array The Kokkos array to be deep copied
     418             :    */
     419             :   void deepCopy(const ArrayBase<T, dimension, index_type> & array);
     420             :   /**
     421             :    * Swap with another Kokkos array
     422             :    * @param array The Kokkos array to be swapped
     423             :    */
     424             :   void swap(ArrayBase<T, dimension, index_type> & array);
     425             : 
     426             :   /**
     427             :    * Assign a scalar value uniformly
     428             :    * @param scalar The scalar value to be assigned
     429             :    */
     430             :   auto & operator=(const T & scalar);
     431             : 
     432             :   /**
     433             :    * Array iterator
     434             :    */
     435             :   class iterator
     436             :   {
     437             :   public:
     438             :     using iterator_category = std::forward_iterator_tag;
     439             :     using value_type = T;
     440             :     using difference_type = std::ptrdiff_t;
     441             :     using pointer = T *;
     442             :     using reference = T &;
     443             : 
     444             :     KOKKOS_FUNCTION iterator() : it(nullptr) {}
     445     7239391 :     KOKKOS_FUNCTION explicit iterator(T * p) : it(p) {}
     446             : 
     447     4009574 :     KOKKOS_FUNCTION reference operator*() const { return *it; }
     448             :     KOKKOS_FUNCTION pointer operator->() const { return it; }
     449     1122124 :     KOKKOS_FUNCTION pointer operator&() const { return it; }
     450     4008767 :     KOKKOS_FUNCTION iterator & operator++()
     451             :     {
     452     4008767 :       ++it;
     453     4008767 :       return *this;
     454             :     }
     455         807 :     KOKKOS_FUNCTION iterator operator++(int)
     456             :     {
     457         807 :       iterator pre = *this;
     458         807 :       ++it;
     459         807 :       return pre;
     460             :     }
     461             :     KOKKOS_FUNCTION friend bool operator==(const iterator & a, const iterator & b)
     462             :     {
     463             :       return a.it == b.it;
     464             :     }
     465     7067364 :     KOKKOS_FUNCTION friend bool operator!=(const iterator & a, const iterator & b)
     466             :     {
     467     7067364 :       return a.it != b.it;
     468             :     }
     469             : 
     470             :   private:
     471             :     pointer it;
     472             :   };
     473             : 
     474             :   /**
     475             :    * Get the beginning iterator
     476             :    * @returns The beginning iterator
     477             :    */
     478     3619732 :   KOKKOS_FUNCTION iterator begin() const
     479             :   {
     480     3619732 :     KOKKOS_IF_ON_HOST(return iterator(_host_data);)
     481             : 
     482      557590 :     return iterator(_device_data);
     483             :   }
     484             :   /**
     485             :    * Get the end iterator
     486             :    * @returns The end iterator
     487             :    */
     488     3619659 :   KOKKOS_FUNCTION iterator end() const
     489             :   {
     490     3619659 :     KOKKOS_IF_ON_HOST(return iterator(_host_data + _size);)
     491             : 
     492      557590 :     return iterator(_device_data + _size);
     493             :   }
     494             : #endif
     495             : 
     496             : protected:
     497             :   /**
     498             :    * Size of each dimension
     499             :    */
     500             :   index_type _n[dimension] = {0};
     501             :   /**
     502             :    * Stride of each dimension
     503             :    */
     504             :   index_type _s[dimension] = {0};
     505             :   /**
     506             :    * Offset of each dimension
     507             :    */
     508             :   signed_index_type _d[dimension] = {0};
     509             :   /**
     510             :    * Flag whether the array indices are offset
     511             :    */
     512             :   bool _is_offset = false;
     513             :   /**
     514             :    * Flag whether host data was allocated using malloc
     515             :    */
     516             :   bool _is_malloc = false;
     517             : 
     518             : #ifdef MOOSE_KOKKOS_SCOPE
     519             :   /**
     520             :    * The internal method to initialize and allocate this array
     521             :    * @tparam host Whether host data will be allocated
     522             :    * @tparam device Whether device data will be allocated
     523             :    * @tparam initialize Whether to initialize host data (calls default constructor)
     524             :    * @param n The size of each dimension
     525             :    */
     526             :   template <bool host, bool device, bool initialize, typename... size_type>
     527             :   void createInternal(size_type... n);
     528             :   /**
     529             :    * The internal method to initialize and allocate this array
     530             :    * @tparam host Whether host data will be allocated
     531             :    * @tparam device Whether device data will be allocated
     532             :    * @tparam initialize Whether to initialize host data (calls default constructor)
     533             :    * @param n The vector containing the size of each dimension
     534             :    */
     535             :   template <bool host, bool device, bool initialize>
     536             :   void createInternal(const std::vector<index_type> & n);
     537             :   /**
     538             :    * The internal method to initialize and allocate this array
     539             :    * @tparam initialize Whether to initialize host data (calls default constructor)
     540             :    * @param n The vector containing the size of each dimension
     541             :    * @param host The flag whether host data will be allocated
     542             :    * @param device The flag whether device data will be allocated
     543             :    */
     544             :   template <bool initialize>
     545             :   void createInternal(const std::vector<index_type> & n, bool host, bool device);
     546             :   /**
     547             :    * The internal method to perform a memory copy
     548             :    * @tparam TargetSpace The Kokkos memory space of target data
     549             :    * @tparam Sourcespace The Kokkos memory space of source data
     550             :    * @param target The pointer to the target data
     551             :    * @param source The pointer to the source data
     552             :    * @param n The number of entries to copy
     553             :    */
     554             :   template <typename TargetSpace, typename SourceSpace>
     555             :   void copyInternal(T * target, const T * source, index_type n);
     556             : #endif
     557             : 
     558             : private:
     559             : #ifdef MOOSE_KOKKOS_SCOPE
     560             :   /**
     561             :    * Allocate host data for an initialized array that has not allocated host data
     562             :    * @tparam initialize Whether to initialize host data (calls default constructor)
     563             :    */
     564             :   template <bool initialize>
     565             :   void allocHost();
     566             :   /**
     567             :    * Allocate device data for an initialized array that has not allocated device data
     568             :    */
     569             :   void allocDevice();
     570             : #endif
     571             :   /**
     572             :    * Free host data
     573             :    */
     574             :   void freeHost();
     575             :   /**
     576             :    * Free device data
     577             :    */
     578             :   void freeDevice();
     579             : 
     580             :   /**
     581             :    * Reference counter
     582             :    */
     583             :   std::shared_ptr<unsigned int> _counter;
     584             :   /**
     585             :    * Flag whether array was initialized
     586             :    */
     587             :   bool _is_init = false;
     588             :   /**
     589             :    * Flag whether host data was allocated
     590             :    */
     591             :   bool _is_host_alloc = false;
     592             :   /**
     593             :    * Flag whether device data was allocated
     594             :    */
     595             :   bool _is_device_alloc = false;
     596             :   /**
     597             :    * Flag whether the host data points to an external data
     598             :    */
     599             :   bool _is_host_alias = false;
     600             :   /**
     601             :    * Flag whether the device data points to an external data
     602             :    */
     603             :   bool _is_device_alias = false;
     604             :   /**
     605             :    * Host data
     606             :    */
     607             :   T * _host_data = nullptr;
     608             :   /**
     609             :    * Device data
     610             :    */
     611             :   T * _device_data = nullptr;
     612             :   /**
     613             :    * Total size
     614             :    */
     615             :   index_type _size = 0;
     616             :   /**
     617             :    * Memory layout type
     618             :    */
     619             :   const LayoutType _layout;
     620             : };
     621             : 
     622             : template <typename T, unsigned int dimension, typename index_type>
     623             : void
     624     2240630 : ArrayBase<T, dimension, index_type>::freeHost()
     625             : {
     626     2240630 :   if (!_is_host_alloc)
     627       60072 :     return;
     628             : 
     629     2180558 :   if (_is_host_alias)
     630             :   {
     631       12732 :     _host_data = nullptr;
     632       12732 :     _is_host_alias = false;
     633             :   }
     634             :   else
     635             :   {
     636     2167826 :     if (!_is_malloc)
     637             :       // Allocated by new
     638     1421581 :       delete[] _host_data;
     639             :     else
     640             :     {
     641             :       // Allocated by malloc
     642    10589417 :       for (index_type i = 0; i < _size; ++i)
     643     8992053 :         _host_data[i].~T();
     644             : 
     645     1597364 :       std::free(_host_data);
     646             :     }
     647             :   }
     648             : 
     649     2180558 :   _is_host_alloc = false;
     650     2180558 :   _is_malloc = false;
     651             : }
     652             : 
     653             : template <typename T, unsigned int dimension, typename index_type>
     654             : void
     655     2217752 : ArrayBase<T, dimension, index_type>::freeDevice()
     656             : {
     657     2217752 :   if (!_is_device_alloc)
     658       23668 :     return;
     659             : 
     660     2194084 :   if (_is_device_alias)
     661             :   {
     662          92 :     _device_data = nullptr;
     663          92 :     _is_device_alias = false;
     664             :   }
     665             :   else
     666     2193992 :     Moose::Kokkos::free(_device_data);
     667             : 
     668     2194084 :   _is_device_alloc = false;
     669             : }
     670             : 
     671             : template <typename T, unsigned int dimension, typename index_type>
     672             : void
     673   109687692 : ArrayBase<T, dimension, index_type>::destroy()
     674             : {
     675   109687692 :   if (!_counter)
     676    62471331 :     return;
     677             : 
     678    47216361 :   if (_counter.use_count() > 1)
     679             :   {
     680    44998609 :     _host_data = nullptr;
     681    44998609 :     _device_data = nullptr;
     682             :   }
     683     2217752 :   else if (_counter.use_count() == 1)
     684             :   {
     685     2217752 :     freeHost();
     686     2217752 :     freeDevice();
     687             :   }
     688             : 
     689    47216361 :   _size = 0;
     690             : 
     691   115981776 :   for (unsigned int i = 0; i < dimension; ++i)
     692             :   {
     693    68765415 :     _n[i] = 0;
     694    68765415 :     _s[i] = 0;
     695    68765415 :     _d[i] = 0;
     696             :   }
     697             : 
     698    47216361 :   _is_init = false;
     699    47216361 :   _is_offset = false;
     700    47216361 :   _is_malloc = false;
     701    47216361 :   _is_host_alloc = false;
     702    47216361 :   _is_device_alloc = false;
     703    47216361 :   _is_host_alias = false;
     704    47216361 :   _is_device_alias = false;
     705             : 
     706    47216361 :   _counter.reset();
     707             : }
     708             : 
     709             : template <typename T, unsigned int dimension, typename index_type>
     710             : void
     711    50750805 : ArrayBase<T, dimension, index_type>::shallowCopy(const ArrayBase<T, dimension, index_type> & array)
     712             : {
     713    50750805 :   if (_layout != array._layout)
     714           0 :     mooseError("Kokkos array error: cannot shallow copy arrays with different layouts.");
     715             : 
     716    50750805 :   destroy();
     717             : 
     718    50750805 :   _counter = array._counter;
     719             : 
     720    50750805 :   _size = array._size;
     721             : 
     722   123363418 :   for (unsigned int i = 0; i < dimension; ++i)
     723             :   {
     724    72612613 :     _n[i] = array._n[i];
     725    72612613 :     _s[i] = array._s[i];
     726    72612613 :     _d[i] = array._d[i];
     727             :   }
     728             : 
     729    50750805 :   _is_init = array._is_init;
     730    50750805 :   _is_offset = array._is_offset;
     731    50750805 :   _is_malloc = array._is_malloc;
     732    50750805 :   _is_host_alloc = array._is_host_alloc;
     733    50750805 :   _is_device_alloc = array._is_device_alloc;
     734    50750805 :   _is_host_alias = array._is_host_alias;
     735    50750805 :   _is_device_alias = array._is_device_alias;
     736             : 
     737    50750805 :   _host_data = array._host_data;
     738    50750805 :   _device_data = array._device_data;
     739    50750805 : }
     740             : 
     741             : #ifdef MOOSE_KOKKOS_SCOPE
     742             : template <typename T, unsigned int dimension, typename index_type>
     743             : void
     744     1108906 : ArrayBase<T, dimension, index_type>::aliasHost(T * ptr)
     745             : {
     746     1108906 :   if (!_is_init)
     747           0 :     mooseError("Kokkos array error: attempted to alias host data before array initialization.");
     748             : 
     749     1108906 :   if (_is_host_alloc && !_is_host_alias)
     750           0 :     mooseError("Kokkos array error: cannot alias host data because host data was not aliased.");
     751             : 
     752     1108906 :   _host_data = ptr;
     753     1108906 :   _is_host_alloc = true;
     754     1108906 :   _is_host_alias = true;
     755     1108906 : }
     756             : 
     757             : template <typename T, unsigned int dimension, typename index_type>
     758             : void
     759         428 : ArrayBase<T, dimension, index_type>::aliasDevice(T * ptr)
     760             : {
     761         428 :   if (!_is_init)
     762           0 :     mooseError("Kokkos array error: attempted to alias device data before array initialization.");
     763             : 
     764         428 :   if (_is_device_alloc && !_is_device_alias)
     765           0 :     mooseError("Kokkos array error: cannot alias device data because device data was not aliased.");
     766             : 
     767         428 :   _device_data = ptr;
     768         428 :   _is_device_alloc = true;
     769         428 :   _is_device_alias = true;
     770         428 : }
     771             : 
     772             : template <typename T, unsigned int dimension, typename index_type>
     773             : template <bool initialize>
     774             : void
     775     2171262 : ArrayBase<T, dimension, index_type>::allocHost()
     776             : {
     777     2171262 :   if (_is_host_alloc)
     778           0 :     return;
     779             : 
     780             :   if constexpr (initialize)
     781             :   {
     782             :     static_assert(
     783             :         std::is_default_constructible<T>::value,
     784             :         "Data type is not default-constructible. Initialization argument should be set to false.");
     785             : 
     786     3202448 :     _host_data = new T[_size];
     787             :   }
     788             :   else
     789     1597750 :     _host_data = static_cast<T *>(std::malloc(_size * sizeof(T)));
     790             : 
     791     2171262 :   _is_host_alloc = true;
     792     2171262 :   _is_malloc = !initialize;
     793             : }
     794             : 
     795             : template <typename T, unsigned int dimension, typename index_type>
     796             : void
     797     2197672 : ArrayBase<T, dimension, index_type>::allocDevice()
     798             : {
     799     2197672 :   if (_is_device_alloc)
     800           0 :     return;
     801             : 
     802     2197672 :   _device_data =
     803     1262940 :       static_cast<T *>(::Kokkos::kokkos_malloc<ExecSpace::memory_space>(_size * sizeof(T)));
     804             : 
     805     2197672 :   _is_device_alloc = true;
     806             : }
     807             : 
     808             : template <typename T, unsigned int dimension, typename index_type>
     809             : template <bool host, bool device, bool initialize>
     810             : void
     811     2221638 : ArrayBase<T, dimension, index_type>::createInternal(const std::vector<index_type> & n)
     812             : {
     813     2221638 :   if (n.size() != dimension)
     814           0 :     mooseError("Kokkos array error: the number of dimensions provided (",
     815           0 :                n.size(),
     816             :                ") must match the array dimension (",
     817             :                dimension,
     818             :                ").");
     819             : 
     820     2221638 :   if (_counter)
     821        1419 :     destroy();
     822             : 
     823     2221638 :   _counter = std::make_shared<unsigned int>();
     824             : 
     825     2221638 :   uint64_t overflow_checker = 1;
     826             : 
     827     2221638 :   _size = 1;
     828     2221638 :   _s[0] = 1;
     829             : 
     830     4637277 :   for (const auto i : make_range(dimension))
     831             :   {
     832     2415639 :     overflow_checker *= n[i];
     833             : 
     834     2415639 :     _n[i] = n[i];
     835     2415639 :     _size *= n[i];
     836             :   }
     837             : 
     838     2221638 :   if (overflow_checker > std::numeric_limits<index_type>::max())
     839           0 :     mooseError("Kokkos array error: the dimensions provided (",
     840             :                Moose::stringify(n),
     841             :                ") has the total size of ",
     842             :                overflow_checker,
     843             :                " which exceeds the limit of ",
     844             :                MooseUtils::prettyCppType<index_type>(),
     845             :                ".");
     846             : 
     847     2221638 :   if (_layout == LayoutType::LEFT)
     848             :   {
     849     2221630 :     _s[0] = 1;
     850             : 
     851     2415611 :     for (unsigned int i = 1; i < dimension; ++i)
     852      193981 :       _s[i] = _s[i - 1] * _n[i - 1];
     853             :   }
     854             :   else
     855             :   {
     856           8 :     _s[dimension - 1] = 1;
     857             : 
     858          28 :     for (int i = dimension - 2; i >= 0; --i)
     859          20 :       _s[i] = _s[i + 1] * _n[i + 1];
     860             :   }
     861             : 
     862             :   if constexpr (host)
     863     2171262 :     allocHost<initialize>();
     864             : 
     865             :   if constexpr (device)
     866     2197672 :     allocDevice();
     867             : 
     868     2221638 :   _is_init = true;
     869     2221638 : }
     870             : 
     871             : template <typename T, unsigned int dimension, typename index_type>
     872             : template <bool initialize>
     873             : void
     874       93698 : ArrayBase<T, dimension, index_type>::createInternal(const std::vector<index_type> & n,
     875             :                                                     bool host,
     876             :                                                     bool device)
     877             : {
     878       93698 :   if (host && device)
     879       92279 :     createInternal<true, true, initialize>(n);
     880        1419 :   else if (host && !device)
     881           0 :     createInternal<true, false, initialize>(n);
     882        1419 :   else if (!host && device)
     883        1419 :     createInternal<false, true, initialize>(n);
     884             :   else
     885           0 :     createInternal<false, false, initialize>(n);
     886       93698 : }
     887             : 
     888             : template <typename T, unsigned int dimension, typename index_type>
     889             : template <bool host, bool device, bool initialize, typename... size_type>
     890             : void
     891      650989 : ArrayBase<T, dimension, index_type>::createInternal(size_type... n)
     892             : {
     893             :   static_assert((std::is_convertible<size_type, index_type>::value && ...),
     894             :                 "All arguments must be convertible to index_type");
     895             :   static_assert(sizeof...(n) == dimension, "Number of arguments should match array dimension");
     896             : 
     897      650989 :   std::vector<index_type> dims;
     898      650989 :   (dims.push_back(n), ...);
     899             : 
     900      650989 :   createInternal<host, device, initialize>(dims);
     901      650989 : }
     902             : 
     903             : template <typename T, unsigned int dimension, typename index_type>
     904             : template <typename TargetSpace, typename SourceSpace>
     905             : void
     906     6218013 : ArrayBase<T, dimension, index_type>::copyInternal(T * target, const T * source, index_type n)
     907             : {
     908     6218013 :   ::Kokkos::Impl::DeepCopy<TargetSpace, SourceSpace>(target, source, n * sizeof(T));
     909     6218013 :   ::Kokkos::fence();
     910     6218013 : }
     911             : 
     912             : template <typename T, unsigned int dimension, typename index_type>
     913             : void
     914        4451 : ArrayBase<T, dimension, index_type>::offset(const std::vector<signed_index_type> & d)
     915             : {
     916        4451 :   if (d.size() > dimension)
     917           0 :     mooseError("Kokkos array error: the number of offsets provided (",
     918           0 :                d.size(),
     919             :                ") cannot be larger than the array dimension (",
     920             :                dimension,
     921             :                ").");
     922             : 
     923        8902 :   for (const auto i : index_range(d))
     924        4451 :     _d[i] = d[i];
     925             : 
     926        4451 :   _is_offset = true;
     927        4451 : }
     928             : 
     929             : template <typename T, unsigned int dimension, typename index_type>
     930             : template <typename... offset_type>
     931             : void
     932        4451 : ArrayBase<T, dimension, index_type>::offset(offset_type... d)
     933             : {
     934             :   static_assert((std::is_convertible<offset_type, signed_index_type>::value && ...),
     935             :                 "All arguments must be convertible to signed_index_type");
     936             :   static_assert(sizeof...(d) == dimension, "Number of arguments should match array dimension");
     937             : 
     938        4451 :   std::vector<signed_index_type> offsets;
     939        4451 :   (offsets.push_back(d), ...);
     940             : 
     941        4451 :   offset(offsets);
     942        4451 : }
     943             : 
     944             : template <typename T, unsigned int dimension, typename index_type>
     945             : void
     946     4025058 : ArrayBase<T, dimension, index_type>::copyToDevice()
     947             : {
     948             :   // If host side memory is not allocated, do nothing
     949     4025058 :   if (!_is_host_alloc)
     950       73048 :     return;
     951             : 
     952             :   // If device side memory is not allocated,
     953     3952010 :   if (!_is_device_alloc)
     954             :   {
     955           0 :     if (_counter.use_count() == 1)
     956             :       // allocate memory if this array is not shared with other arrays
     957           0 :       allocDevice();
     958             :     else
     959             :       // print error if this array is shared with other arrays
     960           0 :       mooseError("Kokkos array error: cannot copy from host to device because device memory "
     961             :                  "was not allocated. Cannot allocate device memory for copy because the array is "
     962             :                  "being shared.");
     963             :   }
     964             : 
     965             :   // Copy from host to device
     966     3952010 :   copyInternal<MemSpace, ::Kokkos::HostSpace>(_device_data, _host_data, _size);
     967             : }
     968             : 
     969             : template <typename T, unsigned int dimension, typename index_type>
     970             : void
     971      791358 : ArrayBase<T, dimension, index_type>::copyToHost()
     972             : {
     973             :   // If device side memory is not allocated, do nothing
     974      791358 :   if (!_is_device_alloc)
     975           0 :     return;
     976             : 
     977             :   // If host side memory is not allocated,
     978      791358 :   if (!_is_host_alloc)
     979             :   {
     980           0 :     if (_counter.use_count() == 1)
     981             :       // allocate memory if this array is not shared with other arrays
     982           0 :       allocHost<false>();
     983             :     else
     984             :       // print error if this array is shared with other arrays
     985           0 :       mooseError("Kokkos array error: cannot copy from device to host because host memory "
     986             :                  "was not allocated. Cannot allocate host memory for copy because the array is "
     987             :                  "being shared.");
     988             :   }
     989             : 
     990             :   // Copy from device to host
     991      791358 :   copyInternal<::Kokkos::HostSpace, MemSpace>(_host_data, _device_data, _size);
     992             : }
     993             : 
     994             : template <typename T, unsigned int dimension, typename index_type>
     995             : void
     996       22878 : ArrayBase<T, dimension, index_type>::moveToDevice(bool should_free_host)
     997             : {
     998             :   static_assert(!is_kokkos_array<T>::value,
     999             :                 "moveToDevice() not allowed for a nested array whose data type is another array.");
    1000             : 
    1001       22878 :   if (should_free_host && _counter.use_count() > 1)
    1002           0 :     mooseError("Kokkos array error: cannot move array from host to device because there is at "
    1003             :                "least one shallow copy of this array still alive.");
    1004             : 
    1005       22878 :   copyToDevice();
    1006             : 
    1007       22878 :   if (_counter.use_count() == 1)
    1008       22878 :     freeHost();
    1009       22878 : }
    1010             : 
    1011             : template <typename T, unsigned int dimension, typename index_type>
    1012             : void
    1013             : ArrayBase<T, dimension, index_type>::moveToHost(bool should_free_device)
    1014             : {
    1015             :   if (should_free_device && _counter.use_count() > 1)
    1016             :     mooseError("Kokkos array error: cannot move array from device to host because there is at "
    1017             :                "least one shallow copy of this array still alive.");
    1018             : 
    1019             :   copyToHost();
    1020             : 
    1021             :   if (_counter.use_count() == 1)
    1022             :     freeDevice();
    1023             : }
    1024             : 
    1025             : template <typename T, unsigned int dimension, typename index_type>
    1026             : void
    1027         256 : ArrayBase<T, dimension, index_type>::copyIn(const T * ptr,
    1028             :                                             MemcpyType dir,
    1029             :                                             index_type n,
    1030             :                                             index_type offset)
    1031             : {
    1032         256 :   if (n > _size)
    1033           0 :     mooseError("Kokkos array error: cannot copy in data larger than the array size.");
    1034             : 
    1035         256 :   if (offset > _size)
    1036           0 :     mooseError("Kokkos array error: offset cannot be larger than the array size.");
    1037             : 
    1038         256 :   if (dir == MemcpyType::HOST_TO_HOST)
    1039             :   {
    1040             :     // If host side memory is not allocated, print error
    1041           0 :     if (!_is_host_alloc)
    1042           0 :       mooseError(
    1043             :           "Kokkos array error: cannot copy in to the array because host memory was not allocated.");
    1044             : 
    1045             :     // Copy from host to host
    1046           0 :     copyInternal<::Kokkos::HostSpace, ::Kokkos::HostSpace>(_host_data + offset, ptr, n);
    1047             :   }
    1048         256 :   else if (dir == MemcpyType::HOST_TO_DEVICE)
    1049             :   {
    1050             :     // If device side memory is not allocated, print error
    1051         256 :     if (!_is_device_alloc)
    1052           0 :       mooseError("Kokkos array error: cannot copy in to the array because device memory was not "
    1053             :                  "allocated.");
    1054             : 
    1055             :     // Copy from host to device
    1056         256 :     copyInternal<MemSpace, ::Kokkos::HostSpace>(_device_data + offset, ptr, n);
    1057             :   }
    1058           0 :   else if (dir == MemcpyType::DEVICE_TO_HOST)
    1059             :   {
    1060             :     // If host side memory is not allocated, print error
    1061           0 :     if (!_is_host_alloc)
    1062           0 :       mooseError(
    1063             :           "Kokkos array error: cannot copy in to the array because host memory was not allocated.");
    1064             : 
    1065             :     // Copy from device to host
    1066           0 :     copyInternal<::Kokkos::HostSpace, MemSpace>(_host_data + offset, ptr, n);
    1067             :   }
    1068           0 :   else if (dir == MemcpyType::DEVICE_TO_DEVICE)
    1069             :   {
    1070             :     // If device side memory is not allocated, print error
    1071           0 :     if (!_is_device_alloc)
    1072           0 :       mooseError("Kokkos array error: cannot copy in to the array because device memory was not "
    1073             :                  "allocated.");
    1074             : 
    1075             :     // Copy from device to device
    1076           0 :     copyInternal<MemSpace, MemSpace>(_device_data + offset, ptr, n);
    1077             :   }
    1078         256 : }
    1079             : 
    1080             : template <typename T, unsigned int dimension, typename index_type>
    1081             : void
    1082        1139 : ArrayBase<T, dimension, index_type>::copyOut(T * ptr,
    1083             :                                              MemcpyType dir,
    1084             :                                              index_type n,
    1085             :                                              index_type offset)
    1086             : {
    1087        1139 :   if (n > _size)
    1088           0 :     mooseError("Kokkos array error: cannot copy out data larger than the array size.");
    1089             : 
    1090        1139 :   if (offset > _size)
    1091           0 :     mooseError("Kokkos array error: offset cannot be larger than the array size.");
    1092             : 
    1093        1139 :   if (dir == MemcpyType::HOST_TO_HOST)
    1094             :   {
    1095             :     // If host side memory is not allocated, print error
    1096           0 :     if (!_is_host_alloc)
    1097           0 :       mooseError("Kokkos array error: cannot copy out from the array because host memory was not "
    1098             :                  "allocated.");
    1099             : 
    1100             :     // Copy from host to host
    1101           0 :     copyInternal<::Kokkos::HostSpace, ::Kokkos::HostSpace>(ptr, _host_data + offset, n);
    1102             :   }
    1103        1139 :   else if (dir == MemcpyType::HOST_TO_DEVICE)
    1104             :   {
    1105             :     // If host side memory is not allocated, print error
    1106           0 :     if (!_is_host_alloc)
    1107           0 :       mooseError("Kokkos array error: cannot copy out from the array because host memory was not "
    1108             :                  "allocated.");
    1109             : 
    1110             :     // Copy from host to device
    1111           0 :     copyInternal<MemSpace, ::Kokkos::HostSpace>(ptr, _host_data + offset, n);
    1112             :   }
    1113        1139 :   else if (dir == MemcpyType::DEVICE_TO_HOST)
    1114             :   {
    1115             :     // If device side memory is not allocated, print error
    1116        1139 :     if (!_is_device_alloc)
    1117           0 :       mooseError("Kokkos array error: cannot copy out from the array because device memory was not "
    1118             :                  "allocated.");
    1119             : 
    1120             :     // Copy from device to host
    1121        1139 :     copyInternal<::Kokkos::HostSpace, MemSpace>(ptr, _device_data + offset, n);
    1122             :   }
    1123           0 :   else if (dir == MemcpyType::DEVICE_TO_DEVICE)
    1124             :   {
    1125             :     // If device side memory is not allocated, print error
    1126           0 :     if (!_is_device_alloc)
    1127           0 :       mooseError("Kokkos array error: cannot copy out from the array because device memory was not "
    1128             :                  "allocated.");
    1129             : 
    1130             :     // Copy from device to device
    1131           0 :     copyInternal<MemSpace, MemSpace>(ptr, _device_data + offset, n);
    1132             :   }
    1133        1139 : }
    1134             : 
    1135             : template <typename T>
    1136             : void
    1137    11337214 : copyToDeviceInner(T & /* data */)
    1138             : {
    1139    11337214 : }
    1140             : 
    1141             : template <typename T, unsigned int dimension, typename index_type, LayoutType layout>
    1142             : void
    1143      266156 : copyToDeviceInner(Array<T, dimension, index_type, layout> & data)
    1144             : {
    1145      266156 :   data.copyToDeviceNested();
    1146      266156 : }
    1147             : 
    1148             : template <typename T, unsigned int dimension, typename index_type>
    1149             : void
    1150      329576 : ArrayBase<T, dimension, index_type>::copyToDeviceNested()
    1151             : {
    1152    11932946 :   for (index_type i = 0; i < _size; ++i)
    1153    11603370 :     copyToDeviceInner(_host_data[i]);
    1154             : 
    1155      329576 :   copyToDevice();
    1156      329576 : }
    1157             : 
    1158             : template <typename T, unsigned int dimension, typename index_type>
    1159             : void
    1160       93698 : ArrayBase<T, dimension, index_type>::deepCopy(const ArrayBase<T, dimension, index_type> & array)
    1161             : {
    1162       93698 :   if (_layout != array._layout)
    1163           0 :     mooseError("Kokkos array error: cannot deep copy arrays with different layouts.");
    1164             : 
    1165       92279 :   if (ArrayDeepCopy<T>::value && !array._is_host_alloc)
    1166           0 :     mooseError(
    1167             :         "Kokkos array error: cannot deep copy using constructor from array without host data.");
    1168             : 
    1169      281094 :   std::vector<index_type> n(std::begin(array._n), std::end(array._n));
    1170             : 
    1171       93698 :   createInternal<false>(n, array._is_host_alloc, array._is_device_alloc);
    1172             : 
    1173             :   if constexpr (ArrayDeepCopy<T>::value)
    1174             :   {
    1175      186339 :     for (index_type i = 0; i < _size; ++i)
    1176       94060 :       new (_host_data + i) T(array._host_data[i]);
    1177             : 
    1178       92279 :     copyToDevice();
    1179             :   }
    1180             :   else
    1181             :   {
    1182        1419 :     if (_is_host_alloc)
    1183           0 :       std::memcpy(_host_data, array._host_data, _size * sizeof(T));
    1184             : 
    1185        1419 :     if (_is_device_alloc)
    1186        1419 :       copyInternal<MemSpace, MemSpace>(_device_data, array._device_data, _size);
    1187             :   }
    1188             : 
    1189      187396 :   for (unsigned int i = 0; i < dimension; ++i)
    1190             :   {
    1191       93698 :     _d[i] = array._d[i];
    1192       93698 :     _s[i] = array._s[i];
    1193             :   }
    1194             : 
    1195       93698 :   _is_offset = array._is_offset;
    1196       93698 : }
    1197             : 
    1198             : template <typename T, unsigned int dimension, typename index_type>
    1199             : void
    1200        2808 : ArrayBase<T, dimension, index_type>::swap(ArrayBase<T, dimension, index_type> & array)
    1201             : {
    1202        2808 :   ArrayBase<T, dimension, index_type> clone(_layout);
    1203             : 
    1204        2808 :   clone.shallowCopy(*this);
    1205        2808 :   this->shallowCopy(array);
    1206        2808 :   array.shallowCopy(clone);
    1207        2808 : }
    1208             : 
    1209             : template <typename T, unsigned int dimension, typename index_type>
    1210             : auto &
    1211      589635 : ArrayBase<T, dimension, index_type>::operator=(const T & scalar)
    1212             : {
    1213      589635 :   if (_is_host_alloc)
    1214      589621 :     std::fill_n(_host_data, _size, scalar);
    1215             : 
    1216      589635 :   if (_is_device_alloc)
    1217      589635 :     ::Kokkos::Experimental::fill_n(ExecSpace(), deviceView(), _size, scalar);
    1218             : 
    1219      589635 :   return *this;
    1220             : }
    1221             : 
    1222             : template <typename T, unsigned int dimension, typename index_type, LayoutType layout>
    1223             : void
    1224        1139 : dataStore(std::ostream & stream, Array<T, dimension, index_type, layout> & array, void * context)
    1225             : {
    1226             :   using ::dataStore;
    1227             : 
    1228        1139 :   bool is_alloc = array.isAlloc();
    1229        1139 :   dataStore(stream, is_alloc, nullptr);
    1230             : 
    1231        1139 :   if (!is_alloc)
    1232           0 :     return;
    1233             : 
    1234        1139 :   std::string type = typeid(T).name();
    1235        1139 :   dataStore(stream, type, nullptr);
    1236             : 
    1237        1139 :   unsigned int dim = dimension;
    1238        1139 :   dataStore(stream, dim, nullptr);
    1239             : 
    1240        2278 :   for (unsigned int dim = 0; dim < dimension; ++dim)
    1241             :   {
    1242        1139 :     auto n = array.n(dim);
    1243        1139 :     dataStore(stream, n, nullptr);
    1244             :   }
    1245             : 
    1246        1139 :   if (array.isDeviceAlloc())
    1247             :   {
    1248             :     // We use malloc/free because we just want a memory copy
    1249             :     // If T is a Kokkos array and we use new/delete or vector to copy it out,
    1250             :     // the arrays will be destroyed on cleanup
    1251             : 
    1252        1139 :     T * data = static_cast<T *>(std::malloc(array.size() * sizeof(T)));
    1253             : 
    1254        1139 :     array.copyOut(data, MemcpyType::DEVICE_TO_HOST, array.size());
    1255             : 
    1256      117441 :     for (index_type i = 0; i < array.size(); ++i)
    1257      116302 :       dataStore(stream, data[i], context);
    1258             : 
    1259        1139 :     std::free(data);
    1260             :   }
    1261             :   else
    1262           0 :     for (auto & value : array)
    1263           0 :       dataStore(stream, value, context);
    1264        1139 : }
    1265             : 
    1266             : template <typename T, unsigned int dimension, typename index_type, LayoutType layout>
    1267             : void
    1268         531 : dataLoad(std::istream & stream, Array<T, dimension, index_type, layout> & array, void * context)
    1269             : {
    1270             :   using ::dataLoad;
    1271             : 
    1272             :   bool is_alloc;
    1273         531 :   dataLoad(stream, is_alloc, nullptr);
    1274             : 
    1275         531 :   if (!is_alloc)
    1276           0 :     return;
    1277             : 
    1278         531 :   std::string from_type_name;
    1279         531 :   dataLoad(stream, from_type_name, nullptr);
    1280             : 
    1281         531 :   if (from_type_name != typeid(T).name())
    1282           0 :     mooseError("Kokkos array error: cannot load array because the stored array is of type '",
    1283             :                MooseUtils::prettyCppType(libMesh::demangle(from_type_name.c_str())),
    1284             :                "' but the loading array is of type '",
    1285             :                MooseUtils::prettyCppType(libMesh::demangle(typeid(T).name())),
    1286             :                "'.");
    1287             : 
    1288             :   unsigned int from_dimension;
    1289         531 :   dataLoad(stream, from_dimension, nullptr);
    1290             : 
    1291         531 :   if (from_dimension != dimension)
    1292           0 :     mooseError("Kokkos array error: cannot load array because the stored array is ",
    1293             :                from_dimension,
    1294             :                "D but the loading array is ",
    1295             :                dimension,
    1296             :                "D.");
    1297             : 
    1298        1062 :   std::vector<index_type> from_n(dimension);
    1299         531 :   std::vector<index_type> n(dimension);
    1300             : 
    1301        1062 :   for (unsigned int dim = 0; dim < dimension; ++dim)
    1302             :   {
    1303         531 :     dataLoad(stream, from_n[dim], nullptr);
    1304         531 :     n[dim] = array.n(dim);
    1305             :   }
    1306             : 
    1307         531 :   if (from_n != n)
    1308           0 :     mooseError("Kokkos array error: cannot load array because the stored array has dimensions (",
    1309             :                Moose::stringify(from_n),
    1310             :                ") but the loading array has dimensions (",
    1311             :                Moose::stringify(n),
    1312             :                ").");
    1313             : 
    1314         531 :   if (array.isHostAlloc())
    1315             :   {
    1316        2706 :     for (auto & value : array)
    1317        2156 :       dataLoad(stream, value, context);
    1318             : 
    1319         275 :     if (array.isDeviceAlloc())
    1320         275 :       array.copyToDevice();
    1321             :   }
    1322             :   else
    1323             :   {
    1324         256 :     std::vector<T> data(array.size());
    1325             : 
    1326       51792 :     for (auto & value : data)
    1327       51536 :       dataLoad(stream, value, context);
    1328             : 
    1329         256 :     array.copyIn(data.data(), MemcpyType::HOST_TO_DEVICE, array.size());
    1330         256 :   }
    1331         531 : }
    1332             : #endif
    1333             : 
    1334             : /**
    1335             :  * The specialization of the Kokkos array class for each dimension.
    1336             :  * All array data that needs to be accessed on device in Kokkos objects should use this class.
    1337             :  * If the array is populated on host and is to be accessed on device, make sure to call
    1338             :  * copyToDevice() after populating data. For a nested Kokkos array, either copyToDeviceNested()
    1339             :  * should be called for the outermost array or copyToDevice() should be called for each instance of
    1340             :  * Kokkos array from the innermost to the outermost. Do not store this object as reference in your
    1341             :  * Kokkos object if it is used on device, because the reference refers to a host object and
    1342             :  * therefore is not accessible on device. If storing it as a reference is required, see
    1343             :  * ReferenceWrapper.
    1344             :  * @tparam T The data type
    1345             :  * @tparam dimension The array dimension size
    1346             :  * @tparam index_type The array index type
    1347             :  * @tparam layout The memory layout type
    1348             :  */
    1349             : ///@{
    1350             : template <typename T, unsigned int dimension, typename index_type, LayoutType layout>
    1351             : class Array : public ArrayBase<T, dimension, index_type>
    1352             : {
    1353             : #ifdef MOOSE_KOKKOS_SCOPE
    1354             :   usingKokkosArrayBaseMembers(T, dimension, index_type);
    1355             : #endif
    1356             : 
    1357             : public:
    1358             :   /**
    1359             :    * Default constructor
    1360             :    */
    1361     1477742 :   Array() : ArrayBase<T, dimension, index_type>(layout) {}
    1362             :   /**
    1363             :    * Copy constructor
    1364             :    */
    1365    18989768 :   Array(const Array<T, dimension, index_type, layout> & array)
    1366    10993979 :     : ArrayBase<T, dimension, index_type>(array)
    1367             :   {
    1368    18989768 :   }
    1369             : #ifdef MOOSE_KOKKOS_SCOPE
    1370             :   /**
    1371             :    * Constructor
    1372             :    * Initialize and allocate array with given dimensions
    1373             :    * This allocates both host and device data
    1374             :    * @param n The size of each dimension
    1375             :    */
    1376             :   template <typename... size_type>
    1377          24 :   Array(size_type... n) : ArrayBase<T, dimension, index_type>(layout, n...)
    1378             :   {
    1379          24 :   }
    1380             : #endif
    1381             : 
    1382             :   /**
    1383             :    * Shallow copy another Kokkos array
    1384             :    * @param array The Kokkos array to be shallow copied
    1385             :    */
    1386             :   auto & operator=(const Array<T, dimension, index_type, layout> & array)
    1387             :   {
    1388             :     this->shallowCopy(array);
    1389             : 
    1390             :     return *this;
    1391             :   }
    1392             : 
    1393             : #ifdef MOOSE_KOKKOS_SCOPE
    1394             :   /**
    1395             :    * Get an array entry
    1396             :    * @param i The index of each dimension
    1397             :    * @returns The reference of the entry depending on the architecture this function is being called
    1398             :    * on
    1399             :    */
    1400             :   template <typename... indices>
    1401             :   KOKKOS_FUNCTION T & operator()(indices... i) const;
    1402             :   /**
    1403             :    * Get an array entry using indices stored in an array
    1404             :    * @param idx The array storing the indices
    1405             :    * @returns The reference of the entry depending on the architecture this function is being
    1406             :    * called on
    1407             :    */
    1408             :   KOKKOS_FUNCTION T & operator()(const signed_index_type (&idx)[dimension]) const
    1409             :   {
    1410             :     return operatorInternal(idx, std::make_integer_sequence<unsigned int, dimension>{});
    1411             :   }
    1412             : #endif
    1413             : 
    1414             : private:
    1415             : #ifdef MOOSE_KOKKOS_SCOPE
    1416             :   /**
    1417             :    * Internal method for calling operator() with array indices
    1418             :    */
    1419             :   template <unsigned int... i>
    1420             :   KOKKOS_FUNCTION T & operatorInternal(const signed_index_type (&idx)[dimension],
    1421             :                                        std::integer_sequence<unsigned int, i...>) const
    1422             :   {
    1423             :     return operator()(idx[i]...);
    1424             :   }
    1425             : #endif
    1426             : };
    1427             : 
    1428             : #ifdef MOOSE_KOKKOS_SCOPE
    1429             : template <typename T, unsigned int dimension, typename index_type, LayoutType layout>
    1430             : template <typename... indices>
    1431             : KOKKOS_FUNCTION T &
    1432  2010445498 : Array<T, dimension, index_type, layout>::operator()(indices... i) const
    1433             : {
    1434             :   static_assert((std::is_convertible<indices, signed_index_type>::value && ...),
    1435             :                 "All arguments must be convertible to signed_index_type");
    1436             :   static_assert(sizeof...(i) == dimension, "Number of arguments should match array dimension");
    1437             : 
    1438             : #ifndef NDEBUG
    1439             :   {
    1440             :     signed_index_type idx[dimension] = {static_cast<signed_index_type>(i)...};
    1441             : 
    1442             :     for (unsigned int d = 0; d < sizeof...(i); ++d)
    1443             :       KOKKOS_ASSERT(idx[d] - _d[d] >= 0 && static_cast<index_type>(idx[d] - _d[d]) < _n[d]);
    1444             :   }
    1445             : #endif
    1446             : 
    1447  2010445498 :   index_type idx = 0;
    1448  2010445498 :   unsigned int d = 0;
    1449             : 
    1450  2010445498 :   if (_is_offset)
    1451             :   {
    1452             :     if constexpr (layout == LayoutType::LEFT)
    1453           0 :       (((idx += (d == 0 ? static_cast<signed_index_type>(i) - _d[d]
    1454           0 :                         : (static_cast<signed_index_type>(i) - _d[d]) * _s[d])),
    1455             :         ++d),
    1456             :        ...);
    1457             :     else
    1458           0 :       (((idx += (d == dimension - 1 ? static_cast<signed_index_type>(i) - _d[d]
    1459           0 :                                     : (static_cast<signed_index_type>(i) - _d[d]) * _s[d])),
    1460             :         ++d),
    1461             :        ...);
    1462             :   }
    1463             :   else
    1464             :   {
    1465             :     if constexpr (layout == LayoutType::LEFT)
    1466  2010442546 :       (((idx +=
    1467  2001889808 :          (d == 0 ? static_cast<signed_index_type>(i) : static_cast<signed_index_type>(i) * _s[d])),
    1468             :         ++d),
    1469             :        ...);
    1470             :     else
    1471       14244 :       (((idx += (d == dimension - 1 ? static_cast<signed_index_type>(i)
    1472       11292 :                                     : static_cast<signed_index_type>(i) * _s[d])),
    1473             :         ++d),
    1474             :        ...);
    1475             :   }
    1476             : 
    1477  2010445498 :   return this->operator[](idx);
    1478             : }
    1479             : #endif
    1480             : 
    1481             : template <typename T, typename index_type>
    1482             : class Array<T, 1, index_type, LayoutType::LEFT> : public ArrayBase<T, 1, index_type>
    1483             : {
    1484             : #ifdef MOOSE_KOKKOS_SCOPE
    1485             :   usingKokkosArrayBaseMembers(T, 1, index_type);
    1486             : #endif
    1487             : 
    1488             : public:
    1489             :   /**
    1490             :    * Default constructor
    1491             :    */
    1492     7909060 :   Array() : ArrayBase<T, 1, index_type>(LayoutType::LEFT) {}
    1493             :   /**
    1494             :    * Copy constructor
    1495             :    */
    1496    29518477 :   Array(const Array<T, 1, index_type, LayoutType::LEFT> & array)
    1497    17233773 :     : ArrayBase<T, 1, index_type>(array)
    1498             :   {
    1499    29518477 :   }
    1500             : #ifdef MOOSE_KOKKOS_SCOPE
    1501             :   /**
    1502             :    * Constructor
    1503             :    * Initialize and allocate array with given size
    1504             :    * This allocates both host and device data
    1505             :    * @param n The array size
    1506             :    */
    1507          60 :   Array(index_type n) : ArrayBase<T, 1, index_type>(LayoutType::LEFT, n) {}
    1508             :   /**
    1509             :    * Constructor
    1510             :    * Initialize and allocate array by copying a standard vector variable
    1511             :    * This allocates and copies to both host and device data
    1512             :    * @param vector The standard vector variable to copy
    1513             :    */
    1514        1628 :   Array(const std::vector<T> & vector) : ArrayBase<T, 1, index_type>(LayoutType::LEFT)
    1515             :   {
    1516        1628 :     *this = vector;
    1517        1628 :   }
    1518             : #endif
    1519             : 
    1520             :   /**
    1521             :    * Shallow copy another Kokkos array
    1522             :    * @param array The Kokkos array to be shallow copied
    1523             :    */
    1524     2326415 :   auto & operator=(const Array<T, 1, index_type, LayoutType::LEFT> & array)
    1525             :   {
    1526     2326415 :     this->shallowCopy(array);
    1527             : 
    1528     2326415 :     return *this;
    1529             :   }
    1530             : 
    1531             : #ifdef MOOSE_KOKKOS_SCOPE
    1532             :   /**
    1533             :    * Copy a standard vector variable
    1534             :    * This re-initializes and re-allocates array with the size of the vector
    1535             :    * @tparam host Whether to allocate and copy to the host data
    1536             :    * @tparam device Whether to allocate and copy to the device data
    1537             :    * @param vector The standard vector variable to copy
    1538             :    */
    1539             :   template <bool host, bool device>
    1540     1471831 :   void copyVector(const std::vector<T> & vector)
    1541             :   {
    1542     2952711 :     this->template createInternal<host, device, false>({static_cast<index_type>(vector.size())});
    1543             : 
    1544             :     if (host)
    1545     1462782 :       std::memcpy(this->hostData(), vector.data(), this->size() * sizeof(T));
    1546             : 
    1547             :     if (device)
    1548     1471831 :       this->template copyInternal<MemSpace, ::Kokkos::HostSpace>(
    1549           0 :           this->deviceData(), vector.data(), this->size());
    1550     1471831 :   }
    1551             :   /**
    1552             :    * Copy a standard set variable
    1553             :    * This re-initializes and re-allocates array with the size of the set
    1554             :    * @tparam host Whether to allocate and copy to the host data
    1555             :    * @tparam device Whether to allocate and copy to the device data
    1556             :    * @param set The standard set variable to copy
    1557             :    */
    1558             :   template <bool host, bool device>
    1559     1450038 :   void copySet(const std::set<T> & set)
    1560             :   {
    1561     1450038 :     std::vector<T> vector(set.begin(), set.end());
    1562             : 
    1563     1450038 :     copyVector<host, device>(vector);
    1564     1450038 :   }
    1565             : 
    1566             :   /**
    1567             :    * Copy a standard vector variable
    1568             :    * This allocates and copies to both host and device data
    1569             :    * @param vector The standard vector variable to copy
    1570             :    */
    1571       21793 :   auto & operator=(const std::vector<T> & vector)
    1572             :   {
    1573       21793 :     copyVector<true, true>(vector);
    1574             : 
    1575       21793 :     return *this;
    1576             :   }
    1577             :   /**
    1578             :    * Copy a standard set variable
    1579             :    * This allocates and copies to both host and device data
    1580             :    * @param set The standard set variable to copy
    1581             :    */
    1582     1440989 :   auto & operator=(const std::set<T> & set)
    1583             :   {
    1584     1440989 :     copySet<true, true>(set);
    1585             : 
    1586     1440989 :     return *this;
    1587             :   }
    1588             :   /**
    1589             :    * Get an array entry
    1590             :    * @param i The array index
    1591             :    * @returns The reference of the entry depending on the architecture this function is being
    1592             :    * called on
    1593             :    */
    1594   186495597 :   KOKKOS_FUNCTION T & operator()(signed_index_type i) const
    1595             :   {
    1596             :     KOKKOS_ASSERT(i - _d[0] >= 0 && static_cast<index_type>(i - _d[0]) < _n[0]);
    1597             : 
    1598   186495597 :     if (_is_offset)
    1599     1188520 :       return this->operator[](i - _d[0]);
    1600             :     else
    1601   185307077 :       return this->operator[](i);
    1602             :   }
    1603             :   /**
    1604             :    * Device BLAS operations
    1605             :    */
    1606             :   ///@{
    1607             :   /**
    1608             :    * Perform \p a * \p x \p op \p b * \p y and write the result to this array
    1609             :    * @param accumulate Whether to accumulate or overwrite the result
    1610             :    */
    1611             :   void axby(const T a,
    1612             :             const Array<T, 1, index_type, LayoutType::LEFT> & x,
    1613             :             const char op,
    1614             :             const T b,
    1615             :             const Array<T, 1, index_type, LayoutType::LEFT> & y,
    1616             :             const bool accumulate = false);
    1617             :   /**
    1618             :    * Scale \p x with \p a and write the result to this array
    1619             :    */
    1620             :   void scal(const T a, const Array<T, 1, index_type, LayoutType::LEFT> & x);
    1621             :   /**
    1622             :    * Scale this array with \p a
    1623             :    */
    1624             :   void scal(const T a);
    1625             :   /**
    1626             :    * Perform dot product between this array and \p x
    1627             :    */
    1628             :   T dot(const Array<T, 1, index_type, LayoutType::LEFT> & x);
    1629             :   /**
    1630             :    * Compute 2-norm of this array
    1631             :    */
    1632             :   T nrm2();
    1633             :   ///}@
    1634             : #endif
    1635             : };
    1636             : ///@}
    1637             : 
    1638             : template <typename T, typename index_type = dof_id_type>
    1639             : using Array1D = Array<T, 1, index_type, LayoutType::LEFT>;
    1640             : template <typename T, typename index_type = dof_id_type, LayoutType layout = LayoutType::LEFT>
    1641             : using Array2D = Array<T, 2, index_type, layout>;
    1642             : template <typename T, typename index_type = dof_id_type, LayoutType layout = LayoutType::LEFT>
    1643             : using Array3D = Array<T, 3, index_type, layout>;
    1644             : template <typename T, typename index_type = dof_id_type, LayoutType layout = LayoutType::LEFT>
    1645             : using Array4D = Array<T, 4, index_type, layout>;
    1646             : template <typename T, typename index_type = dof_id_type, LayoutType layout = LayoutType::LEFT>
    1647             : using Array5D = Array<T, 5, index_type, layout>;
    1648             : 
    1649             : } // namespace Moose::Kokkos

Generated by: LCOV version 1.14