Intrepid2
Intrepid2_TestUtils.hpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Intrepid2 Package
4 //
5 // Copyright 2007 NTESS and the Intrepid2 contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
15 #ifndef Intrepid2_TestUtils_h
16 #define Intrepid2_TestUtils_h
17 
18 #include "Kokkos_Core.hpp"
19 #include "Kokkos_DynRankView.hpp"
20 
21 #include "Intrepid2_Basis.hpp"
25 #include "Intrepid2_PointTools.hpp"
26 #include "Intrepid2_Sacado.hpp" // Sacado includes, guarded by the appropriate preprocessor variable
27 #include "Intrepid2_Utils.hpp"
28 
29 #include "Teuchos_UnitTestHarness.hpp"
30 
31 namespace Intrepid2
32 {
34  constexpr int MAX_FAD_DERIVATIVES_FOR_TESTS = 3;
35 
37 #if defined(KOKKOS_ENABLE_CUDA)
38  using DefaultTestDeviceType = Kokkos::Device<Kokkos::Cuda,Kokkos::CudaSpace>;
39 #elif defined(KOKKOS_ENABLE_HIP)
40  using DefaultTestDeviceType = Kokkos::Device<Kokkos::HIP,Kokkos::HIPSpace>;
41 #else
42  using DefaultTestDeviceType = typename Kokkos::DefaultExecutionSpace::device_type;
43 #endif
44 
46  template <class Scalar>
47  const typename Teuchos::ScalarTraits<Scalar>::magnitudeType
48  smallNumber()
49  {
50  using ST = Teuchos::ScalarTraits<Scalar>;
51  return ST::magnitude(Teuchos::RelErrSmallNumber<ST::hasMachineParameters,Scalar>::smallNumber());
52  }
53 
55  template <class Scalar1, class Scalar2>
56  KOKKOS_INLINE_FUNCTION
57  bool
58  relErrMeetsTol( const Scalar1 &s1, const Scalar2 &s2, const typename Teuchos::ScalarTraits< typename std::common_type<Scalar1,Scalar2>::type >::magnitudeType &smallNumber, const double &tol )
59  {
60  using Scalar = typename std::common_type<Scalar1,Scalar2>::type;
61  const Scalar s1Abs = fabs(s1);
62  const Scalar s2Abs = fabs(s2);
63  const Scalar maxAbs = (s1Abs > s2Abs) ? s1Abs : s2Abs;
64  const Scalar relErr = fabs( s1 - s2 ) / ( smallNumber + maxAbs );
65  return relErr < tol;
66  }
67 
68  template <class Scalar1, class Scalar2>
69  KOKKOS_INLINE_FUNCTION
70  bool
71  errMeetsAbsAndRelTol( const Scalar1 &s1, const Scalar2 &s2, const double &relTol, const double &absTol )
72  {
73  return fabs( s1 - s2 ) <= absTol + fabs(s1) * relTol;
74  }
75 
76  static const double TEST_TOLERANCE_TIGHT = 1.e2 * std::numeric_limits<double>::epsilon();
77 
78  // we use DynRankView for both input points and values
79  template<typename ScalarType, typename DeviceType>
80  using ViewType = Kokkos::DynRankView<ScalarType,DeviceType>;
81 
82  template<typename ScalarType, typename DeviceType>
83  using FixedRankViewType = Kokkos::View<ScalarType,DeviceType>;
84 
85  template<typename ScalarType>
86  KOKKOS_INLINE_FUNCTION bool valuesAreSmall(const ScalarType &a, const ScalarType &b, const double &epsilon)
87  {
88  using std::abs;
89  return (abs(a) < epsilon) && (abs(b) < epsilon);
90  }
91 
92  inline bool approximatelyEqual(double a, double b, double epsilon)
93  {
94  const double larger_magnitude = (std::abs(a) < std::abs(b) ? std::abs(b) : std::abs(a));
95  return std::abs(a - b) <= larger_magnitude * epsilon;
96  }
97 
98  inline bool essentiallyEqual(double a, double b, double epsilon)
99  {
100  const double smaller_magnitude = (std::abs(a) > std::abs(b) ? std::abs(b) : std::abs(a));
101  return std::abs(a - b) <= smaller_magnitude * epsilon;
102  }
103 
104  // conversion from the ref element [0,1] (used by ESEAS) to the ref element [-1,1] (used by Intrepid2)
105  KOKKOS_INLINE_FUNCTION double fromZeroOne(double x_zero_one)
106  {
107  return x_zero_one * 2.0 - 1.0;
108  }
109 
110  // conversion from the ref element [-1,1] (used by Intrepid2) to the ref element [0,1] (used by ESEAS)
111  KOKKOS_INLINE_FUNCTION double toZeroOne(double x_minus_one_one)
112  {
113  return (x_minus_one_one + 1.0) / 2.0;
114  }
115 
116  // conversion from the ref element [0,1] (used by ESEAS) to the ref element [-1,1] (used by Intrepid2)
117  KOKKOS_INLINE_FUNCTION double fromZeroOne_dx(double dx_zero_one)
118  {
119  return dx_zero_one / 2.0;
120  }
121 
122  // conversion from the ref element [-1,1] (used by Intrepid2) to the ref element [0,1] (used by ESEAS)
123  KOKKOS_INLINE_FUNCTION double toZeroOne_dx(double dx_minus_one_one)
124  {
125  return dx_minus_one_one * 2.0;
126  }
127 
128  template<class DeviceViewType>
129  typename DeviceViewType::HostMirror getHostCopy(const DeviceViewType &deviceView)
130  {
131  typename DeviceViewType::HostMirror hostView = Kokkos::create_mirror(deviceView);
132  Kokkos::deep_copy(hostView, deviceView);
133  return hostView;
134  }
135 
136  template<class BasisFamily>
137  inline Teuchos::RCP< Intrepid2::Basis<DefaultTestDeviceType,double,double> > getBasisUsingFamily(shards::CellTopology cellTopo, Intrepid2::EFunctionSpace fs,
138  int polyOrder_x, int polyOrder_y=-1, int polyOrder_z = -1)
139  {
140  using BasisPtr = typename BasisFamily::BasisPtr;
141 
142  BasisPtr basis;
143  using namespace Intrepid2;
144 
145  if (cellTopo.getBaseKey() == shards::Line<>::key)
146  {
147  basis = getLineBasis<BasisFamily>(fs,polyOrder_x);
148  }
149  else if (cellTopo.getBaseKey() == shards::Quadrilateral<>::key)
150  {
151  INTREPID2_TEST_FOR_EXCEPTION(polyOrder_y < 0, std::invalid_argument, "polyOrder_y must be specified");
152  basis = getQuadrilateralBasis<BasisFamily>(fs,polyOrder_x,polyOrder_y);
153  }
154  else if (cellTopo.getBaseKey() == shards::Triangle<>::key)
155  {
156  basis = getTriangleBasis<BasisFamily>(fs,polyOrder_x);
157  }
158  else if (cellTopo.getBaseKey() == shards::Hexahedron<>::key)
159  {
160  INTREPID2_TEST_FOR_EXCEPTION(polyOrder_y < 0, std::invalid_argument, "polyOrder_y must be specified");
161  INTREPID2_TEST_FOR_EXCEPTION(polyOrder_z < 0, std::invalid_argument, "polyOrder_z must be specified");
162  basis = getHexahedronBasis<BasisFamily>(fs,polyOrder_x,polyOrder_y,polyOrder_z);
163  }
164  else if (cellTopo.getBaseKey() == shards::Tetrahedron<>::key)
165  {
166  basis = getTetrahedronBasis<BasisFamily>(fs, polyOrder_x);
167  }
168  else if (cellTopo.getBaseKey() == shards::Wedge<>::key)
169  {
170  INTREPID2_TEST_FOR_EXCEPTION(polyOrder_y < 0, std::invalid_argument, "polyOrder_y must be specified");
171  basis = getWedgeBasis<BasisFamily>(fs,polyOrder_x,polyOrder_y);
172  }
173  else if (cellTopo.getBaseKey() == shards::Pyramid<>::key)
174  {
175  basis = getPyramidBasis<BasisFamily>(fs,polyOrder_x);
176  }
177  else
178  {
179  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported cell topology");
180  }
181  return basis;
182  }
183 
184  template<bool defineVertexFunctions>
185  inline Teuchos::RCP< Intrepid2::Basis<DefaultTestDeviceType,double,double> > getHierarchicalBasis(shards::CellTopology cellTopo, Intrepid2::EFunctionSpace fs,
186  int polyOrder_x, int polyOrder_y=-1, int polyOrder_z = -1)
187  {
188  using DeviceType = DefaultTestDeviceType;
189  using Scalar = double;
190  using namespace Intrepid2;
191 
197 
199 
200  return getBasisUsingFamily<BasisFamily>(cellTopo, fs, polyOrder_x, polyOrder_y, polyOrder_z);
201  }
202 
203  template<typename ValueType, typename DeviceType, class ... DimArgs>
204  inline ViewType<ValueType,DeviceType> getView(const std::string &label, DimArgs... dims)
205  {
206  const bool allocateFadStorage = !(std::is_standard_layout<ValueType>::value && std::is_trivial<ValueType>::value);
207  if (!allocateFadStorage)
208  {
209  return ViewType<ValueType,DeviceType>(label,dims...);
210  }
211  else
212  {
213  return ViewType<ValueType,DeviceType>(label,dims...,MAX_FAD_DERIVATIVES_FOR_TESTS+1);
214  }
215  }
216 
217  // this method is to allow us to switch tests over incrementally; should collapse with ViewType once everything has been switched
218  template<typename ValueType, class ... DimArgs>
219  inline FixedRankViewType< typename RankExpander<ValueType, sizeof...(DimArgs) >::value_type, DefaultTestDeviceType > getFixedRankView(const std::string &label, DimArgs... dims)
220  {
221  const bool allocateFadStorage = !(std::is_standard_layout<ValueType>::value && std::is_trivial<ValueType>::value);
222  using value_type = typename RankExpander<ValueType, sizeof...(dims) >::value_type;
223  if (!allocateFadStorage)
224  {
225  return FixedRankViewType<value_type,DefaultTestDeviceType>(label,dims...);
226  }
227  else
228  {
229  return FixedRankViewType<value_type,DefaultTestDeviceType>(label,dims...,MAX_FAD_DERIVATIVES_FOR_TESTS+1);
230  }
231  }
232 
239  template <typename PointValueType, typename DeviceType>
240  inline ViewType<PointValueType,DeviceType> getInputPointsView(shards::CellTopology &cellTopo, int numPoints_1D)
241  {
242  if (cellTopo.getBaseKey() == shards::Wedge<>::key)
243  {
244  shards::CellTopology lineTopo = shards::CellTopology(shards::getCellTopologyData<shards::Line<> >() );
245  shards::CellTopology triTopo = shards::CellTopology(shards::getCellTopologyData<shards::Triangle<> >() );
246 
247  const ordinal_type order = numPoints_1D - 1;
248  ordinal_type numPoints_tri = PointTools::getLatticeSize(triTopo, order);
249  ordinal_type numPoints_line = PointTools::getLatticeSize(lineTopo, order);
250  ordinal_type numPoints = numPoints_tri * numPoints_line;
251  ordinal_type spaceDim = cellTopo.getDimension();
252 
253  ViewType<PointValueType,DeviceType> inputPointsTri = getView<PointValueType,DeviceType>("input points",numPoints_tri, 2);
254  ViewType<PointValueType,DeviceType> inputPointsLine = getView<PointValueType,DeviceType>("input points",numPoints_line,1);
255  PointTools::getLattice(inputPointsTri, triTopo, order, 0, POINTTYPE_EQUISPACED );
256  PointTools::getLattice(inputPointsLine, lineTopo, order, 0, POINTTYPE_EQUISPACED );
257 
258  ViewType<PointValueType,DeviceType> inputPoints = getView<PointValueType,DeviceType>("input points",numPoints,spaceDim);
259 
260  using ExecutionSpace = typename ViewType<PointValueType,DeviceType>::execution_space;
261 
262  Kokkos::RangePolicy < ExecutionSpace > policy(0,numPoints_tri);
263  Kokkos::parallel_for( policy,
264  KOKKOS_LAMBDA (const ordinal_type &triPointOrdinal )
265  {
266  ordinal_type pointOrdinal = triPointOrdinal * numPoints_line;
267  for (ordinal_type linePointOrdinal=0; linePointOrdinal<numPoints_line; linePointOrdinal++)
268  {
269  inputPoints(pointOrdinal,0) = inputPointsTri( triPointOrdinal,0);
270  inputPoints(pointOrdinal,1) = inputPointsTri( triPointOrdinal,1);
271  inputPoints(pointOrdinal,2) = inputPointsLine(linePointOrdinal,0);
272  pointOrdinal++;
273  }
274  }
275  );
276 
277  return inputPoints;
278  }
279  else
280  {
281  const ordinal_type order = numPoints_1D - 1;
282  ordinal_type numPoints = PointTools::getLatticeSize(cellTopo, order);
283  ordinal_type spaceDim = cellTopo.getDimension();
284 
285  ViewType<PointValueType,DeviceType> inputPoints = getView<PointValueType,DeviceType>("input points",numPoints,spaceDim);
286  PointTools::getLattice(inputPoints, cellTopo, order, 0, POINTTYPE_EQUISPACED );
287 
288  return inputPoints;
289  }
290  }
291 
292  template<typename OutputValueType, typename DeviceType>
293  inline ViewType<OutputValueType,DeviceType> getOutputView(Intrepid2::EFunctionSpace fs, Intrepid2::EOperator op, int basisCardinality, int numPoints, int spaceDim)
294  {
295  switch (fs) {
296  case Intrepid2::FUNCTION_SPACE_HGRAD:
297  switch (op) {
298  case Intrepid2::OPERATOR_VALUE:
299  return getView<OutputValueType,DeviceType>("H^1 value output",basisCardinality,numPoints);
300  case Intrepid2::OPERATOR_GRAD:
301  return getView<OutputValueType,DeviceType>("H^1 derivative output",basisCardinality,numPoints,spaceDim);
302  case Intrepid2::OPERATOR_D1:
303  case Intrepid2::OPERATOR_D2:
304  case Intrepid2::OPERATOR_D3:
305  case Intrepid2::OPERATOR_D4:
306  case Intrepid2::OPERATOR_D5:
307  case Intrepid2::OPERATOR_D6:
308  case Intrepid2::OPERATOR_D7:
309  case Intrepid2::OPERATOR_D8:
310  case Intrepid2::OPERATOR_D9:
311  case Intrepid2::OPERATOR_D10:
312  {
313  const auto dkcard = getDkCardinality(op, spaceDim);
314  return getView<OutputValueType,DeviceType>("H^1 derivative output",basisCardinality,numPoints,dkcard);
315  }
316  default:
317  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported op/fs combination");
318  }
319  case Intrepid2::FUNCTION_SPACE_HCURL:
320  switch (op) {
321  case Intrepid2::OPERATOR_VALUE:
322  return getView<OutputValueType,DeviceType>("H(curl) value output",basisCardinality,numPoints,spaceDim);
323  case Intrepid2::OPERATOR_CURL:
324  if (spaceDim == 2)
325  return getView<OutputValueType,DeviceType>("H(curl) derivative output",basisCardinality,numPoints);
326  else if (spaceDim == 3)
327  return getView<OutputValueType,DeviceType>("H(curl) derivative output",basisCardinality,numPoints,spaceDim);
328  default:
329  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported op/fs combination");
330  }
331  case Intrepid2::FUNCTION_SPACE_HDIV:
332  switch (op) {
333  case Intrepid2::OPERATOR_VALUE:
334  return getView<OutputValueType,DeviceType>("H(div) value output",basisCardinality,numPoints,spaceDim);
335  case Intrepid2::OPERATOR_DIV:
336  return getView<OutputValueType,DeviceType>("H(div) derivative output",basisCardinality,numPoints);
337  default:
338  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported op/fs combination");
339  }
340 
341  case Intrepid2::FUNCTION_SPACE_HVOL:
342  switch (op) {
343  case Intrepid2::OPERATOR_VALUE:
344  return getView<OutputValueType,DeviceType>("H(vol) value output",basisCardinality,numPoints);
345  case Intrepid2::OPERATOR_D1:
346  case Intrepid2::OPERATOR_D2:
347  case Intrepid2::OPERATOR_D3:
348  case Intrepid2::OPERATOR_D4:
349  case Intrepid2::OPERATOR_D5:
350  case Intrepid2::OPERATOR_D6:
351  case Intrepid2::OPERATOR_D7:
352  case Intrepid2::OPERATOR_D8:
353  case Intrepid2::OPERATOR_D9:
354  case Intrepid2::OPERATOR_D10:
355  {
356  const auto dkcard = getDkCardinality(op, spaceDim);
357  return getView<OutputValueType,DeviceType>("H(vol) derivative output",basisCardinality,numPoints,dkcard);
358  }
359  default:
360  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported op/fs combination");
361  }
362  default:
363  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported op/fs combination");
364  }
365  }
366 
367  // ! This returns a vector whose entries are vector<int>s containing 1-3 polynomial orders from 1 up to and including those specified
368  // ! Intended for testing bases that support anisotropic polynomial degree, such as the hierarchical bases
369  inline std::vector< std::vector<int> > getBasisTestCasesUpToDegree(int spaceDim, int minDegree, int polyOrder_x, int polyOrder_y=-1, int polyOrder_z=-1)
370  {
371  std::vector<int> degrees(spaceDim);
372  degrees[0] = polyOrder_x;
373  if (spaceDim > 1) degrees[1] = polyOrder_y;
374  if (spaceDim > 2) degrees[2] = polyOrder_z;
375 
376  int numCases = degrees[0];
377  for (unsigned d=1; d<degrees.size(); d++)
378  {
379  INTREPID2_TEST_FOR_EXCEPTION(degrees[d] < minDegree, std::invalid_argument, "Unsupported degree/minDegree combination");
380  numCases = numCases * (degrees[d] + 1 - minDegree);
381  }
382  std::vector< std::vector<int> > subBasisDegreeTestCases(numCases);
383  for (int caseOrdinal=0; caseOrdinal<numCases; caseOrdinal++)
384  {
385  std::vector<int> subBasisDegrees(degrees.size());
386  int caseRemainder = caseOrdinal;
387  for (int d=degrees.size()-1; d>=0; d--)
388  {
389  int subBasisDegree = caseRemainder % (degrees[d] + 1 - minDegree);
390  caseRemainder = caseRemainder / (degrees[d] + 1 - minDegree);
391  subBasisDegrees[d] = subBasisDegree + minDegree;
392  }
393  subBasisDegreeTestCases[caseOrdinal] = subBasisDegrees;
394  }
395  return subBasisDegreeTestCases;
396  }
397 
399  template<class Functor, class Scalar, int rank>
400  typename ViewType<Scalar,DefaultTestDeviceType>::HostMirror copyFunctorToHostView(const Functor &deviceFunctor)
401  {
402  INTREPID2_TEST_FOR_EXCEPTION(rank != getFunctorRank(deviceFunctor), std::invalid_argument, "functor rank must match the template argument");
403 
404  using DeviceType = DefaultTestDeviceType;
405  ViewType<Scalar,DeviceType> view;
406  const std::string label = "functor copy";
407  const auto &f = deviceFunctor;
408  switch (rank)
409  {
410  case 0:
411  view = getView<Scalar,DeviceType>(label);
412  break;
413  case 1:
414  view = getView<Scalar,DeviceType>(label, f.extent_int(0));
415  break;
416  case 2:
417  view = getView<Scalar,DeviceType>(label, f.extent_int(0), f.extent_int(1));
418  break;
419  case 3:
420  view = getView<Scalar,DeviceType>(label, f.extent_int(0), f.extent_int(1), f.extent_int(2));
421  break;
422  case 4:
423  view = getView<Scalar,DeviceType>(label, f.extent_int(0), f.extent_int(1), f.extent_int(2), f.extent_int(3));
424  break;
425  case 5:
426  view = getView<Scalar,DeviceType>(label, f.extent_int(0), f.extent_int(1), f.extent_int(2), f.extent_int(3), f.extent_int(4));
427  break;
428  case 6:
429  view = getView<Scalar,DeviceType>(label, f.extent_int(0), f.extent_int(1), f.extent_int(2), f.extent_int(3), f.extent_int(4), f.extent_int(5));
430  break;
431  case 7:
432  view = getView<Scalar,DeviceType>(label, f.extent_int(0), f.extent_int(1), f.extent_int(2), f.extent_int(3), f.extent_int(4), f.extent_int(5), f.extent_int(6));
433  break;
434  default:
435  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported functor rank");
436  }
437 
438  int entryCount = view.size();
439 
440  using ExecutionSpace = typename ViewType<Scalar,DeviceType>::execution_space;
441 
442  using ViewIteratorScalar = Intrepid2::ViewIterator<ViewType<Scalar,DeviceType>, Scalar>;
443  using FunctorIteratorScalar = FunctorIterator<Functor, Scalar, rank>;
444 
445  Kokkos::RangePolicy < ExecutionSpace > policy(0,entryCount);
446  Kokkos::parallel_for( policy,
447  KOKKOS_LAMBDA (const int &enumerationIndex )
448  {
449  ViewIteratorScalar vi(view);
450  FunctorIteratorScalar fi(f);
451 
452  vi.setEnumerationIndex(enumerationIndex);
453  fi.setEnumerationIndex(enumerationIndex);
454 
455  vi.set(fi.get());
456  }
457  );
458 
459  auto hostView = Kokkos::create_mirror_view(view);
460  Kokkos::deep_copy(hostView, view);
461  return hostView;
462  }
463 
464  template<class FunctorType, typename Scalar, int rank>
465  void printFunctor(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
466  {
467  auto functorHostCopy = copyFunctorToHostView<FunctorType, Scalar, rank>(functor);
468 
469  std::string name = (functorName == "") ? "Functor" : functorName;
470 
471  out << "\n******** " << name << " (rank " << rank << ") ********\n";
472  out << "dimensions: (";
473  for (int r=0; r<rank; r++)
474  {
475  out << functor.extent_int(r);
476  if (r<rank-1) out << ",";
477  }
478  out << ")\n";
479 
481 
482  bool moreEntries = true;
483  while (moreEntries)
484  {
485  Scalar value = vi.get();
486 
487  auto location = vi.getLocation();
488  out << functorName << "(";
489  for (ordinal_type i=0; i<rank; i++)
490  {
491  out << location[i];
492  if (i<rank-1)
493  {
494  out << ",";
495  }
496  }
497  out << ") " << value << std::endl;
498 
499  moreEntries = (vi.increment() != -1);
500  }
501  out << "\n";
502  }
503 
504  template<class FunctorType>
505  void printFunctor1(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
506  {
507  using Scalar = typename std::remove_reference<decltype(functor(0))>::type;
508  printFunctor<FunctorType, Scalar, 1>(functor, out, functorName);
509  }
510 
511  template<class FunctorType>
512  void printFunctor2(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
513  {
514  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0))>::type>::type;
515  printFunctor<FunctorType, Scalar, 2>(functor, out, functorName);
516  }
517 
518  template<class FunctorType>
519  void printFunctor3(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
520  {
521  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0,0))>::type>::type;
522  printFunctor<FunctorType, Scalar, 3>(functor, out, functorName);
523  }
524 
525  template<class FunctorType>
526  void printFunctor4(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
527  {
528  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0,0,0))>::type>::type;
529  printFunctor<FunctorType, Scalar, 4>(functor, out, functorName);
530  }
531 
532  template<class FunctorType>
533  void printFunctor5(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
534  {
535  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0,0,0,0))>::type>::type;
536  printFunctor<FunctorType, Scalar, 5>(functor, out, functorName);
537  }
538 
539  template<class FunctorType>
540  void printFunctor6(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
541  {
542  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0,0,0,0,0))>::type>::type;
543  printFunctor<FunctorType, Scalar, 6>(functor, out, functorName);
544  }
545 
546  template<class FunctorType>
547  void printFunctor7(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
548  {
549  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0,0,0,0,0,0))>::type>::type;
550  printFunctor<FunctorType, Scalar, 7>(functor, out, functorName);
551  }
552 
553  template<class View>
554  void printView(const View &view, std::ostream &out, const std::string &viewName = "")
555  {
556  using Scalar = typename View::value_type;
557  using HostView = typename View::HostMirror;
558  using HostViewIteratorScalar = Intrepid2::ViewIterator<HostView, Scalar>;
559 
560  auto hostView = getHostCopy(view);
561 
562  HostViewIteratorScalar vi(hostView);
563 
564  bool moreEntries = (vi.nextIncrementRank() != -1);
565  while (moreEntries)
566  {
567  Scalar value = vi.get();
568 
569  auto location = vi.getLocation();
570  out << viewName << "(";
571  for (unsigned i=0; i<getFunctorRank(view); i++)
572  {
573  out << location[i];
574  if (i<getFunctorRank(view)-1)
575  {
576  out << ",";
577  }
578  }
579  out << ") " << value << std::endl;
580 
581  moreEntries = (vi.increment() != -1);
582  }
583  }
584 
585  template <class FunctorType1, class FunctorType2, int rank, typename Scalar=typename FunctorType1::value_type, class ExecutionSpace = typename FunctorType1::execution_space>
586  typename std::enable_if< !(supports_rank<FunctorType1,rank>::value && supports_rank<FunctorType2,rank>::value), void >::type
587  testFloatingEquality(const FunctorType1 &functor1, const FunctorType2 &functor2, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
588  std::string functor1Name = "Functor 1", std::string functor2Name = "Functor 2")
589  {
590  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "testFloatingEquality() called on FunctorType1 or FunctorType2 that does not support the specified rank.\n");
591  }
592 
594  template <class FunctorType1, class FunctorType2, int rank, typename Scalar=typename FunctorType1::value_type, class ExecutionSpace = typename FunctorType1::execution_space>
595  typename std::enable_if< (supports_rank<FunctorType1,rank>::value && supports_rank<FunctorType2,rank>::value), void >::type
596  testFloatingEquality(const FunctorType1 &functor1, const FunctorType2 &functor2, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
597  std::string functor1Name = "Functor 1", std::string functor2Name = "Functor 2")
598  {
599  static_assert( supports_rank<FunctorType1,rank>::value, "Functor1 must support the specified rank through operator().");
600  static_assert( supports_rank<FunctorType2,rank>::value, "Functor2 must support the specified rank through operator().");
601 
602  using Functor1IteratorScalar = FunctorIterator<FunctorType1, Scalar, rank>;
603  using Functor2IteratorScalar = FunctorIterator<FunctorType2, Scalar, rank>;
604 
605  // check that rank/size match
606  TEUCHOS_TEST_FOR_EXCEPTION(getFunctorRank(functor1) != rank, std::invalid_argument, "functor1 and functor2 must agree in rank"); // Kokkos::View does not provide rank() method; getFunctorRank() provides common interface
607  TEUCHOS_TEST_FOR_EXCEPTION(getFunctorRank(functor2) != rank, std::invalid_argument, "functor1 and functor2 must agree in rank"); // Kokkos::View does not provide rank() method; getFunctorRank() provides common interface
608 
609  int entryCount = 1;
610  for (unsigned i=0; i<getFunctorRank(functor1); i++)
611  {
612  TEUCHOS_TEST_FOR_EXCEPTION(functor1.extent_int(i) != functor2.extent_int(i), std::invalid_argument,
613  "functor1 and functor2 must agree in size in each dimension; functor1 has extent "
614  + std::to_string(functor1.extent_int(i)) + " in dimension " + std::to_string(i)
615  + "; functor2 has extent " + std::to_string(functor2.extent_int(i)) );
616  entryCount *= functor1.extent_int(i);
617  }
618  if (entryCount == 0) return; // nothing to test
619 
620  ViewType<bool,ExecutionSpace> valuesMatch = getView<bool,ExecutionSpace>("valuesMatch", entryCount);
621 
622  Kokkos::RangePolicy < ExecutionSpace > policy(0,entryCount);
623  Kokkos::parallel_for( policy,
624  KOKKOS_LAMBDA (const int &enumerationIndex )
625  {
626  Functor1IteratorScalar vi1(functor1);
627  Functor2IteratorScalar vi2(functor2);
628 
629  vi1.setEnumerationIndex(enumerationIndex);
630  vi2.setEnumerationIndex(enumerationIndex);
631 
632  const Scalar & value1 = vi1.get();
633  const Scalar & value2 = vi2.get();
634 
635  bool errMeetsTol = errMeetsAbsAndRelTol(value1, value2, relTol, absTol);
636  valuesMatch(enumerationIndex) = errMeetsTol;
637  }
638  );
639 
640  int failureCount = 0;
641  Kokkos::RangePolicy<ExecutionSpace > reducePolicy(0, entryCount);
642  Kokkos::parallel_reduce( reducePolicy,
643  KOKKOS_LAMBDA( const int &enumerationIndex, int &reducedValue )
644  {
645  reducedValue += valuesMatch(enumerationIndex) ? 0 : 1;
646  }, failureCount);
647 
648  const bool allValuesMatch = (failureCount == 0);
649  success = success && allValuesMatch;
650 
651  if (!allValuesMatch)
652  {
653  // copy functors to host views
654  auto functor1CopyHost = copyFunctorToHostView<FunctorType1,Scalar,rank>(functor1);
655  auto functor2CopyHost = copyFunctorToHostView<FunctorType2,Scalar,rank>(functor2);
656 
657  auto valuesMatchHost = getHostCopy(valuesMatch);
658 
662 
663  bool moreEntries = true;
664  while (moreEntries)
665  {
666  bool errMeetsTol = viMatch.get();
667 
668  if (!errMeetsTol)
669  {
670  const Scalar value1 = vi1.get();
671  const Scalar value2 = vi2.get();
672 
673  success = false;
674  auto location = vi1.getLocation();
675  out << "At location (";
676  for (unsigned i=0; i<getFunctorRank(functor1); i++)
677  {
678  out << location[i];
679  if (i<getFunctorRank(functor1)-1)
680  {
681  out << ",";
682  }
683  }
684  out << ") " << functor1Name << " value != " << functor2Name << " value (";
685  out << value1 << " != " << value2 << "); diff is " << std::abs(value1-value2) << std::endl;
686  }
687 
688  moreEntries = (vi1.increment() != -1);
689  moreEntries = moreEntries && (vi2.increment() != -1);
690  moreEntries = moreEntries && (viMatch.increment() != -1);
691  }
692  }
693  }
694 
695  template <class ViewType, class FunctorType>
696  void testFloatingEquality1(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
697  std::string view1Name = "View", std::string view2Name = "Functor")
698  {
699  testFloatingEquality<ViewType, FunctorType, 1>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
700  }
701 
702  template <class ViewType, class FunctorType>
703  void testFloatingEquality2(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
704  std::string view1Name = "View", std::string view2Name = "Functor")
705  {
706  testFloatingEquality<ViewType, FunctorType, 2>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
707  }
708 
709  template <class ViewType, class FunctorType>
710  void testFloatingEquality3(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
711  std::string view1Name = "View", std::string view2Name = "Functor")
712  {
713  testFloatingEquality<ViewType, FunctorType, 3>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
714  }
715 
716  template <class ViewType, class FunctorType>
717  void testFloatingEquality4(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
718  std::string view1Name = "View", std::string view2Name = "Functor")
719  {
720  testFloatingEquality<ViewType, FunctorType, 4>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
721  }
722 
723  template <class ViewType, class FunctorType>
724  void testFloatingEquality5(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
725  std::string view1Name = "View", std::string view2Name = "Functor")
726  {
727  testFloatingEquality<ViewType, FunctorType, 5>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
728  }
729 
730  template <class ViewType, class FunctorType>
731  void testFloatingEquality6(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
732  std::string view1Name = "View", std::string view2Name = "Functor")
733  {
734  testFloatingEquality<ViewType, FunctorType, 6>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
735  }
736 
737  template <class ViewType, class FunctorType>
738  void testFloatingEquality7(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
739  std::string view1Name = "View", std::string view2Name = "Functor")
740  {
741  testFloatingEquality<ViewType, FunctorType, 7>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
742  }
743 
744  template <class ViewType1, class ViewType2>
745  void testViewFloatingEquality(const ViewType1 &view1, const ViewType2 &view2, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
746  std::string view1Name = "View 1", std::string view2Name = "View 2")
747  {
748  // check that rank/size match
749  TEUCHOS_TEST_FOR_EXCEPTION(view1.rank() != view2.rank(), std::invalid_argument, "views must agree in rank");
750  for (unsigned i=0; i<view1.rank(); i++)
751  {
752  TEUCHOS_TEST_FOR_EXCEPTION(view1.extent_int(i) != view2.extent_int(i), std::invalid_argument, "views must agree in size in each dimension");
753  }
754 
755  if (view1.size() == 0) return; // nothing to test
756 
757  const int rank = view1.rank();
758  switch (rank)
759  {
760  case 0: testFloatingEquality<ViewType1, ViewType2, 0>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
761  case 1: testFloatingEquality<ViewType1, ViewType2, 1>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
762  case 2: testFloatingEquality<ViewType1, ViewType2, 2>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
763  case 3: testFloatingEquality<ViewType1, ViewType2, 3>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
764  case 4: testFloatingEquality<ViewType1, ViewType2, 4>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
765  case 5: testFloatingEquality<ViewType1, ViewType2, 5>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
766  case 6: testFloatingEquality<ViewType1, ViewType2, 6>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
767  case 7: testFloatingEquality<ViewType1, ViewType2, 7>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
768  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported rank");
769  }
770  }
771 
772 } // namespace Intrepid2
773 
774 #ifdef HAVE_INTREPID2_SACADO
775 using Sacado_Fad_DFadType = Sacado::Fad::DFad<double>;
776 #define INTREPID2_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
777 \
778 TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( GROUP_NAME, TEST_NAME, double ) \
779 \
780 TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( GROUP_NAME, TEST_NAME, Sacado_Fad_DFadType ) \
781 
782 #define INTREPID2_OUTPUTSCALAR_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
783 \
784 TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( GROUP_NAME, TEST_NAME, double, double ) \
785 \
786 TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( GROUP_NAME, TEST_NAME, Sacado_Fad_DFadType, Sacado_Fad_DFadType ) \
787 
788 #else
789 #define INTREPID2_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
790 \
791 TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( GROUP_NAME, TEST_NAME, double ) \
792 
793 #define INTREPID2_OUTPUTSCALAR_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
794 \
795 TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( GROUP_NAME, TEST_NAME, double, double ) \
796 
797 #endif
798 
799 #endif /* Intrepid2_TestUtils_h */
Stateless classes that act as factories for two families of hierarchical bases. HierarchicalBasisFami...
Stateless class representing a family of basis functions, templated on H(vol) and H(grad) on the line...
KOKKOS_INLINE_FUNCTION ScalarType get()
SFINAE helper to detect whether a type supports a rank-integral-argument operator().
Header function for Intrepid2::Util class and other utility functions.
static void getLattice(Kokkos::DynRankView< pointValueType, pointProperties...> points, const shards::CellTopology cellType, const ordinal_type order, const ordinal_type offset=0, const EPointType pointType=POINTTYPE_EQUISPACED)
Computes a lattice of points of a given order on a reference simplex, quadrilateral or hexahedron (cu...
Helper to get Scalar[*+] where the number of *&#39;s matches the given rank.
Basis defining Legendre basis on the line, a polynomial subspace of L^2 (a.k.a. H(vol)) on the line...
Basis defining integrated Legendre basis on the line, a polynomial subspace of H(grad) on the line...
essentially, a read-only variant of ViewIterator, for a general functor (extent_int() and rank() supp...
Defines the Intrepid2::FunctorIterator class, as well as the Intrepid2::functor_returns_ref SFINAE he...
A family of basis functions, constructed from H(vol) and H(grad) bases on the line.
Header file to include all Sacado headers that are required if using Intrepid2 with Sacado types...
A helper class that allows iteration over some part of a Kokkos View, while allowing the calling code...
static ordinal_type getLatticeSize(const shards::CellTopology cellType, const ordinal_type order, const ordinal_type offset=0)
Computes the number of points in a lattice of a given order on a simplex (currently disabled for othe...
Header file for the abstract base class Intrepid2::Basis.
Header file for Intrepid2::PointTools class to provide utilities for barycentric coordinates, equispaced lattices, and warp-blend point distrubtions.