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
48
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"
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
65namespace Intrepid2
66{
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::Experimental::HIP,Kokkos::Experimental::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
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
226 using LineBasisGrad = Intrepid2::IntegratedLegendreBasis_HGRAD_LINE<DeviceType, Scalar, Scalar, defineVertexFunctions, true>;
227 using LineBasisVol = Intrepid2::LegendreBasis_HVOL_LINE< DeviceType, Scalar, Scalar>;
228 using TriangleBasisFamily = Intrepid2::HierarchicalTriangleBasisFamily<DeviceType, Scalar, Scalar, defineVertexFunctions>;
229 using TetrahedronBasisFamily = Intrepid2::HierarchicalTetrahedronBasisFamily<DeviceType, Scalar, Scalar, defineVertexFunctions>;
230 using PyramidBasisFamily = Intrepid2::HierarchicalPyramidBasisFamily<DeviceType, Scalar, Scalar, defineVertexFunctions>;
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
514 ViewIterator<decltype(functorHostCopy),Scalar> vi(functorHostCopy);
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
693 FunctorIterator<decltype(functor1CopyHost),Scalar,rank> vi1(functor1CopyHost);
694 FunctorIterator<decltype(functor2CopyHost),Scalar,rank> vi2(functor2CopyHost);
695 Intrepid2::ViewIterator<decltype(valuesMatchHost), bool> viMatch(valuesMatchHost);
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
809using Sacado_Fad_DFadType = Sacado::Fad::DFad<double>;
810#define INTREPID2_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
811\
812TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( GROUP_NAME, TEST_NAME, double ) \
813\
814TEUCHOS_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\
818TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( GROUP_NAME, TEST_NAME, double, double ) \
819\
820TEUCHOS_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\
825TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( GROUP_NAME, TEST_NAME, double ) \
826
827#define INTREPID2_OUTPUTSCALAR_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
828\
829TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( GROUP_NAME, TEST_NAME, double, double ) \
830
831#endif
832
833#endif /* Intrepid2_TestUtils_h */
Header file for the abstract base class Intrepid2::Basis.
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.
Teuchos::RCP< Basis< DeviceType, OutputType, PointType > > BasisPtr
Basis Pointer.
Stateless class representing a family of basis functions, templated on H(vol) and H(grad) on the line...
static BasisFamily::BasisPtr getWedgeBasis(Intrepid2::EFunctionSpace fs, int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Factory method for isotropic wedge bases in the given family.
static BasisFamily::BasisPtr getHexahedronBasis(Intrepid2::EFunctionSpace fs, int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Factory method for isotropic bases on the hexahedron in the given family.
static BasisFamily::BasisPtr getQuadrilateralBasis(Intrepid2::EFunctionSpace fs, int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Factory method for isotropic quadrilateral bases in the given family.
static BasisFamily::BasisPtr getTetrahedronBasis(Intrepid2::EFunctionSpace fs, int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Factory method for isotropic tetrahedron bases in the given family.
static BasisFamily::BasisPtr getTriangleBasis(Intrepid2::EFunctionSpace fs, int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Factory method for isotropic triangle bases in the given family.
static BasisFamily::BasisPtr getPyramidBasis(Intrepid2::EFunctionSpace fs, ordinal_type polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Factory method for pyramid bases in the given family.
static BasisFamily::BasisPtr getLineBasis(Intrepid2::EFunctionSpace fs, int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Factory method for line bases in the given family.
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< Scalar, DefaultTestDeviceType >::HostMirror copyFunctorToHostView(const Functor &deviceFunctor)
Copy the values for the specified functor.
typename Kokkos::DefaultExecutionSpace::device_type DefaultTestDeviceType
Default Kokkos::Device to use for tests; depends on platform.
ViewType< PointValueType, DeviceType > getInputPointsView(shards::CellTopology &cellTopo, int numPoints_1D)
Returns a DynRankView containing regularly-spaced points on the specified cell topology.
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...
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...
Helper to get Scalar[*+] where the number of *'s matches the given rank.