//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //============================================================================ #include #include #include #include #include #include #include #include #include #include #define CHECK_CALL(call) \ VTKM_TEST_ASSERT((call) == vtkm::ErrorCode::Success, "Call resulted in error.") namespace { std::mt19937 g_RandomGenerator; // Establish simple mapping between world and parametric coordinates. // Actual world/parametric coordinates are in a different test. template vtkm::Vec ParametricToWorld(const vtkm::Vec& pcoord) { return T(2) * pcoord - vtkm::Vec(0.25f); } template vtkm::Vec WorldToParametric(const vtkm::Vec& wcoord) { return T(0.5) * (wcoord + vtkm::Vec(0.25f)); } /// Simple structure describing a linear field. Has a convenience class /// for getting values. template struct LinearField { vtkm::Vec Gradient; FieldType OriginValue; template FieldType GetValue(vtkm::Vec coordinates) const { return static_cast((coordinates[0] * this->Gradient[0] + coordinates[1] * this->Gradient[1] + coordinates[2] * this->Gradient[2]) + this->OriginValue); } }; static constexpr vtkm::IdComponent MAX_POINTS = 8; template void GetMinMaxPoints(CellShapeTag, vtkm::CellTraitsTagSizeFixed, vtkm::IdComponent& minPoints, vtkm::IdComponent& maxPoints) { // If this line fails, then MAX_POINTS is not large enough to support all // cell shapes. VTKM_STATIC_ASSERT((vtkm::CellTraits::NUM_POINTS <= MAX_POINTS)); minPoints = maxPoints = vtkm::CellTraits::NUM_POINTS; } template void GetMinMaxPoints(CellShapeTag, vtkm::CellTraitsTagSizeVariable, vtkm::IdComponent& minPoints, vtkm::IdComponent& maxPoints) { minPoints = 1; maxPoints = MAX_POINTS; } template struct TestDerivativeFunctor { template void DoTestWithWCoords(CellShapeTag shape, const WCoordsVecType worldCoordinates, LinearField field, vtkm::Vec expectedGradient) const { vtkm::IdComponent numPoints = worldCoordinates.GetNumberOfComponents(); vtkm::VecVariable fieldValues; for (vtkm::IdComponent pointIndex = 0; pointIndex < numPoints; pointIndex++) { vtkm::Vec3f wcoords = worldCoordinates[pointIndex]; FieldType value = static_cast(field.GetValue(wcoords)); fieldValues.Append(value); } std::uniform_real_distribution randomDist; for (vtkm::IdComponent trial = 0; trial < 5; trial++) { // Generate a random pcoords that we know is in the cell. vtkm::Vec3f pcoords(0); vtkm::FloatDefault totalWeight = 0; for (vtkm::IdComponent pointIndex = 0; pointIndex < numPoints; pointIndex++) { vtkm::Vec3f pointPcoords; CHECK_CALL( vtkm::exec::ParametricCoordinatesPoint(numPoints, pointIndex, shape, pointPcoords)); vtkm::FloatDefault weight = randomDist(g_RandomGenerator); pcoords = pcoords + weight * pointPcoords; totalWeight += weight; } pcoords = (1 / totalWeight) * pcoords; vtkm::Vec computedGradient; CHECK_CALL(vtkm::exec::CellDerivative( fieldValues, worldCoordinates, pcoords, shape, computedGradient)); // Note that some gradients (particularly those near the center of // polygons with 5 or more points) are not very precise. Thus the // tolarance of the test_equal is raised. VTKM_TEST_ASSERT(test_equal(computedGradient, expectedGradient, 0.01), "Gradient is not as expected."); } } template void DoTest(CellShapeTag shape, vtkm::IdComponent numPoints, LinearField field, vtkm::Vec expectedGradient) const { vtkm::VecVariable worldCoordinates; for (vtkm::IdComponent pointIndex = 0; pointIndex < numPoints; pointIndex++) { vtkm::Vec3f pcoords; CHECK_CALL(vtkm::exec::ParametricCoordinatesPoint(numPoints, pointIndex, shape, pcoords)); vtkm::Vec3f wcoords = ParametricToWorld(pcoords); VTKM_TEST_ASSERT(test_equal(pcoords, WorldToParametric(wcoords)), "Test world/parametric conversion broken."); worldCoordinates.Append(wcoords); } this->DoTestWithWCoords(shape, worldCoordinates, field, expectedGradient); } template void DoTest(CellShapeTag shape, vtkm::IdComponent numPoints, vtkm::IdComponent topDim) const { LinearField field; vtkm::Vec expectedGradient; using FieldTraits = vtkm::VecTraits; using FieldComponentType = typename FieldTraits::ComponentType; // Correct topDim for polygons with fewer than 3 points. if (topDim > numPoints - 1) { topDim = numPoints - 1; } for (vtkm::IdComponent fieldComponent = 0; fieldComponent < FieldTraits::GetNumberOfComponents(FieldType()); fieldComponent++) { FieldTraits::SetComponent(field.OriginValue, fieldComponent, 0.0); } field.Gradient = vtkm::make_Vec(FieldType(1.0f), FieldType(1.0f), FieldType(1.0f)); expectedGradient[0] = ((topDim > 0) ? field.Gradient[0] : FieldType(0)); expectedGradient[1] = ((topDim > 1) ? field.Gradient[1] : FieldType(0)); expectedGradient[2] = ((topDim > 2) ? field.Gradient[2] : FieldType(0)); this->DoTest(shape, numPoints, field, expectedGradient); for (vtkm::IdComponent fieldComponent = 0; fieldComponent < FieldTraits::GetNumberOfComponents(FieldType()); fieldComponent++) { FieldTraits::SetComponent(field.OriginValue, fieldComponent, FieldComponentType(-7.0f)); } field.Gradient = vtkm::make_Vec(FieldType(0.25f), FieldType(14.0f), FieldType(11.125f)); expectedGradient[0] = ((topDim > 0) ? field.Gradient[0] : FieldType(0)); expectedGradient[1] = ((topDim > 1) ? field.Gradient[1] : FieldType(0)); expectedGradient[2] = ((topDim > 2) ? field.Gradient[2] : FieldType(0)); this->DoTest(shape, numPoints, field, expectedGradient); for (vtkm::IdComponent fieldComponent = 0; fieldComponent < FieldTraits::GetNumberOfComponents(FieldType()); fieldComponent++) { FieldTraits::SetComponent(field.OriginValue, fieldComponent, FieldComponentType(5.0f)); } field.Gradient = vtkm::make_Vec(FieldType(-11.125f), FieldType(-0.25f), FieldType(14.0f)); expectedGradient[0] = ((topDim > 0) ? field.Gradient[0] : FieldType(0)); expectedGradient[1] = ((topDim > 1) ? field.Gradient[1] : FieldType(0)); expectedGradient[2] = ((topDim > 2) ? field.Gradient[2] : FieldType(0)); this->DoTest(shape, numPoints, field, expectedGradient); std::uniform_real_distribution randomDist(-20.0f, 20.0f); for (vtkm::IdComponent fieldComponent = 0; fieldComponent < FieldTraits::GetNumberOfComponents(FieldType()); fieldComponent++) { FieldTraits::SetComponent(field.OriginValue, fieldComponent, randomDist(g_RandomGenerator)); FieldTraits::SetComponent(field.Gradient[0], fieldComponent, randomDist(g_RandomGenerator)); FieldTraits::SetComponent(field.Gradient[1], fieldComponent, randomDist(g_RandomGenerator)); FieldTraits::SetComponent(field.Gradient[2], fieldComponent, randomDist(g_RandomGenerator)); } expectedGradient[0] = ((topDim > 0) ? field.Gradient[0] : FieldType(0)); expectedGradient[1] = ((topDim > 1) ? field.Gradient[1] : FieldType(0)); expectedGradient[2] = ((topDim > 2) ? field.Gradient[2] : FieldType(0)); this->DoTest(shape, numPoints, field, expectedGradient); } template void operator()(CellShapeTag) const { vtkm::IdComponent minPoints; vtkm::IdComponent maxPoints; GetMinMaxPoints( CellShapeTag(), typename vtkm::CellTraits::IsSizeFixed(), minPoints, maxPoints); std::cout << "--- Test shape tag directly" << std::endl; for (vtkm::IdComponent numPoints = minPoints; numPoints <= maxPoints; numPoints++) { this->DoTest( CellShapeTag(), numPoints, vtkm::CellTraits::TOPOLOGICAL_DIMENSIONS); } std::cout << "--- Test generic shape tag" << std::endl; vtkm::CellShapeTagGeneric genericShape(CellShapeTag::Id); for (vtkm::IdComponent numPoints = minPoints; numPoints <= maxPoints; numPoints++) { this->DoTest(genericShape, numPoints, vtkm::CellTraits::TOPOLOGICAL_DIMENSIONS); } } void operator()(vtkm::CellShapeTagEmpty) const { std::cout << "Skipping empty cell shape. No derivative." << std::endl; } }; void TestDerivative() { vtkm::UInt32 seed = static_cast(std::time(nullptr)); std::cout << "Seed: " << seed << std::endl; g_RandomGenerator.seed(seed); std::cout << "======== Float32 ==========================" << std::endl; vtkm::testing::Testing::TryAllCellShapes(TestDerivativeFunctor()); std::cout << "======== Float64 ==========================" << std::endl; vtkm::testing::Testing::TryAllCellShapes(TestDerivativeFunctor()); std::cout << "======== Vec ===================" << std::endl; vtkm::testing::Testing::TryAllCellShapes(TestDerivativeFunctor()); std::cout << "======== Vec ===================" << std::endl; vtkm::testing::Testing::TryAllCellShapes(TestDerivativeFunctor()); std::uniform_real_distribution randomDist(-20.0, 20.0); vtkm::Vec3f origin = vtkm::Vec3f(0.25f, 0.25f, 0.25f); vtkm::Vec3f spacing = vtkm::Vec3f(2.0f, 2.0f, 2.0f); LinearField scalarField; scalarField.OriginValue = randomDist(g_RandomGenerator); scalarField.Gradient = vtkm::make_Vec( randomDist(g_RandomGenerator), randomDist(g_RandomGenerator), randomDist(g_RandomGenerator)); vtkm::Vec3f_64 expectedScalarGradient = scalarField.Gradient; TestDerivativeFunctor testFunctorScalar; std::cout << "======== Uniform Point Coordinates 3D =====" << std::endl; testFunctorScalar.DoTestWithWCoords(vtkm::CellShapeTagHexahedron(), vtkm::VecAxisAlignedPointCoordinates<3>(origin, spacing), scalarField, expectedScalarGradient); std::cout << "======== Uniform Point Coordinates 2D =====" << std::endl; expectedScalarGradient[2] = 0.0; testFunctorScalar.DoTestWithWCoords(vtkm::CellShapeTagQuad(), vtkm::VecAxisAlignedPointCoordinates<2>(origin, spacing), scalarField, expectedScalarGradient); std::cout << "======== Uniform Point Coordinates 1D =====" << std::endl; expectedScalarGradient[1] = 0.0; testFunctorScalar.DoTestWithWCoords(vtkm::CellShapeTagLine(), vtkm::VecAxisAlignedPointCoordinates<1>(origin, spacing), scalarField, expectedScalarGradient); LinearField vectorField; vectorField.OriginValue = vtkm::make_Vec( randomDist(g_RandomGenerator), randomDist(g_RandomGenerator), randomDist(g_RandomGenerator)); vectorField.Gradient = vtkm::make_Vec( vtkm::make_Vec( randomDist(g_RandomGenerator), randomDist(g_RandomGenerator), randomDist(g_RandomGenerator)), vtkm::make_Vec( randomDist(g_RandomGenerator), randomDist(g_RandomGenerator), randomDist(g_RandomGenerator)), vtkm::make_Vec( randomDist(g_RandomGenerator), randomDist(g_RandomGenerator), randomDist(g_RandomGenerator))); vtkm::Vec expectedVectorGradient = vectorField.Gradient; TestDerivativeFunctor testFunctorVector; std::cout << "======== Uniform Point Coordinates 3D =====" << std::endl; testFunctorVector.DoTestWithWCoords(vtkm::CellShapeTagHexahedron(), vtkm::VecAxisAlignedPointCoordinates<3>(origin, spacing), vectorField, expectedVectorGradient); std::cout << "======== Uniform Point Coordinates 2D =====" << std::endl; expectedVectorGradient[2] = vtkm::Vec3f_64(0.0); testFunctorVector.DoTestWithWCoords(vtkm::CellShapeTagQuad(), vtkm::VecAxisAlignedPointCoordinates<2>(origin, spacing), vectorField, expectedVectorGradient); std::cout << "======== Uniform Point Coordinates 1D =====" << std::endl; expectedVectorGradient[1] = vtkm::Vec3f_64(0.0); testFunctorVector.DoTestWithWCoords(vtkm::CellShapeTagLine(), vtkm::VecAxisAlignedPointCoordinates<1>(origin, spacing), vectorField, expectedVectorGradient); } } // anonymous namespace int UnitTestCellDerivative(int argc, char* argv[]) { return vtkm::cont::testing::Testing::Run(TestDerivative, argc, argv); }