Intrepid2
Intrepid2_TestUtils.hpp
Go to the documentation of this file.
1 // @HEADER
2 // ************************************************************************
3 //
4 // Intrepid2 Package
5 // Copyright (2007) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact Kyungjoo Kim (kyukim@sandia.gov),
38 // Mauro Perego (mperego@sandia.gov), or
39 // Nate Roberts (nvrober@sandia.gov)
40 //
41 // ************************************************************************
42 // @HEADER
43 
49 #ifndef Intrepid2_TestUtils_h
50 #define Intrepid2_TestUtils_h
51 
52 #include "Kokkos_Core.hpp"
53 #include "Kokkos_DynRankView.hpp"
54 
55 #include "Intrepid2_Basis.hpp"
59 #include "Intrepid2_PointTools.hpp"
60 #include "Intrepid2_Sacado.hpp" // Sacado includes, guarded by the appropriate preprocessor variable
61 #include "Intrepid2_Utils.hpp"
62 
63 #include "Teuchos_UnitTestHarness.hpp"
64 
65 namespace Intrepid2
66 {
68  constexpr int MAX_FAD_DERIVATIVES_FOR_TESTS = 3;
69 
71 #if defined(KOKKOS_ENABLE_CUDA)
72  using DefaultTestDeviceType = Kokkos::Device<Kokkos::Cuda,Kokkos::CudaSpace>;
73 #elif defined(KOKKOS_ENABLE_HIP)
74  using DefaultTestDeviceType = Kokkos::Device<Kokkos::HIP,Kokkos::HIPSpace>;
75 #else
76  using DefaultTestDeviceType = typename Kokkos::DefaultExecutionSpace::device_type;
77 #endif
78 
80  template <class Scalar>
81  const typename Teuchos::ScalarTraits<Scalar>::magnitudeType
82  smallNumber()
83  {
84  using ST = Teuchos::ScalarTraits<Scalar>;
85  return ST::magnitude(Teuchos::RelErrSmallNumber<ST::hasMachineParameters,Scalar>::smallNumber());
86  }
87 
89  template <class Scalar1, class Scalar2>
90  KOKKOS_INLINE_FUNCTION
91  bool
92  relErrMeetsTol( const Scalar1 &s1, const Scalar2 &s2, const typename Teuchos::ScalarTraits< typename std::common_type<Scalar1,Scalar2>::type >::magnitudeType &smallNumber, const double &tol )
93  {
94  using Scalar = typename std::common_type<Scalar1,Scalar2>::type;
95  const Scalar s1Abs = fabs(s1);
96  const Scalar s2Abs = fabs(s2);
97  const Scalar maxAbs = (s1Abs > s2Abs) ? s1Abs : s2Abs;
98  const Scalar relErr = fabs( s1 - s2 ) / ( smallNumber + maxAbs );
99  return relErr < tol;
100  }
101 
102  template <class Scalar1, class Scalar2>
103  KOKKOS_INLINE_FUNCTION
104  bool
105  errMeetsAbsAndRelTol( const Scalar1 &s1, const Scalar2 &s2, const double &relTol, const double &absTol )
106  {
107  return fabs( s1 - s2 ) <= absTol + fabs(s1) * relTol;
108  }
109 
110  static const double TEST_TOLERANCE_TIGHT = 1.e2 * std::numeric_limits<double>::epsilon();
111 
112  // we use DynRankView for both input points and values
113  template<typename ScalarType, typename DeviceType>
114  using ViewType = Kokkos::DynRankView<ScalarType,DeviceType>;
115 
116  template<typename ScalarType, typename DeviceType>
117  using FixedRankViewType = Kokkos::View<ScalarType,DeviceType>;
118 
119  template<typename ScalarType>
120  KOKKOS_INLINE_FUNCTION bool valuesAreSmall(const ScalarType &a, const ScalarType &b, const double &epsilon)
121  {
122  using std::abs;
123  return (abs(a) < epsilon) && (abs(b) < epsilon);
124  }
125 
126  inline bool approximatelyEqual(double a, double b, double epsilon)
127  {
128  const double larger_magnitude = (std::abs(a) < std::abs(b) ? std::abs(b) : std::abs(a));
129  return std::abs(a - b) <= larger_magnitude * epsilon;
130  }
131 
132  inline bool essentiallyEqual(double a, double b, double epsilon)
133  {
134  const double smaller_magnitude = (std::abs(a) > std::abs(b) ? std::abs(b) : std::abs(a));
135  return std::abs(a - b) <= smaller_magnitude * epsilon;
136  }
137 
138  // conversion from the ref element [0,1] (used by ESEAS) to the ref element [-1,1] (used by Intrepid2)
139  KOKKOS_INLINE_FUNCTION double fromZeroOne(double x_zero_one)
140  {
141  return x_zero_one * 2.0 - 1.0;
142  }
143 
144  // conversion from the ref element [-1,1] (used by Intrepid2) to the ref element [0,1] (used by ESEAS)
145  KOKKOS_INLINE_FUNCTION double toZeroOne(double x_minus_one_one)
146  {
147  return (x_minus_one_one + 1.0) / 2.0;
148  }
149 
150  // conversion from the ref element [0,1] (used by ESEAS) to the ref element [-1,1] (used by Intrepid2)
151  KOKKOS_INLINE_FUNCTION double fromZeroOne_dx(double dx_zero_one)
152  {
153  return dx_zero_one / 2.0;
154  }
155 
156  // conversion from the ref element [-1,1] (used by Intrepid2) to the ref element [0,1] (used by ESEAS)
157  KOKKOS_INLINE_FUNCTION double toZeroOne_dx(double dx_minus_one_one)
158  {
159  return dx_minus_one_one * 2.0;
160  }
161 
162  template<class DeviceViewType>
163  typename DeviceViewType::HostMirror getHostCopy(const DeviceViewType &deviceView)
164  {
165  typename DeviceViewType::HostMirror hostView = Kokkos::create_mirror(deviceView);
166  Kokkos::deep_copy(hostView, deviceView);
167  return hostView;
168  }
169 
170  template<class BasisFamily>
171  inline Teuchos::RCP< Intrepid2::Basis<DefaultTestDeviceType,double,double> > getBasisUsingFamily(shards::CellTopology cellTopo, Intrepid2::EFunctionSpace fs,
172  int polyOrder_x, int polyOrder_y=-1, int polyOrder_z = -1)
173  {
174  using BasisPtr = typename BasisFamily::BasisPtr;
175 
176  BasisPtr basis;
177  using namespace Intrepid2;
178 
179  if (cellTopo.getBaseKey() == shards::Line<>::key)
180  {
181  basis = getLineBasis<BasisFamily>(fs,polyOrder_x);
182  }
183  else if (cellTopo.getBaseKey() == shards::Quadrilateral<>::key)
184  {
185  INTREPID2_TEST_FOR_EXCEPTION(polyOrder_y < 0, std::invalid_argument, "polyOrder_y must be specified");
186  basis = getQuadrilateralBasis<BasisFamily>(fs,polyOrder_x,polyOrder_y);
187  }
188  else if (cellTopo.getBaseKey() == shards::Triangle<>::key)
189  {
190  basis = getTriangleBasis<BasisFamily>(fs,polyOrder_x);
191  }
192  else if (cellTopo.getBaseKey() == shards::Hexahedron<>::key)
193  {
194  INTREPID2_TEST_FOR_EXCEPTION(polyOrder_y < 0, std::invalid_argument, "polyOrder_y must be specified");
195  INTREPID2_TEST_FOR_EXCEPTION(polyOrder_z < 0, std::invalid_argument, "polyOrder_z must be specified");
196  basis = getHexahedronBasis<BasisFamily>(fs,polyOrder_x,polyOrder_y,polyOrder_z);
197  }
198  else if (cellTopo.getBaseKey() == shards::Tetrahedron<>::key)
199  {
200  basis = getTetrahedronBasis<BasisFamily>(fs, polyOrder_x);
201  }
202  else if (cellTopo.getBaseKey() == shards::Wedge<>::key)
203  {
204  INTREPID2_TEST_FOR_EXCEPTION(polyOrder_y < 0, std::invalid_argument, "polyOrder_y must be specified");
205  basis = getWedgeBasis<BasisFamily>(fs,polyOrder_x,polyOrder_y);
206  }
207  else if (cellTopo.getBaseKey() == shards::Pyramid<>::key)
208  {
209  basis = getPyramidBasis<BasisFamily>(fs,polyOrder_x);
210  }
211  else
212  {
213  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported cell topology");
214  }
215  return basis;
216  }
217 
218  template<bool defineVertexFunctions>
219  inline Teuchos::RCP< Intrepid2::Basis<DefaultTestDeviceType,double,double> > getHierarchicalBasis(shards::CellTopology cellTopo, Intrepid2::EFunctionSpace fs,
220  int polyOrder_x, int polyOrder_y=-1, int polyOrder_z = -1)
221  {
222  using DeviceType = DefaultTestDeviceType;
223  using Scalar = double;
224  using namespace Intrepid2;
225 
231 
233 
234  return getBasisUsingFamily<BasisFamily>(cellTopo, fs, polyOrder_x, polyOrder_y, polyOrder_z);
235  }
236 
237  template<typename ValueType, typename DeviceType, class ... DimArgs>
238  inline ViewType<ValueType,DeviceType> getView(const std::string &label, DimArgs... dims)
239  {
240  const bool allocateFadStorage = !std::is_pod<ValueType>::value;
241  if (!allocateFadStorage)
242  {
243  return ViewType<ValueType,DeviceType>(label,dims...);
244  }
245  else
246  {
247  return ViewType<ValueType,DeviceType>(label,dims...,MAX_FAD_DERIVATIVES_FOR_TESTS+1);
248  }
249  }
250 
251  // this method is to allow us to switch tests over incrementally; should collapse with ViewType once everything has been switched
252  template<typename ValueType, class ... DimArgs>
253  inline FixedRankViewType< typename RankExpander<ValueType, sizeof...(DimArgs) >::value_type, DefaultTestDeviceType > getFixedRankView(const std::string &label, DimArgs... dims)
254  {
255  const bool allocateFadStorage = !std::is_pod<ValueType>::value;
256  using value_type = typename RankExpander<ValueType, sizeof...(dims) >::value_type;
257  if (!allocateFadStorage)
258  {
259  return FixedRankViewType<value_type,DefaultTestDeviceType>(label,dims...);
260  }
261  else
262  {
263  return FixedRankViewType<value_type,DefaultTestDeviceType>(label,dims...,MAX_FAD_DERIVATIVES_FOR_TESTS+1);
264  }
265  }
266 
273  template <typename PointValueType, typename DeviceType>
274  inline ViewType<PointValueType,DeviceType> getInputPointsView(shards::CellTopology &cellTopo, int numPoints_1D)
275  {
276  if (cellTopo.getBaseKey() == shards::Wedge<>::key)
277  {
278  shards::CellTopology lineTopo = shards::CellTopology(shards::getCellTopologyData<shards::Line<> >() );
279  shards::CellTopology triTopo = shards::CellTopology(shards::getCellTopologyData<shards::Triangle<> >() );
280 
281  const ordinal_type order = numPoints_1D - 1;
282  ordinal_type numPoints_tri = PointTools::getLatticeSize(triTopo, order);
283  ordinal_type numPoints_line = PointTools::getLatticeSize(lineTopo, order);
284  ordinal_type numPoints = numPoints_tri * numPoints_line;
285  ordinal_type spaceDim = cellTopo.getDimension();
286 
287  ViewType<PointValueType,DeviceType> inputPointsTri = getView<PointValueType,DeviceType>("input points",numPoints_tri, 2);
288  ViewType<PointValueType,DeviceType> inputPointsLine = getView<PointValueType,DeviceType>("input points",numPoints_line,1);
289  PointTools::getLattice(inputPointsTri, triTopo, order, 0, POINTTYPE_EQUISPACED );
290  PointTools::getLattice(inputPointsLine, lineTopo, order, 0, POINTTYPE_EQUISPACED );
291 
292  ViewType<PointValueType,DeviceType> inputPoints = getView<PointValueType,DeviceType>("input points",numPoints,spaceDim);
293 
294  using ExecutionSpace = typename ViewType<PointValueType,DeviceType>::execution_space;
295 
296  Kokkos::RangePolicy < ExecutionSpace > policy(0,numPoints_tri);
297  Kokkos::parallel_for( policy,
298  KOKKOS_LAMBDA (const ordinal_type &triPointOrdinal )
299  {
300  ordinal_type pointOrdinal = triPointOrdinal * numPoints_line;
301  for (ordinal_type linePointOrdinal=0; linePointOrdinal<numPoints_line; linePointOrdinal++)
302  {
303  inputPoints(pointOrdinal,0) = inputPointsTri( triPointOrdinal,0);
304  inputPoints(pointOrdinal,1) = inputPointsTri( triPointOrdinal,1);
305  inputPoints(pointOrdinal,2) = inputPointsLine(linePointOrdinal,0);
306  pointOrdinal++;
307  }
308  }
309  );
310 
311  return inputPoints;
312  }
313  else
314  {
315  const ordinal_type order = numPoints_1D - 1;
316  ordinal_type numPoints = PointTools::getLatticeSize(cellTopo, order);
317  ordinal_type spaceDim = cellTopo.getDimension();
318 
319  ViewType<PointValueType,DeviceType> inputPoints = getView<PointValueType,DeviceType>("input points",numPoints,spaceDim);
320  PointTools::getLattice(inputPoints, cellTopo, order, 0, POINTTYPE_EQUISPACED );
321 
322  return inputPoints;
323  }
324  }
325 
326  template<typename OutputValueType, typename DeviceType>
327  inline ViewType<OutputValueType,DeviceType> getOutputView(Intrepid2::EFunctionSpace fs, Intrepid2::EOperator op, int basisCardinality, int numPoints, int spaceDim)
328  {
329  switch (fs) {
330  case Intrepid2::FUNCTION_SPACE_HGRAD:
331  switch (op) {
332  case Intrepid2::OPERATOR_VALUE:
333  return getView<OutputValueType,DeviceType>("H^1 value output",basisCardinality,numPoints);
334  case Intrepid2::OPERATOR_GRAD:
335  return getView<OutputValueType,DeviceType>("H^1 derivative output",basisCardinality,numPoints,spaceDim);
336  case Intrepid2::OPERATOR_D1:
337  case Intrepid2::OPERATOR_D2:
338  case Intrepid2::OPERATOR_D3:
339  case Intrepid2::OPERATOR_D4:
340  case Intrepid2::OPERATOR_D5:
341  case Intrepid2::OPERATOR_D6:
342  case Intrepid2::OPERATOR_D7:
343  case Intrepid2::OPERATOR_D8:
344  case Intrepid2::OPERATOR_D9:
345  case Intrepid2::OPERATOR_D10:
346  {
347  const auto dkcard = getDkCardinality(op, spaceDim);
348  return getView<OutputValueType,DeviceType>("H^1 derivative output",basisCardinality,numPoints,dkcard);
349  }
350  default:
351  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported op/fs combination");
352  }
353  case Intrepid2::FUNCTION_SPACE_HCURL:
354  switch (op) {
355  case Intrepid2::OPERATOR_VALUE:
356  return getView<OutputValueType,DeviceType>("H(curl) value output",basisCardinality,numPoints,spaceDim);
357  case Intrepid2::OPERATOR_CURL:
358  if (spaceDim == 2)
359  return getView<OutputValueType,DeviceType>("H(curl) derivative output",basisCardinality,numPoints);
360  else if (spaceDim == 3)
361  return getView<OutputValueType,DeviceType>("H(curl) derivative output",basisCardinality,numPoints,spaceDim);
362  default:
363  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported op/fs combination");
364  }
365  case Intrepid2::FUNCTION_SPACE_HDIV:
366  switch (op) {
367  case Intrepid2::OPERATOR_VALUE:
368  return getView<OutputValueType,DeviceType>("H(div) value output",basisCardinality,numPoints,spaceDim);
369  case Intrepid2::OPERATOR_DIV:
370  return getView<OutputValueType,DeviceType>("H(div) derivative output",basisCardinality,numPoints);
371  default:
372  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported op/fs combination");
373  }
374 
375  case Intrepid2::FUNCTION_SPACE_HVOL:
376  switch (op) {
377  case Intrepid2::OPERATOR_VALUE:
378  return getView<OutputValueType,DeviceType>("H(vol) value output",basisCardinality,numPoints);
379  case Intrepid2::OPERATOR_D1:
380  case Intrepid2::OPERATOR_D2:
381  case Intrepid2::OPERATOR_D3:
382  case Intrepid2::OPERATOR_D4:
383  case Intrepid2::OPERATOR_D5:
384  case Intrepid2::OPERATOR_D6:
385  case Intrepid2::OPERATOR_D7:
386  case Intrepid2::OPERATOR_D8:
387  case Intrepid2::OPERATOR_D9:
388  case Intrepid2::OPERATOR_D10:
389  {
390  const auto dkcard = getDkCardinality(op, spaceDim);
391  return getView<OutputValueType,DeviceType>("H(vol) derivative output",basisCardinality,numPoints,dkcard);
392  }
393  default:
394  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported op/fs combination");
395  }
396  default:
397  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported op/fs combination");
398  }
399  }
400 
401  // ! This returns a vector whose entries are vector<int>s containing 1-3 polynomial orders from 1 up to and including those specified
402  // ! Intended for testing bases that support anisotropic polynomial degree, such as the hierarchical bases
403  inline std::vector< std::vector<int> > getBasisTestCasesUpToDegree(int spaceDim, int minDegree, int polyOrder_x, int polyOrder_y=-1, int polyOrder_z=-1)
404  {
405  std::vector<int> degrees(spaceDim);
406  degrees[0] = polyOrder_x;
407  if (spaceDim > 1) degrees[1] = polyOrder_y;
408  if (spaceDim > 2) degrees[2] = polyOrder_z;
409 
410  int numCases = degrees[0];
411  for (unsigned d=1; d<degrees.size(); d++)
412  {
413  INTREPID2_TEST_FOR_EXCEPTION(degrees[d] < minDegree, std::invalid_argument, "Unsupported degree/minDegree combination");
414  numCases = numCases * (degrees[d] + 1 - minDegree);
415  }
416  std::vector< std::vector<int> > subBasisDegreeTestCases(numCases);
417  for (int caseOrdinal=0; caseOrdinal<numCases; caseOrdinal++)
418  {
419  std::vector<int> subBasisDegrees(degrees.size());
420  int caseRemainder = caseOrdinal;
421  for (int d=degrees.size()-1; d>=0; d--)
422  {
423  int subBasisDegree = caseRemainder % (degrees[d] + 1 - minDegree);
424  caseRemainder = caseRemainder / (degrees[d] + 1 - minDegree);
425  subBasisDegrees[d] = subBasisDegree + minDegree;
426  }
427  subBasisDegreeTestCases[caseOrdinal] = subBasisDegrees;
428  }
429  return subBasisDegreeTestCases;
430  }
431 
433  template<class Functor, class Scalar, int rank>
434  typename ViewType<Scalar,DefaultTestDeviceType>::HostMirror copyFunctorToHostView(const Functor &deviceFunctor)
435  {
436  INTREPID2_TEST_FOR_EXCEPTION(rank != getFunctorRank(deviceFunctor), std::invalid_argument, "functor rank must match the template argument");
437 
438  using DeviceType = DefaultTestDeviceType;
439  ViewType<Scalar,DeviceType> view;
440  const std::string label = "functor copy";
441  const auto &f = deviceFunctor;
442  switch (rank)
443  {
444  case 0:
445  view = getView<Scalar,DeviceType>(label);
446  break;
447  case 1:
448  view = getView<Scalar,DeviceType>(label, f.extent_int(0));
449  break;
450  case 2:
451  view = getView<Scalar,DeviceType>(label, f.extent_int(0), f.extent_int(1));
452  break;
453  case 3:
454  view = getView<Scalar,DeviceType>(label, f.extent_int(0), f.extent_int(1), f.extent_int(2));
455  break;
456  case 4:
457  view = getView<Scalar,DeviceType>(label, f.extent_int(0), f.extent_int(1), f.extent_int(2), f.extent_int(3));
458  break;
459  case 5:
460  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));
461  break;
462  case 6:
463  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));
464  break;
465  case 7:
466  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));
467  break;
468  default:
469  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported functor rank");
470  }
471 
472  int entryCount = view.size();
473 
474  using ExecutionSpace = typename ViewType<Scalar,DeviceType>::execution_space;
475 
476  using ViewIteratorScalar = Intrepid2::ViewIterator<ViewType<Scalar,DeviceType>, Scalar>;
477  using FunctorIteratorScalar = FunctorIterator<Functor, Scalar, rank>;
478 
479  Kokkos::RangePolicy < ExecutionSpace > policy(0,entryCount);
480  Kokkos::parallel_for( policy,
481  KOKKOS_LAMBDA (const int &enumerationIndex )
482  {
483  ViewIteratorScalar vi(view);
484  FunctorIteratorScalar fi(f);
485 
486  vi.setEnumerationIndex(enumerationIndex);
487  fi.setEnumerationIndex(enumerationIndex);
488 
489  vi.set(fi.get());
490  }
491  );
492 
493  auto hostView = Kokkos::create_mirror_view(view);
494  Kokkos::deep_copy(hostView, view);
495  return hostView;
496  }
497 
498  template<class FunctorType, typename Scalar, int rank>
499  void printFunctor(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
500  {
501  auto functorHostCopy = copyFunctorToHostView<FunctorType, Scalar, rank>(functor);
502 
503  std::string name = (functorName == "") ? "Functor" : functorName;
504 
505  out << "\n******** " << name << " (rank " << rank << ") ********\n";
506  out << "dimensions: (";
507  for (int r=0; r<rank; r++)
508  {
509  out << functor.extent_int(r);
510  if (r<rank-1) out << ",";
511  }
512  out << ")\n";
513 
515 
516  bool moreEntries = true;
517  while (moreEntries)
518  {
519  Scalar value = vi.get();
520 
521  auto location = vi.getLocation();
522  out << functorName << "(";
523  for (ordinal_type i=0; i<rank; i++)
524  {
525  out << location[i];
526  if (i<rank-1)
527  {
528  out << ",";
529  }
530  }
531  out << ") " << value << std::endl;
532 
533  moreEntries = (vi.increment() != -1);
534  }
535  out << "\n";
536  }
537 
538  template<class FunctorType>
539  void printFunctor1(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
540  {
541  using Scalar = typename std::remove_reference<decltype(functor(0))>::type;
542  printFunctor<FunctorType, Scalar, 1>(functor, out, functorName);
543  }
544 
545  template<class FunctorType>
546  void printFunctor2(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
547  {
548  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0))>::type>::type;
549  printFunctor<FunctorType, Scalar, 2>(functor, out, functorName);
550  }
551 
552  template<class FunctorType>
553  void printFunctor3(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
554  {
555  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0,0))>::type>::type;
556  printFunctor<FunctorType, Scalar, 3>(functor, out, functorName);
557  }
558 
559  template<class FunctorType>
560  void printFunctor4(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
561  {
562  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0,0,0))>::type>::type;
563  printFunctor<FunctorType, Scalar, 4>(functor, out, functorName);
564  }
565 
566  template<class FunctorType>
567  void printFunctor5(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
568  {
569  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0,0,0,0))>::type>::type;
570  printFunctor<FunctorType, Scalar, 5>(functor, out, functorName);
571  }
572 
573  template<class FunctorType>
574  void printFunctor6(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
575  {
576  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0,0,0,0,0))>::type>::type;
577  printFunctor<FunctorType, Scalar, 6>(functor, out, functorName);
578  }
579 
580  template<class FunctorType>
581  void printFunctor7(const FunctorType &functor, std::ostream &out, const std::string &functorName = "")
582  {
583  using Scalar = typename std::remove_const<typename std::remove_reference<decltype(functor(0,0,0,0,0,0,0))>::type>::type;
584  printFunctor<FunctorType, Scalar, 7>(functor, out, functorName);
585  }
586 
587  template<class View>
588  void printView(const View &view, std::ostream &out, const std::string &viewName = "")
589  {
590  using Scalar = typename View::value_type;
591  using HostView = typename View::HostMirror;
592  using HostViewIteratorScalar = Intrepid2::ViewIterator<HostView, Scalar>;
593 
594  auto hostView = getHostCopy(view);
595 
596  HostViewIteratorScalar vi(hostView);
597 
598  bool moreEntries = (vi.nextIncrementRank() != -1);
599  while (moreEntries)
600  {
601  Scalar value = vi.get();
602 
603  auto location = vi.getLocation();
604  out << viewName << "(";
605  for (unsigned i=0; i<getFunctorRank(view); i++)
606  {
607  out << location[i];
608  if (i<getFunctorRank(view)-1)
609  {
610  out << ",";
611  }
612  }
613  out << ") " << value << std::endl;
614 
615  moreEntries = (vi.increment() != -1);
616  }
617  }
618 
619  template <class FunctorType1, class FunctorType2, int rank, typename Scalar=typename FunctorType1::value_type, class ExecutionSpace = typename FunctorType1::execution_space>
620  typename std::enable_if< !(supports_rank<FunctorType1,rank>::value && supports_rank<FunctorType2,rank>::value), void >::type
621  testFloatingEquality(const FunctorType1 &functor1, const FunctorType2 &functor2, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
622  std::string functor1Name = "Functor 1", std::string functor2Name = "Functor 2")
623  {
624  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "testFloatingEquality() called on FunctorType1 or FunctorType2 that does not support the specified rank.\n");
625  }
626 
628  template <class FunctorType1, class FunctorType2, int rank, typename Scalar=typename FunctorType1::value_type, class ExecutionSpace = typename FunctorType1::execution_space>
629  typename std::enable_if< (supports_rank<FunctorType1,rank>::value && supports_rank<FunctorType2,rank>::value), void >::type
630  testFloatingEquality(const FunctorType1 &functor1, const FunctorType2 &functor2, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
631  std::string functor1Name = "Functor 1", std::string functor2Name = "Functor 2")
632  {
633  static_assert( supports_rank<FunctorType1,rank>::value, "Functor1 must support the specified rank through operator().");
634  static_assert( supports_rank<FunctorType2,rank>::value, "Functor2 must support the specified rank through operator().");
635 
636  using Functor1IteratorScalar = FunctorIterator<FunctorType1, Scalar, rank>;
637  using Functor2IteratorScalar = FunctorIterator<FunctorType2, Scalar, rank>;
638 
639  // check that rank/size match
640  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
641  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
642 
643  int entryCount = 1;
644  for (unsigned i=0; i<getFunctorRank(functor1); i++)
645  {
646  TEUCHOS_TEST_FOR_EXCEPTION(functor1.extent_int(i) != functor2.extent_int(i), std::invalid_argument,
647  "functor1 and functor2 must agree in size in each dimension; functor1 has extent "
648  + std::to_string(functor1.extent_int(i)) + " in dimension " + std::to_string(i)
649  + "; functor2 has extent " + std::to_string(functor2.extent_int(i)) );
650  entryCount *= functor1.extent_int(i);
651  }
652  if (entryCount == 0) return; // nothing to test
653 
654  ViewType<bool,ExecutionSpace> valuesMatch = getView<bool,ExecutionSpace>("valuesMatch", entryCount);
655 
656  Kokkos::RangePolicy < ExecutionSpace > policy(0,entryCount);
657  Kokkos::parallel_for( policy,
658  KOKKOS_LAMBDA (const int &enumerationIndex )
659  {
660  Functor1IteratorScalar vi1(functor1);
661  Functor2IteratorScalar vi2(functor2);
662 
663  vi1.setEnumerationIndex(enumerationIndex);
664  vi2.setEnumerationIndex(enumerationIndex);
665 
666  const Scalar & value1 = vi1.get();
667  const Scalar & value2 = vi2.get();
668 
669  bool errMeetsTol = errMeetsAbsAndRelTol(value1, value2, relTol, absTol);
670  valuesMatch(enumerationIndex) = errMeetsTol;
671  }
672  );
673 
674  int failureCount = 0;
675  Kokkos::RangePolicy<ExecutionSpace > reducePolicy(0, entryCount);
676  Kokkos::parallel_reduce( reducePolicy,
677  KOKKOS_LAMBDA( const int &enumerationIndex, int &reducedValue )
678  {
679  reducedValue += valuesMatch(enumerationIndex) ? 0 : 1;
680  }, failureCount);
681 
682  const bool allValuesMatch = (failureCount == 0);
683  success = success && allValuesMatch;
684 
685  if (!allValuesMatch)
686  {
687  // copy functors to host views
688  auto functor1CopyHost = copyFunctorToHostView<FunctorType1,Scalar,rank>(functor1);
689  auto functor2CopyHost = copyFunctorToHostView<FunctorType2,Scalar,rank>(functor2);
690 
691  auto valuesMatchHost = getHostCopy(valuesMatch);
692 
696 
697  bool moreEntries = true;
698  while (moreEntries)
699  {
700  bool errMeetsTol = viMatch.get();
701 
702  if (!errMeetsTol)
703  {
704  const Scalar value1 = vi1.get();
705  const Scalar value2 = vi2.get();
706 
707  success = false;
708  auto location = vi1.getLocation();
709  out << "At location (";
710  for (unsigned i=0; i<getFunctorRank(functor1); i++)
711  {
712  out << location[i];
713  if (i<getFunctorRank(functor1)-1)
714  {
715  out << ",";
716  }
717  }
718  out << ") " << functor1Name << " value != " << functor2Name << " value (";
719  out << value1 << " != " << value2 << "); diff is " << std::abs(value1-value2) << std::endl;
720  }
721 
722  moreEntries = (vi1.increment() != -1);
723  moreEntries = moreEntries && (vi2.increment() != -1);
724  moreEntries = moreEntries && (viMatch.increment() != -1);
725  }
726  }
727  }
728 
729  template <class ViewType, class FunctorType>
730  void testFloatingEquality1(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
731  std::string view1Name = "View", std::string view2Name = "Functor")
732  {
733  testFloatingEquality<ViewType, FunctorType, 1>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
734  }
735 
736  template <class ViewType, class FunctorType>
737  void testFloatingEquality2(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
738  std::string view1Name = "View", std::string view2Name = "Functor")
739  {
740  testFloatingEquality<ViewType, FunctorType, 2>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
741  }
742 
743  template <class ViewType, class FunctorType>
744  void testFloatingEquality3(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
745  std::string view1Name = "View", std::string view2Name = "Functor")
746  {
747  testFloatingEquality<ViewType, FunctorType, 3>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
748  }
749 
750  template <class ViewType, class FunctorType>
751  void testFloatingEquality4(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
752  std::string view1Name = "View", std::string view2Name = "Functor")
753  {
754  testFloatingEquality<ViewType, FunctorType, 4>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
755  }
756 
757  template <class ViewType, class FunctorType>
758  void testFloatingEquality5(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
759  std::string view1Name = "View", std::string view2Name = "Functor")
760  {
761  testFloatingEquality<ViewType, FunctorType, 5>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
762  }
763 
764  template <class ViewType, class FunctorType>
765  void testFloatingEquality6(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
766  std::string view1Name = "View", std::string view2Name = "Functor")
767  {
768  testFloatingEquality<ViewType, FunctorType, 6>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
769  }
770 
771  template <class ViewType, class FunctorType>
772  void testFloatingEquality7(const ViewType &view, const FunctorType &functor, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
773  std::string view1Name = "View", std::string view2Name = "Functor")
774  {
775  testFloatingEquality<ViewType, FunctorType, 7>(view, functor, relTol, absTol, out, success, view1Name, view2Name);
776  }
777 
778  template <class ViewType1, class ViewType2>
779  void testViewFloatingEquality(const ViewType1 &view1, const ViewType2 &view2, double relTol, double absTol, Teuchos::FancyOStream &out, bool &success,
780  std::string view1Name = "View 1", std::string view2Name = "View 2")
781  {
782  // check that rank/size match
783  TEUCHOS_TEST_FOR_EXCEPTION(view1.rank() != view2.rank(), std::invalid_argument, "views must agree in rank");
784  for (unsigned i=0; i<view1.rank(); i++)
785  {
786  TEUCHOS_TEST_FOR_EXCEPTION(view1.extent_int(i) != view2.extent_int(i), std::invalid_argument, "views must agree in size in each dimension");
787  }
788 
789  if (view1.size() == 0) return; // nothing to test
790 
791  const int rank = view1.rank();
792  switch (rank)
793  {
794  case 0: testFloatingEquality<ViewType1, ViewType2, 0>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
795  case 1: testFloatingEquality<ViewType1, ViewType2, 1>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
796  case 2: testFloatingEquality<ViewType1, ViewType2, 2>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
797  case 3: testFloatingEquality<ViewType1, ViewType2, 3>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
798  case 4: testFloatingEquality<ViewType1, ViewType2, 4>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
799  case 5: testFloatingEquality<ViewType1, ViewType2, 5>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
800  case 6: testFloatingEquality<ViewType1, ViewType2, 6>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
801  case 7: testFloatingEquality<ViewType1, ViewType2, 7>(view1, view2, relTol, absTol, out, success, view1Name, view2Name); break;
802  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported rank");
803  }
804  }
805 
806 } // namespace Intrepid2
807 
808 #ifdef HAVE_INTREPID2_SACADO
809 using Sacado_Fad_DFadType = Sacado::Fad::DFad<double>;
810 #define INTREPID2_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
811 \
812 TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( GROUP_NAME, TEST_NAME, double ) \
813 \
814 TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( GROUP_NAME, TEST_NAME, Sacado_Fad_DFadType ) \
815 
816 #define INTREPID2_OUTPUTSCALAR_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
817 \
818 TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( GROUP_NAME, TEST_NAME, double, double ) \
819 \
820 TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( GROUP_NAME, TEST_NAME, Sacado_Fad_DFadType, Sacado_Fad_DFadType ) \
821 
822 #else
823 #define INTREPID2_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
824 \
825 TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( GROUP_NAME, TEST_NAME, double ) \
826 
827 #define INTREPID2_OUTPUTSCALAR_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
828 \
829 TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( GROUP_NAME, TEST_NAME, double, double ) \
830 
831 #endif
832 
833 #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.