49 #ifndef Intrepid2_TestUtils_h
50 #define Intrepid2_TestUtils_h
52 #include "Kokkos_Core.hpp"
53 #include "Kokkos_DynRankView.hpp"
61 #include "Teuchos_UnitTestHarness.hpp"
63 static const double TEST_TOLERANCE_TIGHT = 1.e2 * std::numeric_limits<double>::epsilon();
66 template<
typename ScalarType>
67 using ViewType = Kokkos::DynRankView<ScalarType,Kokkos::DefaultExecutionSpace>;
69 template<
typename ScalarType>
70 inline bool valuesAreSmall(ScalarType a, ScalarType b,
double epsilon)
73 return (abs(a) < epsilon) && (abs(b) < epsilon);
76 inline bool approximatelyEqual(
double a,
double b,
double epsilon)
78 const double larger_magnitude = (std::abs(a) < std::abs(b) ? std::abs(b) : std::abs(a));
79 return std::abs(a - b) <= larger_magnitude * epsilon;
82 inline bool essentiallyEqual(
double a,
double b,
double epsilon)
84 const double smaller_magnitude = (std::abs(a) > std::abs(b) ? std::abs(b) : std::abs(a));
85 return std::abs(a - b) <= smaller_magnitude * epsilon;
89 KOKKOS_INLINE_FUNCTION
double fromZeroOne(
double x_zero_one)
91 return x_zero_one * 2.0 - 1.0;
95 KOKKOS_INLINE_FUNCTION
double toZeroOne(
double x_minus_one_one)
97 return (x_minus_one_one + 1.0) / 2.0;
101 KOKKOS_INLINE_FUNCTION
double fromZeroOne_dx(
double dx_zero_one)
103 return dx_zero_one / 2.0;
107 KOKKOS_INLINE_FUNCTION
double toZeroOne_dx(
double dx_minus_one_one)
109 return dx_minus_one_one * 2.0;
112 template<
class DeviceViewType>
113 typename DeviceViewType::HostMirror getHostCopy(
const DeviceViewType &deviceView)
115 typename DeviceViewType::HostMirror hostView = Kokkos::create_mirror(deviceView);
116 Kokkos::deep_copy(hostView, deviceView);
120 template<
class BasisFamily>
121 inline Teuchos::RCP< Intrepid2::Basis<Kokkos::DefaultExecutionSpace,double,double> > getBasisUsingFamily(shards::CellTopology cellTopo, Intrepid2::EFunctionSpace fs,
122 int polyOrder_x,
int polyOrder_y=-1,
int polyOrder_z = -1)
124 using BasisPtr =
typename BasisFamily::BasisPtr;
127 using namespace Intrepid2;
129 if (cellTopo.getBaseKey() == shards::Line<>::key)
131 basis = getLineBasis<BasisFamily>(fs,polyOrder_x);
133 else if (cellTopo.getBaseKey() == shards::Quadrilateral<>::key)
135 INTREPID2_TEST_FOR_EXCEPTION(polyOrder_y < 0, std::invalid_argument,
"polyOrder_y must be specified");
136 basis = getQuadrilateralBasis<BasisFamily>(fs,polyOrder_x,polyOrder_y);
138 else if (cellTopo.getBaseKey() == shards::Triangle<>::key)
140 basis = getTriangleBasis<BasisFamily>(fs,polyOrder_x);
142 else if (cellTopo.getBaseKey() == shards::Hexahedron<>::key)
144 INTREPID2_TEST_FOR_EXCEPTION(polyOrder_y < 0, std::invalid_argument,
"polyOrder_y must be specified");
145 INTREPID2_TEST_FOR_EXCEPTION(polyOrder_z < 0, std::invalid_argument,
"polyOrder_z must be specified");
146 basis = getHexahedronBasis<BasisFamily>(fs,polyOrder_x,polyOrder_y,polyOrder_z);
148 else if (cellTopo.getBaseKey() == shards::Tetrahedron<>::key)
150 basis = getTetrahedronBasis<BasisFamily>(fs, polyOrder_x);
154 INTREPID2_TEST_FOR_EXCEPTION(
true, std::invalid_argument,
"Unsupported cell topology");
159 template<
bool defineVertexFunctions>
160 inline Teuchos::RCP< Intrepid2::Basis<Kokkos::DefaultExecutionSpace,double,double> > getHierarchicalBasis(shards::CellTopology cellTopo, Intrepid2::EFunctionSpace fs,
161 int polyOrder_x,
int polyOrder_y=-1,
int polyOrder_z = -1)
163 using ExecSpace = Kokkos::DefaultExecutionSpace;
164 using Scalar = double;
165 using namespace Intrepid2;
174 return getBasisUsingFamily<BasisFamily>(cellTopo, fs, polyOrder_x, polyOrder_y, polyOrder_z);
177 template<
typename ValueType,
class ... DimArgs>
178 inline ViewType<ValueType> getView(
const std::string &label, DimArgs... dims)
180 const bool allocateFadStorage = !std::is_pod<ValueType>::value;
181 constexpr
int maxDerivatives = 3;
182 if (!allocateFadStorage)
184 return ViewType<ValueType>(label,dims...);
188 return ViewType<ValueType>(label,dims...,maxDerivatives+1);
195 template <
typename Po
intValueType>
198 ViewType<PointValueType> inputPoints = getView<PointValueType>(
"line input points",numPoints,1);
199 Kokkos::parallel_for(numPoints, KOKKOS_LAMBDA(
const int i)
201 double x_zero_one = i * 1.0 / (numPoints-1);
202 inputPoints(i,0) = PointValueType(fromZeroOne(x_zero_one));
212 template <
typename Po
intValueType>
215 ViewType<PointValueType> inputPoints = getView<PointValueType>(
"hex input points",numPoints_1D*numPoints_1D*numPoints_1D,3);
216 Kokkos::parallel_for(numPoints_1D, KOKKOS_LAMBDA(
const int i)
218 double x_zero_one = i * 1.0 / (numPoints_1D-1);
219 for (
int j=0; j<numPoints_1D; j++)
221 double y_zero_one = j * 1.0 / (numPoints_1D-1);
222 for (
int k=0; k<numPoints_1D; k++)
224 double z_zero_one = k * 1.0 / (numPoints_1D-1);
225 int pointOrdinal = (i*numPoints_1D+j)*numPoints_1D+k;
226 inputPoints(pointOrdinal,0) = PointValueType(fromZeroOne(x_zero_one));
227 inputPoints(pointOrdinal,1) = PointValueType(fromZeroOne(y_zero_one));
228 inputPoints(pointOrdinal,2) = PointValueType(fromZeroOne(z_zero_one));
240 template <
typename Po
intValueType>
243 ViewType<PointValueType> inputPoints = getView<PointValueType>(
"quad input points",numPoints_1D*numPoints_1D,2);
245 Kokkos::parallel_for(numPoints_1D, KOKKOS_LAMBDA(
const int i)
247 double x_zero_one = i * 1.0 / (numPoints_1D-1);
248 for (
int j=0; j<numPoints_1D; j++)
250 double y_zero_one = j * 1.0 / (numPoints_1D-1);
251 int pointOrdinal = i*numPoints_1D+j;
252 inputPoints(pointOrdinal,0) = PointValueType(fromZeroOne(x_zero_one));
253 inputPoints(pointOrdinal,1) = PointValueType(fromZeroOne(y_zero_one));
264 template <
typename Po
intValueType>
267 const int numPoints = numPointsBase*(numPointsBase+1)*(numPointsBase+2)/6;
268 ViewType<PointValueType> inputPoints = getView<PointValueType>(
"tetrahedron input points",numPoints,3);
269 Kokkos::parallel_for(numPointsBase, KOKKOS_LAMBDA(
const int d0)
275 int pointOrdinalOffset = 0;
276 for (
int i=0; i<d0; i++)
278 for (
int j=0; j<numPointsBase-i; j++)
280 pointOrdinalOffset += (numPointsBase - i - j);
284 double z_zero_one = d0 * 1.0 / (numPointsBase-1);
285 int pointOrdinal = pointOrdinalOffset;
286 for (
int d1=0; d1<numPointsBase-d0; d1++)
288 double y_zero_one = d1 * 1.0 / (numPointsBase-1);
289 for (
int d2=0; d2<numPointsBase-d0-d1; d2++)
291 double x_zero_one = d2 * 1.0 / (numPointsBase-1);
293 inputPoints(pointOrdinal,0) = PointValueType(x_zero_one);
294 inputPoints(pointOrdinal,1) = PointValueType(y_zero_one);
295 inputPoints(pointOrdinal,2) = PointValueType(z_zero_one);
309 template <
typename Po
intValueType>
312 const int numPoints = numPointsBase*(numPointsBase+1)/2;
313 ViewType<PointValueType> inputPoints = getView<PointValueType>(
"triangle input points",numPoints,2);
314 Kokkos::parallel_for(numPointsBase, KOKKOS_LAMBDA(
const int row)
316 int rowPointOrdinalOffset = 0;
317 for (
int i=0; i<row; i++)
319 rowPointOrdinalOffset += (numPointsBase - i);
321 double y_zero_one = row * 1.0 / (numPointsBase-1);
322 for (
int col=0; col<numPointsBase-row; col++)
324 const int pointOrdinal = rowPointOrdinalOffset + col;
325 double x_zero_one = col * 1.0 / (numPointsBase-1);
326 inputPoints(pointOrdinal,0) = PointValueType(x_zero_one);
327 inputPoints(pointOrdinal,1) = PointValueType(y_zero_one);
333 template <
typename Po
intValueType>
334 inline ViewType<PointValueType> getInputPointsView(shards::CellTopology &cellTopo,
int numPoints_1D)
336 if (cellTopo.getBaseKey() == shards::Line<>::key)
338 return lineInputPointsView<PointValueType>(numPoints_1D);
340 else if (cellTopo.getBaseKey() == shards::Quadrilateral<>::key)
342 return quadInputPointsView<PointValueType>(numPoints_1D);
344 else if (cellTopo.getBaseKey() == shards::Hexahedron<>::key)
346 return hexInputPointsView<PointValueType>(numPoints_1D);
348 else if (cellTopo.getBaseKey() == shards::Triangle<>::key)
350 return triInputPointsView<PointValueType>(numPoints_1D);
352 else if (cellTopo.getBaseKey() == shards::Tetrahedron<>::key)
354 return tetInputPointsView<PointValueType>(numPoints_1D);
358 INTREPID2_TEST_FOR_EXCEPTION(
true, std::invalid_argument,
"Unsupported Cell Topology");
362 template<
typename OutputValueType>
363 inline ViewType<OutputValueType> getOutputView(Intrepid2::EFunctionSpace fs, Intrepid2::EOperator op,
int basisCardinality,
int numPoints,
int spaceDim)
366 case Intrepid2::FUNCTION_SPACE_HGRAD:
368 case Intrepid2::OPERATOR_VALUE:
369 return getView<OutputValueType>(
"H^1 value output",basisCardinality,numPoints);
370 case Intrepid2::OPERATOR_GRAD:
371 return getView<OutputValueType>(
"H^1 derivative output",basisCardinality,numPoints,spaceDim);
372 case Intrepid2::OPERATOR_D1:
373 case Intrepid2::OPERATOR_D2:
374 case Intrepid2::OPERATOR_D3:
375 case Intrepid2::OPERATOR_D4:
376 case Intrepid2::OPERATOR_D5:
377 case Intrepid2::OPERATOR_D6:
378 case Intrepid2::OPERATOR_D7:
379 case Intrepid2::OPERATOR_D8:
380 case Intrepid2::OPERATOR_D9:
381 case Intrepid2::OPERATOR_D10:
383 const auto dkcard = getDkCardinality(op, spaceDim);
384 return getView<OutputValueType>(
"H^1 derivative output",basisCardinality,numPoints,dkcard);
387 INTREPID2_TEST_FOR_EXCEPTION(
true, std::invalid_argument,
"Unsupported op/fs combination");
389 case Intrepid2::FUNCTION_SPACE_HCURL:
391 case Intrepid2::OPERATOR_VALUE:
392 return getView<OutputValueType>(
"H(curl) value output",basisCardinality,numPoints,spaceDim);
393 case Intrepid2::OPERATOR_CURL:
395 return getView<OutputValueType>(
"H(curl) derivative output",basisCardinality,numPoints);
396 else if (spaceDim == 3)
397 return getView<OutputValueType>(
"H(curl) derivative output",basisCardinality,numPoints,spaceDim);
399 INTREPID2_TEST_FOR_EXCEPTION(
true, std::invalid_argument,
"Unsupported op/fs combination");
401 case Intrepid2::FUNCTION_SPACE_HDIV:
403 case Intrepid2::OPERATOR_VALUE:
404 return getView<OutputValueType>(
"H(div) value output",basisCardinality,numPoints,spaceDim);
405 case Intrepid2::OPERATOR_DIV:
406 return getView<OutputValueType>(
"H(div) derivative output",basisCardinality,numPoints);
408 INTREPID2_TEST_FOR_EXCEPTION(
true, std::invalid_argument,
"Unsupported op/fs combination");
411 case Intrepid2::FUNCTION_SPACE_HVOL:
413 case Intrepid2::OPERATOR_VALUE:
414 return getView<OutputValueType>(
"H(vol) value output",basisCardinality,numPoints);
415 case Intrepid2::OPERATOR_D1:
416 case Intrepid2::OPERATOR_D2:
417 case Intrepid2::OPERATOR_D3:
418 case Intrepid2::OPERATOR_D4:
419 case Intrepid2::OPERATOR_D5:
420 case Intrepid2::OPERATOR_D6:
421 case Intrepid2::OPERATOR_D7:
422 case Intrepid2::OPERATOR_D8:
423 case Intrepid2::OPERATOR_D9:
424 case Intrepid2::OPERATOR_D10:
426 const auto dkcard = getDkCardinality(op, spaceDim);
427 return getView<OutputValueType>(
"H(vol) derivative output",basisCardinality,numPoints,dkcard);
430 INTREPID2_TEST_FOR_EXCEPTION(
true, std::invalid_argument,
"Unsupported op/fs combination");
433 INTREPID2_TEST_FOR_EXCEPTION(
true, std::invalid_argument,
"Unsupported op/fs combination");
439 inline std::vector< std::vector<int> > getBasisTestCasesUpToDegree(
int spaceDim,
int minDegree,
int polyOrder_x,
int polyOrder_y=-1,
int polyOrder_z = -1)
441 std::vector<int> degrees(spaceDim);
442 degrees[0] = polyOrder_x;
443 if (spaceDim > 1) degrees[1] = polyOrder_y;
444 if (spaceDim > 2) degrees[2] = polyOrder_z;
446 int numCases = degrees[0];
447 for (
unsigned d=1; d<degrees.size(); d++)
449 numCases = numCases * (degrees[d] + 1 - minDegree);
451 std::vector< std::vector<int> > subBasisDegreeTestCases(numCases);
452 for (
int caseOrdinal=0; caseOrdinal<numCases; caseOrdinal++)
454 std::vector<int> subBasisDegrees(degrees.size());
455 int caseRemainder = caseOrdinal;
456 for (
int d=degrees.size()-1; d>=0; d--)
458 int subBasisDegree = caseRemainder % (degrees[d] + 1 - minDegree);
459 caseRemainder = caseRemainder / (degrees[d] + 1 - minDegree);
460 subBasisDegrees[d] = subBasisDegree + minDegree;
462 subBasisDegreeTestCases[caseOrdinal] = subBasisDegrees;
464 return subBasisDegreeTestCases;
467 template <
class ViewType>
468 void testViewFloatingEquality(ViewType &view1, ViewType &view2,
double relTol,
double absTol, Teuchos::FancyOStream &out,
bool &success,
469 std::string view1Name =
"View 1", std::string view2Name =
"View 2")
471 using Scalar =
typename ViewType::value_type;
472 using HostView =
typename ViewType::HostMirror;
475 auto hostView1 = getHostCopy(view1);
476 auto hostView2 = getHostCopy(view2);
479 TEUCHOS_TEST_FOR_EXCEPTION(view1.rank() != view2.rank(), std::invalid_argument,
"views must agree in rank");
480 for (
unsigned i=0; i<view1.rank(); i++)
482 TEUCHOS_TEST_FOR_EXCEPTION(view1.extent_int(i) != view2.extent_int(i), std::invalid_argument,
"views must agree in size in each dimension");
485 if (view1.size() == 0)
return;
487 HostViewIteratorScalar vi1(hostView1);
488 HostViewIteratorScalar vi2(hostView2);
490 double maxDiff = 0.0;
491 double maxRelativeDiff = 0.0;
492 bool differencesFound =
false;
494 bool moreEntries = (vi1.nextIncrementRank() != -1) && (vi2.nextIncrementRank() != -1);
497 Scalar value1 = vi1.get();
498 Scalar value2 = vi2.get();
500 const bool small = valuesAreSmall(value1, value2, absTol);
501 const bool valuesMatch = small || essentiallyEqual(value1, value2, relTol);
505 differencesFound =
true;
507 auto location = vi1.getLocation();
508 out <<
"At location (";
509 for (
unsigned i=0; i<view1.rank(); i++)
512 if (i<view1.rank()-1)
517 out <<
") " << view1Name <<
" value != " << view2Name <<
" value (";
518 out << value1 <<
" != " << value2 <<
"); diff is " << std::abs(value1-value2) << std::endl;
520 maxDiff = std::max(maxDiff, std::abs(value1-value2));
521 double smallerMagnitude = (std::abs(value1) > std::abs(value2) ? std::abs(value2) : std::abs(value1));
522 if (smallerMagnitude > 0)
524 const double relativeDiff = std::abs(value1 - value2) / smallerMagnitude;
525 maxRelativeDiff = std::max(maxRelativeDiff, relativeDiff);
529 moreEntries = (vi1.increment() != -1);
530 moreEntries = moreEntries && (vi2.increment() != -1);
533 if (differencesFound)
535 out <<
"max difference = " << maxDiff << std::endl;
536 out <<
"max relative difference = " << maxRelativeDiff << std::endl;
540 #ifdef HAVE_INTREPID2_SACADO
541 #include <Sacado.hpp>
542 using Sacado_Fad_DFadType = Sacado::Fad::DFad<double>;
543 #define INTREPID2_OUTPUTSCALAR_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
545 TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( GROUP_NAME, TEST_NAME, double, double ) \
547 TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( GROUP_NAME, TEST_NAME, Sacado_Fad_DFadType, Sacado_Fad_DFadType ) \
550 #define INTREPID2_OUTPUTSCALAR_POINTSCALAR_TEST_INSTANT(GROUP_NAME, TEST_NAME) \
552 TEUCHOS_UNIT_TEST_TEMPLATE_2_INSTANT( GROUP_NAME, TEST_NAME, double, double ) \
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...
ViewType< PointValueType > quadInputPointsView(int numPoints_1D)
Returns a View containing equispaced points on the quadrilateral.
ViewType< PointValueType > hexInputPointsView(int numPoints_1D)
Returns a View containing equispaced points on the hexahedron.
Header function for Intrepid2::Util class and other utility functions.
ViewType< PointValueType > tetInputPointsView(int numPointsBase)
Returns a View containing regularly-spaced points on the tetrahedron.
ViewType< PointValueType > triInputPointsView(int numPointsBase)
Returns a View containing regularly-spaced points on the triangle.
Basis defining Legendre basis on the line, a polynomial subspace of L^2 (a.k.a. H(vol)) on the line...
ViewType< PointValueType > lineInputPointsView(int numPoints)
Returns a View containing equispaced points on the line.
Basis defining integrated Legendre basis on the line, a polynomial subspace of H(grad) on the line...
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...
Header file for the abstract base class Intrepid2::Basis.