1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level LICENSE file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5 
6 #include "gtest/gtest.h"
7 
8 #include "axom/primal/geometry/NumericArray.hpp"
9 
10 #include "axom/core/execution/execution_space.hpp"  // for execution_space traits
11 #include "axom/core/execution/for_all.hpp"          // for_all()
12 
13 #include "axom/slic/core/SimpleLogger.hpp"
14 using axom::slic::SimpleLogger;
15 
16 using namespace axom;
17 
18 //------------------------------------------------------------------------------
19 template <typename ExecSpace>
check_numeric_array_policy()20 void check_numeric_array_policy()
21 {
22   const int DIM = 3;
23   using NumericArrayType = primal::NumericArray<double, DIM>;
24 
25   double* coords =
26     axom::allocate<double>(DIM, axom::execution_space<ExecSpace>::allocatorID());
27 
28   axom::for_all<ExecSpace>(
29     1,
30     AXOM_LAMBDA(int /*i*/) {
31       NumericArrayType ones(1.0);
32       ones.to_array(coords);
33     });
34 
35   EXPECT_EQ(NumericArrayType(coords), NumericArrayType(1));
36   axom::deallocate(coords);
37 }
38 
39 //------------------------------------------------------------------------------
TEST(primal_numeric_array,constructors)40 TEST(primal_numeric_array, constructors)
41 {
42   static const int DIM = 5;
43   typedef double CoordType;
44   typedef primal::NumericArray<CoordType, DIM> QArray;
45 
46   QArray arr1;
47   EXPECT_EQ(QArray::size(), DIM);
48   for(int i = 0; i < DIM; ++i)
49   {
50     EXPECT_EQ(arr1[i], CoordType());
51   }
52 
53   CoordType val = 5.;
54   QArray arr2(val);
55   EXPECT_EQ(QArray::size(), DIM);
56   for(int i = 0; i < DIM; ++i)
57   {
58     EXPECT_EQ(arr2[i], val);
59   }
60 
61   CoordType val3 = 5.;
62   int halfPt = DIM / 2;
63   QArray arr3(val3, halfPt);
64   for(int i = 0; i < DIM; ++i)
65   {
66     CoordType expVal = i < halfPt ? val3 : CoordType();
67     EXPECT_EQ(arr3[i], expVal);
68   }
69 
70   // Compare array initialized from arbitrary array
71   CoordType valsArr[DIM] = {12., 23., 34., 45., 56.432};
72   QArray arrArr(valsArr);
73   for(int i = 0; i < DIM; ++i)
74   {
75     EXPECT_EQ(arrArr[i], valsArr[i]);
76   }
77 
78   QArray arrHalfVec(valsArr, halfPt);
79   for(int i = 0; i < DIM; ++i)
80   {
81     CoordType expVal = i < halfPt ? valsArr[i] : CoordType();
82     EXPECT_EQ(arrHalfVec[i], expVal);
83   }
84 
85   primal::NumericArray<int, 3> fromInitializerListRightSize = {10, 20, 30};
86   for(int i = 0; i < 3; ++i)
87   {
88     EXPECT_EQ(10 * (i + 1), fromInitializerListRightSize[i]);
89   }
90 
91   primal::NumericArray<int, 3> fromInitializerListTooLong = {10, 20, 30, 40};
92   for(int i = 0; i < 3; ++i)
93   {
94     EXPECT_EQ(10 * (i + 1), fromInitializerListTooLong[i]);
95   }
96 
97   primal::NumericArray<int, 5> fromInitializerListTooShort = {10, 20};
98   for(int i = 0; i < 2; ++i)
99   {
100     EXPECT_EQ(10 * (i + 1), fromInitializerListTooShort[i]);
101   }
102   for(int i = 2; i < 5; ++i)
103   {
104     EXPECT_EQ(0, fromInitializerListTooShort[i]);
105   }
106 
107   primal::NumericArray<int, 3> fromInitializerNoEqualsSign {10, 20, 30};
108   for(int i = 0; i < 3; ++i)
109   {
110     EXPECT_EQ(10 * (i + 1), fromInitializerNoEqualsSign[i]);
111   }
112 }
113 
114 //------------------------------------------------------------------------------
TEST(primal_numeric_array,num_array_to_array)115 TEST(primal_numeric_array, num_array_to_array)
116 {
117   static const int DIM = 5;
118   typedef double CoordType;
119   typedef primal::NumericArray<CoordType, DIM> QArray;
120 
121   // Compare array initialized from arbitrary array
122   CoordType valsArr[DIM] = {12., 23., 34., 45., 56.432};
123   QArray arrArr(valsArr);
124 
125   for(int i = 0; i < DIM; ++i)
126   {
127     EXPECT_EQ(arrArr[i], valsArr[i]);
128   }
129 
130   // Copy data into new array
131   CoordType arrCopy[DIM];
132   arrArr.to_array(arrCopy);
133   for(int i = 0; i < DIM; ++i)
134   {
135     EXPECT_EQ(arrCopy[i], valsArr[i]);
136     EXPECT_EQ(arrCopy[i], arrArr.data()[i]);
137   }
138 }
139 
140 //------------------------------------------------------------------------------
TEST(primal_numeric_array,component_wise_arithmetic)141 TEST(primal_numeric_array, component_wise_arithmetic)
142 {
143   static const int DIM = 3;
144   typedef double CoordType;
145   typedef primal::NumericArray<CoordType, DIM> QArray;
146 
147   CoordType ca1[] = {3, 0, 1.2};
148   CoordType ca2[] = {0, 4, 1.2};
149   CoordType caSum[] = {3, 4, 2.4};
150   CoordType caDiff[] = {-3, 4, 0};
151   CoordType scalar = 5.3;
152 
153   CoordType caScalar[] = {scalar * 3, scalar * 4, scalar * 2.4};
154 
155   CoordType caProduct[] = {-3 * 3, 4 * 4, 0. * 2.4};
156 
157   QArray arr1(ca1);
158   QArray arr2(ca2);
159 
160   // testing component-wise addition and subtraction
161   EXPECT_EQ(QArray(caDiff), arr2 - arr1);
162   EXPECT_EQ(QArray(caSum), arr2 + arr1);
163   EXPECT_EQ(arr2 + arr1, arr1 + arr2);
164 
165   QArray aSum(arr1);
166   aSum += arr2;
167   EXPECT_EQ(aSum, QArray(caSum));
168 
169   QArray aDiff(arr2);
170   aDiff -= arr1;
171   EXPECT_EQ(aDiff, QArray(caDiff));
172 
173   // Testing scalar multiplication
174   EXPECT_EQ(QArray(caScalar), QArray(caSum) * scalar);
175   EXPECT_EQ(QArray(caScalar), scalar * QArray(caSum));
176   EXPECT_EQ(QArray(caScalar), (arr1 + arr2) * scalar);
177 
178   // Testing unary negation
179   EXPECT_EQ(-arr1, QArray() - arr1);
180 
181   // Testing component-wise product
182   EXPECT_EQ(QArray(caProduct), aSum * aDiff);
183   EXPECT_EQ(QArray(caProduct), aDiff * aSum);
184 
185   // Testing component-wise product
186   EXPECT_EQ(QArray(caProduct) / aSum, aDiff);
187 }
188 
189 //------------------------------------------------------------------------------
TEST(primal_numeric_array,component_min_max)190 TEST(primal_numeric_array, component_min_max)
191 {
192   static const int DIM = 3;
193   typedef int CoordType;
194   typedef primal::NumericArray<CoordType, DIM> QArray;
195 
196   CoordType incSeq[] = {1, 2, 3};
197   CoordType decSeq[] = {3, 2, 1};
198   CoordType upDownSeq[] = {5, 8, 3};
199   CoordType downUpSeq[] = {6, 2, 5};
200 
201   QArray incArr(incSeq);
202   QArray decArr(decSeq);
203   QArray udArr(upDownSeq);
204   QArray duArr(downUpSeq);
205 
206   // testing component-wise min and max functions
207   EXPECT_EQ(incArr.max(), 3);
208   EXPECT_EQ(decArr.max(), 3);
209   EXPECT_EQ(incArr.argMax(), 2);
210   EXPECT_EQ(decArr.argMax(), 0);
211 
212   EXPECT_EQ(udArr.max(), 8);
213   EXPECT_EQ(udArr.argMax(), 1);
214 
215   EXPECT_EQ(incArr.min(), 1);
216   EXPECT_EQ(decArr.min(), 1);
217   EXPECT_EQ(incArr.argMin(), 0);
218   EXPECT_EQ(decArr.argMin(), 2);
219 
220   EXPECT_EQ(duArr.min(), 2);
221   EXPECT_EQ(duArr.argMin(), 1);
222 }
223 
224 //------------------------------------------------------------------------------
TEST(primal_numeric_array,clamping)225 TEST(primal_numeric_array, clamping)
226 {
227   static const int DIM = 3;
228   typedef int CoordType;
229   typedef primal::NumericArray<CoordType, DIM> QArray;
230 
231   CoordType seq[] = {15, 4, 2};
232   CoordType seqClampUp7[] = {7, 4, 2};
233   CoordType seqClampLow3[] = {15, 4, 3};
234   CoordType seqClamp37[] = {7, 4, 3};
235 
236   // testing component-wise clamping functions
237   QArray seqUp(seq);
238   EXPECT_EQ(seqUp.max(), 15);
239   EXPECT_EQ(seqUp.clampUpper(7).max(), 7);
240   EXPECT_EQ(seqUp, QArray(seqClampUp7));
241 
242   QArray seqLow(seq);
243   EXPECT_EQ(seqLow.min(), 2);
244   EXPECT_EQ(seqLow.clampLower(3).min(), 3);
245   EXPECT_EQ(seqLow, QArray(seqClampLow3));
246 
247   QArray seqBoth(seq);
248   seqBoth.clamp(3, 7);
249   EXPECT_EQ(seqBoth.min(), 3);
250   EXPECT_EQ(seqBoth.max(), 7);
251   EXPECT_EQ(seqBoth, QArray(seqClamp37));
252 
253   // Test that order of clamping doesn't matter
254   EXPECT_EQ(seqBoth, seqLow.clampUpper(7));
255   EXPECT_EQ(seqBoth, seqUp.clampLower(3));
256 
257   EXPECT_EQ(QArray(seq).clamp(3, 7), QArray(seq).clampUpper(7).clampLower(3));
258   EXPECT_EQ(QArray(seq).clamp(3, 7), QArray(seq).clampLower(3).clampUpper(7));
259 
260 #ifdef AXOM_DEBUG
261   // NOTE: AXOM_DEBUG is disabled in release mode, so this test will only fail
262   // in
263   // debug mode
264   SLIC_INFO("Checking that clamping with ill-formed range throws an assert.");
265 
266   ::testing::FLAGS_gtest_death_test_style = "threadsafe";
267   EXPECT_DEATH_IF_SUPPORTED(QArray(seq).clamp(7, 3), "");
268 
269 #endif
270 }
271 
272 //------------------------------------------------------------------------------
AXOM_CUDA_TEST(primal_numeric_array,numeric_array_check_policies)273 AXOM_CUDA_TEST(primal_numeric_array, numeric_array_check_policies)
274 {
275   using seq_exec = axom::SEQ_EXEC;
276   check_numeric_array_policy<seq_exec>();
277 
278 #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_OPENMP) && \
279   defined(RAJA_ENABLE_OPENMP)
280 
281   using omp_exec = axom::OMP_EXEC;
282   check_numeric_array_policy<omp_exec>();
283 
284 #endif
285 
286 #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_CUDA) && \
287   defined(RAJA_ENABLE_CUDA) && defined(AXOM_USE_UMPIRE)
288 
289   using cuda_exec = axom::CUDA_EXEC<512>;
290 
291   check_numeric_array_policy<cuda_exec>();
292 #endif
293 }
294 
295 //----------------------------------------------------------------------
296 //----------------------------------------------------------------------
297 
main(int argc,char * argv[])298 int main(int argc, char* argv[])
299 {
300   int result = 0;
301 
302   ::testing::InitGoogleTest(&argc, argv);
303 
304   SimpleLogger logger;  // create & initialize test logger,
305 
306   // finalized when exiting main scope
307 
308   result = RUN_ALL_TESTS();
309 
310   return result;
311 }
312