//============================================================================ // 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. // // Copyright 2014 Sandia Corporation. // Copyright 2014 UT-Battelle, LLC. // Copyright 2014. Los Alamos National Security // // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, // the U.S. Government retains certain rights in this software. // // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National // Laboratory (LANL), the U.S. Government retains certain rights in // this software. //============================================================================ #ifndef vtk_m_cont_testing_TestingDeviceAdapter_h #define vtk_m_cont_testing_TestingDeviceAdapter_h #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #endif namespace vtkm { namespace cont { namespace testing { namespace { struct SortLess { template VTKM_EXEC_CONT_EXPORT bool operator()(const T& a,const T& b) const { typedef typename vtkm::TypeTraits::DimensionalityTag Dimensionality; return this->compare(a,b,Dimensionality()); } template VTKM_EXEC_CONT_EXPORT bool compare(const T& a,const T& b, vtkm::TypeTraitsScalarTag) const { return a < b; } template VTKM_EXEC_CONT_EXPORT bool compare(const T& a,const T& b, vtkm::TypeTraitsVectorTag) const { const vtkm::IdComponent SIZE = vtkm::VecTraits::NUM_COMPONENTS; bool valid = true; for(vtkm::IdComponent i=0; (i < SIZE) && valid; ++i) { valid = a[i] < b[i]; } return valid; } }; struct SortGreater { template VTKM_EXEC_CONT_EXPORT bool operator()(const T& a,const T& b) const { typedef typename vtkm::TypeTraits::DimensionalityTag Dimensionality; return this->compare(a,b,Dimensionality()); } template VTKM_EXEC_CONT_EXPORT bool compare(const T& a,const T& b, vtkm::TypeTraitsScalarTag) const { return a > b; } template VTKM_EXEC_CONT_EXPORT bool compare(const T& a,const T& b, vtkm::TypeTraitsVectorTag) const { const vtkm::IdComponent SIZE = vtkm::VecTraits::NUM_COMPONENTS; bool valid = true; for(vtkm::IdComponent i=0; (i < SIZE) && valid; ++i) { valid = a[i] > b[i]; } return valid; } }; } //in the namespace vtkm::cont::testing so device adapters //that don't use shared memory space can specialize this template struct CopyInto { template VTKM_CONT_EXPORT void operator()( vtkm::cont::internal::ArrayManagerExecution< T, StorageTagType, DeviceTagType>& manager, T* start) { typedef vtkm::cont::internal::ArrayManagerExecution< T, StorageTagType, DeviceTagType> ArrayManagerExecution; vtkm::cont::ArrayPortalToIterators< typename ArrayManagerExecution::PortalConstType> iterators(manager.GetPortalConst()); std::copy(iterators.GetBegin(), iterators.GetEnd(), start); } }; #define ERROR_MESSAGE "Got an error." #define ARRAY_SIZE 500 #define OFFSET 1000 #define DIM 64 /// This class has a single static member, Run, that tests the templated /// DeviceAdapter for conformance. /// template struct TestingDeviceAdapter { private: typedef vtkm::cont::StorageTagBasic StorageTag; typedef vtkm::cont::ArrayHandle IdArrayHandle; typedef vtkm::cont::ArrayHandle ScalarArrayHandle; typedef vtkm::cont::internal::ArrayManagerExecution< vtkm::Id, StorageTag, DeviceAdapterTag> IdArrayManagerExecution; typedef vtkm::cont::internal::Storage IdStorage; typedef typename IdArrayHandle::template ExecutionTypes ::Portal IdPortalType; typedef typename IdArrayHandle::template ExecutionTypes ::PortalConst IdPortalConstType; typedef vtkm::cont::ArrayHandle,StorageTag> Vec3ArrayHandle; typedef vtkm::cont::DeviceAdapterAlgorithm Algorithm; public: // Cuda kernels have to be public (in Cuda 4.0). struct CopyArrayKernel { VTKM_CONT_EXPORT CopyArrayKernel(const IdPortalConstType &input, const IdPortalType &output) : InputArray(input), OutputArray(output) { } VTKM_EXEC_EXPORT void operator()( vtkm::Id index, const vtkm::exec::internal::ErrorMessageBuffer &) const { this->OutputArray.Set(index, this->InputArray.Get(index)); } VTKM_CONT_EXPORT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &) { } IdPortalConstType InputArray; IdPortalType OutputArray; }; struct ClearArrayKernel { VTKM_CONT_EXPORT ClearArrayKernel(const IdPortalType &array) : Array(array) { } VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const { this->Array.Set(index, OFFSET); } VTKM_CONT_EXPORT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &) { } IdPortalType Array; }; struct ClearArrayMapKernel //: public vtkm::exec::WorkletMapField { // typedef void ControlSignature(Field(Out)); // typedef void ExecutionSignature(_1); template VTKM_EXEC_EXPORT void operator()(T& value) const { value = OFFSET; } }; struct AddArrayKernel { VTKM_CONT_EXPORT AddArrayKernel(const IdPortalType &array) : Array(array) { } VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const { this->Array.Set(index, this->Array.Get(index) + index); } VTKM_CONT_EXPORT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &) { } IdPortalType Array; }; struct OneErrorKernel { VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const { if (index == ARRAY_SIZE/2) { this->ErrorMessage.RaiseError(ERROR_MESSAGE); } } VTKM_CONT_EXPORT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &errorMessage) { this->ErrorMessage = errorMessage; } vtkm::exec::internal::ErrorMessageBuffer ErrorMessage; }; struct AllErrorKernel { VTKM_EXEC_EXPORT void operator()(vtkm::Id vtkmNotUsed(index)) const { this->ErrorMessage.RaiseError(ERROR_MESSAGE); } VTKM_CONT_EXPORT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &errorMessage) { this->ErrorMessage = errorMessage; } vtkm::exec::internal::ErrorMessageBuffer ErrorMessage; }; struct OffsetPlusIndexKernel { VTKM_CONT_EXPORT OffsetPlusIndexKernel(const IdPortalType &array) : Array(array) { } VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const { this->Array.Set(index, OFFSET + index); } VTKM_CONT_EXPORT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &) { } IdPortalType Array; }; struct MarkOddNumbersKernel { VTKM_CONT_EXPORT MarkOddNumbersKernel(const IdPortalType &array) : Array(array) { } VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const { this->Array.Set(index, index%2); } VTKM_CONT_EXPORT void SetErrorMessageBuffer( const vtkm::exec::internal::ErrorMessageBuffer &) { } IdPortalType Array; }; struct NGMult //: public vtkm::exec::WorkletMapField { // typedef void ControlSignature(Field(In), Field(In), Field(Out)); // typedef _3 ExecutionSignature(_1, _2); template VTKM_EXEC_EXPORT T operator()(T a, T b) const { return a * b; } }; struct NGNoOp //: public vtkm::exec::WorkletMapField { // typedef void ControlSignature(Field(In), Field(Out)); // typedef _2 ExecutionSignature(_1); template VTKM_EXEC_EXPORT T operator()(T a) const { return a; } }; struct FuseAll { template VTKM_EXEC_EXPORT bool operator()(const T&, const T&) const { //binary predicates for unique return true if they are the same return true; } }; private: template static VTKM_CONT_EXPORT vtkm::cont::ArrayHandle MakeArrayHandle(const T *array, vtkm::Id length) { return vtkm::cont::make_ArrayHandle(array, length, StorageTagBasic()); } template static VTKM_CONT_EXPORT vtkm::cont::ArrayHandle MakeArrayHandle(const std::vector& array) { return vtkm::cont::make_ArrayHandle(array, StorageTagBasic()); } static VTKM_CONT_EXPORT void TestDeviceAdapterTag() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing device adapter tag" << std::endl; typedef vtkm::cont::internal::DeviceAdapterTraits Traits; typedef vtkm::cont::internal::DeviceAdapterTraits< vtkm::cont::DeviceAdapterTagError> ErrorTraits; VTKM_TEST_ASSERT(Traits::GetId() == Traits::GetId(), "Device adapter Id does not equal itself."); VTKM_TEST_ASSERT(Traits::GetId() != ErrorTraits::GetId(), "Device adapter Id not distinguishable from others."); } // Note: this test does not actually test to make sure the data is available // in the execution environment. It tests to make sure data gets to the array // and back, but it is possible that the data is not available in the // execution environment. static VTKM_CONT_EXPORT void TestArrayManagerExecution() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing ArrayManagerExecution" << std::endl; typedef vtkm::cont::internal::ArrayManagerExecution< vtkm::FloatDefault,StorageTagBasic,DeviceAdapterTag> ArrayManagerExecution; // Create original input array. vtkm::FloatDefault inputArray[ARRAY_SIZE*2]; for (vtkm::Id index = 0; index < ARRAY_SIZE*2; index++) { inputArray[index] = vtkm::FloatDefault(index); } ::vtkm::cont::internal::ArrayPortalFromIterators inputPortal(inputArray, inputArray+ARRAY_SIZE*2); ArrayManagerExecution inputManager; inputManager.LoadDataForInput( ::vtkm::cont::internal::ArrayPortalFromIterators(inputPortal)); // Change size. inputManager.Shrink(ARRAY_SIZE); // Copy array back. The issue is we need to know if we are accessing // an array manger that shares memory with the control side. If so // it doesn't support RetrieveOutputData. // the naive way is to use ArrayPortalToIteratorBegin but that fails // since it only works with portals from arrayhandles, as the // arrayhandle does all the syncing. //The solution is to a class that the cuda device adapter can specialize //that handles copying back into control space vtkm::FloatDefault outputArray[ARRAY_SIZE]; CopyInto()(inputManager, outputArray); // Check array. for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) { VTKM_TEST_ASSERT(outputArray[index] == index, "Did not get correct values from array."); } } static VTKM_CONT_EXPORT void TestOutOfMemory() { // Only test out of memory with 64 bit ids. If there are 32 bit ids on // a 64 bit OS (common), it is simply too hard to get a reliable allocation // that is too much memory. #ifdef VTKM_USE_64BIT_IDS std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Out of Memory" << std::endl; try { std::cout << "Do array allocation that should fail." << std::endl; vtkm::cont::internal::ArrayManagerExecution< vtkm::Vector4,StorageTagBasic,DeviceAdapterTag> bigManager; vtkm::cont::internal::Storage< vtkm::Vector4, StorageTagBasic> supportArray; const vtkm::Id bigSize = 0x7FFFFFFFFFFFFFFFLL; bigManager.AllocateArrayForOutput(supportArray, bigSize); // It does not seem reasonable to get here. The previous call should fail. VTKM_TEST_FAIL("A ridiculously sized allocation succeeded. Either there " "was a failure that was not reported but should have been " "or the width of vtkm::Id is not large enough to express all " "array sizes."); } catch (vtkm::cont::ErrorControlOutOfMemory error) { std::cout << "Got the expected error: " << error.GetMessage() << std::endl; } #else std::cout << "--------- Skiping out of memory test" << std::endl; #endif } VTKM_CONT_EXPORT static void TestTimer() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Timer" << std::endl; vtkm::cont::Timer timer; #ifndef _WIN32 sleep(1); #else Sleep(1000); #endif vtkm::Float64 elapsedTime = timer.GetElapsedTime(); std::cout << "Elapsed time: " << elapsedTime << std::endl; VTKM_TEST_ASSERT(elapsedTime > 0.999, "Timer did not capture full second wait."); VTKM_TEST_ASSERT(elapsedTime < 2.0, "Timer counted too far or system really busy."); } static VTKM_CONT_EXPORT void TestAlgorithmSchedule() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing single value Scheduling with vtkm::Id" << std::endl; { std::cout << "Allocating execution array" << std::endl; IdStorage storage; IdArrayManagerExecution manager; manager.AllocateArrayForOutput(storage, 1); std::cout << "Running clear." << std::endl; Algorithm::Schedule(ClearArrayKernel(manager.GetPortal()), 1); std::cout << "Running add." << std::endl; Algorithm::Schedule(AddArrayKernel(manager.GetPortal()), 1); std::cout << "Checking results." << std::endl; manager.RetrieveOutputData(storage); for (vtkm::Id index = 0; index < 1; index++) { vtkm::Id value = storage.GetPortalConst().Get(index); VTKM_TEST_ASSERT(value == index + OFFSET, "Got bad value for single value scheduled kernel."); } } //release memory std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Schedule with vtkm::Id" << std::endl; { std::cout << "Allocating execution array" << std::endl; IdStorage storage; IdArrayManagerExecution manager; manager.AllocateArrayForOutput(storage, ARRAY_SIZE); std::cout << "Running clear." << std::endl; Algorithm::Schedule(ClearArrayKernel(manager.GetPortal()), ARRAY_SIZE); std::cout << "Running add." << std::endl; Algorithm::Schedule(AddArrayKernel(manager.GetPortal()), ARRAY_SIZE); std::cout << "Checking results." << std::endl; manager.RetrieveOutputData(storage); for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) { vtkm::Id value = storage.GetPortalConst().Get(index); VTKM_TEST_ASSERT(value == index + OFFSET, "Got bad value for scheduled kernels."); } } //release memory //verify that the schedule call works with id3 std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Schedule with vtkm::Id3" << std::endl; { std::cout << "Allocating execution array" << std::endl; IdStorage storage; IdArrayManagerExecution manager; vtkm::Id DIM_SIZE = vtkm::Id(std::pow(ARRAY_SIZE, 1/3.0f)); manager.AllocateArrayForOutput(storage, DIM_SIZE * DIM_SIZE * DIM_SIZE); vtkm::Id3 maxRange(DIM_SIZE); std::cout << "Running clear." << std::endl; Algorithm::Schedule(ClearArrayKernel(manager.GetPortal()), maxRange); std::cout << "Running add." << std::endl; Algorithm::Schedule(AddArrayKernel(manager.GetPortal()), maxRange); std::cout << "Checking results." << std::endl; manager.RetrieveOutputData(storage); const vtkm::Id maxId = DIM_SIZE * DIM_SIZE * DIM_SIZE; for (vtkm::Id index = 0; index < maxId; index++) { vtkm::Id value = storage.GetPortalConst().Get(index); VTKM_TEST_ASSERT(value == index + OFFSET, "Got bad value for scheduled vtkm::Id3 kernels."); } } //release memory } // static VTKM_CONT_EXPORT void TestDispatcher() // { // std::cout << "-------------------------------------------" << std::endl; // std::cout << "Testing vtkm::cont::Dispatcher* classes" << std::endl; // std::cout << "Testing vtkm::cont::Dispatcher with array of size 1" << std::endl; // std::vector singleElement; singleElement.push_back(1234); // IdArrayHandle hSingleElement = MakeArrayHandle(singleElement); // IdArrayHandle hResult; // vtkm::cont::DispatcherMapField< NGNoOp, DeviceAdapterTag > dispatcherNoOp; // dispatcherNoOp.Invoke( hSingleElement, hResult ); // // output // std::cout << "hResult.GetNumberOfValues(): " << hResult.GetNumberOfValues() << std::endl; // for (vtkm::Id i = 0; i < hResult.GetNumberOfValues(); ++i) // { // std::cout << hResult.GetPortalConstControl().Get(i) << ","; // } // std::cout << std::endl; // // assert // VTKM_TEST_ASSERT( // hSingleElement.GetNumberOfValues() == hResult.GetNumberOfValues(), // "out handle of single scheduling is wrong size"); // VTKM_TEST_ASSERT(singleElement[0] == 1234, // "output of single scheduling is incorrect"); // std::vector field(ARRAY_SIZE); // for (vtkm::Id i = 0; i < ARRAY_SIZE; i++) // { // field[i]=i; // } // ScalarArrayHandle fieldHandle = MakeArrayHandle(field); // ScalarArrayHandle multHandle; // std::cout << "Running NG Multiply worklet with two handles" << std::endl; // vtkm::cont::DispatcherMapField< NGMult, DeviceAdapterTag > dispatcherMult; // dispatcherMult.Invoke( fieldHandle, fieldHandle, multHandle ); // typename ScalarArrayHandle::PortalConstControl multPortal = // multHandle.GetPortalConstControl(); // for (vtkm::Id i = 0; i < ARRAY_SIZE; i++) // { // vtkm::FloatDefault squareValue = multPortal.Get(i); // vtkm::FloatDefault squareTrue = field[i]*field[i]; // VTKM_TEST_ASSERT(test_equal(squareValue, squareTrue), // "Got bad multiply result"); // } // std::cout << "Running NG Multiply worklet with handle and constant" << std::endl; // dispatcherMult.Invoke(4.0f,fieldHandle, multHandle); // multPortal = multHandle.GetPortalConstControl(); // for (vtkm::Id i = 0; i < ARRAY_SIZE; i++) // { // vtkm::FloatDefault squareValue = multPortal.Get(i); // vtkm::FloatDefault squareTrue = field[i]*4.0f; // VTKM_TEST_ASSERT(test_equal(squareValue, squareTrue), // "Got bad multiply result"); // } // std::cout << "Testing Schedule on Subset" << std::endl; // std::vector fullField(ARRAY_SIZE); // std::vector subSetLookup(ARRAY_SIZE/2); // for (vtkm::Id i = 0; i < ARRAY_SIZE; i++) // { // field[i]=i; // if(i%2==0) // { // subSetLookup[i/2]=i; // } // } // IdArrayHandle subSetLookupHandle = MakeArrayHandle(subSetLookup); // ScalarArrayHandle fullFieldHandle = MakeArrayHandle(fullField); // std::cout << "Running clear on subset." << std::endl; // vtkm::cont::DispatcherMapField< ClearArrayMapKernel, // DeviceAdapterTag > dispatcherClear; // dispatcherClear.Invoke( // make_Permutation(subSetLookupHandle,fullFieldHandle,ARRAY_SIZE)); // for (vtkm::Id index = 0; index < ARRAY_SIZE; index+=2) // { // vtkm::Id value = fullFieldHandle.GetPortalConstControl().Get(index); // VTKM_TEST_ASSERT(value == OFFSET, // "Got bad value for subset scheduled kernel."); // } // } static VTKM_CONT_EXPORT void TestStreamCompact() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Stream Compact" << std::endl; //test the version of compact that takes in input and uses it as a stencil //and uses the index of each item as the value to place in the result vector IdArrayHandle array; IdArrayHandle result; //construct the index array Algorithm::Schedule( MarkOddNumbersKernel(array.PrepareForOutput(ARRAY_SIZE, DeviceAdapterTag())), ARRAY_SIZE); Algorithm::StreamCompact(array, result); VTKM_TEST_ASSERT(result.GetNumberOfValues() == array.GetNumberOfValues()/2, "result of compacation has an incorrect size"); for (vtkm::Id index = 0; index < result.GetNumberOfValues(); index++) { const vtkm::Id value = result.GetPortalConstControl().Get(index); VTKM_TEST_ASSERT(value == (index*2)+1, "Incorrect value in compaction results."); } } static VTKM_CONT_EXPORT void TestStreamCompactWithStencil() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Stream Compact with stencil" << std::endl; IdArrayHandle array; IdArrayHandle stencil; IdArrayHandle result; //construct the index array Algorithm::Schedule( OffsetPlusIndexKernel(array.PrepareForOutput(ARRAY_SIZE, DeviceAdapterTag())), ARRAY_SIZE); Algorithm::Schedule( MarkOddNumbersKernel(stencil.PrepareForOutput(ARRAY_SIZE, DeviceAdapterTag())), ARRAY_SIZE); Algorithm::StreamCompact(array,stencil,result); VTKM_TEST_ASSERT(result.GetNumberOfValues() == array.GetNumberOfValues()/2, "result of compacation has an incorrect size"); for (vtkm::Id index = 0; index < result.GetNumberOfValues(); index++) { const vtkm::Id value = result.GetPortalConstControl().Get(index); VTKM_TEST_ASSERT(value == (OFFSET + (index*2)+1), "Incorrect value in compaction result."); } } static VTKM_CONT_EXPORT void TestOrderedUniqueValues() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Testing Sort, Unique, LowerBounds and UpperBounds" << std::endl; vtkm::Id testData[ARRAY_SIZE]; for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) { testData[i]= OFFSET+(i % 50); } IdArrayHandle input = MakeArrayHandle(testData, ARRAY_SIZE); IdArrayHandle handle; IdArrayHandle handle1; IdArrayHandle temp; Algorithm::Copy(input,temp); Algorithm::Sort(temp); Algorithm::Unique(temp); //verify lower and upper bounds work Algorithm::LowerBounds(temp,input,handle); Algorithm::UpperBounds(temp,input,handle1); // Check to make sure that temp was resized correctly during Unique. // (This was a discovered bug at one point.) temp.GetPortalConstControl(); // Forces copy back to control. temp.ReleaseResourcesExecution(); // Make sure not counting on execution. VTKM_TEST_ASSERT( temp.GetNumberOfValues() == 50, "Unique did not resize array (or size did not copy to control)."); for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) { vtkm::Id value = handle.GetPortalConstControl().Get(i); vtkm::Id value1 = handle1.GetPortalConstControl().Get(i); VTKM_TEST_ASSERT(value == i % 50, "Got bad value (LowerBounds)"); VTKM_TEST_ASSERT(value1 >= i % 50, "Got bad value (UpperBounds)"); } std::cout << "Testing Sort, Unique, LowerBounds and UpperBounds with random values" << std::endl; //now test it works when the id are not incrementing const vtkm::Id RANDOMDATA_SIZE = 6; vtkm::Id randomData[RANDOMDATA_SIZE]; randomData[0]=500; // 2 (lower), 3 (upper) randomData[1]=955; // 3 (lower), 4 (upper) randomData[2]=955; // 3 (lower), 4 (upper) randomData[3]=120; // 0 (lower), 1 (upper) randomData[4]=320; // 1 (lower), 2 (upper) randomData[5]=955; // 3 (lower), 4 (upper) //change the control structure under the handle input = MakeArrayHandle(randomData, RANDOMDATA_SIZE); Algorithm::Copy(input,handle); VTKM_TEST_ASSERT(handle.GetNumberOfValues() == RANDOMDATA_SIZE, "Handle incorrect size after setting new control data"); Algorithm::Copy(input,handle1); VTKM_TEST_ASSERT(handle.GetNumberOfValues() == RANDOMDATA_SIZE, "Handle incorrect size after setting new control data"); Algorithm::Copy(handle,temp); VTKM_TEST_ASSERT(temp.GetNumberOfValues() == RANDOMDATA_SIZE, "Copy failed"); Algorithm::Sort(temp); Algorithm::Unique(temp); Algorithm::LowerBounds(temp,handle); Algorithm::UpperBounds(temp,handle1); VTKM_TEST_ASSERT(handle.GetNumberOfValues() == RANDOMDATA_SIZE, "LowerBounds returned incorrect size"); std::copy( vtkm::cont::ArrayPortalToIteratorBegin(handle.GetPortalConstControl()), vtkm::cont::ArrayPortalToIteratorEnd(handle.GetPortalConstControl()), randomData); VTKM_TEST_ASSERT(randomData[0] == 2, "Got bad value - LowerBounds"); VTKM_TEST_ASSERT(randomData[1] == 3, "Got bad value - LowerBounds"); VTKM_TEST_ASSERT(randomData[2] == 3, "Got bad value - LowerBounds"); VTKM_TEST_ASSERT(randomData[3] == 0, "Got bad value - LowerBounds"); VTKM_TEST_ASSERT(randomData[4] == 1, "Got bad value - LowerBounds"); VTKM_TEST_ASSERT(randomData[5] == 3, "Got bad value - LowerBounds"); VTKM_TEST_ASSERT(handle1.GetNumberOfValues() == RANDOMDATA_SIZE, "UppererBounds returned incorrect size"); std::copy( vtkm::cont::ArrayPortalToIteratorBegin(handle1.GetPortalConstControl()), vtkm::cont::ArrayPortalToIteratorEnd(handle1.GetPortalConstControl()), randomData); VTKM_TEST_ASSERT(randomData[0] == 3, "Got bad value - UpperBound"); VTKM_TEST_ASSERT(randomData[1] == 4, "Got bad value - UpperBound"); VTKM_TEST_ASSERT(randomData[2] == 4, "Got bad value - UpperBound"); VTKM_TEST_ASSERT(randomData[3] == 1, "Got bad value - UpperBound"); VTKM_TEST_ASSERT(randomData[4] == 2, "Got bad value - UpperBound"); VTKM_TEST_ASSERT(randomData[5] == 4, "Got bad value - UpperBound"); } static VTKM_CONT_EXPORT void TestSortWithComparisonObject() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Sort with comparison object" << std::endl; vtkm::Id testData[ARRAY_SIZE]; for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) { testData[i]= OFFSET+((ARRAY_SIZE-i) % 50); } IdArrayHandle input = MakeArrayHandle(testData, ARRAY_SIZE); IdArrayHandle sorted; IdArrayHandle comp_sorted; Algorithm::Copy(input,sorted); Algorithm::Copy(input,comp_sorted); //Validate the standard sort is correct Algorithm::Sort(sorted); for (vtkm::Id i = 0; i < ARRAY_SIZE-1; ++i) { vtkm::Id sorted1 = sorted.GetPortalConstControl().Get(i); vtkm::Id sorted2 = sorted.GetPortalConstControl().Get(i+1); // std::cout << sorted1 << " <= " << sorted2 << std::endl; VTKM_TEST_ASSERT(sorted1 <= sorted2, "Values not properly sorted."); } //Validate the sort, and SortGreater are inverse Algorithm::Sort(comp_sorted,SortGreater()); for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) { vtkm::Id sorted1 = sorted.GetPortalConstControl().Get(i); vtkm::Id sorted2 = comp_sorted.GetPortalConstControl().Get(ARRAY_SIZE - (i + 1)); // std::cout << sorted1 << "==" << sorted2 << std::endl; VTKM_TEST_ASSERT(sorted1 == sorted2, "Got bad sort values when using SortGreater"); } Algorithm::Sort(comp_sorted,SortLess()); for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) { vtkm::Id sorted1 = sorted.GetPortalConstControl().Get(i); vtkm::Id sorted2 = comp_sorted.GetPortalConstControl().Get(i); VTKM_TEST_ASSERT(sorted1 == sorted2, "Got bad sort values when using SortLesser"); } } // static VTKM_CONT_EXPORT void TestSortByKey() // { // std::cout << "-------------------------------------------------" << std::endl; // std::cout << "Sort by keys" << std::endl; // vtkm::Id testKeys[ARRAY_SIZE]; // vtkm::Vector3 testValues[ARRAY_SIZE]; // vtkm::Vector3 grad(1.0,1.0,1.0); // for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) // { // testKeys[i] = ARRAY_SIZE - i; // testValues[i] = vtkm::Vector3(i); // } // IdArrayHandle keys = MakeArrayHandle(testKeys, ARRAY_SIZE); // Vec3ArrayHandle values = MakeArrayHandle(testValues, ARRAY_SIZE); // IdArrayHandle sorted_keys; // Vec3ArrayHandle sorted_values; // Algorithm::Copy(keys,sorted_keys); // Algorithm::Copy(values,sorted_values); // Algorithm::SortByKey(sorted_keys,sorted_values); // for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) // { // //keys should be sorted from 1 to ARRAY_SIZE // //values should be sorted from (ARRAY_SIZE-1) to 0 // vtkm::FloatDefault sorted_value = // sorted_values.GetPortalConstControl().Get(i)[0]; // vtkm::Id sorted_key = sorted_keys.GetPortalConstControl().Get(i); // VTKM_TEST_ASSERT( (sorted_key == (i+1)) , "Got bad SortByKeys key"); // VTKM_TEST_ASSERT( (sorted_value == (ARRAY_SIZE-1-i)), // "Got bad SortByKeys value"); // } // // this will return everything back to what it was before sorting // Algorithm::SortByKey(sorted_keys,sorted_values,SortGreater()); // for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) // { // //keys should be sorted from ARRAY_SIZE to 1 // //values should be sorted from 0 to (ARRAY_SIZE-1) // vtkm::FloatDefault sorted_value = // sorted_values.GetPortalConstControl().Get(i)[0]; // vtkm::Id sorted_key = sorted_keys.GetPortalConstControl().Get(i); // VTKM_TEST_ASSERT( (sorted_key == (ARRAY_SIZE-i)), // "Got bad SortByKeys key"); // VTKM_TEST_ASSERT( (sorted_value == i), // "Got bad SortByKeys value"); // } // //this is here to verify we can sort by vtkm::Tuples // Algorithm::SortByKey(sorted_values,sorted_keys); // for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) // { // //keys should be sorted from ARRAY_SIZE to 1 // //values should be sorted from 0 to (ARRAY_SIZE-1) // vtkm::FloatDefault sorted_value = // sorted_values.GetPortalConstControl().Get(i)[0]; // vtkm::Id sorted_key = sorted_keys.GetPortalConstControl().Get(i); // VTKM_TEST_ASSERT( (sorted_key == (ARRAY_SIZE-i)), // "Got bad SortByKeys key"); // VTKM_TEST_ASSERT( (sorted_value == i), // "Got bad SortByKeys value"); // } // } static VTKM_CONT_EXPORT void TestLowerBoundsWithComparisonObject() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Testing LowerBounds with comparison object" << std::endl; vtkm::Id testData[ARRAY_SIZE]; for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) { testData[i]= OFFSET+(i % 50); } IdArrayHandle input = MakeArrayHandle(testData, ARRAY_SIZE); IdArrayHandle temp; Algorithm::Copy(input,temp); Algorithm::Sort(temp); Algorithm::Unique(temp); IdArrayHandle handle; //verify lower bounds work Algorithm::LowerBounds(temp,input,handle,SortLess()); // Check to make sure that temp was resized correctly during Unique. // (This was a discovered bug at one point.) temp.GetPortalConstControl(); // Forces copy back to control. temp.ReleaseResourcesExecution(); // Make sure not counting on execution. VTKM_TEST_ASSERT( temp.GetNumberOfValues() == 50, "Unique did not resize array (or size did not copy to control)."); for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) { vtkm::Id value = handle.GetPortalConstControl().Get(i); VTKM_TEST_ASSERT(value == i % 50, "Got bad LowerBounds value with SortLess"); } } static VTKM_CONT_EXPORT void TestUpperBoundsWithComparisonObject() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Testing UpperBounds with comparison object" << std::endl; vtkm::Id testData[ARRAY_SIZE]; for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) { testData[i]= OFFSET+(i % 50); } IdArrayHandle input = MakeArrayHandle(testData, ARRAY_SIZE); IdArrayHandle temp; Algorithm::Copy(input,temp); Algorithm::Sort(temp); Algorithm::Unique(temp); IdArrayHandle handle; //verify upper bounds work Algorithm::UpperBounds(temp,input,handle,SortLess()); // Check to make sure that temp was resized correctly during Unique. // (This was a discovered bug at one point.) temp.GetPortalConstControl(); // Forces copy back to control. temp.ReleaseResourcesExecution(); // Make sure not counting on execution. VTKM_TEST_ASSERT( temp.GetNumberOfValues() == 50, "Unique did not resize array (or size did not copy to control)."); for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) { vtkm::Id value = handle.GetPortalConstControl().Get(i); VTKM_TEST_ASSERT(value == (i % 50)+1, "Got bad UpperBounds value with SortLess"); } } static VTKM_CONT_EXPORT void TestUniqueWithComparisonObject() { std::cout << "-------------------------------------------------" << std::endl; std::cout << "Testing Unique with comparison object" << std::endl; vtkm::Id testData[ARRAY_SIZE]; for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) { testData[i]= OFFSET+(i % 50); } IdArrayHandle input = MakeArrayHandle(testData, ARRAY_SIZE); IdArrayHandle temp; Algorithm::Copy(input,temp); Algorithm::Sort(temp); Algorithm::Unique(temp, FuseAll()); // Check to make sure that temp was resized correctly during Unique. // (This was a discovered bug at one point.) temp.GetPortalConstControl(); // Forces copy back to control. temp.ReleaseResourcesExecution(); // Make sure not counting on execution. std::cout << "temp size: " << temp.GetNumberOfValues() << std::endl; VTKM_TEST_ASSERT( temp.GetNumberOfValues() == 1, "Unique did not resize array (or size did not copy to control)."); vtkm::Id value = temp.GetPortalConstControl().Get(0); VTKM_TEST_ASSERT(value == OFFSET, "Got bad unique value"); } static VTKM_CONT_EXPORT void TestScanInclusive() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Inclusive Scan" << std::endl; //construct the index array IdArrayHandle array; Algorithm::Schedule( ClearArrayKernel(array.PrepareForOutput(ARRAY_SIZE, DeviceAdapterTag())), ARRAY_SIZE); //we know have an array whose sum is equal to OFFSET * ARRAY_SIZE, //let's validate that vtkm::Id sum = Algorithm::ScanInclusive(array, array); VTKM_TEST_ASSERT(sum == OFFSET * ARRAY_SIZE, "Got bad sum from Inclusive Scan"); //each value should be equal to the Triangle Number of that index //ie 1, 3, 6, 10, 15, 21 ... vtkm::Id partialSum = 0; vtkm::Id triangleNumber = 0; for(vtkm::Id i=0, pos=1; i < ARRAY_SIZE; ++i, ++pos) { const vtkm::Id value = array.GetPortalConstControl().Get(i); partialSum += value; triangleNumber = ((pos*(pos+1))/2); VTKM_TEST_ASSERT(partialSum == triangleNumber * OFFSET, "Incorrect partial sum"); } } static VTKM_CONT_EXPORT void TestScanExclusive() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Exclusive Scan" << std::endl; //construct the index array IdArrayHandle array; Algorithm::Schedule( ClearArrayKernel(array.PrepareForOutput(ARRAY_SIZE, DeviceAdapterTag())), ARRAY_SIZE); // we know have an array whose sum = (OFFSET * ARRAY_SIZE), // let's validate that vtkm::Id sum = Algorithm::ScanExclusive(array, array); VTKM_TEST_ASSERT(sum == (OFFSET * ARRAY_SIZE), "Got bad sum from Exclusive Scan"); //each value should be equal to the Triangle Number of that index //ie 0, 1, 3, 6, 10, 15, 21 ... vtkm::Id partialSum = 0; vtkm::Id triangleNumber = 0; for(vtkm::Id i=0, pos=0; i < ARRAY_SIZE; ++i, ++pos) { const vtkm::Id value = array.GetPortalConstControl().Get(i); partialSum += value; triangleNumber = ((pos*(pos+1))/2); VTKM_TEST_ASSERT(partialSum == triangleNumber * OFFSET, "Incorrect partial sum"); } } static VTKM_CONT_EXPORT void TestErrorExecution() { std::cout << "-------------------------------------------" << std::endl; std::cout << "Testing Exceptions in Execution Environment" << std::endl; std::cout << "Generating one error." << std::endl; std::string message; try { Algorithm::Schedule(OneErrorKernel(), ARRAY_SIZE); } catch (vtkm::cont::ErrorExecution error) { std::cout << "Got expected error: " << error.GetMessage() << std::endl; message = error.GetMessage(); } VTKM_TEST_ASSERT(message == ERROR_MESSAGE, "Did not get expected error message."); std::cout << "Generating lots of errors." << std::endl; message = ""; try { Algorithm::Schedule(AllErrorKernel(), ARRAY_SIZE); } catch (vtkm::cont::ErrorExecution error) { std::cout << "Got expected error: " << error.GetMessage() << std::endl; message = error.GetMessage(); } VTKM_TEST_ASSERT(message == ERROR_MESSAGE, "Did not get expected error message."); } // template // static VTKM_CONT_EXPORT void TestWorkletMapField() // { // std::cout << "-------------------------------------------" << std::endl; // std::cout << "Testing basic map field worklet" << std::endl; // //use a scoped pointer that constructs and fills a grid of the // //right type // vtkm::cont::testing::TestGrid // grid(DIM); // vtkm::Vector3 trueGradient = vtkm::make_Vector3(1.0, 1.0, 1.0); // std::vector field(grid->GetNumberOfPoints()); // std::cout << "Number of Points in the grid: " // << grid->GetNumberOfPoints() // << std::endl; // for (vtkm::Id pointIndex = 0; // pointIndex < grid->GetNumberOfPoints(); // pointIndex++) // { // vtkm::Vector3 coordinates = grid.GetPointCoordinates(pointIndex); // field[pointIndex] = vtkm::dot(coordinates, trueGradient); // } // ScalarArrayHandle fieldHandle = MakeArrayHandle(field); // ScalarArrayHandle squareHandle; // std::cout << "Running Square worklet" << std::endl; // vtkm::cont::DispatcherMapField dispatcher; // dispatcher.Invoke(fieldHandle, squareHandle); // typename ScalarArrayHandle::PortalConstControl squarePortal = // squareHandle.GetPortalConstControl(); // std::cout << "Checking result" << std::endl; // for (vtkm::Id pointIndex = 0; // pointIndex < grid->GetNumberOfPoints(); // pointIndex++) // { // vtkm::FloatDefault squareValue = squarePortal.Get(pointIndex); // vtkm::FloatDefault squareTrue = field[pointIndex]*field[pointIndex]; // VTKM_TEST_ASSERT(test_equal(squareValue, squareTrue), // "Got bad square"); // } // } // template // static VTKM_CONT_EXPORT void TestWorkletFieldMapError() // { // std::cout << "-------------------------------------------" << std::endl; // std::cout << "Testing map field worklet error" << std::endl; // vtkm::cont::testing::TestGrid // grid(DIM); // std::cout << "Running field map worklet that errors" << std::endl; // bool gotError = false; // try // { // vtkm::cont::DispatcherMapField< vtkm::worklet::testing::FieldMapError, // DeviceAdapterTag> dispatcher; // dispatcher.Invoke( grid.GetRealGrid().GetPointCoordinates() ); // } // catch (vtkm::cont::ErrorExecution error) // { // std::cout << "Got expected ErrorExecution object." << std::endl; // std::cout << error.GetMessage() << std::endl; // gotError = true; // } // VTKM_TEST_ASSERT(gotError, "Never got the error thrown."); // } // template // static VTKM_CONT_EXPORT void TestWorkletMapCell() // { // std::cout << "-------------------------------------------" << std::endl; // std::cout << "Testing basic map cell worklet" << std::endl; // if (vtkm::CellTraits::TOPOLOGICAL_DIMENSIONS < 3) // { // std::cout << "Skipping. Too hard to check gradient " // << "on cells with topological dimension < 3" << std::endl; // } // else // { // // Calling a separate Impl function because the CUDA compiler is good // // enough to optimize the if statement as a constant expression and // // then complains about unreachable statements after a return. // TestWorkletMapCellImpl(); // } // } // template // static VTKM_CONT_EXPORT void TestWorkletMapCellImpl() // { // vtkm::cont::testing::TestGrid // grid(DIM); // vtkm::Vector3 trueGradient = vtkm::make_Vector3(1.0, 1.0, 1.0); // std::vector field(grid->GetNumberOfPoints()); // for (vtkm::Id pointIndex = 0; // pointIndex < grid->GetNumberOfPoints(); // pointIndex++) // { // vtkm::Vector3 coordinates = grid.GetPointCoordinates(pointIndex); // field[pointIndex] = vtkm::dot(coordinates, trueGradient); // } // ScalarArrayHandle fieldHandle = MakeArrayHandle(field); // Vec3ArrayHandle gradientHandle; // std::cout << "Running CellGradient worklet" << std::endl; // vtkm::cont::DispatcherMapCell< vtkm::worklet::CellGradient, // DeviceAdapterTag> dispatcher; // dispatcher.Invoke(grid.GetRealGrid(), // grid->GetPointCoordinates(), // fieldHandle, // gradientHandle); // typename Vec3ArrayHandle::PortalConstControl gradientPortal = // gradientHandle.GetPortalConstControl(); // std::cout << "Checking result" << std::endl; // for (vtkm::Id cellIndex = 0; // cellIndex < grid->GetNumberOfCells(); // cellIndex++) // { // vtkm::Vector3 gradientValue = gradientPortal.Get(cellIndex); // VTKM_TEST_ASSERT(test_equal(gradientValue, trueGradient), // "Got bad gradient"); // } // } // template // static VTKM_CONT_EXPORT void TestWorkletCellMapError() // { // std::cout << "-------------------------------------------" << std::endl; // std::cout << "Testing map cell worklet error" << std::endl; // vtkm::cont::testing::TestGrid // grid(DIM); // std::cout << "Running cell map worklet that errors" << std::endl; // bool gotError = false; // try // { // vtkm::cont::DispatcherMapCell< vtkm::worklet::testing::CellMapError, // DeviceAdapterTag> dispatcher; // dispatcher.Invoke( grid.GetRealGrid() ); // } // catch (vtkm::cont::ErrorExecution error) // { // std::cout << "Got expected ErrorExecution object." << std::endl; // std::cout << error.GetMessage() << std::endl; // gotError = true; // } // VTKM_TEST_ASSERT(gotError, "Never got the error thrown."); // } // struct TestWorklets // { // template // VTKM_CONT_EXPORT void operator()(const GridType&) const // { // TestWorkletMapField(); // TestWorkletFieldMapError(); // TestWorkletMapCell(); // TestWorkletCellMapError(); // } // }; struct TestAll { VTKM_CONT_EXPORT void operator()() const { std::cout << "Doing DeviceAdapter tests" << std::endl; TestArrayManagerExecution(); // TestOutOfMemory(); // TestTimer(); // TestAlgorithmSchedule(); // TestErrorExecution(); // TestScanInclusive(); // TestScanExclusive(); // TestSortWithComparisonObject(); // // TestSortByKey(); // TestLowerBoundsWithComparisonObject(); // TestUpperBoundsWithComparisonObject(); // TestUniqueWithComparisonObject(); // TestOrderedUniqueValues(); //tests Copy, LowerBounds, Sort, Unique // // TestDispatcher(); // TestStreamCompactWithStencil(); // TestStreamCompact(); // std::cout << "Doing Worklet tests with all grid type" << std::endl; // vtkm::cont::testing::GridTesting::TryAllGridTypes( // TestWorklets(), StorageTagBasic()); } }; public: /// Run a suite of tests to check to see if a DeviceAdapter properly supports /// all members and classes required for driving Dax algorithms. Returns an /// error code that can be returned from the main function of a test. /// static VTKM_CONT_EXPORT int Run() { return vtkm::cont::testing::Testing::Run(TestAll()); } }; #undef ERROR_MESSAGE #undef ARRAY_SIZE #undef OFFSET #undef DIM } } } // namespace vtkm::cont::testing #endif //vtk_m_cont_testing_TestingDeviceAdapter_h