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