1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level COPYRIGHT file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5
6 #include "axom/core/Array.hpp"
7 #include "axom/core/ArrayView.hpp"
8 #include "axom/core/memory_management.hpp"
9
10 #include "gtest/gtest.h"
11
12 #include <algorithm>
13
14 namespace axom
15 {
16 namespace internal
17 {
18 /*!
19 * \brief Calculate the new capacity for an Array given an increase in the
20 * size.
21 * \param [in] v, the Array in question.
22 * \param [in] increase, the amount the size will increase by
23 * \return the new capacity.
24 */
25 template <typename T>
calc_new_capacity(Array<T> & v,IndexType increase)26 IndexType calc_new_capacity(Array<T>& v, IndexType increase)
27 {
28 IndexType new_num_elements = v.size() + increase;
29 if(new_num_elements > v.capacity())
30 {
31 return new_num_elements * v.getResizeRatio() + 0.5;
32 }
33
34 return v.capacity();
35 }
36
37 /*!
38 * \brief Check if two Arrays are copies. Does not check the resize ratio.
39 * \param [in] lhs, the first Array to compare.
40 * \param [in] rhs, the second Array to compare.
41 * \return the new capacity.
42 */
43 template <typename T>
check_copy(const Array<T> & lhs,const Array<T> & rhs)44 void check_copy(const Array<T>& lhs, const Array<T>& rhs)
45 {
46 EXPECT_EQ(lhs.size(), rhs.size());
47 EXPECT_EQ(lhs.capacity(), rhs.capacity());
48
49 const T* lhs_data = lhs.data();
50 const T* rhs_data = rhs.data();
51 EXPECT_EQ(lhs_data, rhs_data);
52 }
53
54 /*!
55 * \brief Check that the storage of an Array is working properly.
56 * \param [in] v the Array to check.
57 */
58 template <typename T>
check_storage(Array<T> & v)59 void check_storage(Array<T>& v)
60 {
61 EXPECT_TRUE(v.empty());
62 EXPECT_EQ(v.size(), 0);
63
64 IndexType capacity = v.capacity();
65 const T* data_ptr = v.data();
66
67 /* Push back up to half the capacity. */
68 for(T i = 0; i < capacity / 2; ++i)
69 {
70 v.push_back(i);
71 }
72
73 /* Check the array metadata. */
74 EXPECT_TRUE(!v.empty());
75 EXPECT_EQ(v.size(), capacity / 2);
76 EXPECT_EQ(v.capacity(), capacity);
77 EXPECT_EQ(v.data(), data_ptr);
78
79 /* Push back up to the full capacity. */
80 for(T i = capacity / 2; i < capacity; ++i)
81 {
82 v.push_back(i);
83 }
84
85 /* Check the array metadata. */
86 EXPECT_TRUE(!v.empty());
87 EXPECT_EQ(v.size(), capacity);
88 EXPECT_EQ(v.capacity(), capacity);
89 EXPECT_EQ(v.data(), data_ptr);
90
91 /* Check the array data using the [] operator and the raw pointer. */
92 for(IndexType i = 0; i < capacity; ++i)
93 {
94 EXPECT_EQ(v[i], i);
95 EXPECT_EQ(data_ptr[i], i);
96 }
97
98 /* Set the array data to new values using the [] operator. */
99 for(IndexType i = 0; i < capacity; ++i)
100 {
101 v[i] = i - 5 * i + 7;
102 }
103
104 /* Check the array data using the [] operator and the raw pointer. */
105 for(IndexType i = 0; i < capacity; ++i)
106 {
107 EXPECT_EQ(v[i], i - 5 * i + 7);
108 EXPECT_EQ(data_ptr[i], i - 5 * i + 7);
109 }
110
111 /* Check the array metadata. */
112 EXPECT_TRUE(!v.empty());
113 EXPECT_EQ(v.size(), capacity);
114 EXPECT_EQ(v.capacity(), capacity);
115 EXPECT_EQ(v.data(), data_ptr);
116 }
117
118 /*!
119 * \brief Check that the fill method is working properly.
120 * \param [in] v the Array to check.
121 */
122 template <typename T>
check_fill(Array<T> & v)123 void check_fill(Array<T>& v)
124 {
125 constexpr T MAGIC_NUM_0 = 55;
126 constexpr T MAGIC_NUM_1 = 6834;
127 const IndexType capacity = v.capacity();
128 const IndexType size = v.size();
129 const double ratio = v.getResizeRatio();
130 const T* const data_ptr = v.data();
131
132 /* Fill the Array with MAGIC_NUM_0. */
133 v.fill(MAGIC_NUM_0);
134
135 /* Check the meta data. */
136 EXPECT_EQ(capacity, v.capacity());
137 EXPECT_EQ(size, v.size());
138 EXPECT_EQ(ratio, v.getResizeRatio());
139 EXPECT_EQ(data_ptr, v.data());
140
141 /* Check that the entries are all MAGIC_NUM_0. */
142 for(IndexType i = 0; i < size; ++i)
143 {
144 EXPECT_EQ(v[i], MAGIC_NUM_0);
145 }
146
147 /* Fill the Array with MAGIC_NUM_1. */
148 v.fill(MAGIC_NUM_1);
149
150 /* Check the meta data. */
151 EXPECT_EQ(capacity, v.capacity());
152 EXPECT_EQ(size, v.size());
153 EXPECT_EQ(ratio, v.getResizeRatio());
154 EXPECT_EQ(data_ptr, v.data());
155
156 /* Check that the entries are all MAGIC_NUM_1. */
157 for(IndexType i = 0; i < size; ++i)
158 {
159 EXPECT_EQ(v[i], MAGIC_NUM_1);
160 }
161 }
162
163 /*!
164 * \brief Check that the set method is working properly.
165 * \param [in] v the Array to check.
166 */
167 template <typename T>
check_set(Array<T> & v)168 void check_set(Array<T>& v)
169 {
170 constexpr T ZERO = 0;
171 const IndexType capacity = v.capacity();
172 const IndexType size = v.size();
173 const double ratio = v.getResizeRatio();
174 const T* const data_ptr = v.data();
175
176 /* Allocate a buffer half the size of the array. Fill it up with sequential
177 * values. */
178 const IndexType buffer_size = size / 2;
179 T* buffer = allocate<T>(buffer_size);
180 for(IndexType i = 0; i < buffer_size; ++i)
181 {
182 buffer[i] = i;
183 }
184
185 /* Set all the values in the array to zero. */
186 v.fill(ZERO);
187
188 /* Set the first half of the elements in the array to the sequential values in
189 * buffer. */
190 v.set(buffer, buffer_size, 0);
191
192 /* Check the array metadata. */
193 EXPECT_EQ(capacity, v.capacity());
194 EXPECT_EQ(size, v.size());
195 EXPECT_EQ(ratio, v.getResizeRatio());
196 EXPECT_EQ(data_ptr, v.data());
197
198 /* Check that the first half of the elements in the array are equivalent to
199 * those in buffer. */
200 for(IndexType i = 0; i < buffer_size; ++i)
201 {
202 EXPECT_EQ(v[i], buffer[i]);
203 }
204
205 /* Check that the second half of the elements in the array are all zero. */
206 for(IndexType i = buffer_size; i < size; ++i)
207 {
208 EXPECT_EQ(v[i], ZERO);
209 }
210
211 /* Reset the values in buffer to the next sequential values. */
212 for(IndexType i = 0; i < buffer_size; ++i)
213 {
214 buffer[i] = i + buffer_size;
215 }
216
217 /* Set the second half of the elements in the array to the new sequential
218 * values in buffer. */
219 v.set(buffer, buffer_size, buffer_size);
220
221 /* Check the array metadata. */
222 EXPECT_EQ(capacity, v.capacity());
223 EXPECT_EQ(size, v.size());
224 EXPECT_EQ(ratio, v.getResizeRatio());
225 EXPECT_EQ(data_ptr, v.data());
226
227 /* Check that all the elements in the array now hold sequential values. */
228 for(IndexType i = 0; i < 2 * buffer_size; ++i)
229 {
230 EXPECT_EQ(v[i], i);
231 }
232
233 deallocate(buffer);
234 }
235
236 /*!
237 * \brief Check that the resizing of an Array is working properly.
238 * \param [in] v the Array to check.
239 */
240 template <typename T>
check_resize(Array<T> & v)241 void check_resize(Array<T>& v)
242 {
243 /* Resize the array up to the capacity */
244 IndexType capacity = v.capacity();
245 v.resize(capacity);
246 IndexType size = capacity;
247
248 /* Check that the size equals the capacity. */
249 EXPECT_EQ(v.size(), v.capacity());
250
251 /* Set the existing data in v */
252 for(IndexType i = 0; i < size; ++i)
253 {
254 v[i] = static_cast<T>(i - 5 * i + 7);
255 }
256
257 /* Push back a new element, should resize. */
258 IndexType old_capacity = capacity;
259 capacity = calc_new_capacity(v, 1);
260 v.push_back(size - 5 * size + 7);
261 size++;
262
263 /* Check that it resized properly */
264 EXPECT_GT(capacity, old_capacity);
265 EXPECT_EQ(v.capacity(), capacity);
266 EXPECT_EQ(v.size(), size);
267
268 /* Check that the data is still intact. */
269 for(IndexType i = 0; i < size; ++i)
270 {
271 EXPECT_EQ(v[i], i - 5 * i + 7);
272 }
273
274 /* Push back 1000 elements */
275 const IndexType n_elements = 1000;
276
277 T* values = allocate<T>(n_elements);
278 for(IndexType i = 0; i < n_elements; ++i)
279 {
280 IndexType i_real = i + size;
281 values[i] = i_real - 5 * i_real + 7;
282 }
283
284 /* Push back the new elements. */
285 capacity = calc_new_capacity(v, n_elements);
286 v.insert(size, n_elements, values);
287 size += n_elements;
288
289 /* Check that size and capacity are as expected. */
290 EXPECT_EQ(v.capacity(), capacity);
291 EXPECT_EQ(v.size(), size);
292
293 /* Check that the data is still intact. */
294 for(IndexType i = 0; i < size; ++i)
295 {
296 EXPECT_EQ(v[i], i - 5 * i + 7);
297 }
298
299 /* Reduce the size down to 500 elements */
300 T* data_address = v.data();
301 size = 500;
302 v.resize(size);
303
304 /* Check the metadata. */
305 EXPECT_EQ(v.size(), size);
306 EXPECT_EQ(v.capacity(), capacity);
307 EXPECT_EQ(v.data(), data_address);
308
309 /* Check the data. */
310 for(IndexType i = 0; i < size; ++i)
311 {
312 EXPECT_EQ(v[i], i - 5 * i + 7);
313 }
314
315 /* Shrink the vector */
316 capacity = size;
317 v.shrink();
318
319 /* Check that the capacity and size are as expected. */
320 EXPECT_EQ(v.capacity(), capacity);
321 EXPECT_EQ(v.size(), size);
322
323 /* Check that the data is intact. */
324 for(IndexType i = 0; i < size; ++i)
325 {
326 EXPECT_EQ(v[i], i - 5 * i + 7);
327 }
328
329 /* Push back a new element, should resize. */
330 old_capacity = capacity;
331 capacity = calc_new_capacity(v, 1);
332 v.push_back(size - 5 * size + 7);
333 size++;
334
335 /* Check the new size and capacity. */
336 EXPECT_GT(capacity, old_capacity);
337 EXPECT_EQ(v.capacity(), capacity);
338 EXPECT_EQ(v.size(), size);
339
340 /* Check that the data is intact. */
341 for(IndexType i = 0; i < size; ++i)
342 {
343 EXPECT_EQ(v[i], i - 5 * i + 7);
344 }
345
346 /* Reset the data */
347 T* data_ptr = v.data();
348 for(IndexType i = 0; i < size; ++i)
349 {
350 data_ptr[i] = i;
351 }
352
353 /* Push back a bunch of elements to fill in up to the capacity. Resize should
354 * not occur. */
355 old_capacity = capacity;
356 for(IndexType i = size; i < old_capacity; ++i)
357 {
358 v.push_back(i);
359 size++;
360 EXPECT_EQ(v.capacity(), old_capacity);
361 EXPECT_EQ(v.size(), size);
362 EXPECT_EQ(v.data(), data_ptr);
363 }
364
365 EXPECT_EQ(v.size(), old_capacity);
366
367 /* Push back a final element that should trigger a resize. */
368 capacity = calc_new_capacity(v, old_capacity - size + 1);
369 v.push_back(size);
370 size++;
371
372 /* Check the new capacity and size. */
373 EXPECT_GT(capacity, old_capacity);
374 EXPECT_EQ(v.capacity(), capacity);
375 EXPECT_EQ(v.size(), size);
376
377 /* Check the data. */
378 for(IndexType i = 0; i < size; ++i)
379 {
380 EXPECT_EQ(v[i], i);
381 }
382
383 deallocate(values);
384 values = nullptr;
385 }
386
387 /*!
388 * \brief Check that the insertion into an Array is working properly.
389 * \param [in] v the Array to check.
390 */
391 template <typename T>
check_insert(Array<T> & v)392 void check_insert(Array<T>& v)
393 {
394 /* Resize the array up to the capacity */
395 IndexType capacity = v.capacity();
396 v.resize(capacity);
397 IndexType size = capacity;
398
399 EXPECT_EQ(v.size(), v.capacity());
400
401 /* Set the existing data in v */
402 for(IndexType i = 0; i < size; ++i)
403 {
404 v[i] = i - 5 * i + 7;
405 }
406
407 /* Insert a new element, should resize. */
408 IndexType old_capacity = capacity;
409 capacity = calc_new_capacity(v, 1);
410 v.insert(v.size(), 1, size - 5 * size + 7);
411 size++;
412
413 /* Check that it resized properly */
414 EXPECT_GT(capacity, old_capacity);
415 EXPECT_EQ(v.capacity(), capacity);
416 EXPECT_EQ(v.size(), size);
417 for(IndexType i = 0; i < size; ++i)
418 {
419 EXPECT_EQ(v[i], i - 5 * i + 7);
420 }
421
422 /* Insert 1000 elements */
423 const IndexType n_elements = 1000;
424 T* values = allocate<T>(n_elements);
425 for(IndexType i = 0; i < n_elements; ++i)
426 {
427 IndexType i_real = i + size;
428 values[i] = i_real - 5 * i_real + 7;
429 }
430
431 capacity = calc_new_capacity(v, n_elements);
432 v.insert(size, n_elements, values);
433 size += n_elements;
434
435 /* Check that it resizes properly */
436 EXPECT_EQ(v.capacity(), capacity);
437 EXPECT_EQ(v.size(), size);
438 for(IndexType i = 0; i < size; ++i)
439 {
440 EXPECT_EQ(v[i], i - 5 * i + 7);
441 }
442
443 capacity = size;
444 v.shrink();
445 IndexType n_insert_front = 100;
446
447 /* Reset the data */
448 T* data_ptr = v.data();
449 for(IndexType i = 0; i < size; ++i)
450 {
451 data_ptr[i] = i + n_insert_front;
452 }
453
454 /* Insert into the front of the Array. */
455 for(IndexType i = n_insert_front - 1; i >= 0; i--)
456 {
457 capacity = calc_new_capacity(v, 1);
458 v.insert(0, 1, i);
459 size++;
460 }
461
462 /* Check that the insertion worked as expected */
463 EXPECT_EQ(v.capacity(), capacity);
464 EXPECT_EQ(v.size(), size);
465 for(IndexType i = 0; i < size; ++i)
466 {
467 EXPECT_EQ(v[i], i);
468 }
469
470 deallocate(values);
471 values = nullptr;
472 }
473
474 /*!
475 * \brief Check that the insertion into an Array is working properly
476 * for iterators.
477 * \param [in] v the Array to check.
478 */
479 template <typename T>
check_insert_iterator(Array<T> & v)480 void check_insert_iterator(Array<T>& v)
481 {
482 /* Resize the array up to the capacity */
483 IndexType capacity = v.capacity();
484 v.resize(capacity);
485 IndexType size = capacity;
486
487 EXPECT_EQ(v.size(), v.capacity());
488
489 /* Set the existing data in v */
490 for(IndexType i = 0; i < size; ++i)
491 {
492 v[i] = i - 5 * i + 7;
493 }
494
495 /* Insert a new element, should resize. */
496 IndexType old_capacity = capacity;
497 capacity = calc_new_capacity(v, 1);
498 typename axom::Array<T>::ArrayIterator ret =
499 v.insert(v.end(), 1, size - 5 * size + 7);
500 size++;
501
502 /* Check that it resized properly */
503 EXPECT_GT(capacity, old_capacity);
504 EXPECT_EQ(v.capacity(), capacity);
505 EXPECT_EQ(v.size(), size);
506 EXPECT_EQ(ret, v.end() - 1);
507 for(IndexType i = 0; i < size; ++i)
508 {
509 EXPECT_EQ(v[i], i - 5 * i + 7);
510 }
511
512 /* Insert 1000 elements */
513 const IndexType n_elements = 1000;
514 T* values = allocate<T>(n_elements);
515 for(IndexType i = 0; i < n_elements; ++i)
516 {
517 IndexType i_real = i + size;
518 values[i] = i_real - 5 * i_real + 7;
519 }
520
521 capacity = calc_new_capacity(v, n_elements);
522 typename axom::Array<T>::ArrayIterator ret2 =
523 v.insert(v.end(), n_elements, values);
524
525 EXPECT_EQ(ret2, v.begin() + size);
526 size += n_elements;
527
528 /* Check that it resizes properly */
529 EXPECT_EQ(v.capacity(), capacity);
530 EXPECT_EQ(v.size(), size);
531 for(IndexType i = 0; i < size; ++i)
532 {
533 EXPECT_EQ(v[i], i - 5 * i + 7);
534 }
535
536 capacity = size;
537 v.shrink();
538 IndexType n_insert_front = 100;
539
540 /* Reset the data */
541 T* data_ptr = v.data();
542 for(IndexType i = 0; i < size; ++i)
543 {
544 data_ptr[i] = i + n_insert_front;
545 }
546
547 /* Insert into the front of the Array. */
548 for(IndexType i = n_insert_front - 1; i >= 0; i--)
549 {
550 capacity = calc_new_capacity(v, 1);
551 typename axom::Array<T>::ArrayIterator ret3 = v.insert(v.begin(), 1, i);
552 EXPECT_EQ(ret3, v.begin());
553 size++;
554 }
555
556 /* Check that the insertion worked as expected */
557 EXPECT_EQ(v.capacity(), capacity);
558 EXPECT_EQ(v.size(), size);
559 for(IndexType i = 0; i < size; ++i)
560 {
561 EXPECT_EQ(v[i], i);
562 }
563
564 deallocate(values);
565 values = nullptr;
566 }
567
568 /*!
569 * \brief Check that emplace() into an Array is working properly.
570 * \param [in] v the Array to check.
571 */
572 template <typename T>
check_emplace(Array<T> & v)573 void check_emplace(Array<T>& v)
574 {
575 /* Resize the array up to the capacity */
576 IndexType capacity = v.capacity();
577 v.resize(capacity);
578 IndexType size = capacity;
579
580 EXPECT_EQ(v.size(), v.capacity());
581
582 /* Set the existing data in v */
583 for(IndexType i = 0; i < size; ++i)
584 {
585 v[i] = i - 5 * i + 7;
586 }
587
588 /* Emplace a new element, should resize. */
589 IndexType old_capacity = capacity;
590 capacity = calc_new_capacity(v, 1);
591 typename axom::Array<T>::ArrayIterator ret =
592 v.emplace(v.end(), size - 5 * size + 7);
593 size++;
594
595 /* Check that it resized properly */
596 EXPECT_GT(capacity, old_capacity);
597 EXPECT_EQ(v.size(), size);
598 EXPECT_EQ(ret, v.end() - 1);
599 for(IndexType i = 0; i < size; ++i)
600 {
601 EXPECT_EQ(v[i], i - 5 * i + 7);
602 }
603
604 /* Emplace_back 1000 elements */
605 const IndexType n_elements = 1000;
606 for(IndexType i = 0; i < n_elements; ++i)
607 {
608 IndexType i_real = i + size;
609 v.emplace_back(i_real - 5 * i_real + 7);
610 }
611
612 size += n_elements;
613
614 /* Check that it resizes properly */
615 EXPECT_EQ(v.size(), size);
616 for(IndexType i = 0; i < size; ++i)
617 {
618 EXPECT_EQ(v[i], i - 5 * i + 7);
619 }
620
621 capacity = size;
622 v.shrink();
623 IndexType n_insert_front = 100;
624
625 /* Reset the data */
626 T* data_ptr = v.data();
627 for(IndexType i = 0; i < size; ++i)
628 {
629 data_ptr[i] = i + n_insert_front;
630 }
631
632 /* Emplace into the front of the Array. */
633 for(IndexType i = n_insert_front - 1; i >= 0; i--)
634 {
635 capacity = calc_new_capacity(v, 1);
636 typename axom::Array<T>::ArrayIterator ret3 = v.emplace(v.begin(), i);
637 EXPECT_EQ(ret3, v.begin());
638 size++;
639 }
640
641 /* Check that the emplace worked as expected */
642 EXPECT_EQ(v.capacity(), capacity);
643 EXPECT_EQ(v.size(), size);
644 for(IndexType i = 0; i < size; ++i)
645 {
646 EXPECT_EQ(v[i], i);
647 }
648 }
649
650 template <typename T>
check_swap(Array<T> & v)651 void check_swap(Array<T>& v)
652 {
653 axom::Array<T> v_two(v.size());
654
655 /* Push 0...size elements */
656 for(int i = 0; i < v.size(); i++)
657 {
658 v[i] = i;
659 v_two[i] = -i;
660 }
661
662 /* Create copies */
663 axom::Array<T> v_copy(v);
664 axom::Array<T> v_two_copy(v_two);
665
666 EXPECT_EQ(v, v_copy);
667 EXPECT_EQ(v_two, v_two_copy);
668
669 /* Swap */
670 v.swap(v_two);
671
672 EXPECT_NE(v, v_two);
673 EXPECT_NE(v, v_copy);
674
675 /* Swap back */
676 v.swap(v_two);
677
678 EXPECT_EQ(v, v_copy);
679 EXPECT_EQ(v_two, v_two_copy);
680 }
681
682 template <typename T, int DIM, axom::MemorySpace SPACE>
check_alloc(Array<T,DIM,SPACE> & v,const int id)683 void check_alloc(Array<T, DIM, SPACE>& v, const int id)
684 {
685 // Verify allocation
686 EXPECT_EQ(v.getAllocatorID(), id);
687 EXPECT_EQ(v.size(), v.capacity());
688
689 // Use introspection to verify pointer if available
690 #if defined(AXOM_USE_UMPIRE)
691 umpire::ResourceManager& rm = umpire::ResourceManager::getInstance();
692 auto v_allocator = rm.getAllocator(v.data());
693 EXPECT_EQ(v_allocator.getId(), id);
694 #endif
695 }
696
697 /*!
698 * \brief Check an external array for defects.
699 * \param [in] v the external array to check.
700 */
701 template <typename T>
check_external_view(ArrayView<T> & v)702 void check_external_view(ArrayView<T>& v)
703 {
704 const IndexType size = v.size();
705 const IndexType num_values = size;
706 T* const data_ptr = v.data();
707
708 /* Set the elements in the array. */
709 for(IndexType i = 0; i < size; ++i)
710 {
711 v[i] = i;
712 }
713
714 /* Check the elements using the raw pointer. */
715 for(IndexType i = 0; i < num_values; ++i)
716 {
717 EXPECT_EQ(data_ptr[i], i);
718 }
719
720 /* Set the elements using the raw pointer. */
721 for(IndexType i = 0; i < size; ++i)
722 {
723 data_ptr[i] = i * i;
724 }
725
726 /* Check the elements using the () operator. */
727 for(IndexType i = 0; i < size; ++i)
728 {
729 EXPECT_EQ(v[i], i * i);
730 }
731
732 EXPECT_EQ(size, v.size());
733 EXPECT_EQ(data_ptr, v.data());
734 }
735
736 // FIXME: HIP
737 #if defined(__CUDACC__) && defined(AXOM_USE_UMPIRE)
738
739 template <typename T>
assign_raw(T * data,int N)740 __global__ void assign_raw(T* data, int N)
741 {
742 for(int i = 0; i < N; i++)
743 {
744 data[i] = i;
745 }
746 }
747
748 template <typename T, int DIM, axom::MemorySpace SPACE>
assign_view(ArrayView<T,DIM,SPACE> view)749 __global__ void assign_view(ArrayView<T, DIM, SPACE> view)
750 {
751 for(int i = 0; i < view.size(); i++)
752 {
753 view[i] = i * 2;
754 }
755 }
756
757 /*!
758 * \brief Check that an array can be modified/accessed from device code
759 * \param [in] v the array to check.
760 */
761 template <typename T, int DIM, axom::MemorySpace SPACE>
check_device(Array<T,DIM,SPACE> & v)762 void check_device(Array<T, DIM, SPACE>& v)
763 {
764 const IndexType size = v.size();
765 // Then assign to it via a raw device pointer
766 assign_raw<<<1, 1>>>(v.data(), size);
767
768 // Check the contents of the array by assigning to a Dynamic array
769 // The default Umpire allocator should be Host, so we can access it from the CPU
770 Array<T, 1> check_raw_array_dynamic = v;
771 EXPECT_EQ(check_raw_array_dynamic.size(), size);
772 for(int i = 0; i < check_raw_array_dynamic.size(); i++)
773 {
774 EXPECT_EQ(check_raw_array_dynamic[i], i);
775 }
776
777 // Then check the contents by assigning to an explicitly Host array
778 Array<T, 1, axom::MemorySpace::Host> check_raw_array_host = v;
779 EXPECT_EQ(check_raw_array_host.size(), size);
780 for(int i = 0; i < check_raw_array_host.size(); i++)
781 {
782 EXPECT_EQ(check_raw_array_host[i], i);
783 }
784
785 // Then modify the underlying data via a view
786 ArrayView<T, DIM, SPACE> view(v);
787 assign_view<<<1, 1>>>(view);
788
789 // Check the contents of the array by assigning to a Dynamic array
790 // The default Umpire allocator should be Host, so we can access it from the CPU
791 Array<T, 1> check_view_array_dynamic = view;
792 EXPECT_EQ(check_view_array_dynamic.size(), size);
793 for(int i = 0; i < check_view_array_dynamic.size(); i++)
794 {
795 EXPECT_EQ(check_view_array_dynamic[i], i * 2);
796 }
797
798 // Then check the contents by assigning to an explicitly Host array
799 Array<T, 1, axom::MemorySpace::Host> check_view_array_host = view;
800 EXPECT_EQ(check_view_array_host.size(), size);
801 for(int i = 0; i < check_view_array_host.size(); i++)
802 {
803 EXPECT_EQ(check_view_array_host[i], i * 2);
804 }
805 }
806
807 template <typename T>
assign_raw_2d(T * data,int M,int N)808 __global__ void assign_raw_2d(T* data, int M, int N)
809 {
810 for(int i = 0; i < N; i++)
811 {
812 for(int j = 0; j < N; j++)
813 {
814 data[i * N + j] = i * i + j;
815 }
816 }
817 }
818
819 template <typename T, axom::MemorySpace SPACE>
assign_view_2d(ArrayView<T,2,SPACE> view)820 __global__ void assign_view_2d(ArrayView<T, 2, SPACE> view)
821 {
822 for(int i = 0; i < view.shape()[0]; i++)
823 {
824 for(int j = 0; j < view.shape()[1]; j++)
825 {
826 view(i, j) = j * j + i;
827 }
828 }
829 }
830
831 /*!
832 * \brief Check that a 2D array can be modified/accessed from device code
833 * \param [in] v the array to check.
834 */
835 template <typename T, axom::MemorySpace SPACE>
check_device_2D(Array<T,2,SPACE> & v)836 void check_device_2D(Array<T, 2, SPACE>& v)
837 {
838 const IndexType size = v.size();
839 const IndexType M = v.shape()[0];
840 const IndexType N = v.shape()[1];
841 // Then assign to it via a raw device pointer
842 assign_raw_2d<<<1, 1>>>(v.data(), M, N);
843
844 // Check the contents of the array by assigning to a Dynamic array
845 // The default Umpire allocator should be Host, so we can access it from the CPU
846 Array<T, 2> check_raw_array_dynamic = v;
847 EXPECT_EQ(check_raw_array_dynamic.size(), size);
848 EXPECT_EQ(check_raw_array_dynamic.shape(), v.shape());
849
850 for(int i = 0; i < M; i++)
851 {
852 for(int j = 0; j < N; j++)
853 {
854 EXPECT_EQ(check_raw_array_dynamic(i, j), i * i + j);
855 }
856 }
857
858 // Then check the contents by assigning to an explicitly Host array
859 Array<T, 2, axom::MemorySpace::Host> check_raw_array_host = v;
860 EXPECT_EQ(check_raw_array_host.size(), size);
861 EXPECT_EQ(check_raw_array_host.shape(), v.shape());
862
863 for(int i = 0; i < M; i++)
864 {
865 for(int j = 0; j < N; j++)
866 {
867 EXPECT_EQ(check_raw_array_host(i, j), i * i + j);
868 }
869 }
870
871 // Then modify the underlying data via a view
872 ArrayView<T, 2, SPACE> view(v);
873 assign_view_2d<<<1, 1>>>(view);
874
875 // Check the contents of the array by assigning to a Dynamic array
876 // The default Umpire allocator should be Host, so we can access it from the CPU
877 Array<T, 2> check_view_array_dynamic = view;
878 EXPECT_EQ(check_view_array_dynamic.size(), size);
879 EXPECT_EQ(check_view_array_dynamic.shape(), v.shape());
880
881 for(int i = 0; i < M; i++)
882 {
883 for(int j = 0; j < N; j++)
884 {
885 EXPECT_EQ(check_view_array_dynamic(i, j), j * j + i);
886 }
887 }
888
889 // Then check the contents by assigning to an explicitly Host array
890 Array<T, 2, axom::MemorySpace::Host> check_view_array_host = view;
891 EXPECT_EQ(check_view_array_host.size(), size);
892 EXPECT_EQ(check_view_array_host.shape(), v.shape());
893
894 for(int i = 0; i < M; i++)
895 {
896 for(int j = 0; j < N; j++)
897 {
898 EXPECT_EQ(check_view_array_host(i, j), j * j + i);
899 }
900 }
901 }
902
903 #endif // defined(__CUDACC__) && defined(AXOM_USE_UMPIRE)
904
905 } /* end namespace internal */
906
907 //------------------------------------------------------------------------------
908 // UNIT TESTS
909 //------------------------------------------------------------------------------
910
911 //------------------------------------------------------------------------------
TEST(core_array,checkStorage)912 TEST(core_array, checkStorage)
913 {
914 constexpr IndexType ZERO = 0;
915
916 for(IndexType capacity = 2; capacity < 512; capacity *= 2)
917 {
918 Array<int> v_int(ZERO, capacity);
919 internal::check_storage(v_int);
920
921 Array<double> v_double(ZERO, capacity);
922 internal::check_storage(v_double);
923 }
924 }
925
926 //------------------------------------------------------------------------------
TEST(core_array,checkFill)927 TEST(core_array, checkFill)
928 {
929 for(IndexType capacity = 2; capacity < 512; capacity *= 2)
930 {
931 IndexType size = capacity / 2;
932 Array<int> v_int(size, capacity);
933 internal::check_fill(v_int);
934
935 Array<double> v_double(size, capacity);
936 internal::check_fill(v_double);
937 }
938 }
939
940 //------------------------------------------------------------------------------
TEST(core_array,checkSet)941 TEST(core_array, checkSet)
942 {
943 for(IndexType size = 2; size < 512; size *= 2)
944 {
945 Array<int> v_int(size);
946 internal::check_set(v_int);
947
948 Array<double> v_double(size);
949 internal::check_set(v_double);
950 }
951 }
952
953 //------------------------------------------------------------------------------
TEST(core_array,checkResize)954 TEST(core_array, checkResize)
955 {
956 constexpr IndexType ZERO = 0;
957
958 for(double ratio = 1.0; ratio <= 2.0; ratio += 0.5)
959 {
960 for(IndexType capacity = 2; capacity <= 512; capacity *= 2)
961 {
962 Array<int> v_int(ZERO, capacity);
963 v_int.setResizeRatio(ratio);
964 internal::check_resize(v_int);
965
966 Array<double> v_double(ZERO, capacity);
967 v_double.setResizeRatio(ratio);
968 internal::check_resize(v_double);
969 }
970 }
971 }
972
973 //------------------------------------------------------------------------------
TEST(core_array_DeathTest,checkResize)974 TEST(core_array_DeathTest, checkResize)
975 {
976 const char IGNORE_OUTPUT[] = ".*";
977 constexpr IndexType ZERO = 0;
978 IndexType size = 100;
979
980 /* Resizing isn't allowed with a ratio less than 1.0. */
981 Array<int> v_int(ZERO, size);
982 v_int.setResizeRatio(0.99);
983 EXPECT_DEATH_IF_SUPPORTED(internal::check_resize(v_int), IGNORE_OUTPUT);
984 }
985
986 //------------------------------------------------------------------------------
TEST(core_array,checkInsert)987 TEST(core_array, checkInsert)
988 {
989 constexpr IndexType ZERO = 0;
990
991 for(double ratio = 1.0; ratio <= 2.0; ratio += 0.5)
992 {
993 for(IndexType capacity = 2; capacity <= 512; capacity *= 2)
994 {
995 Array<int> v_int(ZERO, capacity);
996 v_int.setResizeRatio(ratio);
997 internal::check_insert(v_int);
998
999 Array<double> v_double(ZERO, capacity);
1000 v_double.setResizeRatio(ratio);
1001 internal::check_insert(v_double);
1002 }
1003 }
1004 }
1005
1006 //------------------------------------------------------------------------------
TEST(core_array,checkInsertIterator)1007 TEST(core_array, checkInsertIterator)
1008 {
1009 constexpr IndexType ZERO = 0;
1010
1011 for(double ratio = 1.0; ratio <= 2.0; ratio += 0.5)
1012 {
1013 for(IndexType capacity = 10; capacity <= 512; capacity *= 2)
1014 {
1015 Array<int> v_int(ZERO, capacity);
1016 v_int.setResizeRatio(ratio);
1017 internal::check_insert_iterator(v_int);
1018
1019 Array<double> v_double(ZERO, capacity);
1020 v_double.setResizeRatio(ratio);
1021 internal::check_insert_iterator(v_double);
1022 }
1023 }
1024 }
1025
1026 //------------------------------------------------------------------------------
TEST(core_array,checkEmplace)1027 TEST(core_array, checkEmplace)
1028 {
1029 constexpr IndexType ZERO = 0;
1030
1031 for(double ratio = 1.0; ratio <= 2.0; ratio += 0.5)
1032 {
1033 for(IndexType capacity = 10; capacity <= 512; capacity *= 2)
1034 {
1035 Array<int> v_int(ZERO, capacity);
1036 v_int.setResizeRatio(ratio);
1037 internal::check_emplace(v_int);
1038
1039 Array<double> v_double(ZERO, capacity);
1040 v_double.setResizeRatio(ratio);
1041 internal::check_emplace(v_double);
1042 }
1043 }
1044 }
1045
1046 //------------------------------------------------------------------------------
TEST(core_array,checkSwap)1047 TEST(core_array, checkSwap)
1048 {
1049 for(IndexType size = 10; size <= 512; size *= 2)
1050 {
1051 Array<int> v_int(size);
1052 internal::check_swap(v_int);
1053
1054 Array<double> v_double(size);
1055 internal::check_swap(v_double);
1056 }
1057 }
1058
1059 //------------------------------------------------------------------------------
TEST(core_array,checkAlloc)1060 TEST(core_array, checkAlloc)
1061 {
1062 std::vector<int> memory_locations
1063 {
1064 #if defined(AXOM_USE_UMPIRE)
1065 axom::getUmpireResourceAllocatorID(umpire::resource::Host)
1066 #if defined(UMPIRE_ENABLE_DEVICE)
1067 ,
1068 axom::getUmpireResourceAllocatorID(umpire::resource::Device)
1069 #endif
1070 #if defined(UMPIRE_ENABLE_UM)
1071 ,
1072 axom::getUmpireResourceAllocatorID(umpire::resource::Unified)
1073 #endif
1074 #if defined(UMPIRE_ENABLE_CONST)
1075 ,
1076 axom::getUmpireResourceAllocatorID(umpire::resource::Constant)
1077 #endif
1078 #if defined(UMPIRE_ENABLE_PINNED)
1079 ,
1080 axom::getUmpireResourceAllocatorID(umpire::resource::Pinned)
1081 #endif
1082 #endif
1083 };
1084
1085 for(double ratio = 1.0; ratio <= 2.0; ratio += 0.5)
1086 {
1087 for(IndexType capacity = 4; capacity <= 512; capacity *= 2)
1088 {
1089 // First use the dynamic option
1090 for(int id : memory_locations)
1091 {
1092 Array<int, 1, axom::MemorySpace::Dynamic> v_int(capacity, capacity, id);
1093 internal::check_alloc(v_int, id);
1094
1095 Array<double, 1, axom::MemorySpace::Dynamic> v_double(capacity,
1096 capacity,
1097 id);
1098 internal::check_alloc(v_double, id);
1099 }
1100 // Then, if Umpire is available, we can use the space as an explicit template parameter
1101 #ifdef AXOM_USE_UMPIRE
1102 #ifdef UMPIRE_ENABLE_DEVICE
1103 Array<int, 1, axom::MemorySpace::Device> v_int_device(capacity, capacity);
1104 internal::check_alloc(
1105 v_int_device,
1106 axom::getUmpireResourceAllocatorID(umpire::resource::Device));
1107 Array<double, 1, axom::MemorySpace::Device> v_double_device(capacity,
1108 capacity);
1109 internal::check_alloc(
1110 v_double_device,
1111 axom::getUmpireResourceAllocatorID(umpire::resource::Device));
1112 #endif
1113 #ifdef UMPIRE_ENABLE_UM
1114 Array<int, 1, axom::MemorySpace::Unified> v_int_unified(capacity, capacity);
1115 internal::check_alloc(
1116 v_int_unified,
1117 axom::getUmpireResourceAllocatorID(umpire::resource::Unified));
1118 Array<double, 1, axom::MemorySpace::Unified> v_double_unified(capacity,
1119 capacity);
1120 internal::check_alloc(
1121 v_double_unified,
1122 axom::getUmpireResourceAllocatorID(umpire::resource::Unified));
1123 #endif
1124 #ifdef UMPIRE_ENABLE_CONST
1125 Array<int, 1, axom::MemorySpace::Constant> v_int_const(capacity, capacity);
1126 internal::check_alloc(
1127 v_int_const,
1128 axom::getUmpireResourceAllocatorID(umpire::resource::Constant));
1129 Array<double, 1, axom::MemorySpace::Constant> v_double_const(capacity,
1130 capacity);
1131 internal::check_alloc(
1132 v_double_const,
1133 axom::getUmpireResourceAllocatorID(umpire::resource::Constant));
1134 #endif
1135 #ifdef UMPIRE_ENABLE_PINNED
1136 Array<int, 1, axom::MemorySpace::Pinned> v_int_pinned(capacity, capacity);
1137 internal::check_alloc(
1138 v_int_pinned,
1139 axom::getUmpireResourceAllocatorID(umpire::resource::Pinned));
1140 Array<double, 1, axom::MemorySpace::Pinned> v_double_pinned(capacity,
1141 capacity);
1142 internal::check_alloc(
1143 v_double_pinned,
1144 axom::getUmpireResourceAllocatorID(umpire::resource::Pinned));
1145 #endif
1146 #endif
1147 }
1148 }
1149 }
1150
1151 //------------------------------------------------------------------------------
TEST(core_array,checkExternalView)1152 TEST(core_array, checkExternalView)
1153 {
1154 constexpr double MAGIC_NUM = 5683578.8;
1155 constexpr IndexType MAX_SIZE = 256;
1156 constexpr IndexType MAX_VALUES = MAX_SIZE;
1157 union DataBuffer
1158 {
1159 int ints[MAX_SIZE];
1160 double doubles[MAX_SIZE];
1161 };
1162
1163 DataBuffer buffer;
1164 std::fill_n(buffer.doubles, MAX_VALUES, MAGIC_NUM);
1165
1166 for(IndexType size = 16; size <= MAX_SIZE; size *= 2)
1167 {
1168 ArrayView<int> v_int_view(buffer.ints, size);
1169 EXPECT_EQ(v_int_view.data(), buffer.ints);
1170 internal::check_external_view(v_int_view);
1171
1172 ArrayView<double> v_double_view(buffer.doubles, size);
1173 EXPECT_EQ(v_double_view.data(), buffer.doubles);
1174 internal::check_external_view(v_double_view);
1175
1176 /* Set v_double's data to MAGIC_NUM */
1177 std::fill_n(v_double_view.data(), size, MAGIC_NUM);
1178
1179 /* Check that the data still exists in the buffer */
1180 for(IndexType i = 0; i < MAX_VALUES; ++i)
1181 {
1182 EXPECT_EQ(buffer.doubles[i], MAGIC_NUM);
1183 }
1184 }
1185 }
1186
1187 //------------------------------------------------------------------------------
TEST(core_array,checkIterator)1188 TEST(core_array, checkIterator)
1189 {
1190 constexpr int SIZE = 1000;
1191 axom::Array<int> v_int(SIZE);
1192
1193 /* Push 0...999 elements */
1194 for(int i = 0; i < SIZE; i++)
1195 {
1196 v_int[i] = i;
1197 }
1198
1199 EXPECT_EQ(*v_int.begin(), 0);
1200 EXPECT_EQ(*(v_int.end() - 1), SIZE - 1);
1201 EXPECT_EQ(v_int.size(), SIZE);
1202
1203 /* Erase nothing */
1204 axom::Array<int>::ArrayIterator ret1 =
1205 v_int.erase(v_int.begin() + SIZE / 2, v_int.begin() + SIZE / 2);
1206
1207 EXPECT_EQ(ret1, v_int.begin() + SIZE / 2);
1208 EXPECT_EQ(v_int.size(), SIZE);
1209
1210 /* Erase half the elements */
1211 axom::Array<int>::ArrayIterator ret2 =
1212 v_int.erase(v_int.begin(), v_int.begin() + SIZE / 2);
1213
1214 EXPECT_EQ(ret2, v_int.begin());
1215 EXPECT_EQ(*v_int.begin(), SIZE / 2);
1216 EXPECT_EQ(*(v_int.end() - 1), SIZE - 1);
1217 EXPECT_EQ(v_int.size(), SIZE / 2);
1218
1219 /* Erase first, last elements */
1220 axom::Array<int>::ArrayIterator ret3 = v_int.erase(v_int.begin());
1221
1222 EXPECT_EQ(ret3, v_int.begin());
1223 EXPECT_EQ(*v_int.begin(), SIZE / 2 + 1);
1224
1225 axom::Array<int>::ArrayIterator ret4 = v_int.erase(v_int.end() - 1);
1226
1227 EXPECT_EQ(ret4, v_int.end());
1228 EXPECT_EQ(*(v_int.end() - 1), SIZE - 2);
1229
1230 /* Clear the rest of the array */
1231 v_int.clear();
1232 EXPECT_EQ(v_int.size(), 0);
1233 }
1234
1235 //------------------------------------------------------------------------------
TEST(core_array,check_move_copy)1236 TEST(core_array, check_move_copy)
1237 {
1238 constexpr int MAGIC_INT = 255;
1239 constexpr double MAGIC_DOUBLE = 5683578.8;
1240
1241 for(IndexType capacity = 2; capacity < 512; capacity *= 2)
1242 {
1243 IndexType size = capacity;
1244
1245 /* Check copy and move semantics for Array of ints */
1246 Array<int> v_int(size, capacity);
1247 v_int.fill(MAGIC_INT);
1248
1249 Array<int> v_int_copy_ctor(v_int);
1250 Array<int> v_int_copy_assign;
1251 v_int_copy_assign = v_int;
1252 EXPECT_EQ(v_int, v_int_copy_ctor);
1253 EXPECT_EQ(v_int, v_int_copy_assign);
1254
1255 Array<int> v_int_move_assign;
1256 v_int_move_assign = std::move(v_int_copy_assign);
1257 Array<int> v_int_move_ctor = std::move(v_int_copy_ctor);
1258 EXPECT_EQ(v_int, v_int_move_assign);
1259 EXPECT_EQ(v_int, v_int_move_ctor);
1260 EXPECT_EQ(v_int_copy_assign.data(), nullptr);
1261 EXPECT_EQ(v_int_copy_ctor.data(), nullptr);
1262
1263 /* Check copy and move semantics for Array of doubles */
1264 Array<double> v_double(size, capacity);
1265 v_double.fill(MAGIC_DOUBLE);
1266
1267 Array<double> v_double_copy_ctor(v_double);
1268 Array<double> v_double_copy_assign;
1269 v_double_copy_assign = v_double;
1270 EXPECT_EQ(v_double, v_double_copy_ctor);
1271 EXPECT_EQ(v_double, v_double_copy_assign);
1272
1273 Array<double> v_double_move_assign;
1274 v_double_move_assign = std::move(v_double_copy_assign);
1275 Array<double> v_double_move_ctor = std::move(v_double_copy_ctor);
1276 EXPECT_EQ(v_double, v_double_move_assign);
1277 EXPECT_EQ(v_double, v_double_move_ctor);
1278 EXPECT_EQ(v_double_copy_assign.data(), nullptr);
1279 EXPECT_EQ(v_double_copy_ctor.data(), nullptr);
1280 }
1281 }
1282
1283 //------------------------------------------------------------------------------
TEST(core_array,check_view_move_copy)1284 TEST(core_array, check_view_move_copy)
1285 {
1286 constexpr int MAGIC_INT = 255;
1287 constexpr double MAGIC_DOUBLE = 5683578.8;
1288
1289 for(IndexType capacity = 2; capacity < 512; capacity *= 2)
1290 {
1291 IndexType size = capacity;
1292
1293 /* Check copy and move semantics for ArrayView of ints */
1294 std::vector<int> ints(size, MAGIC_INT);
1295 ArrayView<int> v_int_view(ints.data(), size);
1296
1297 ArrayView<int> v_int_view_copy_ctor(v_int_view);
1298 ArrayView<int> v_int_view_copy_assign;
1299 v_int_view_copy_assign = v_int_view;
1300 EXPECT_EQ(v_int_view, v_int_view_copy_ctor);
1301 EXPECT_EQ(v_int_view, v_int_view_copy_assign);
1302
1303 /* Check copy and move semantics for ArrayView of doubles */
1304 std::vector<double> doubles(size, MAGIC_DOUBLE);
1305 ArrayView<double> v_double_view(doubles.data(), size);
1306
1307 ArrayView<double> v_double_view_copy_ctor(v_double_view);
1308 ArrayView<double> v_double_view_copy_assign;
1309 v_double_view_copy_assign = v_double_view;
1310 EXPECT_EQ(v_double_view, v_double_view_copy_ctor);
1311 EXPECT_EQ(v_double_view, v_double_view_copy_assign);
1312 }
1313 }
1314
1315 //------------------------------------------------------------------------------
TEST(core_array,check_multidimensional)1316 TEST(core_array, check_multidimensional)
1317 {
1318 constexpr int MAGIC_INT = 255;
1319 constexpr double MAGIC_DOUBLE = 5683578.8;
1320
1321 // First test multidimensional int arrays
1322 Array<int, 2> v_int;
1323 v_int.resize(2, 2);
1324 v_int.fill(MAGIC_INT);
1325 // Make sure the number of elements and contents are correct
1326 EXPECT_EQ(v_int.size(), 2 * 2);
1327 StackArray<IndexType, 2> expected_shape = {2, 2};
1328 EXPECT_EQ(v_int.shape(), expected_shape);
1329 for(const auto val : v_int)
1330 {
1331 EXPECT_EQ(val, MAGIC_INT);
1332 }
1333 // Then assign different values to each element
1334 v_int(0, 0) = 1;
1335 v_int(0, 1) = 2;
1336 v_int(1, 0) = 3;
1337 v_int(1, 1) = 4;
1338
1339 // FIXME: Should we add a std::initializer_list ctor?
1340 Array<int> v_int_flat(4);
1341 v_int_flat[0] = 1;
1342 v_int_flat[1] = 2;
1343 v_int_flat[2] = 3;
1344 v_int_flat[3] = 4;
1345 StackArray<IndexType, 1> expected_flat_shape = {4};
1346 EXPECT_EQ(v_int_flat.shape(), expected_flat_shape);
1347
1348 for(int i = 0; i < v_int_flat.size(); i++)
1349 {
1350 // For a multidim array, op[] is a "flat" index into the raw data
1351 EXPECT_EQ(v_int[i], v_int_flat[i]);
1352 }
1353
1354 Array<double, 3> v_double(4, 3, 2);
1355 v_double.fill(MAGIC_DOUBLE);
1356 EXPECT_EQ(v_double.size(), 4 * 3 * 2);
1357 StackArray<IndexType, 3> expected_double_shape = {4, 3, 2};
1358 EXPECT_EQ(v_double.shape(), expected_double_shape);
1359 for(const auto val : v_double)
1360 {
1361 EXPECT_EQ(val, MAGIC_DOUBLE);
1362 }
1363
1364 Array<double> v_double_flat(4 * 3 * 2);
1365 int double_flat_idx = 0;
1366 for(int i = 0; i < v_double.shape()[0]; i++)
1367 {
1368 for(int j = 0; j < v_double.shape()[1]; j++)
1369 {
1370 for(int k = 0; k < v_double.shape()[2]; k++)
1371 {
1372 v_double(i, j, k) = (i * i) + (5 * j) + k;
1373 v_double_flat[double_flat_idx++] = (i * i) + (5 * j) + k;
1374 }
1375 }
1376 }
1377
1378 for(int i = 0; i < v_double.size(); i++)
1379 {
1380 // For a multidim array, op[] is a "flat" index into the raw data
1381 EXPECT_EQ(v_double[i], v_double_flat[i]);
1382 }
1383 }
1384
1385 //------------------------------------------------------------------------------
TEST(core_array,check_multidimensional_view)1386 TEST(core_array, check_multidimensional_view)
1387 {
1388 constexpr int MAGIC_INT = 255;
1389 constexpr double MAGIC_DOUBLE = 5683578.8;
1390
1391 // First test multidimensional int arrays
1392 int v_int_arr[] = {MAGIC_INT, MAGIC_INT, MAGIC_INT, MAGIC_INT};
1393 ArrayView<int, 2> v_int_view(v_int_arr, 2, 2);
1394 // Make sure the number of elements and contents are correct
1395 EXPECT_EQ(v_int_view.size(), 2 * 2);
1396 StackArray<IndexType, 2> expected_shape = {2, 2};
1397 EXPECT_EQ(v_int_view.shape(), expected_shape);
1398 for(const auto val : v_int_view)
1399 {
1400 EXPECT_EQ(val, MAGIC_INT);
1401 }
1402 // Then assign different values to each element
1403 v_int_view(0, 0) = 1;
1404 v_int_view(0, 1) = 2;
1405 v_int_view(1, 0) = 3;
1406 v_int_view(1, 1) = 4;
1407
1408 // FIXME: Should we add a std::initializer_list ctor?
1409 int v_int_flat_arr[] = {1, 2, 3, 4};
1410 ArrayView<int> v_int_flat_view(v_int_flat_arr, 4);
1411 StackArray<IndexType, 1> expected_flat_shape = {4};
1412 EXPECT_EQ(v_int_flat_view.shape(), expected_flat_shape);
1413
1414 for(int i = 0; i < v_int_flat_view.size(); i++)
1415 {
1416 // For a multidim array, op[] is a "flat" index into the raw data
1417 EXPECT_EQ(v_int_view[i], v_int_flat_view[i]);
1418 }
1419
1420 double v_double_arr[4 * 3 * 2];
1421 std::fill_n(v_double_arr, 4 * 3 * 2, MAGIC_DOUBLE);
1422 ArrayView<double, 3> v_double_view(v_double_arr, 4, 3, 2);
1423 EXPECT_EQ(v_double_view.size(), 4 * 3 * 2);
1424 StackArray<IndexType, 3> expected_double_shape = {4, 3, 2};
1425 EXPECT_EQ(v_double_view.shape(), expected_double_shape);
1426 for(const auto val : v_double_view)
1427 {
1428 EXPECT_EQ(val, MAGIC_DOUBLE);
1429 }
1430
1431 double v_double_flat_arr[4 * 3 * 2];
1432 ArrayView<double> v_double_flat_view(v_double_flat_arr, 4 * 3 * 2);
1433 int double_flat_idx = 0;
1434 for(int i = 0; i < v_double_view.shape()[0]; i++)
1435 {
1436 for(int j = 0; j < v_double_view.shape()[1]; j++)
1437 {
1438 for(int k = 0; k < v_double_view.shape()[2]; k++)
1439 {
1440 v_double_view(i, j, k) = (i * i) + (5 * j) + k;
1441 v_double_flat_view[double_flat_idx++] = (i * i) + (5 * j) + k;
1442 }
1443 }
1444 }
1445
1446 for(int i = 0; i < v_double_view.size(); i++)
1447 {
1448 // For a multidim array, op[] is a "flat" index into the raw data
1449 EXPECT_EQ(v_double_view[i], v_double_flat_view[i]);
1450 }
1451 }
1452
1453 //------------------------------------------------------------------------------
TEST(core_array,checkDevice)1454 TEST(core_array, checkDevice)
1455 {
1456 // FIXME: HIP
1457 #if !defined(__CUDACC__) || !defined(AXOM_USE_UMPIRE) || \
1458 !defined(UMPIRE_ENABLE_DEVICE)
1459 GTEST_SKIP()
1460 << "CUDA is not available, skipping tests that use Array in device code";
1461 #else
1462 for(IndexType capacity = 2; capacity < 512; capacity *= 2)
1463 {
1464 // Allocate a Dynamic array in Device memory
1465 Array<int, 1, axom::MemorySpace::Dynamic> v_int_dynamic(
1466 capacity,
1467 capacity,
1468 axom::getUmpireResourceAllocatorID(umpire::resource::Device));
1469
1470 internal::check_device(v_int_dynamic);
1471
1472 Array<double, 1, axom::MemorySpace::Dynamic> v_double_dynamic(
1473 capacity,
1474 capacity,
1475 axom::getUmpireResourceAllocatorID(umpire::resource::Device));
1476
1477 internal::check_device(v_double_dynamic);
1478
1479 // Then allocate an explicitly Device array
1480 Array<int, 1, axom::MemorySpace::Device> v_int_device(capacity, capacity);
1481 internal::check_device(v_int_device);
1482
1483 Array<double, 1, axom::MemorySpace::Device> v_double_device(capacity,
1484 capacity);
1485 internal::check_device(v_double_device);
1486 }
1487 #endif
1488 }
1489
1490 //------------------------------------------------------------------------------
TEST(core_array,checkDevice2D)1491 TEST(core_array, checkDevice2D)
1492 {
1493 // FIXME: HIP
1494 #if !defined(__CUDACC__) || !defined(AXOM_USE_UMPIRE) || \
1495 !defined(UMPIRE_ENABLE_DEVICE)
1496 GTEST_SKIP()
1497 << "CUDA is not available, skipping tests that use Array in device code";
1498 #else
1499 for(IndexType capacity = 2; capacity < 512; capacity *= 2)
1500 {
1501 // Allocate an explicitly Device array
1502 Array<int, 2, axom::MemorySpace::Device> v_int_device(capacity, capacity);
1503 internal::check_device_2D(v_int_device);
1504
1505 Array<double, 2, axom::MemorySpace::Device> v_double_device(capacity,
1506 capacity);
1507 internal::check_device_2D(v_double_device);
1508 }
1509 #endif
1510 }
1511
1512 } /* end namespace axom */
1513