From 4bd340156b029dc63cb230965649bfdb5d05b11e Mon Sep 17 00:00:00 2001 From: Dave Pugmire Date: Tue, 23 Jan 2024 16:57:10 -0500 Subject: [PATCH 01/73] Use async termination. --- vtkm/filter/flow/FilterParticleAdvection.h | 3 +- .../FilterParticleAdvectionSteadyState.cxx | 10 +- .../FilterParticleAdvectionUnsteadyState.cxx | 9 +- vtkm/filter/flow/internal/AdvectAlgorithm.h | 158 +++++++++++++----- .../flow/internal/AdvectAlgorithmThreaded.h | 29 ++-- 5 files changed, 140 insertions(+), 69 deletions(-) diff --git a/vtkm/filter/flow/FilterParticleAdvection.h b/vtkm/filter/flow/FilterParticleAdvection.h index aef43bf374..49f37c6330 100644 --- a/vtkm/filter/flow/FilterParticleAdvection.h +++ b/vtkm/filter/flow/FilterParticleAdvection.h @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace vtkm @@ -104,7 +105,7 @@ protected: bool BlockIdsSet = false; std::vector BlockIds; - + vtkm::filter::flow::internal::BoundsMap BoundsMap; vtkm::Id NumberOfSteps = 0; vtkm::cont::UnknownArrayHandle Seeds; vtkm::filter::flow::IntegrationSolverType SolverType = diff --git a/vtkm/filter/flow/FilterParticleAdvectionSteadyState.cxx b/vtkm/filter/flow/FilterParticleAdvectionSteadyState.cxx index c70c2b3d6e..5f4711a394 100644 --- a/vtkm/filter/flow/FilterParticleAdvectionSteadyState.cxx +++ b/vtkm/filter/flow/FilterParticleAdvectionSteadyState.cxx @@ -58,13 +58,15 @@ FilterParticleAdvectionSteadyState::DoExecutePartitions( DataSetIntegratorSteadyState; this->ValidateOptions(); + if (this->BlockIdsSet) + this->BoundsMap = vtkm::filter::flow::internal::BoundsMap(input, this->BlockIds); + else + this->BoundsMap = vtkm::filter::flow::internal::BoundsMap(input); - - vtkm::filter::flow::internal::BoundsMap boundsMap(input); std::vector dsi; for (vtkm::Id i = 0; i < input.GetNumberOfPartitions(); i++) { - vtkm::Id blockId = boundsMap.GetLocalBlockId(i); + vtkm::Id blockId = this->BoundsMap.GetLocalBlockId(i); auto dataset = input.GetPartition(i); // Build the field for the current dataset @@ -78,7 +80,7 @@ FilterParticleAdvectionSteadyState::DoExecutePartitions( } vtkm::filter::flow::internal::ParticleAdvector pav( - boundsMap, dsi, this->UseThreadedAlgorithm, this->UseAsynchronousCommunication); + this->BoundsMap, dsi, this->UseThreadedAlgorithm, this->UseAsynchronousCommunication); vtkm::cont::ArrayHandle particles; this->Seeds.AsArrayHandle(particles); diff --git a/vtkm/filter/flow/FilterParticleAdvectionUnsteadyState.cxx b/vtkm/filter/flow/FilterParticleAdvectionUnsteadyState.cxx index 7d72cfcc86..33963047bb 100644 --- a/vtkm/filter/flow/FilterParticleAdvectionUnsteadyState.cxx +++ b/vtkm/filter/flow/FilterParticleAdvectionUnsteadyState.cxx @@ -55,12 +55,15 @@ FilterParticleAdvectionUnsteadyState::DoExecutePartitions( using DSIType = vtkm::filter::flow::internal:: DataSetIntegratorUnsteadyState; - vtkm::filter::flow::internal::BoundsMap boundsMap(input); + if (this->BlockIdsSet) + this->BoundsMap = vtkm::filter::flow::internal::BoundsMap(input, this->BlockIds); + else + this->BoundsMap = vtkm::filter::flow::internal::BoundsMap(input); std::vector dsi; for (vtkm::Id i = 0; i < input.GetNumberOfPartitions(); i++) { - vtkm::Id blockId = boundsMap.GetLocalBlockId(i); + vtkm::Id blockId = this->BoundsMap.GetLocalBlockId(i); auto ds1 = input.GetPartition(i); auto ds2 = this->Input2.GetPartition(i); @@ -85,7 +88,7 @@ FilterParticleAdvectionUnsteadyState::DoExecutePartitions( analysis); } vtkm::filter::flow::internal::ParticleAdvector pav( - boundsMap, dsi, this->UseThreadedAlgorithm, this->UseAsynchronousCommunication); + this->BoundsMap, dsi, this->UseThreadedAlgorithm, this->UseAsynchronousCommunication); vtkm::cont::ArrayHandle particles; this->Seeds.AsArrayHandle(particles); diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index 112d98501f..fdc47708a6 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -15,6 +15,10 @@ #include #include #include +#ifdef VTKM_ENABLE_MPI +#include +#include +#endif namespace vtkm { @@ -25,6 +29,83 @@ namespace flow namespace internal { +class AdvectAlgorithmTerminator +{ +public: + AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) + : MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) + { + } + + void AddWork() + { +#ifdef VTKM_ENABLE_MPI + this->Dirty = 1; +#endif + } + + bool Done() const { return this->State == AdvectAlgorithmTerminatorState::DONE; } + + void Control(bool haveLocalWork) + { +#ifdef VTKM_ENABLE_MPI + if (this->State == STATE_0 && !haveLocalWork) + { + MPI_Ibarrier(this->MPIComm, &this->StateReq); + this->Dirty = 0; + this->State = STATE_1; + } + else if (this->State == STATE_1) + { + MPI_Status status; + int flag; + MPI_Test(&this->StateReq, &flag, &status); + if (flag == 1) + { + int localDirty = this->Dirty; + MPI_Iallreduce( + &localDirty, &this->AllDirty, 1, MPI_INT, MPI_LOR, this->MPIComm, &this->StateReq); + this->State = STATE_2; + } + } + else if (this->State == STATE_2) + { + MPI_Status status; + int flag; + MPI_Test(&this->StateReq, &flag, &status); + if (flag == 1) + { + if (this->AllDirty == 0) //done + this->State = DONE; + else + this->State = STATE_0; //reset. + } + } +#else + if (!haveLocalWork) + this->State = DONE; +#endif + } + +private: + enum AdvectAlgorithmTerminatorState + { + STATE_0, + STATE_1, + STATE_2, + DONE + }; + + AdvectAlgorithmTerminatorState State = AdvectAlgorithmTerminatorState::STATE_0; + +#ifdef VTKM_ENABLE_MPI + std::atomic Dirty; + int AllDirty = 0; + MPI_Request StateReq; + MPI_Comm MPIComm; +#endif +}; + template class AdvectAlgorithm { @@ -39,6 +120,7 @@ public: , NumRanks(this->Comm.size()) , Rank(this->Comm.rank()) , UseAsynchronousCommunication(useAsyncComm) + , Terminator(this->Comm) { } @@ -97,27 +179,21 @@ public: vtkm::filter::flow::internal::ParticleMessenger messenger( this->Comm, this->UseAsynchronousCommunication, this->BoundsMap, 1, 128); - this->ComputeTotalNumParticles(); - - while (this->TotalNumTerminatedParticles < this->TotalNumParticles) + while (!this->Terminator.Done()) { std::vector v; - vtkm::Id numTerm = 0, blockId = -1; + vtkm::Id blockId = -1; if (this->GetActiveParticles(v, blockId)) { //make this a pointer to avoid the copy? auto& block = this->GetDataSet(blockId); DSIHelperInfo bb(v, this->BoundsMap, this->ParticleBlockIDsMap); block.Advect(bb, this->StepSize); - numTerm = this->UpdateResult(bb); + this->UpdateResult(bb); } - vtkm::Id numTermMessages = 0; - this->Communicate(messenger, numTerm, numTermMessages); - - this->TotalNumTerminatedParticles += (numTerm + numTermMessages); - if (this->TotalNumTerminatedParticles > this->TotalNumParticles) - throw vtkm::cont::ErrorFilterExecution("Particle count error"); + this->Communicate(messenger); + this->Terminator.Control(!this->Active.empty()); } } @@ -128,19 +204,6 @@ public: this->ParticleBlockIDsMap.clear(); } - void ComputeTotalNumParticles() - { - vtkm::Id numLocal = static_cast(this->Inactive.size()); - for (const auto& it : this->Active) - numLocal += it.second.size(); - -#ifdef VTKM_ENABLE_MPI - vtkmdiy::mpi::all_reduce(this->Comm, numLocal, this->TotalNumParticles, std::plus{}); -#else - this->TotalNumParticles = numLocal; -#endif - } - DataSetIntegrator& GetDataSet(vtkm::Id id) { for (auto& it : this->Blocks) @@ -213,9 +276,7 @@ public: return !particles.empty(); } - void Communicate(vtkm::filter::flow::internal::ParticleMessenger& messenger, - vtkm::Id numLocalTerminations, - vtkm::Id& numTermMessages) + void Communicate(vtkm::filter::flow::internal::ParticleMessenger& messenger) { std::vector outgoing; std::vector outgoingRanks; @@ -224,16 +285,17 @@ public: std::vector incoming; std::unordered_map> incomingBlockIDs; - numTermMessages = 0; + bool block = false; #ifdef VTKM_ENABLE_MPI - block = this->GetBlockAndWait(messenger.UsingSyncCommunication(), numLocalTerminations); + block = this->GetBlockAndWait(messenger.UsingSyncCommunication()); #endif + vtkm::Id numTermMessages; messenger.Exchange(outgoing, outgoingRanks, this->ParticleBlockIDsMap, - numLocalTerminations, + 0, incoming, incomingBlockIDs, numTermMessages, @@ -311,17 +373,22 @@ public: { VTKM_ASSERT(particles.size() == idsMap.size()); - for (auto pit = particles.begin(); pit != particles.end(); pit++) + if (!particles.empty()) { - vtkm::Id particleID = pit->GetID(); - const auto& it = idsMap.find(particleID); - VTKM_ASSERT(it != idsMap.end() && !it->second.empty()); - vtkm::Id blockId = it->second[0]; - this->Active[blockId].emplace_back(*pit); - } + this->Terminator.AddWork(); - for (const auto& it : idsMap) - this->ParticleBlockIDsMap[it.first] = it.second; + for (auto pit = particles.begin(); pit != particles.end(); pit++) + { + vtkm::Id particleID = pit->GetID(); + const auto& it = idsMap.find(particleID); + VTKM_ASSERT(it != idsMap.end() && !it->second.empty()); + vtkm::Id blockId = it->second[0]; + this->Active[blockId].emplace_back(*pit); + } + + for (const auto& it : idsMap) + this->ParticleBlockIDsMap[it.first] = it.second; + } } virtual void UpdateInactive(const std::vector& particles, @@ -351,7 +418,7 @@ public: } - virtual bool GetBlockAndWait(const bool& syncComm, const vtkm::Id& numLocalTerm) + virtual bool GetBlockAndWait(const bool& syncComm) { bool haveNoWork = this->Active.empty() && this->Inactive.empty(); @@ -367,9 +434,11 @@ public: //2. numLocalTerm + this->TotalNumberOfTerminatedParticles == this->TotalNumberOfParticles //So, if neither are true, we can safely block and wait for communication to come in. - if (haveNoWork && - (numLocalTerm + this->TotalNumTerminatedParticles < this->TotalNumParticles)) - return true; + // if (this->Terminator.State == AdvectAlgorithmTerminator::AdvectAlgorithmTerminatorState::STATE_2) + // return true; + + // if (haveNoWork && (numLocalTerm + this->TotalNumTerminatedParticles < this->TotalNumParticles)) + // return true; return false; } @@ -388,9 +457,8 @@ public: std::unordered_map> ParticleBlockIDsMap; vtkm::Id Rank; vtkm::FloatDefault StepSize; - vtkm::Id TotalNumParticles = 0; - vtkm::Id TotalNumTerminatedParticles = 0; bool UseAsynchronousCommunication = true; + AdvectAlgorithmTerminator Terminator; }; } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h index db4e651fbc..8531644bb9 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h @@ -50,8 +50,6 @@ public: void Go() override { - this->ComputeTotalNumParticles(); - std::vector workerThreads; workerThreads.emplace_back(std::thread(AdvectAlgorithmThreaded::Worker, this)); this->Manage(); @@ -63,6 +61,12 @@ public: } protected: + bool HaveActiveParticles() + { + std::lock_guard lock(this->Mutex); + return !this->Active.empty(); + } + bool GetActiveParticles(std::vector& particles, vtkm::Id& blockId) override { std::lock_guard lock(this->Mutex); @@ -144,38 +148,31 @@ protected: vtkm::filter::flow::internal::ParticleMessenger messenger( this->Comm, useAsync, this->BoundsMap, 1, 128); - while (this->TotalNumTerminatedParticles < this->TotalNumParticles) + while (!this->Terminator.Done()) { std::unordered_map>> workerResults; this->GetWorkerResults(workerResults); - vtkm::Id numTerm = 0; for (auto& it : workerResults) - { for (auto& r : it.second) - numTerm += this->UpdateResult(r); - } - - vtkm::Id numTermMessages = 0; - this->Communicate(messenger, numTerm, numTermMessages); + this->UpdateResult(r); - this->TotalNumTerminatedParticles += (numTerm + numTermMessages); - if (this->TotalNumTerminatedParticles > this->TotalNumParticles) - throw vtkm::cont::ErrorFilterExecution("Particle count error"); + this->Communicate(messenger); + this->Terminator.Control(this->HaveActiveParticles()); } //Let the workers know that we are done. this->SetDone(); } - bool GetBlockAndWait(const bool& syncComm, const vtkm::Id& numLocalTerm) override + bool GetBlockAndWait(const bool& syncComm) override { std::lock_guard lock(this->Mutex); if (this->Done) return true; - return (this->AdvectAlgorithm::GetBlockAndWait(syncComm, numLocalTerm) && - !this->WorkerActivate && this->WorkerResults.empty()); + return (this->AdvectAlgorithm::GetBlockAndWait(syncComm) && !this->WorkerActivate && + this->WorkerResults.empty()); } void GetWorkerResults( -- GitLab From dd96f1144b4c50324e7432fd4087dee831f2ce0c Mon Sep 17 00:00:00 2001 From: Dave Pugmire Date: Wed, 24 Jan 2024 07:23:55 -0500 Subject: [PATCH 02/73] Fixes for non-mpi code... --- vtkm/filter/flow/internal/AdvectAlgorithm.h | 4 ++++ vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index fdc47708a6..bb716dde82 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -32,8 +32,12 @@ namespace internal class AdvectAlgorithmTerminator { public: +#ifdef VTKM_ENABLE_MPI AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) : MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) +#else + AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) +#endif { } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h index 8531644bb9..015673e659 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h @@ -39,7 +39,6 @@ public: bool useAsyncComm) : AdvectAlgorithm(bm, blocks, useAsyncComm) , Done(false) - , WorkerActivate(false) { //For threaded algorithm, the particles go out of scope in the Work method. //When this happens, they are destructed by the time the Manage thread gets them. @@ -61,10 +60,11 @@ public: } protected: - bool HaveActiveParticles() + bool HaveAnyWork() { std::lock_guard lock(this->Mutex); - return !this->Active.empty(); + //We have work if there particles in any queues or a worker is busy. + return !this->Active.empty() || !this->Inactive.empty() || this->WorkerActivate; } bool GetActiveParticles(std::vector& particles, vtkm::Id& blockId) override @@ -158,7 +158,7 @@ protected: this->UpdateResult(r); this->Communicate(messenger); - this->Terminator.Control(this->HaveActiveParticles()); + this->Terminator.Control(this->HaveAnyWork()); } //Let the workers know that we are done. @@ -190,7 +190,7 @@ protected: std::atomic Done; std::mutex Mutex; - bool WorkerActivate; + bool WorkerActivate = false; std::condition_variable WorkerActivateCondition; std::unordered_map>> WorkerResults; }; -- GitLab From c6f0e369863da049248183eef3d581920307b50a Mon Sep 17 00:00:00 2001 From: Vicente Adolfo Bolea Sanchez Date: Mon, 24 Jun 2024 18:07:45 -0400 Subject: [PATCH 03/73] ci: update kokkos hip to 6.1, ubuntu 2204 --- .gitlab-ci.yml | 5 +- .../kokkos-hip/Dockerfile | 19 ++++- .../kokkos-hip/kokkos_cmake_config.cmake | 3 - .gitlab/ci/ubuntu1804.yml | 8 +- .gitlab/ci/ubuntu2004.yml | 57 +------------ .gitlab/ci/ubuntu2204.yml | 85 +++++++++++++++++++ README.md | 2 + 7 files changed, 113 insertions(+), 66 deletions(-) rename .gitlab/ci/docker/{ubuntu2004 => ubuntu2204}/kokkos-hip/Dockerfile (70%) rename .gitlab/ci/docker/{ubuntu2004 => ubuntu2204}/kokkos-hip/kokkos_cmake_config.cmake (82%) create mode 100644 .gitlab/ci/ubuntu2204.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 87c9354323..6bcce3136d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -69,8 +69,8 @@ extends: - .docker_image -.ubuntu2004_hip_kokkos: &ubuntu2004_hip_kokkos - image: "kitware/vtkm:ci-ubuntu2004_hip_kokkos-20230220" +.ubuntu2204_hip_kokkos: &ubuntu2204_hip_kokkos + image: "kitware/vtkm:ci-ubuntu2204_hip_kokkos-20240625" extends: - .docker_image @@ -245,4 +245,5 @@ include: - local: '/.gitlab/ci/ubuntu1604.yml' - local: '/.gitlab/ci/ubuntu1804.yml' - local: '/.gitlab/ci/ubuntu2004.yml' + - local: '/.gitlab/ci/ubuntu2204.yml' - local: '/.gitlab/ci/windows10.yml' diff --git a/.gitlab/ci/docker/ubuntu2004/kokkos-hip/Dockerfile b/.gitlab/ci/docker/ubuntu2204/kokkos-hip/Dockerfile similarity index 70% rename from .gitlab/ci/docker/ubuntu2004/kokkos-hip/Dockerfile rename to .gitlab/ci/docker/ubuntu2204/kokkos-hip/Dockerfile index b9232768f7..e69c5a18bc 100644 --- a/.gitlab/ci/docker/ubuntu2004/kokkos-hip/Dockerfile +++ b/.gitlab/ci/docker/ubuntu2204/kokkos-hip/Dockerfile @@ -10,7 +10,7 @@ ## ##============================================================================= -FROM rocm/dev-ubuntu-20.04 +FROM rocm/dev-ubuntu-22.04 LABEL maintainer "Vicente Adolfo Bolea Sanchez" # Base dependencies for building VTK-m projects @@ -58,11 +58,22 @@ ENV PATH "/opt/cmake/bin:${PATH}" ENV CMAKE_PREFIX_PATH "/opt/rocm/lib/cmake:/opt/rocm/lib:${CMAKE_PREFIX_PATH}" ENV CMAKE_GENERATOR "Ninja" -# Build and install Kokkos -ARG KOKKOS_VERSION=3.7.01 +ENV KOKKOS_VERSION=3.7.01 COPY kokkos_cmake_config.cmake kokkos_cmake_config.cmake RUN curl -L https://github.com/kokkos/kokkos/archive/refs/tags/$KOKKOS_VERSION.tar.gz | tar -xzf - && \ - cmake -S kokkos-$KOKKOS_VERSION -B build -C kokkos_cmake_config.cmake && \ + cmake -S kokkos-$KOKKOS_VERSION -B build -C kokkos_cmake_config.cmake \ + -DCMAKE_PREFIX_INSTALL=/opt/kokkos/$KOKKOS_VERSION \ + -DKokkos_ARCH_VEGA900=ON && \ + cmake --build build -v && \ + cmake --install build && \ + rm -rf build kokkos-$KOKKOS_VERSION + +ENV KOKKOS_VERSION=4.3.01 +COPY kokkos_cmake_config.cmake kokkos_cmake_config.cmake +RUN curl -L https://github.com/kokkos/kokkos/archive/refs/tags/$KOKKOS_VERSION.tar.gz | tar -xzf - && \ + cmake -S kokkos-$KOKKOS_VERSION -B build -C kokkos_cmake_config.cmake \ + -DCMAKE_PREFIX_INSTALL=/opt/kokkos/$KOKKOS_VERSION \ + -DKokkos_ARCH_VEGA906=ON && \ cmake --build build -v && \ cmake --install build && \ rm -rf build kokkos-$KOKKOS_VERSION diff --git a/.gitlab/ci/docker/ubuntu2004/kokkos-hip/kokkos_cmake_config.cmake b/.gitlab/ci/docker/ubuntu2204/kokkos-hip/kokkos_cmake_config.cmake similarity index 82% rename from .gitlab/ci/docker/ubuntu2004/kokkos-hip/kokkos_cmake_config.cmake rename to .gitlab/ci/docker/ubuntu2204/kokkos-hip/kokkos_cmake_config.cmake index 7b71f7b656..0bbf33e6b3 100644 --- a/.gitlab/ci/docker/ubuntu2004/kokkos-hip/kokkos_cmake_config.cmake +++ b/.gitlab/ci/docker/ubuntu2204/kokkos-hip/kokkos_cmake_config.cmake @@ -9,13 +9,10 @@ ##============================================================================ set(CMAKE_BUILD_TYPE "release" CACHE STRING "") -set(CMAKE_INSTALL_PREFIX /opt/kokkos CACHE PATH "") set(CMAKE_C_COMPILER /opt/rocm/llvm/bin/clang CACHE FILEPATH "") set(CMAKE_CXX_COMPILER /opt/rocm/llvm/bin/clang++ CACHE FILEPATH "") -set(CMAKE_CXX_STANDARD "14" CACHE STRING "") set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "") set(Kokkos_ENABLE_SERIAL ON CACHE BOOL "") -set(Kokkos_ARCH_VEGA900 ON CACHE BOOL "") set(Kokkos_ENABLE_HIP ON CACHE BOOL "") set(Kokkos_ENABLE_HIP_RELOCATABLE_DEVICE_CODE OFF CACHE BOOL "") diff --git a/.gitlab/ci/ubuntu1804.yml b/.gitlab/ci/ubuntu1804.yml index 58d94a30fc..ffe5b38568 100644 --- a/.gitlab/ci/ubuntu1804.yml +++ b/.gitlab/ci/ubuntu1804.yml @@ -196,7 +196,7 @@ test:ubuntu1804_clang8: # Build on ubuntu1804 with kokkos and test on ubuntu1804 # Uses CUDA 11 -build:ubuntu1804_kokkos: +build:ubuntu1804_kokkos37: tags: - build - vtkm @@ -212,7 +212,7 @@ build:ubuntu1804_kokkos: CMAKE_BUILD_TYPE: Release VTKM_SETTINGS: "benchmarks+kokkos+turing+64bit_floats+shared" -test:ubuntu1804_kokkos: +test:ubuntu1804_kokkos37: tags: - test - vtkm @@ -225,9 +225,9 @@ test:ubuntu1804_kokkos: - .cmake_test_linux - .run_automatically dependencies: - - build:ubuntu1804_kokkos + - build:ubuntu1804_kokkos37 needs: - - build:ubuntu1804_kokkos + - build:ubuntu1804_kokkos37 build:ubuntu1804_cuda_perftest: tags: diff --git a/.gitlab/ci/ubuntu2004.yml b/.gitlab/ci/ubuntu2004.yml index 0d1c283659..53b7981c17 100644 --- a/.gitlab/ci/ubuntu2004.yml +++ b/.gitlab/ci/ubuntu2004.yml @@ -10,7 +10,7 @@ ## ##============================================================================= -build:ubuntu2004_kokkos: +build:ubuntu2004_kokkos37: tags: - build - vtkm @@ -25,7 +25,7 @@ build:ubuntu2004_kokkos: CMAKE_PREFIX_PATH: "/opt/anari" VTKM_SETTINGS: "kokkos+shared+64bit_floats+rendering+anari" -test:ubuntu2004_kokkos: +test:ubuntu2004_kokkos37: tags: - test - vtkm @@ -36,55 +36,6 @@ test:ubuntu2004_kokkos: - .cmake_test_linux - .run_automatically dependencies: - - build:ubuntu2004_kokkos + - build:ubuntu2004_kokkos37 needs: - - build:ubuntu2004_kokkos - -build:ubuntu2004_hip_kokkos: - tags: - - vtkm - - docker - - linux-x86_64 - - radeon - extends: - - .ubuntu2004_hip_kokkos - - .cmake_build_linux - - .run_automatically - variables: - CMAKE_BUILD_TYPE: "RelWithDebInfo" - VTKM_SETTINGS: "benchmarks+kokkos+hip+no_rendering+ccache" - - CMAKE_PREFIX_PATH: "/opt/rocm/lib/cmake" - LD_LIBRARY_PATH: "/opt/rocm/lib" - CMAKE_HIP_COMPILER: "/opt/rocm/llvm/bin/clang++" - Kokkos_CXX_COMPILER: "/opt/rocm/llvm/bin/clang++" - CMAKE_HIP_ARCHITECTURES: "gfx900" - - # -isystem= is not affected by CCACHE_BASEDIR, thus we must ignore it - CCACHE_IGNOREOPTIONS: "-isystem=*" - CCACHE_BASEDIR: "$CI_PROJECT_DIR" - CCACHE_COMPILERCHECK: "content" - CCACHE_NOHASHDIR: "true" - CCACHE_RESHARE: "true" - after_script: - - ccache -v -s - - ccache -z - -test:ubuntu2004_hip_kokkos: - tags: - - vtkm - - docker - - linux-x86_64 - - radeon - extends: - - .ubuntu2004_hip_kokkos - - .cmake_test_linux - - .run_upstream_branches - variables: - CTEST_MAX_PARALLELISM: 1 - CTEST_EXCLUSIONS: "UnitTestWorkletParticleAdvection" - dependencies: - - build:ubuntu2004_hip_kokkos - needs: - - build:ubuntu2004_hip_kokkos - timeout: 3 hours + - build:ubuntu2004_kokkos37 diff --git a/.gitlab/ci/ubuntu2204.yml b/.gitlab/ci/ubuntu2204.yml new file mode 100644 index 0000000000..a070320b00 --- /dev/null +++ b/.gitlab/ci/ubuntu2204.yml @@ -0,0 +1,85 @@ +##============================================================================= +## +## 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. +## +##============================================================================= + +.kokkos_rocm_vars: &kokkos_rocm_vars + variables: + CCACHE_BASEDIR: "$CI_PROJECT_DIR" + CCACHE_COMPILERCHECK: "content" + # -isystem= is not affected by CCACHE_BASEDIR, thus we must ignore it + CCACHE_IGNOREOPTIONS: "-isystem=*" + CCACHE_NOHASHDIR: "true" + CCACHE_RESHARE: "true" + + CMAKE_BUILD_TYPE: "RelWithDebInfo" + CMAKE_HIP_COMPILER: "/opt/rocm/llvm/bin/clang++" + Kokkos_CXX_COMPILER: "/opt/rocm/llvm/bin/clang++" + LD_LIBRARY_PATH: "/opt/rocm/lib" + CXX: "hipcc" + +build:ubuntu2204_hip_kokkos37: + tags: + - vtkm + - docker + - linux-x86_64 + - radeon + extends: + - .ubuntu2204_hip_kokkos + - .cmake_build_linux + - .kokkos_rocm_vars + - .run_automatically + variables: + CMAKE_BUILD_TYPE: "RelWithDebInfo" + CMAKE_HIP_ARCHITECTURES: "gfx900" + Kokkos_DIR: "/opt/kokkos/3.7.01/" + VTKM_SETTINGS: "benchmarks+kokkos+hip+no_rendering+ccache" + after_script: + - ccache -v -s + - ccache -z + +test:ubuntu2204_hip_kokkos37: + tags: + - vtkm + - docker + - linux-x86_64 + - radeon + extends: + - .ubuntu2204_hip_kokkos + - .cmake_test_linux + - .run_upstream_branches + variables: + CTEST_MAX_PARALLELISM: 1 + CTEST_EXCLUSIONS: "UnitTestWorkletParticleAdvection" + dependencies: + - build:ubuntu2204_hip_kokkos37 + needs: + - build:ubuntu2204_hip_kokkos37 + timeout: 3 hours + +build:ubuntu2204_hip_kokkos43: + tags: + - vtkm + - docker + - linux-x86_64 + - radeon + extends: + - .ubuntu2204_hip_kokkos + - .cmake_build_linux + - .kokkos_rocm_vars + - .run_automatically + variables: + CMAKE_BUILD_TYPE: "RelWithDebInfo" + CMAKE_HIP_ARCHITECTURES: "gfx906" + Kokkos_DIR: "/opt/kokkos/4.3.01/" + VTKM_SETTINGS: "benchmarks+kokkos+hip+no_rendering+ccache" + after_script: + - ccache -v -s + - ccache -z diff --git a/README.md b/README.md index aad908b955..bde7565b80 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,8 @@ Optional dependencies are: + Kokkos Device Adapter + [Kokkos](https://kokkos.github.io/) 3.7+ + + CXX env variable or CMAKE_CXX_COMPILER should be set to + hipcc when using Kokkos device adapter with HIP (ROCM>=6). + CUDA Device Adapter + [Cuda Toolkit 9.2, >= 10.2](https://developer.nvidia.com/cuda-toolkit) + Note CUDA >= 10.2 is required on Windows -- GitLab From 4f0db5f2ea59f8f1dbd6c56d7f5dbaf349669cdc Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Mon, 1 Jul 2024 15:18:04 -0400 Subject: [PATCH 04/73] Added log entry when a cast and call fallback is used Several places in VTK-m use the `CastAndCallForTypesWithFallback` method in `UnknownArrayHandle`. The method works well for catching both common and corner cases. However, there was no way to know if the efficient direct method or the (supposedly) less likely fallback of copying data to a float array was used. VTK-m now adds a log event, registered at the "INFO" level, whenever data is copied to a fallback float array. This helps developers monitor the eficiency of their code. --- docs/changelog/fallback-log.md | 10 ++++ vtkm/cont/UnknownArrayHandle.h | 3 + .../testing/UnitTestUnknownArrayHandle.cxx | 57 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 docs/changelog/fallback-log.md diff --git a/docs/changelog/fallback-log.md b/docs/changelog/fallback-log.md new file mode 100644 index 0000000000..be6e6d065d --- /dev/null +++ b/docs/changelog/fallback-log.md @@ -0,0 +1,10 @@ +## Added log entry when a cast and call fallback is used + +Several places in VTK-m use the `CastAndCallForTypesWithFallback` method in +`UnknownArrayHandle`. The method works well for catching both common and +corner cases. However, there was no way to know if the efficient direct +method or the (supposedly) less likely fallback of copying data to a float +array was used. VTK-m now adds a log event, registered at the "INFO" level, +whenever data is copied to a fallback float array. This helps developers +monitor the eficiency of their code. + diff --git a/vtkm/cont/UnknownArrayHandle.h b/vtkm/cont/UnknownArrayHandle.h index 714a8f255d..f36b024230 100644 --- a/vtkm/cont/UnknownArrayHandle.h +++ b/vtkm/cont/UnknownArrayHandle.h @@ -1207,6 +1207,9 @@ VTKM_CONT void UnknownArrayHandle::CastAndCallForTypesWithFloatFallback(Functor& if (!called) { // Copy to a float array and try again + VTKM_LOG_F(vtkm::cont::LogLevel::Info, + "Cast and call from %s failed. Copying to basic float array.", + this->GetArrayTypeName().c_str()); vtkm::cont::UnknownArrayHandle floatArray = this->NewInstanceFloatBasic(); floatArray.DeepCopyFrom(*this); vtkm::ListForEach(detail::UnknownArrayHandleTry{}, diff --git a/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx b/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx index fa66c6f2a8..f411af3ad8 100644 --- a/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx +++ b/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx @@ -295,6 +295,60 @@ void TryNewInstance(vtkm::cont::UnknownArrayHandle originalArray) floatArray.AsArrayHandle(staticFloatArray); } +template +struct CheckActualTypeFunctor +{ + template + void operator()(const vtkm::cont::ArrayHandle& array, bool& called) const + { + called = true; + VTKM_TEST_ASSERT(array.GetNumberOfValues() == ARRAY_SIZE, "Unexpected array size."); + auto portal = array.ReadPortal(); + for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i) + { + T retrieved = portal.Get(i); + ActualT expected = TestValue(i, ActualT{}); + VTKM_TEST_ASSERT(test_equal(retrieved, expected)); + } + } +}; + +template +void TryCastAndCallFallback() +{ + vtkm::cont::UnknownArrayHandle array = CreateArrayUnknown(T{}); + + using FallbackTypes = vtkm::List, + vtkm::Vec, 2>>; + bool called = false; + array.CastAndCallForTypesWithFloatFallback( + CheckActualTypeFunctor{}, called); + VTKM_TEST_ASSERT( + called, "The functor was never called (and apparently a bad value exception not thrown)."); +} + +void TryCastAndCallFallback() +{ + std::cout << " Scalar array." << std::endl; + TryCastAndCallFallback(); + + std::cout << " Equivalent scalar." << std::endl; + TryCastAndCallFallback(); + + std::cout << " Basic Vec." << std::endl; + TryCastAndCallFallback(); + + std::cout << " Vec of Vecs." << std::endl; + TryCastAndCallFallback>(); + + std::cout << " Vec of Vecs of Vecs." << std::endl; + TryCastAndCallFallback, 2>>(); +} + template void TryAsMultiplexer(vtkm::cont::UnknownArrayHandle sourceArray) { @@ -660,6 +714,9 @@ void TestUnknownArrayHandle() std::cout << "Try AsArrayHandle" << std::endl; TryAsArrayHandle(); + std::cout << "Try CastAndCall with fallback" << std::endl; + TryCastAndCallFallback(); + std::cout << "Try ExtractComponent" << std::endl; TryExtractComponent(); -- GitLab From 310579b9a76016e83370fa3fcbe6f5f1a59f101f Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Mon, 1 Jul 2024 17:33:43 -0400 Subject: [PATCH 05/73] Load options from environment variables Some common VTK-m options such as the device and log level could be specified on the command line but not through environment variables. It is not always possible to set VTK-m command line options, so environment variables are added. Also added documentation to the user's guide about what options are available and how to set them. --- docs/changelog/env-options.md | 9 ++++ docs/users-guide/initialization.rst | 38 +++++++++++++++ vtkm/cont/Initialize.cxx | 72 +++++++++++++++++++++-------- vtkm/cont/Logging.cxx | 19 ++++++-- vtkm/cont/Logging.h | 5 +- 5 files changed, 119 insertions(+), 24 deletions(-) create mode 100644 docs/changelog/env-options.md diff --git a/docs/changelog/env-options.md b/docs/changelog/env-options.md new file mode 100644 index 0000000000..857d8446ba --- /dev/null +++ b/docs/changelog/env-options.md @@ -0,0 +1,9 @@ +## Load options from environment variables + +Some common VTK-m options such as the device and log level could be +specified on the command line but not through environment variables. It is +not always possible to set VTK-m command line options, so environment +variables are added. + +Also added documentation to the user's guide about what options are +available and how to set them. diff --git a/docs/users-guide/initialization.rst b/docs/users-guide/initialization.rst index 16fd8fc60c..ae02d1f69b 100644 --- a/docs/users-guide/initialization.rst +++ b/docs/users-guide/initialization.rst @@ -19,6 +19,44 @@ But it can also optionally take the ``argc`` and ``argv`` arguments to the ``mai |VTKm| accepts arguments that, for example, configure the compute device to use or establish logging levels. Any arguments that are handled by |VTKm| are removed from the ``argc``/``argv`` list so that your program can then respond to the remaining arguments. +Many options can also be set with environment variables. +If both the environment variable and command line argument are provided, the command line argument is used. +The following table lists the currently supported options. + +.. list-table:: |VTKm| command line arguments and environment variable options. + :widths: 23 22 15 40 + :header-rows: 1 + + * - Command Line Argument + - Environment Variable + - Default Value + - Description + * - ``--vtkm-help`` + - + - + - Causes the program to print information about |VTKm| command line arguments and then exits the program. + * - ``--vtkm-log-level`` + - ``VTKM_LOG_LEVEL`` + - ``WARNING`` + - Specifies the logging level. + Valid values are ``INFO``, ``WARNING``, ``ERROR``, ``FATAL``, and ``OFF``. + This can also be set to a numeric value for the logging level. + * - ``--vtkm-device`` + - ``VTKM_DEVICE`` + - + - Force |VTKm| to use the specified device. + If not specified or ``Any`` given, then any available device may be used. + * - ``--vtkm-num-threads`` + - ``VTKM_NUM_THREADS`` + - + - Set the number of threads to use on a multi-core device. + If not specified, the device will use the cores available in the system. + * - ``--vtkm-device-instance`` + - ``VTKM_DEVICE_INSTANCE`` + - + - Selects the device to use when more than one device device of a given type is available. + The device is specified with a numbered index. + :func:`vtkm::cont::Initialize` returns a :struct:`vtkm::cont::InitializeResult` structure. This structure contains information about the supported arguments and options selected during initialization. diff --git a/vtkm/cont/Initialize.cxx b/vtkm/cont/Initialize.cxx index bee25c5ec9..cdbb29d701 100644 --- a/vtkm/cont/Initialize.cxx +++ b/vtkm/cont/Initialize.cxx @@ -17,6 +17,7 @@ #include +#include #include #include @@ -123,7 +124,7 @@ InitializeResult Initialize(int& argc, char* argv[], InitializeOptions opts) } else { - vtkm::cont::InitLogging(argc, argv, loggingFlag); + vtkm::cont::InitLogging(argc, argv, loggingFlag, "VTKM_LOG_LEVEL"); } if (!vtkmdiy::mpi::environment::initialized()) { @@ -225,37 +226,70 @@ InitializeResult Initialize(int& argc, char* argv[], InitializeOptions opts) vtkm::cont::DeviceAdapterTagAny{}, runtimeDeviceOptions, argc, argv); } + // Check for device on command line. if (options[opt::OptionIndex::DEVICE]) { const char* arg = options[opt::OptionIndex::DEVICE].arg; - auto id = vtkm::cont::make_DeviceAdapterId(arg); - if (id != vtkm::cont::DeviceAdapterTagAny{}) + config.Device = vtkm::cont::make_DeviceAdapterId(arg); + } + // If not on command line, check for device in environment variable. + if (config.Device == vtkm::cont::DeviceAdapterTagUndefined{}) + { + const char* deviceEnv = std::getenv("VTKM_DEVICE"); + if (deviceEnv != nullptr) { - vtkm::cont::GetRuntimeDeviceTracker().ForceDevice(id); + auto id = vtkm::cont::make_DeviceAdapterId(std::getenv("VTKM_DEVICE")); + if (VtkmDeviceArg::DeviceIsAvailable(id)) + { + config.Device = id; + } + else + { + // Got invalid device. Log an error, but continue to do the default action for + // the device (i.e., ignore the environment variable setting). + VTKM_LOG_S(vtkm::cont::LogLevel::Error, + "Invalid device `" + << deviceEnv + << "` specified in VTKM_DEVICE environment variable. Ignoring."); + VTKM_LOG_S(vtkm::cont::LogLevel::Error, + "Valid devices are: " << VtkmDeviceArg::GetValidDeviceNames()); + } + } + } + // If still not defined, check to see if "any" device should be added. + if ((config.Device == vtkm::cont::DeviceAdapterTagUndefined{}) && + (opts & InitializeOptions::DefaultAnyDevice) != InitializeOptions::None) + { + config.Device = vtkm::cont::DeviceAdapterTagAny{}; + } + // Set the state for the device selected. + if (config.Device == vtkm::cont::DeviceAdapterTagUndefined{}) + { + if ((opts & InitializeOptions::RequireDevice) != InitializeOptions::None) + { + auto devices = VtkmDeviceArg::GetValidDeviceNames(); + VTKM_LOG_S(vtkm::cont::LogLevel::Fatal, "Device not given on command line."); + std::cerr << "Target device must be specified via --vtkm-device.\n" + "Valid devices: " + << devices << std::endl; + if ((opts & InitializeOptions::AddHelp) != InitializeOptions::None) + { + std::cerr << config.Usage; + } + exit(1); } else { - vtkm::cont::GetRuntimeDeviceTracker().Reset(); + // No device specified. Do nothing and let VTK-m decide what it is going to do. } - config.Device = id; } - else if ((opts & InitializeOptions::DefaultAnyDevice) != InitializeOptions::None) + else if (config.Device == vtkm::cont::DeviceAdapterTagAny{}) { vtkm::cont::GetRuntimeDeviceTracker().Reset(); - config.Device = vtkm::cont::DeviceAdapterTagAny{}; } - else if ((opts & InitializeOptions::RequireDevice) != InitializeOptions::None) + else { - auto devices = VtkmDeviceArg::GetValidDeviceNames(); - VTKM_LOG_S(vtkm::cont::LogLevel::Error, "Device not given on command line."); - std::cerr << "Target device must be specified via --vtkm-device.\n" - "Valid devices: " - << devices << std::endl; - if ((opts & InitializeOptions::AddHelp) != InitializeOptions::None) - { - std::cerr << config.Usage; - } - exit(1); + vtkm::cont::GetRuntimeDeviceTracker().ForceDevice(config.Device); } diff --git a/vtkm/cont/Logging.cxx b/vtkm/cont/Logging.cxx index e4d8510abc..51f5418258 100644 --- a/vtkm/cont/Logging.cxx +++ b/vtkm/cont/Logging.cxx @@ -30,7 +30,7 @@ #endif // VTKM_ENABLE_LOGGING -#include +#include #include #include #include @@ -108,7 +108,10 @@ namespace cont { VTKM_CONT -void InitLogging(int& argc, char* argv[], const std::string& loggingFlag) +void InitLogging(int& argc, + char* argv[], + const std::string& loggingFlag, + const std::string& loggingEnv) { SetLogLevelName(vtkm::cont::LogLevel::Off, "Off"); SetLogLevelName(vtkm::cont::LogLevel::Fatal, "FATL"); @@ -130,8 +133,16 @@ void InitLogging(int& argc, char* argv[], const std::string& loggingFlag) loguru::set_verbosity_to_name_callback(&verbosityToNameCallback); loguru::set_name_to_verbosity_callback(&nameToVerbosityCallback); - // Set the default log level to warning - SetStderrLogLevel(vtkm::cont::LogLevel::Warn); + const char* envLevel = std::getenv(loggingEnv.c_str()); + if (envLevel != nullptr) + { + SetStderrLogLevel(envLevel); + } + else + { + // Set the default log level to warning + SetStderrLogLevel(vtkm::cont::LogLevel::Warn); + } loguru::init(argc, argv, loggingFlag.c_str()); } #else // VTKM_ENABLE_LOGGING diff --git a/vtkm/cont/Logging.h b/vtkm/cont/Logging.h index a251cde14c..617e00d515 100644 --- a/vtkm/cont/Logging.h +++ b/vtkm/cont/Logging.h @@ -371,7 +371,10 @@ enum class LogLevel */ VTKM_CONT_EXPORT VTKM_CONT -void InitLogging(int& argc, char* argv[], const std::string& loggingFlag = "--vtkm-log-level"); +void InitLogging(int& argc, + char* argv[], + const std::string& loggingFlag = "--vtkm-log-level", + const std::string& loggingEnv = "VTKM_LOG_LEVEL"); VTKM_CONT_EXPORT VTKM_CONT void InitLogging(); -- GitLab From ade25db6d56ae8391a895b03a7fd39e490741a13 Mon Sep 17 00:00:00 2001 From: Dave Pugmire Date: Wed, 3 Jul 2024 07:42:39 -0400 Subject: [PATCH 06/73] Create ParticleExchanger. --- vtkm/filter/flow/internal/AdvectAlgorithm.h | 106 +++----- .../flow/internal/AdvectAlgorithmTerminator.h | 111 +++++++++ vtkm/filter/flow/internal/CMakeLists.txt | 2 + vtkm/filter/flow/internal/ParticleExchanger.h | 228 ++++++++++++++++++ 4 files changed, 370 insertions(+), 77 deletions(-) create mode 100644 vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h create mode 100644 vtkm/filter/flow/internal/ParticleExchanger.h diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index bb716dde82..b826a01a8d 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -12,8 +12,10 @@ #define vtk_m_filter_flow_internal_AdvectAlgorithm_h #include +#include #include #include +#include #include #ifdef VTKM_ENABLE_MPI #include @@ -29,86 +31,26 @@ namespace flow namespace internal { -class AdvectAlgorithmTerminator -{ -public: -#ifdef VTKM_ENABLE_MPI - AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) - : MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) -#else - AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) -#endif - { - } +/* +ParticleMessenger::Exchange() + - SendParticles(outData--> map[dstRank]=vector of pairs); + -- SendParticles(map...) + --- for each m : map SendParticles(m); + ---- SendParticles(dst, container) + ----- serialize, SendData(dst, buff); + ------ SendDataAsync(dst,buff) + ------- header??, req=mpi_isend(), store req. - void AddWork() - { -#ifdef VTKM_ENABLE_MPI - this->Dirty = 1; -#endif - } + - RecvAny(data, block); + -- RecvData(tags, buffers, block) + --- RecvDataAsyncProbe(tag, buffers, block) + ---- while (true) + ----- if block: MPI_Probe() msgReceived=true + ----- else : MPI_Iprobe msgReceived = check + ----- if msgRecvd: MPI_Get_count(), MPI_Recv(), buffers, blockAndWait=false - bool Done() const { return this->State == AdvectAlgorithmTerminatorState::DONE; } - void Control(bool haveLocalWork) - { -#ifdef VTKM_ENABLE_MPI - if (this->State == STATE_0 && !haveLocalWork) - { - MPI_Ibarrier(this->MPIComm, &this->StateReq); - this->Dirty = 0; - this->State = STATE_1; - } - else if (this->State == STATE_1) - { - MPI_Status status; - int flag; - MPI_Test(&this->StateReq, &flag, &status); - if (flag == 1) - { - int localDirty = this->Dirty; - MPI_Iallreduce( - &localDirty, &this->AllDirty, 1, MPI_INT, MPI_LOR, this->MPIComm, &this->StateReq); - this->State = STATE_2; - } - } - else if (this->State == STATE_2) - { - MPI_Status status; - int flag; - MPI_Test(&this->StateReq, &flag, &status); - if (flag == 1) - { - if (this->AllDirty == 0) //done - this->State = DONE; - else - this->State = STATE_0; //reset. - } - } -#else - if (!haveLocalWork) - this->State = DONE; -#endif - } - -private: - enum AdvectAlgorithmTerminatorState - { - STATE_0, - STATE_1, - STATE_2, - DONE - }; - - AdvectAlgorithmTerminatorState State = AdvectAlgorithmTerminatorState::STATE_0; - -#ifdef VTKM_ENABLE_MPI - std::atomic Dirty; - int AllDirty = 0; - MPI_Request StateReq; - MPI_Comm MPIComm; -#endif -}; +*/ template class AdvectAlgorithm @@ -125,6 +67,7 @@ public: , Rank(this->Comm.rank()) , UseAsynchronousCommunication(useAsyncComm) , Terminator(this->Comm) + , Exchanger(this->Comm) { } @@ -280,6 +223,11 @@ public: return !particles.empty(); } + void ExchangeParticles() + { + // this->Exchanger.Exchange(outgoing, outgoingRanks, this->ParticleBlockIDsMap, incoming, incomingBlockIDs, block); + } + void Communicate(vtkm::filter::flow::internal::ParticleMessenger& messenger) { std::vector outgoing; @@ -295,6 +243,8 @@ public: block = this->GetBlockAndWait(messenger.UsingSyncCommunication()); #endif + // this->Exchanger.Exchange(outgoing, outgoingRanks, this->ParticleBlockIDsMap, incoming, incomingBlockIDs, block); + vtkm::Id numTermMessages; messenger.Exchange(outgoing, outgoingRanks, @@ -463,6 +413,8 @@ public: vtkm::FloatDefault StepSize; bool UseAsynchronousCommunication = true; AdvectAlgorithmTerminator Terminator; + + ParticleExchanger Exchanger; }; } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h new file mode 100644 index 0000000000..7b955a3268 --- /dev/null +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -0,0 +1,111 @@ +//============================================================================ +// 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. +//============================================================================ + +#ifndef vtk_m_filter_flow_internal_AdvectAlgorithmTerminator_h +#define vtk_m_filter_flow_internal_AdvectAlgorithmTerminator_h + +namespace vtkm +{ +namespace filter +{ +namespace flow +{ +namespace internal +{ + +class AdvectAlgorithmTerminator +{ +public: +#ifdef VTKM_ENABLE_MPI + AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) + : MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) +#else + AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) +#endif + { + } + + void AddWork() + { +#ifdef VTKM_ENABLE_MPI + this->Dirty = 1; +#endif + } + + bool Done() const { return this->State == AdvectAlgorithmTerminatorState::DONE; } + + void Control(bool haveLocalWork) + { +#ifdef VTKM_ENABLE_MPI + if (this->State == STATE_0 && !haveLocalWork) + { + MPI_Ibarrier(this->MPIComm, &this->StateReq); + this->Dirty = 0; + this->State = STATE_1; + } + else if (this->State == STATE_1) + { + MPI_Status status; + int flag; + MPI_Test(&this->StateReq, &flag, &status); + if (flag == 1) + { + int localDirty = this->Dirty; + MPI_Iallreduce( + &localDirty, &this->AllDirty, 1, MPI_INT, MPI_LOR, this->MPIComm, &this->StateReq); + this->State = STATE_2; + } + } + else if (this->State == STATE_2) + { + MPI_Status status; + int flag; + MPI_Test(&this->StateReq, &flag, &status); + if (flag == 1) + { + if (this->AllDirty == 0) //done + this->State = DONE; + else + this->State = STATE_0; //reset. + } + } +#else + if (!haveLocalWork) + this->State = DONE; +#endif + } + +private: + enum AdvectAlgorithmTerminatorState + { + STATE_0, + STATE_1, + STATE_2, + DONE + }; + + AdvectAlgorithmTerminatorState State = AdvectAlgorithmTerminatorState::STATE_0; + +#ifdef VTKM_ENABLE_MPI + std::atomic Dirty; + int AllDirty = 0; + MPI_Request StateReq; + MPI_Comm MPIComm; +#endif +}; + + +} +} +} +} //vtkm::filter::flow::internal + + +#endif //vtk_m_filter_flow_internal_AdvectAlgorithmTerminator_h diff --git a/vtkm/filter/flow/internal/CMakeLists.txt b/vtkm/filter/flow/internal/CMakeLists.txt index b59dedcebd..880c04fa53 100644 --- a/vtkm/filter/flow/internal/CMakeLists.txt +++ b/vtkm/filter/flow/internal/CMakeLists.txt @@ -10,6 +10,7 @@ set(headers AdvectAlgorithm.h + AdvectAlgorithmTerminator.h AdvectAlgorithmThreaded.h BoundsMap.h DataSetIntegrator.h @@ -19,6 +20,7 @@ set(headers LagrangianStructureHelpers.h Messenger.h ParticleAdvector.h + ParticleExchanger.h ParticleMessenger.h ) diff --git a/vtkm/filter/flow/internal/ParticleExchanger.h b/vtkm/filter/flow/internal/ParticleExchanger.h new file mode 100644 index 0000000000..f5ad02ca6c --- /dev/null +++ b/vtkm/filter/flow/internal/ParticleExchanger.h @@ -0,0 +1,228 @@ +//============================================================================ +// 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. +//============================================================================ + +#ifndef vtk_m_filter_flow_internal_ParticleExchanger_h +#define vtk_m_filter_flow_internal_ParticleExchanger_h + +namespace vtkm +{ +namespace filter +{ +namespace flow +{ +namespace internal +{ + +template +class ParticleExchanger +{ +public: +#ifdef VTKM_ENABLE_MPI + ParticleExchanger(vtkmdiy::mpi::communicator& comm) + : MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) + , NumRanks(comm.size()) + , Rank(comm.rank()) +#else + ParticleExchanger(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) +#endif + { + } +#ifdef VTKM_ENABLE_MPI + ~ParticleExchanger() { this->CleanupSendBuffers(); } +#endif + + void Exchange(const std::vector& outData, + const std::vector& outRanks, + const std::unordered_map>& outBlockIDsMap, + std::vector& inData, + std::unordered_map>& inDataBlockIDsMap, + bool blockAndWait) + { + VTKM_ASSERT(outData.size() == outRanks.size()); + + if (this->NumRanks == 1) + this->SerialExchange(outData, outBlockIDsMap, inData, inDataBlockIDsMap); +#ifdef VTKM_ENABLE_MPI + else + { + this->SendParticles(outData, outRanks, outBlockIDsMap); + this->RecvParticles(inData, inDataBlockIDsMap, blockAndWait); + this->CleanupSendBuffers(); + } +#endif + } + +private: + // pair(vector of particles, vector of blockIds) + //using ParticleCommType = std::pair, std::vector>; + // pair(particle, bids); + using ParticleCommType = std::pair>; + + void CleanupSendBuffers() { std::cout << "IMPLEMENT ME!!!" << std::endl; } + + void SendParticles(const std::vector& outData, + const std::vector& outRanks, + const std::unordered_map>& outBlockIDsMap) + { + if (outData.empty()) + return; + + //create the send data: vector of particles, vector of vector of blockIds. + std::size_t n = outData.size(); + std::unordered_map> sendData; + + // dst, vector of pair(particles, blockIds) + for (std::size_t i = 0; i < n; i++) + { + const auto& bids = outBlockIDsMap.find(outData[i].GetID())->second; + sendData[outRanks[i]].emplace_back(std::make_pair(std::move(outData[i]), std::move(bids))); + } + + //Send to dst, vector> + for (auto& si : sendData) + this->SendParticlesToDst(si.first, si.second); + } + + void SendParticlesToDst(int dst, const std::vector& data) + { + if (dst == this->Rank) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Error, "Error. Sending a particle to yourself."); + return; + } + + //Serialize vector(pair(particle, bids)) and send. + vtkmdiy::MemoryBuffer* bb = new vtkmdiy::MemoryBuffer(); + vtkmdiy::save(*bb, data); + + MPI_Request req; + int err = MPI_Isend(bb->buffer.data(), bb->size(), MPI_BYTE, dst, 0, this->MPIComm, &req); + if (err != MPI_SUCCESS) + throw vtkm::cont::ErrorFilterExecution("Error in MPI_Isend inside Messenger::SendData"); + this->SendBuffers[req] = bb; + } + + void RecvParticles(std::vector& inData, + std::unordered_map>& inDataBlockIDsMap, + bool blockAndWait) const + { + inData.resize(0); + inDataBlockIDsMap.clear(); + + std::vector buffers; + + MPI_Status status; + while (true) + { + bool msgReceived = false; + int err; + if (blockAndWait) + { + err = MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, this->MPIComm, &status); + if (err != MPI_SUCCESS) + throw vtkm::cont::ErrorFilterExecution( + "Error in MPI_Probe in ParticleExchanger::RecvParticles"); + msgReceived = true; + } + else + { + int flag = 0; + err = MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, this->MPIComm, &flag, &status); + if (err != MPI_SUCCESS) + throw vtkm::cont::ErrorFilterExecution( + "Error in MPI_Probe in ParticleExchanger::RecvParticles"); + msgReceived = (flag == 1); + } + + if (msgReceived) + { + int incomingSize; + err = MPI_Get_count(&status, MPI_BYTE, &incomingSize); + if (err != MPI_SUCCESS) + throw vtkm::cont::ErrorFilterExecution( + "Error in MPI_Probe in ParticleExchanger::RecvParticles"); + + std::vector recvBuff; + recvBuff.resize(incomingSize); + MPI_Status recvStatus; + + err = MPI_Recv(recvBuff.data(), + incomingSize, + MPI_BYTE, + status.MPI_SOURCE, + status.MPI_TAG, + this->MPIComm, + &recvStatus); + if (err != MPI_SUCCESS) + throw vtkm::cont::ErrorFilterExecution( + "Error in MPI_Probe in ParticleExchanger::RecvParticles"); + + vtkmdiy::MemoryBuffer memBuff; + vtkmdiy::save(memBuff, recvBuff); + buffers.emplace_back(std::move(memBuff)); + + blockAndWait = false; //Check one more time to see if anything else arrived. + } + else + { + break; + } + } + + //Unpack buffers into particle data. + //buffers: vector)> + for (auto& b : buffers) + { + std::vector data; + vtkmdiy::load(b, data); + + for (auto& d : data) + { + const auto& particle = d.first; + const auto& bids = d.second; + inDataBlockIDsMap[particle.GetID()] = std::move(bids); + inData.emplace_back(std::move(particle)); + } + } + } + + void SerialExchange(const std::vector& outData, + const std::unordered_map>& outBlockIDsMap, + std::vector& inData, + std::unordered_map>& inDataBlockIDsMap) + { + //Copy output to input. + for (const auto& p : outData) + { + const auto& bids = outBlockIDsMap.find(p.GetID())->second; + inData.emplace_back(p); + inDataBlockIDsMap[p.GetID()] = bids; + } + } + + +#ifdef VTKM_ENABLE_MPI + MPI_Comm MPIComm; + vtkm::Id NumRanks; + vtkm::Id Rank; + std::unordered_map SendBuffers; +#else + vtkm::Id NumRanks = 1; + vtkm::Id Rank = 0; +#endif +}; + +} +} +} +} //vtkm::filter::flow::internal + + +#endif //vtk_m_filter_flow_internal_ParticleExchanger_h -- GitLab From 3d1d6091d1e1e40e05b989837c7541cfd5e6ab19 Mon Sep 17 00:00:00 2001 From: Dave Pugmire Date: Wed, 3 Jul 2024 23:19:26 -0400 Subject: [PATCH 07/73] Fix bugs, cleanup code. --- vtkm/filter/flow/internal/AdvectAlgorithm.h | 56 +------ .../flow/internal/AdvectAlgorithmThreaded.h | 17 +- vtkm/filter/flow/internal/ParticleExchanger.h | 157 ++++++++++-------- 3 files changed, 91 insertions(+), 139 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index b826a01a8d..4419f6e4b4 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -16,7 +16,6 @@ #include #include #include -#include #ifdef VTKM_ENABLE_MPI #include #include @@ -123,9 +122,6 @@ public: //Advect all the particles. virtual void Go() { - vtkm::filter::flow::internal::ParticleMessenger messenger( - this->Comm, this->UseAsynchronousCommunication, this->BoundsMap, 1, 128); - while (!this->Terminator.Done()) { std::vector v; @@ -139,7 +135,7 @@ public: this->UpdateResult(bb); } - this->Communicate(messenger); + this->ExchangeParticles(); this->Terminator.Control(!this->Active.empty()); } } @@ -224,11 +220,6 @@ public: } void ExchangeParticles() - { - // this->Exchanger.Exchange(outgoing, outgoingRanks, this->ParticleBlockIDsMap, incoming, incomingBlockIDs, block); - } - - void Communicate(vtkm::filter::flow::internal::ParticleMessenger& messenger) { std::vector outgoing; std::vector outgoingRanks; @@ -238,22 +229,8 @@ public: std::vector incoming; std::unordered_map> incomingBlockIDs; - bool block = false; -#ifdef VTKM_ENABLE_MPI - block = this->GetBlockAndWait(messenger.UsingSyncCommunication()); -#endif - - // this->Exchanger.Exchange(outgoing, outgoingRanks, this->ParticleBlockIDsMap, incoming, incomingBlockIDs, block); - - vtkm::Id numTermMessages; - messenger.Exchange(outgoing, - outgoingRanks, - this->ParticleBlockIDsMap, - 0, - incoming, - incomingBlockIDs, - numTermMessages, - block); + this->Exchanger.Exchange( + outgoing, outgoingRanks, this->ParticleBlockIDsMap, incoming, incomingBlockIDs); //Cleanup what was sent. for (const auto& p : outgoing) @@ -371,33 +348,6 @@ public: return numTerm; } - - virtual bool GetBlockAndWait(const bool& syncComm) - { - bool haveNoWork = this->Active.empty() && this->Inactive.empty(); - - //Using syncronous communication we should only block and wait if we have no particles - if (syncComm) - { - return haveNoWork; - } - else - { - //Otherwise, for asyncronous communication, there are only two cases where blocking would deadlock. - //1. There are active particles. - //2. numLocalTerm + this->TotalNumberOfTerminatedParticles == this->TotalNumberOfParticles - //So, if neither are true, we can safely block and wait for communication to come in. - - // if (this->Terminator.State == AdvectAlgorithmTerminator::AdvectAlgorithmTerminatorState::STATE_2) - // return true; - - // if (haveNoWork && (numLocalTerm + this->TotalNumTerminatedParticles < this->TotalNumParticles)) - // return true; - - return false; - } - } - //Member data // {blockId, std::vector of particles} std::unordered_map> Active; diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h index 015673e659..c73bc1e34a 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h @@ -15,7 +15,6 @@ #include #include #include -#include #include @@ -144,10 +143,6 @@ protected: "Forcing asynchronous communication."); } - bool useAsync = true; - vtkm::filter::flow::internal::ParticleMessenger messenger( - this->Comm, useAsync, this->BoundsMap, 1, 128); - while (!this->Terminator.Done()) { std::unordered_map>> workerResults; @@ -157,7 +152,7 @@ protected: for (auto& r : it.second) this->UpdateResult(r); - this->Communicate(messenger); + this->ExchangeParticles(); this->Terminator.Control(this->HaveAnyWork()); } @@ -165,16 +160,6 @@ protected: this->SetDone(); } - bool GetBlockAndWait(const bool& syncComm) override - { - std::lock_guard lock(this->Mutex); - if (this->Done) - return true; - - return (this->AdvectAlgorithm::GetBlockAndWait(syncComm) && !this->WorkerActivate && - this->WorkerResults.empty()); - } - void GetWorkerResults( std::unordered_map>>& results) { diff --git a/vtkm/filter/flow/internal/ParticleExchanger.h b/vtkm/filter/flow/internal/ParticleExchanger.h index f5ad02ca6c..b5a4b6a7d9 100644 --- a/vtkm/filter/flow/internal/ParticleExchanger.h +++ b/vtkm/filter/flow/internal/ParticleExchanger.h @@ -35,15 +35,14 @@ public: { } #ifdef VTKM_ENABLE_MPI - ~ParticleExchanger() { this->CleanupSendBuffers(); } + ~ParticleExchanger() { this->CleanupSendBuffers(false); } #endif void Exchange(const std::vector& outData, const std::vector& outRanks, const std::unordered_map>& outBlockIDsMap, std::vector& inData, - std::unordered_map>& inDataBlockIDsMap, - bool blockAndWait) + std::unordered_map>& inDataBlockIDsMap) { VTKM_ASSERT(outData.size() == outRanks.size()); @@ -53,8 +52,8 @@ public: else { this->SendParticles(outData, outRanks, outBlockIDsMap); - this->RecvParticles(inData, inDataBlockIDsMap, blockAndWait); - this->CleanupSendBuffers(); + this->RecvParticles(inData, inDataBlockIDsMap); + this->CleanupSendBuffers(true); } #endif } @@ -65,7 +64,44 @@ private: // pair(particle, bids); using ParticleCommType = std::pair>; - void CleanupSendBuffers() { std::cout << "IMPLEMENT ME!!!" << std::endl; } + void CleanupSendBuffers(bool checkRequests) + { + if (!checkRequests) + { + for (auto& entry : this->SendBuffers) + delete entry.second; + this->SendBuffers.clear(); + return; + } + + std::vector requests; + for (auto& req : this->SendBuffers) + requests.emplace_back(req.first); + + std::vector status(requests.size()); + std::vector indices(requests.size()); + int num = 0; + int err = MPI_Testsome(requests.size(), requests.data(), &num, indices.data(), status.data()); + if (err != MPI_SUCCESS) + throw vtkm::cont::ErrorFilterExecution( + "Error with MPI_Testsome in ParticleExchanger::CleanupSendBuffers"); + +#if 0 + if (num > 0) + { + std::size_t sz = static_cast(num); + for (std::size_t i = 0; i < sz; i++) + { + auto it = this->SendBuffers.find(requests[indices[i]]); + if (it == this->SendBuffers.end()) + throw vtkm::cont::ErrorFilterExecution("Error with requests in ParticleExchanger::CleanupSendBuffers"); + +// delete it->second; +// this->SendBuffers.erase(it->first); + } + } +#endif + } void SendParticles(const std::vector& outData, const std::vector& outRanks, @@ -82,7 +118,8 @@ private: for (std::size_t i = 0; i < n; i++) { const auto& bids = outBlockIDsMap.find(outData[i].GetID())->second; - sendData[outRanks[i]].emplace_back(std::make_pair(std::move(outData[i]), std::move(bids))); + //sendData[outRanks[i]].emplace_back(std::make_pair(std::move(outData[i]), std::move(bids))); + sendData[outRanks[i]].emplace_back(std::make_pair(outData[i], bids)); } //Send to dst, vector> @@ -101,6 +138,7 @@ private: //Serialize vector(pair(particle, bids)) and send. vtkmdiy::MemoryBuffer* bb = new vtkmdiy::MemoryBuffer(); vtkmdiy::save(*bb, data); + bb->reset(); MPI_Request req; int err = MPI_Isend(bb->buffer.data(), bb->size(), MPI_BYTE, dst, 0, this->MPIComm, &req); @@ -110,8 +148,7 @@ private: } void RecvParticles(std::vector& inData, - std::unordered_map>& inDataBlockIDsMap, - bool blockAndWait) const + std::unordered_map>& inDataBlockIDsMap) const { inData.resize(0); inDataBlockIDsMap.clear(); @@ -121,75 +158,55 @@ private: MPI_Status status; while (true) { - bool msgReceived = false; - int err; - if (blockAndWait) - { - err = MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, this->MPIComm, &status); - if (err != MPI_SUCCESS) - throw vtkm::cont::ErrorFilterExecution( - "Error in MPI_Probe in ParticleExchanger::RecvParticles"); - msgReceived = true; - } - else - { - int flag = 0; - err = MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, this->MPIComm, &flag, &status); - if (err != MPI_SUCCESS) - throw vtkm::cont::ErrorFilterExecution( - "Error in MPI_Probe in ParticleExchanger::RecvParticles"); - msgReceived = (flag == 1); - } + int flag = 0; + int err = MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, this->MPIComm, &flag, &status); + if (err != MPI_SUCCESS) + throw vtkm::cont::ErrorFilterExecution( + "Error in MPI_Probe in ParticleExchanger::RecvParticles"); - if (msgReceived) - { - int incomingSize; - err = MPI_Get_count(&status, MPI_BYTE, &incomingSize); - if (err != MPI_SUCCESS) - throw vtkm::cont::ErrorFilterExecution( - "Error in MPI_Probe in ParticleExchanger::RecvParticles"); - - std::vector recvBuff; - recvBuff.resize(incomingSize); - MPI_Status recvStatus; - - err = MPI_Recv(recvBuff.data(), - incomingSize, - MPI_BYTE, - status.MPI_SOURCE, - status.MPI_TAG, - this->MPIComm, - &recvStatus); - if (err != MPI_SUCCESS) - throw vtkm::cont::ErrorFilterExecution( - "Error in MPI_Probe in ParticleExchanger::RecvParticles"); - - vtkmdiy::MemoryBuffer memBuff; - vtkmdiy::save(memBuff, recvBuff); - buffers.emplace_back(std::move(memBuff)); - - blockAndWait = false; //Check one more time to see if anything else arrived. - } - else - { + if (flag == 0) //no message arrived we are done. break; - } - } - //Unpack buffers into particle data. - //buffers: vector)> - for (auto& b : buffers) - { - std::vector data; - vtkmdiy::load(b, data); + //Otherwise, recv the incoming data + int incomingSize; + err = MPI_Get_count(&status, MPI_BYTE, &incomingSize); + if (err != MPI_SUCCESS) + throw vtkm::cont::ErrorFilterExecution( + "Error in MPI_Probe in ParticleExchanger::RecvParticles"); + + std::vector recvBuff; + recvBuff.resize(incomingSize); + MPI_Status recvStatus; + + err = MPI_Recv(recvBuff.data(), + incomingSize, + MPI_BYTE, + status.MPI_SOURCE, + status.MPI_TAG, + this->MPIComm, + &recvStatus); + if (err != MPI_SUCCESS) + throw vtkm::cont::ErrorFilterExecution( + "Error in MPI_Probe in ParticleExchanger::RecvParticles"); + + //Add incoming data to inData and inDataBlockIds. + vtkmdiy::MemoryBuffer memBuff; + memBuff.save_binary(recvBuff.data(), incomingSize); + memBuff.reset(); - for (auto& d : data) + std::vector data; + vtkmdiy::load(memBuff, data); + memBuff.reset(); + for (const auto& d : data) { const auto& particle = d.first; const auto& bids = d.second; - inDataBlockIDsMap[particle.GetID()] = std::move(bids); - inData.emplace_back(std::move(particle)); + inDataBlockIDsMap[particle.GetID()] = bids; + inData.emplace_back(particle); } + + //Note, we don't terminate the while loop here. We want to go back and + //check if any messages came in while buffers were being processed. } } -- GitLab From baf8daff5692aba9d2e97cf42cc21893ea4ee208 Mon Sep 17 00:00:00 2001 From: Dave Pugmire Date: Fri, 5 Jul 2024 14:28:46 -0400 Subject: [PATCH 08/73] Removing unneeded code. Remove Sync comm. --- vtkm/filter/flow/CMakeLists.txt | 1 - vtkm/filter/flow/FilterParticleAdvection.h | 15 +- .../FilterParticleAdvectionSteadyState.cxx | 2 +- .../FilterParticleAdvectionUnsteadyState.cxx | 2 +- vtkm/filter/flow/internal/AdvectAlgorithm.h | 6 +- .../flow/internal/AdvectAlgorithmThreaded.h | 12 +- vtkm/filter/flow/internal/CMakeLists.txt | 2 - vtkm/filter/flow/internal/Messenger.cxx | 491 ------------------ vtkm/filter/flow/internal/Messenger.h | 155 ------ vtkm/filter/flow/internal/ParticleAdvector.h | 7 +- vtkm/filter/flow/internal/ParticleMessenger.h | 376 -------------- vtkm/filter/flow/testing/CMakeLists.txt | 12 +- vtkm/filter/flow/testing/TestingFlow.cxx | 29 +- vtkm/filter/flow/testing/TestingFlow.h | 6 - .../UnitTestAdvectionAsynchronousMPI.cxx | 59 --- ...ronousMPI.cxx => UnitTestAdvectionMPI.cxx} | 12 +- .../testing/UnitTestParticleMessengerMPI.cxx | 298 ----------- ...hronousMPI.cxx => UnitTestPathlineMPI.cxx} | 12 +- .../UnitTestPathlineSynchronousMPI.cxx | 59 --- ...onousMPI.cxx => UnitTestStreamlineMPI.cxx} | 12 +- .../UnitTestStreamlineSynchronousMPI.cxx | 59 --- 21 files changed, 37 insertions(+), 1590 deletions(-) delete mode 100644 vtkm/filter/flow/internal/Messenger.cxx delete mode 100644 vtkm/filter/flow/internal/Messenger.h delete mode 100644 vtkm/filter/flow/internal/ParticleMessenger.h delete mode 100644 vtkm/filter/flow/testing/UnitTestAdvectionAsynchronousMPI.cxx rename vtkm/filter/flow/testing/{UnitTestAdvectionSynchronousMPI.cxx => UnitTestAdvectionMPI.cxx} (71%) delete mode 100644 vtkm/filter/flow/testing/UnitTestParticleMessengerMPI.cxx rename vtkm/filter/flow/testing/{UnitTestPathlineAsynchronousMPI.cxx => UnitTestPathlineMPI.cxx} (71%) delete mode 100644 vtkm/filter/flow/testing/UnitTestPathlineSynchronousMPI.cxx rename vtkm/filter/flow/testing/{UnitTestStreamlineAsynchronousMPI.cxx => UnitTestStreamlineMPI.cxx} (71%) delete mode 100644 vtkm/filter/flow/testing/UnitTestStreamlineSynchronousMPI.cxx diff --git a/vtkm/filter/flow/CMakeLists.txt b/vtkm/filter/flow/CMakeLists.txt index ffbc522adf..5c274d3899 100644 --- a/vtkm/filter/flow/CMakeLists.txt +++ b/vtkm/filter/flow/CMakeLists.txt @@ -24,7 +24,6 @@ set(flow_headers ) set(flow_sources - internal/Messenger.cxx FilterParticleAdvection.cxx ) diff --git a/vtkm/filter/flow/FilterParticleAdvection.h b/vtkm/filter/flow/FilterParticleAdvection.h index 9f0ecb71c7..3ec9085614 100644 --- a/vtkm/filter/flow/FilterParticleAdvection.h +++ b/vtkm/filter/flow/FilterParticleAdvection.h @@ -11,6 +11,7 @@ #ifndef vtk_m_filter_flow_FilterParticleAdvection_h #define vtk_m_filter_flow_FilterParticleAdvection_h +#include #include #include #include @@ -90,15 +91,20 @@ public: VTKM_CONT void SetUseThreadedAlgorithm(bool val) { this->UseThreadedAlgorithm = val; } + VTKM_DEPRECATED(2.2, "All communication is asynchronous no."); VTKM_CONT - void SetUseAsynchronousCommunication() { this->UseAsynchronousCommunication = true; } + void SetUseAsynchronousCommunication() {} + VTKM_DEPRECATED(2.2, "All communication is asynchronous no."); VTKM_CONT - bool GetUseAsynchronousCommunication() { return this->UseAsynchronousCommunication; } + bool GetUseAsynchronousCommunication() { return true; } + VTKM_DEPRECATED(2.2, "All communication is asynchronous no."); VTKM_CONT - void SetUseSynchronousCommunication() { this->UseAsynchronousCommunication = false; } + void SetUseSynchronousCommunication() {} + VTKM_DEPRECATED(2.2, "All communication is asynchronous no."); VTKM_CONT - bool GetUseSynchronousCommunication() { return !this->GetUseAsynchronousCommunication(); } + bool GetUseSynchronousCommunication() { return false; } + protected: VTKM_CONT virtual void ValidateOptions() const; @@ -111,7 +117,6 @@ protected: vtkm::filter::flow::IntegrationSolverType SolverType = vtkm::filter::flow::IntegrationSolverType::RK4_TYPE; vtkm::FloatDefault StepSize = 0; - bool UseAsynchronousCommunication = true; bool UseThreadedAlgorithm = false; vtkm::filter::flow::VectorFieldType VecFieldType = vtkm::filter::flow::VectorFieldType::VELOCITY_FIELD_TYPE; diff --git a/vtkm/filter/flow/FilterParticleAdvectionSteadyState.cxx b/vtkm/filter/flow/FilterParticleAdvectionSteadyState.cxx index 5f4711a394..751e9f3918 100644 --- a/vtkm/filter/flow/FilterParticleAdvectionSteadyState.cxx +++ b/vtkm/filter/flow/FilterParticleAdvectionSteadyState.cxx @@ -80,7 +80,7 @@ FilterParticleAdvectionSteadyState::DoExecutePartitions( } vtkm::filter::flow::internal::ParticleAdvector pav( - this->BoundsMap, dsi, this->UseThreadedAlgorithm, this->UseAsynchronousCommunication); + this->BoundsMap, dsi, this->UseThreadedAlgorithm); vtkm::cont::ArrayHandle particles; this->Seeds.AsArrayHandle(particles); diff --git a/vtkm/filter/flow/FilterParticleAdvectionUnsteadyState.cxx b/vtkm/filter/flow/FilterParticleAdvectionUnsteadyState.cxx index 33963047bb..c85045fe9b 100644 --- a/vtkm/filter/flow/FilterParticleAdvectionUnsteadyState.cxx +++ b/vtkm/filter/flow/FilterParticleAdvectionUnsteadyState.cxx @@ -88,7 +88,7 @@ FilterParticleAdvectionUnsteadyState::DoExecutePartitions( analysis); } vtkm::filter::flow::internal::ParticleAdvector pav( - this->BoundsMap, dsi, this->UseThreadedAlgorithm, this->UseAsynchronousCommunication); + this->BoundsMap, dsi, this->UseThreadedAlgorithm); vtkm::cont::ArrayHandle particles; this->Seeds.AsArrayHandle(particles); diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index 4419f6e4b4..bf23f2732e 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -57,14 +57,11 @@ class AdvectAlgorithm public: using ParticleType = typename DSIType::PType; - AdvectAlgorithm(const vtkm::filter::flow::internal::BoundsMap& bm, - std::vector& blocks, - bool useAsyncComm) + AdvectAlgorithm(const vtkm::filter::flow::internal::BoundsMap& bm, std::vector& blocks) : Blocks(blocks) , BoundsMap(bm) , NumRanks(this->Comm.size()) , Rank(this->Comm.rank()) - , UseAsynchronousCommunication(useAsyncComm) , Terminator(this->Comm) , Exchanger(this->Comm) { @@ -361,7 +358,6 @@ public: std::unordered_map> ParticleBlockIDsMap; vtkm::Id Rank; vtkm::FloatDefault StepSize; - bool UseAsynchronousCommunication = true; AdvectAlgorithmTerminator Terminator; ParticleExchanger Exchanger; diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h index c73bc1e34a..e79302048e 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h @@ -34,9 +34,8 @@ public: using ParticleType = typename DSIType::PType; AdvectAlgorithmThreaded(const vtkm::filter::flow::internal::BoundsMap& bm, - std::vector& blocks, - bool useAsyncComm) - : AdvectAlgorithm(bm, blocks, useAsyncComm) + std::vector& blocks) + : AdvectAlgorithm(bm, blocks) , Done(false) { //For threaded algorithm, the particles go out of scope in the Work method. @@ -136,13 +135,6 @@ protected: void Manage() { - if (!this->UseAsynchronousCommunication) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Synchronous communication not supported for AdvectAlgorithmThreaded." - "Forcing asynchronous communication."); - } - while (!this->Terminator.Done()) { std::unordered_map>> workerResults; diff --git a/vtkm/filter/flow/internal/CMakeLists.txt b/vtkm/filter/flow/internal/CMakeLists.txt index 880c04fa53..31f79e4510 100644 --- a/vtkm/filter/flow/internal/CMakeLists.txt +++ b/vtkm/filter/flow/internal/CMakeLists.txt @@ -18,10 +18,8 @@ set(headers DataSetIntegratorUnsteadyState.h GridMetaData.h LagrangianStructureHelpers.h - Messenger.h ParticleAdvector.h ParticleExchanger.h - ParticleMessenger.h ) # Note: The C++ source files are added to the flow library diff --git a/vtkm/filter/flow/internal/Messenger.cxx b/vtkm/filter/flow/internal/Messenger.cxx deleted file mode 100644 index 52c582f201..0000000000 --- a/vtkm/filter/flow/internal/Messenger.cxx +++ /dev/null @@ -1,491 +0,0 @@ -//============================================================================ -// 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. -//============================================================================ - -int R = 1; - -#include -#include -#include - -#include -#include -#include - -#ifdef VTKM_ENABLE_MPI -#include -#endif - -namespace vtkm -{ -namespace filter -{ -namespace flow -{ -namespace internal -{ - -VTKM_CONT -#ifdef VTKM_ENABLE_MPI -Messenger::Messenger(vtkmdiy::mpi::communicator& comm, bool useAsyncComm) - : MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) - , MsgID(0) - , NumRanks(comm.size()) - , Rank(comm.rank()) - , UseAsynchronousCommunication(useAsyncComm) -#else -Messenger::Messenger(vtkmdiy::mpi::communicator& vtkmNotUsed(comm), bool vtkmNotUsed(useAsyncComm)) -#endif -{ -} - -#ifdef VTKM_ENABLE_MPI -VTKM_CONT -void Messenger::RegisterTag(int tag, std::size_t num_recvs, std::size_t size) -{ - if (this->MessageTagInfo.find(tag) != this->MessageTagInfo.end() || tag == TAG_ANY) - { - std::stringstream msg; - msg << "Invalid message tag: " << tag << std::endl; - throw vtkm::cont::ErrorFilterExecution(msg.str()); - } - this->MessageTagInfo[tag] = std::pair(num_recvs, size); -} - -std::size_t Messenger::CalcMessageBufferSize(std::size_t msgSz) -{ - return sizeof(int) // rank - // std::vector msg; - // msg.size() - + sizeof(std::size_t) - // msgSz ints. - + msgSz * sizeof(int); -} - -void Messenger::InitializeBuffers() -{ - //Setup receive buffers. - for (const auto& it : this->MessageTagInfo) - { - int tag = it.first; - std::size_t num = it.second.first; - std::size_t sz = it.second.second; - for (std::size_t i = 0; i < num; i++) - this->PostRecv(tag, sz); - } -} - -void Messenger::CleanupRequests(int tag) -{ - std::vector delKeys; - for (const auto& i : this->RecvBuffers) - { - if (tag == TAG_ANY || tag == i.first.second) - delKeys.emplace_back(i.first); - } - - if (!delKeys.empty()) - { - for (auto& v : delKeys) - { - char* buff = this->RecvBuffers[v]; - MPI_Cancel(&(v.first)); - delete[] buff; - this->RecvBuffers.erase(v); - } - } -} - -void Messenger::PostRecv(int tag) -{ - auto it = this->MessageTagInfo.find(tag); - if (it != this->MessageTagInfo.end()) - this->PostRecv(tag, it->second.second); -} - -void Messenger::PostRecv(int tag, std::size_t sz, int src) -{ - sz += sizeof(Messenger::Header); - char* buff = new char[sz]; - memset(buff, 0, sz); - - MPI_Request req; - if (src == -1) - MPI_Irecv(buff, sz, MPI_BYTE, MPI_ANY_SOURCE, tag, this->MPIComm, &req); - else - MPI_Irecv(buff, sz, MPI_BYTE, src, tag, this->MPIComm, &req); - - RequestTagPair entry(req, tag); - this->RecvBuffers[entry] = buff; -} - -void Messenger::CheckPendingSendRequests() -{ - std::vector reqTags; - this->CheckRequests(this->SendBuffers, {}, false, reqTags); - - //Cleanup any send buffers that have completed. - for (const auto& rt : reqTags) - { - auto entry = this->SendBuffers.find(rt); - if (entry != this->SendBuffers.end()) - { - delete[] entry->second; - this->SendBuffers.erase(entry); - } - } -} - -void Messenger::CheckRequests(const std::map& buffers, - const std::set& tagsToCheck, - bool BlockAndWait, - std::vector& reqTags) -{ - std::vector req, copy; - std::vector tags; - - reqTags.resize(0); - - //Check the buffers for the specified tags. - for (const auto& it : buffers) - { - if (tagsToCheck.empty() || tagsToCheck.find(it.first.second) != tagsToCheck.end()) - { - req.emplace_back(it.first.first); - copy.emplace_back(it.first.first); - tags.emplace_back(it.first.second); - } - } - - //Nothing.. - if (req.empty()) - return; - - //Check the outstanding requests. - std::vector status(req.size()); - std::vector indices(req.size()); - int num = 0; - - int err; - if (BlockAndWait) - err = MPI_Waitsome(req.size(), req.data(), &num, indices.data(), status.data()); - else - err = MPI_Testsome(req.size(), req.data(), &num, indices.data(), status.data()); - if (err != MPI_SUCCESS) - throw vtkm::cont::ErrorFilterExecution("Error with MPI_Testsome in Messenger::RecvData"); - - //Add the req/tag to the return vector. - reqTags.reserve(static_cast(num)); - for (int i = 0; i < num; i++) - reqTags.emplace_back(RequestTagPair(copy[indices[i]], tags[indices[i]])); -} - -bool Messenger::PacketCompare(const char* a, const char* b) -{ - Messenger::Header ha, hb; - memcpy(&ha, a, sizeof(ha)); - memcpy(&hb, b, sizeof(hb)); - - return ha.packet < hb.packet; -} - -void Messenger::PrepareForSend(int tag, - const vtkmdiy::MemoryBuffer& buff, - std::vector& buffList) -{ - auto it = this->MessageTagInfo.find(tag); - if (it == this->MessageTagInfo.end()) - { - std::stringstream msg; - msg << "Message tag not found: " << tag << std::endl; - throw vtkm::cont::ErrorFilterExecution(msg.str()); - } - - std::size_t bytesLeft = buff.size(); - std::size_t maxDataLen = it->second.second; - Messenger::Header header; - header.tag = tag; - header.rank = this->Rank; - header.id = this->GetMsgID(); - header.numPackets = 1; - if (buff.size() > maxDataLen) - header.numPackets += buff.size() / maxDataLen; - - header.packet = 0; - header.packetSz = 0; - header.dataSz = 0; - - buffList.resize(header.numPackets); - std::size_t pos = 0; - for (std::size_t i = 0; i < header.numPackets; i++) - { - header.packet = i; - if (i == (header.numPackets - 1)) - header.dataSz = bytesLeft; - else - header.dataSz = maxDataLen; - - header.packetSz = header.dataSz + sizeof(header); - char* b = new char[header.packetSz]; - - //Write the header. - char* bPtr = b; - memcpy(bPtr, &header, sizeof(header)); - bPtr += sizeof(header); - - //Write the data. - memcpy(bPtr, &buff.buffer[pos], header.dataSz); - pos += header.dataSz; - - buffList[i] = b; - bytesLeft -= maxDataLen; - } -} - -void Messenger::SendDataAsync(int dst, int tag, const vtkmdiy::MemoryBuffer& buff) -{ - std::vector bufferList; - - //Add headers, break into multiple buffers if needed. - this->PrepareForSend(tag, buff, bufferList); - - Messenger::Header header; - for (const auto& b : bufferList) - { - memcpy(&header, b, sizeof(header)); - MPI_Request req; - int err = MPI_Isend(b, header.packetSz, MPI_BYTE, dst, tag, this->MPIComm, &req); - if (err != MPI_SUCCESS) - throw vtkm::cont::ErrorFilterExecution("Error in MPI_Isend inside Messenger::SendData"); - - //Add it to sendBuffers - RequestTagPair entry(req, tag); - this->SendBuffers[entry] = b; - } -} - -void Messenger::SendDataSync(int dst, int tag, vtkmdiy::MemoryBuffer& buff) -{ - auto entry = std::make_pair(dst, std::move(buff)); - auto it = this->SyncSendBuffers.find(tag); - if (it == this->SyncSendBuffers.end()) - { - std::vector> vec; - vec.push_back(std::move(entry)); - this->SyncSendBuffers.insert(std::make_pair(tag, std::move(vec))); - } - else - it->second.emplace_back(std::move(entry)); - - it = this->SyncSendBuffers.find(tag); -} - -bool Messenger::RecvDataAsync(const std::set& tags, - std::vector>& buffers, - bool blockAndWait) -{ - buffers.resize(0); - - std::vector reqTags; - this->CheckRequests(this->RecvBuffers, tags, blockAndWait, reqTags); - - //Nothing came in. - if (reqTags.empty()) - return false; - - std::vector incomingBuffers; - incomingBuffers.reserve(reqTags.size()); - for (const auto& rt : reqTags) - { - auto it = this->RecvBuffers.find(rt); - if (it == this->RecvBuffers.end()) - throw vtkm::cont::ErrorFilterExecution("receive buffer not found"); - - incomingBuffers.emplace_back(it->second); - this->RecvBuffers.erase(it); - } - - this->ProcessReceivedBuffers(incomingBuffers, buffers); - - //Re-post receives - for (const auto& rt : reqTags) - this->PostRecv(rt.second); - - return !buffers.empty(); -} - -bool Messenger::RecvDataSync(const std::set& tags, - std::vector>& buffers, - bool blockAndWait) -{ - buffers.resize(0); - if (!blockAndWait) - return false; - - //Exchange data - for (auto tag : tags) - { - //Determine the number of messages being sent to each rank and the maximum size. - std::vector maxBuffSize(this->NumRanks, 0), numMessages(this->NumRanks, 0); - - const auto& it = this->SyncSendBuffers.find(tag); - if (it != this->SyncSendBuffers.end()) - { - for (const auto& i : it->second) - { - int buffSz = i.second.size(); - maxBuffSize[i.first] = - vtkm::Max(maxBuffSize[i.first], buffSz); //static_cast(i.second.size())); - numMessages[i.first]++; - } - } - - int err = MPI_Allreduce( - MPI_IN_PLACE, maxBuffSize.data(), this->NumRanks, MPI_INT, MPI_MAX, this->MPIComm); - if (err != MPI_SUCCESS) - throw vtkm::cont::ErrorFilterExecution("Error in MPI_Isend inside Messenger::RecvDataSync"); - - err = MPI_Allreduce( - MPI_IN_PLACE, numMessages.data(), this->NumRanks, MPI_INT, MPI_SUM, this->MPIComm); - if (err != MPI_SUCCESS) - throw vtkm::cont::ErrorFilterExecution("Error in MPI_Isend inside Messenger::RecvDataSync"); - - MPI_Status status; - std::vector recvBuff; - for (int r = 0; r < this->NumRanks; r++) - { - int numMsgs = numMessages[r]; - - if (numMsgs == 0) - continue; - - //Rank r needs some stuff.. - if (this->Rank == r) - { - int maxSz = maxBuffSize[r]; - recvBuff.resize(maxSz); - for (int n = 0; n < numMsgs; n++) - { - err = - MPI_Recv(recvBuff.data(), maxSz, MPI_BYTE, MPI_ANY_SOURCE, 0, this->MPIComm, &status); - if (err != MPI_SUCCESS) - throw vtkm::cont::ErrorFilterExecution( - "Error in MPI_Recv inside Messenger::RecvDataSync"); - - std::pair entry; - entry.first = tag; - entry.second.buffer = recvBuff; - buffers.emplace_back(std::move(entry)); - } - } - else - { - if (it != this->SyncSendBuffers.end()) - { - //it = > - for (const auto& dst_buff : it->second) - { - if (dst_buff.first == r) - { - err = MPI_Send(dst_buff.second.buffer.data(), - dst_buff.second.size(), - MPI_BYTE, - r, - 0, - this->MPIComm); - if (err != MPI_SUCCESS) - throw vtkm::cont::ErrorFilterExecution( - "Error in MPI_Send inside Messenger::RecvDataSync"); - } - } - } - } - } - - //Clean up the send buffers. - if (it != this->SyncSendBuffers.end()) - this->SyncSendBuffers.erase(it); - } - - MPI_Barrier(this->MPIComm); - - return !buffers.empty(); -} - -void Messenger::ProcessReceivedBuffers(std::vector& incomingBuffers, - std::vector>& buffers) -{ - for (auto& buff : incomingBuffers) - { - //Grab the header. - Messenger::Header header; - memcpy(&header, buff, sizeof(header)); - - //Only 1 packet, strip off header and add to list. - if (header.numPackets == 1) - { - std::pair entry; - entry.first = header.tag; - entry.second.save_binary((char*)(buff + sizeof(header)), header.dataSz); - entry.second.reset(); - buffers.emplace_back(std::move(entry)); - - delete[] buff; - } - - //Multi packet.... - else - { - RankIdPair k(header.rank, header.id); - auto i2 = this->RecvPackets.find(k); - - //First packet. Create a new list and add it. - if (i2 == this->RecvPackets.end()) - { - std::list l; - l.emplace_back(buff); - this->RecvPackets[k] = l; - } - else - { - i2->second.emplace_back(buff); - - // The last packet came in, merge into one MemStream. - if (i2->second.size() == header.numPackets) - { - //Sort the packets into proper order. - i2->second.sort(Messenger::PacketCompare); - - std::pair entry; - entry.first = header.tag; - for (const auto& listIt : i2->second) - { - char* bi = listIt; - - Messenger::Header header2; - memcpy(&header2, bi, sizeof(header2)); - entry.second.save_binary((char*)(bi + sizeof(header2)), header2.dataSz); - delete[] bi; - } - - entry.second.reset(); - buffers.emplace_back(std::move(entry)); - this->RecvPackets.erase(i2); - } - } - } - } -} - -#endif - -} -} -} -} // vtkm::filter::flow::internal diff --git a/vtkm/filter/flow/internal/Messenger.h b/vtkm/filter/flow/internal/Messenger.h deleted file mode 100644 index 42d554ae51..0000000000 --- a/vtkm/filter/flow/internal/Messenger.h +++ /dev/null @@ -1,155 +0,0 @@ -//============================================================================ -// 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. -//============================================================================ - -#ifndef vtk_m_filter_flow_internal_Messenger_h -#define vtk_m_filter_flow_internal_Messenger_h - -#include -#include -#include - -#include -#include -#include -#include - -#ifdef VTKM_ENABLE_MPI -#include -#endif - -namespace vtkm -{ -namespace filter -{ -namespace flow -{ -namespace internal -{ - -class VTKM_FILTER_FLOW_EXPORT Messenger -{ -public: - VTKM_CONT Messenger(vtkmdiy::mpi::communicator& comm, bool useAsyncComm); - VTKM_CONT virtual ~Messenger() - { -#ifdef VTKM_ENABLE_MPI - this->CleanupRequests(); -#endif - } - - int GetRank() const { return this->Rank; } - int GetNumRanks() const { return this->NumRanks; } - -#ifdef VTKM_ENABLE_MPI - VTKM_CONT void RegisterTag(int tag, std::size_t numRecvs, std::size_t size); - - bool UsingSyncCommunication() const { return !this->UsingAsyncCommunication(); } - bool UsingAsyncCommunication() const { return this->UseAsynchronousCommunication; } - -protected: - static std::size_t CalcMessageBufferSize(std::size_t msgSz); - - void InitializeBuffers(); - void CheckPendingSendRequests(); - void CleanupRequests(int tag = TAG_ANY); - void SendData(int dst, int tag, vtkmdiy::MemoryBuffer& buff) - { - if (this->UseAsynchronousCommunication) - this->SendDataAsync(dst, tag, buff); - else - this->SendDataSync(dst, tag, buff); - } - bool RecvData(const std::set& tags, - std::vector>& buffers, - bool blockAndWait = false) - { - if (this->UseAsynchronousCommunication) - return this->RecvDataAsync(tags, buffers, blockAndWait); - else - return this->RecvDataSync(tags, buffers, blockAndWait); - } - -private: - void SendDataAsync(int dst, int tag, const vtkmdiy::MemoryBuffer& buff); - void SendDataSync(int dst, int tag, vtkmdiy::MemoryBuffer& buff); - bool RecvDataAsync(const std::set& tags, - std::vector>& buffers, - bool blockAndWait); - bool RecvDataSync(const std::set& tags, - std::vector>& buffers, - bool blockAndWait); - void PostRecv(int tag); - void PostRecv(int tag, std::size_t sz, int src = -1); - - - //Message headers. - typedef struct - { - int rank, tag; - std::size_t id, numPackets, packet, packetSz, dataSz; - } Header; - - void PrepareForSend(int tag, const vtkmdiy::MemoryBuffer& buff, std::vector& buffList); - vtkm::Id GetMsgID() { return this->MsgID++; } - static bool PacketCompare(const char* a, const char* b); - void ProcessReceivedBuffers(std::vector& incomingBuffers, - std::vector>& buffers); - - // Send/Recv buffer management structures. - using RequestTagPair = std::pair; - using RankIdPair = std::pair; - - //Member data - // - std::map>> SyncSendBuffers; - std::map> MessageTagInfo; - MPI_Comm MPIComm; - std::size_t MsgID; - int NumRanks; - int Rank; - std::map RecvBuffers; - std::map> RecvPackets; - std::map SendBuffers; - static constexpr int TAG_ANY = -1; - bool UseAsynchronousCommunication = true; - - void CheckRequests(const std::map& buffer, - const std::set& tags, - bool BlockAndWait, - std::vector& reqTags); - -#else -protected: - static constexpr int NumRanks = 1; - static constexpr int Rank = 0; -#endif -}; - - -template -std::ostream& operator<<(std::ostream& os, const std::vector& v) -{ - os << "["; - for (std::size_t i = 0; i < v.size(); ++i) - { - os << v[i]; - if (i != v.size() - 1) - os << ", "; - } - os << "]"; - return os; -} - -} -} -} -} // vtkm::filter::flow::internal - -#endif // vtk_m_filter_flow_internal_Messenger_h diff --git a/vtkm/filter/flow/internal/ParticleAdvector.h b/vtkm/filter/flow/internal/ParticleAdvector.h index 4409411758..a864eb00df 100644 --- a/vtkm/filter/flow/internal/ParticleAdvector.h +++ b/vtkm/filter/flow/internal/ParticleAdvector.h @@ -33,12 +33,10 @@ public: ParticleAdvector(const vtkm::filter::flow::internal::BoundsMap& bm, const std::vector& blocks, - const bool& useThreaded, - const bool& useAsyncComm) + const bool& useThreaded) : Blocks(blocks) , BoundsMap(bm) , UseThreadedAlgorithm(useThreaded) - , UseAsynchronousCommunication(useAsyncComm) { } @@ -62,7 +60,7 @@ private: vtkm::cont::PartitionedDataSet RunAlgo(const vtkm::cont::ArrayHandle& seeds, vtkm::FloatDefault stepSize) { - AlgorithmType algo(this->BoundsMap, this->Blocks, this->UseAsynchronousCommunication); + AlgorithmType algo(this->BoundsMap, this->Blocks); algo.Execute(seeds, stepSize); return algo.GetOutput(); } @@ -70,7 +68,6 @@ private: std::vector Blocks; vtkm::filter::flow::internal::BoundsMap BoundsMap; bool UseThreadedAlgorithm; - bool UseAsynchronousCommunication = true; }; } diff --git a/vtkm/filter/flow/internal/ParticleMessenger.h b/vtkm/filter/flow/internal/ParticleMessenger.h deleted file mode 100644 index fb56354ac0..0000000000 --- a/vtkm/filter/flow/internal/ParticleMessenger.h +++ /dev/null @@ -1,376 +0,0 @@ -//============================================================================ -// 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. -//============================================================================ - -#ifndef vtk_m_filter_flow_internal_ParticleMessenger_h -#define vtk_m_filter_flow_internal_ParticleMessenger_h - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace vtkm -{ -namespace filter -{ -namespace flow -{ -namespace internal -{ - -template -class VTKM_FILTER_FLOW_EXPORT ParticleMessenger : public vtkm::filter::flow::internal::Messenger -{ - //sendRank, message - using MsgCommType = std::pair>; - - //particle + blockIDs. - using ParticleCommType = std::pair>; - - //sendRank, vector of ParticleCommType. - using ParticleRecvCommType = std::pair>; - -public: - VTKM_CONT ParticleMessenger(vtkmdiy::mpi::communicator& comm, - bool useAsyncComm, - const vtkm::filter::flow::internal::BoundsMap& bm, - int msgSz = 1, - int numParticles = 128, - int numBlockIds = 2); - VTKM_CONT ~ParticleMessenger() {} - - VTKM_CONT void Exchange(const std::vector& outData, - const std::vector& outRanks, - const std::unordered_map>& outBlockIDsMap, - vtkm::Id numLocalTerm, - std::vector& inData, - std::unordered_map>& inDataBlockIDsMap, - vtkm::Id& numTerminateMessages, - bool blockAndWait = false); - -protected: -#ifdef VTKM_ENABLE_MPI - static constexpr int MSG_TERMINATE = 1; - - enum { MESSAGE_TAG = 0x42000, PARTICLE_TAG = 0x42001 }; - - VTKM_CONT void RegisterMessages(int msgSz, int nParticles, int numBlockIds); - - // Send/Recv particles - VTKM_CONT - template - class Container, - typename Allocator = std::allocator

> - inline void SendParticles(int dst, const Container& c); - - VTKM_CONT - template - class Container, - typename Allocator = std::allocator

> - inline void SendParticles(const std::unordered_map>& m); - - // Send/Recv messages. - VTKM_CONT void SendMsg(int dst, const std::vector& msg); - VTKM_CONT void SendAllMsg(const std::vector& msg); - VTKM_CONT bool RecvMsg(std::vector& msgs) { return RecvAny(&msgs, NULL, false); } - - // Send/Recv datasets. - VTKM_CONT bool RecvAny(std::vector* msgs, - std::vector* recvParticles, - bool blockAndWait); - const vtkm::filter::flow::internal::BoundsMap& BoundsMap; - -#endif - - VTKM_CONT void SerialExchange( - const std::vector& outData, - const std::vector& outRanks, - const std::unordered_map>& outBlockIDsMap, - vtkm::Id numLocalTerm, - std::vector& inData, - std::unordered_map>& inDataBlockIDsMap, - bool blockAndWait) const; - - static std::size_t CalcParticleBufferSize(std::size_t nParticles, std::size_t numBlockIds = 2); -}; - -//methods - -VTKM_CONT -template -ParticleMessenger::ParticleMessenger( - vtkmdiy::mpi::communicator& comm, - bool useAsyncComm, - const vtkm::filter::flow::internal::BoundsMap& boundsMap, - int msgSz, - int numParticles, - int numBlockIds) - : Messenger(comm, useAsyncComm) -#ifdef VTKM_ENABLE_MPI - , BoundsMap(boundsMap) -#endif -{ -#ifdef VTKM_ENABLE_MPI - this->RegisterMessages(msgSz, numParticles, numBlockIds); -#else - (void)(boundsMap); - (void)(msgSz); - (void)(numParticles); - (void)(numBlockIds); -#endif -} - -template -std::size_t ParticleMessenger::CalcParticleBufferSize(std::size_t nParticles, - std::size_t nBlockIds) -{ - ParticleType pTmp; - std::size_t pSize = ParticleType::Sizeof(); - -#ifndef NDEBUG - vtkmdiy::MemoryBuffer buff; - ParticleType p; - vtkmdiy::save(buff, p); - - //Make sure the buffer size is correct. - //If this fires, then the size of the class has changed. - VTKM_ASSERT(pSize == buff.size()); -#endif - - return - // rank - sizeof(int) - //std::vector p; - //p.size() - + sizeof(std::size_t) - //nParticles of ParticleType - + nParticles * pSize - // std::vector blockIDs for each particle. - // blockIDs.size() for each particle - + nParticles * sizeof(std::size_t) - // nBlockIDs of vtkm::Id for each particle. - + nParticles * nBlockIds * sizeof(vtkm::Id); -} - -VTKM_CONT -template -void ParticleMessenger::SerialExchange( - const std::vector& outData, - const std::vector& vtkmNotUsed(outRanks), - const std::unordered_map>& outBlockIDsMap, - vtkm::Id vtkmNotUsed(numLocalTerm), - std::vector& inData, - std::unordered_map>& inDataBlockIDsMap, - bool vtkmNotUsed(blockAndWait)) const -{ - for (auto& p : outData) - { - const auto& bids = outBlockIDsMap.find(p.GetID())->second; - inData.emplace_back(p); - inDataBlockIDsMap[p.GetID()] = bids; - } -} - -VTKM_CONT -template -void ParticleMessenger::Exchange( - const std::vector& outData, - const std::vector& outRanks, - const std::unordered_map>& outBlockIDsMap, - vtkm::Id numLocalTerm, - std::vector& inData, - std::unordered_map>& inDataBlockIDsMap, - vtkm::Id& numTerminateMessages, - bool blockAndWait) -{ - VTKM_ASSERT(outData.size() == outRanks.size()); - - numTerminateMessages = 0; - inDataBlockIDsMap.clear(); - - if (this->GetNumRanks() == 1) - return this->SerialExchange( - outData, outRanks, outBlockIDsMap, numLocalTerm, inData, inDataBlockIDsMap, blockAndWait); - -#ifdef VTKM_ENABLE_MPI - - //dstRank, vector of (particles,blockIDs) - std::unordered_map> sendData; - - std::size_t numP = outData.size(); - for (std::size_t i = 0; i < numP; i++) - { - const auto& bids = outBlockIDsMap.find(outData[i].GetID())->second; - sendData[outRanks[i]].emplace_back(std::make_pair(outData[i], bids)); - } - - //Do all the sends first. - if (numLocalTerm > 0) - this->SendAllMsg({ MSG_TERMINATE, static_cast(numLocalTerm) }); - this->SendParticles(sendData); - this->CheckPendingSendRequests(); - - //Check if we have anything coming in. - std::vector particleData; - std::vector msgData; - if (RecvAny(&msgData, &particleData, blockAndWait)) - { - for (const auto& it : particleData) - for (const auto& v : it.second) - { - const auto& p = v.first; - const auto& bids = v.second; - inData.emplace_back(p); - inDataBlockIDsMap[p.GetID()] = bids; - } - - for (const auto& m : msgData) - { - if (m.second[0] == MSG_TERMINATE) - numTerminateMessages += static_cast(m.second[1]); - } - } -#endif -} - - -#ifdef VTKM_ENABLE_MPI - -VTKM_CONT -template -void ParticleMessenger::RegisterMessages(int msgSz, int nParticles, int numBlockIds) -{ - //Determine buffer size for msg and particle tags. - std::size_t messageBuffSz = CalcMessageBufferSize(msgSz + 1); - std::size_t particleBuffSz = CalcParticleBufferSize(nParticles, numBlockIds); - - int numRecvs = std::min(64, this->GetNumRanks() - 1); - - this->RegisterTag(ParticleMessenger::MESSAGE_TAG, numRecvs, messageBuffSz); - this->RegisterTag(ParticleMessenger::PARTICLE_TAG, numRecvs, particleBuffSz); - - this->InitializeBuffers(); -} - -VTKM_CONT -template -void ParticleMessenger::SendMsg(int dst, const std::vector& msg) -{ - vtkmdiy::MemoryBuffer buff; - - //Write data. - vtkmdiy::save(buff, this->GetRank()); - vtkmdiy::save(buff, msg); - this->SendData(dst, ParticleMessenger::MESSAGE_TAG, buff); -} - -VTKM_CONT -template -void ParticleMessenger::SendAllMsg(const std::vector& msg) -{ - for (int i = 0; i < this->GetNumRanks(); i++) - if (i != this->GetRank()) - this->SendMsg(i, msg); -} - -VTKM_CONT -template -bool ParticleMessenger::RecvAny(std::vector* msgs, - std::vector* recvParticles, - bool blockAndWait) -{ - std::set tags; - if (msgs) - { - tags.insert(ParticleMessenger::MESSAGE_TAG); - msgs->resize(0); - } - if (recvParticles) - { - tags.insert(ParticleMessenger::PARTICLE_TAG); - recvParticles->resize(0); - } - - if (tags.empty()) - return false; - - std::vector> buffers; - if (!this->RecvData(tags, buffers, blockAndWait)) - return false; - - for (auto& buff : buffers) - { - if (buff.first == ParticleMessenger::MESSAGE_TAG) - { - int sendRank; - std::vector m; - vtkmdiy::load(buff.second, sendRank); - vtkmdiy::load(buff.second, m); - msgs->emplace_back(std::make_pair(sendRank, m)); - } - else if (buff.first == ParticleMessenger::PARTICLE_TAG) - { - int sendRank; - std::vector particles; - - vtkmdiy::load(buff.second, sendRank); - vtkmdiy::load(buff.second, particles); - recvParticles->emplace_back(std::make_pair(sendRank, particles)); - } - } - - return true; -} - -VTKM_CONT -template -template class Container, typename Allocator> -inline void ParticleMessenger::SendParticles(int dst, - const Container& c) -{ - if (dst == this->GetRank()) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Error, "Error. Sending a particle to yourself."); - return; - } - if (c.empty()) - return; - - vtkmdiy::MemoryBuffer bb; - vtkmdiy::save(bb, this->GetRank()); - vtkmdiy::save(bb, c); - this->SendData(dst, ParticleMessenger::PARTICLE_TAG, bb); -} - -VTKM_CONT -template -template class Container, typename Allocator> -inline void ParticleMessenger::SendParticles( - const std::unordered_map>& m) -{ - for (const auto& mit : m) - if (!mit.second.empty()) - this->SendParticles(mit.first, mit.second); -} -#endif - -} -} -} -} // vtkm::filter::flow::internal - -#endif // vtk_m_filter_flow_internal_ParticleMessenger_h diff --git a/vtkm/filter/flow/testing/CMakeLists.txt b/vtkm/filter/flow/testing/CMakeLists.txt index 1f2562c061..5a28b4c770 100644 --- a/vtkm/filter/flow/testing/CMakeLists.txt +++ b/vtkm/filter/flow/testing/CMakeLists.txt @@ -37,14 +37,10 @@ vtkm_unit_tests( #if MPI is enabled. if (VTKm_ENABLE_MPI) set(mpi_unit_tests - UnitTestAdvectionAsynchronousMPI.cxx - UnitTestAdvectionSynchronousMPI.cxx - UnitTestParticleMessengerMPI.cxx - UnitTestPathlineAsynchronousMPI.cxx - UnitTestPathlineSynchronousMPI.cxx + UnitTestAdvectionMPI.cxx + UnitTestPathlineMPI.cxx UnitTestStreamlineAMRMPI.cxx - UnitTestStreamlineAsynchronousMPI.cxx - UnitTestStreamlineSynchronousMPI.cxx + UnitTestStreamlineMPI.cxx ) set(mpi_helpers TestingFlow.cxx @@ -56,5 +52,5 @@ if (VTKm_ENABLE_MPI) SOURCES ${mpi_unit_tests} ${mpi_helpers} USE_VTKM_JOB_POOL ) - set_tests_properties(UnitTestAdvectionAsynchronousMPI_mpi PROPERTIES TIMEOUT 500) + set_tests_properties(UnitTestAdvectionMPI_mpi PROPERTIES TIMEOUT 500) endif() diff --git a/vtkm/filter/flow/testing/TestingFlow.cxx b/vtkm/filter/flow/testing/TestingFlow.cxx index 6ece724d04..a2d3d92fe2 100644 --- a/vtkm/filter/flow/testing/TestingFlow.cxx +++ b/vtkm/filter/flow/testing/TestingFlow.cxx @@ -142,7 +142,6 @@ void TestPartitionedDataSet(vtkm::Id nPerRank, bool useGhost, FilterType fType, bool useThreaded, - bool useAsyncComm, bool useBlockIds, bool duplicateBlocks) { @@ -166,11 +165,6 @@ void TestPartitionedDataSet(vtkm::Id nPerRank, std::cout << " - using ghost cells"; if (useThreaded) std::cout << " - using threaded"; - if (useAsyncComm) - std::cout << " - usingAsyncComm"; - else - std::cout << " - usingSyncComm"; - if (useBlockIds) std::cout << " - using block IDs"; if (duplicateBlocks) @@ -223,15 +217,8 @@ void TestPartitionedDataSet(vtkm::Id nPerRank, if (fType == STREAMLINE) { vtkm::filter::flow::Streamline streamline; - SetFilter(streamline, - stepSize, - numSteps, - fieldName, - seedArray, - useThreaded, - useAsyncComm, - useBlockIds, - blockIds); + SetFilter( + streamline, stepSize, numSteps, fieldName, seedArray, useThreaded, useBlockIds, blockIds); auto out = streamline.Execute(pds); vtkm::Id numOutputs = out.GetNumberOfPartitions(); @@ -255,7 +242,6 @@ void TestPartitionedDataSet(vtkm::Id nPerRank, fieldName, seedArray, useThreaded, - useAsyncComm, useBlockIds, blockIds); @@ -284,15 +270,8 @@ void TestPartitionedDataSet(vtkm::Id nPerRank, AddVectorFields(pds2, fieldName, vecX); vtkm::filter::flow::Pathline pathline; - SetFilter(pathline, - stepSize, - numSteps, - fieldName, - seedArray, - useThreaded, - useAsyncComm, - useBlockIds, - blockIds); + SetFilter( + pathline, stepSize, numSteps, fieldName, seedArray, useThreaded, useBlockIds, blockIds); pathline.SetPreviousTime(time0); pathline.SetNextTime(time1); diff --git a/vtkm/filter/flow/testing/TestingFlow.h b/vtkm/filter/flow/testing/TestingFlow.h index 016b7db793..5eec5196aa 100644 --- a/vtkm/filter/flow/testing/TestingFlow.h +++ b/vtkm/filter/flow/testing/TestingFlow.h @@ -46,7 +46,6 @@ void SetFilter(FilterType& filter, const std::string& fieldName, vtkm::cont::ArrayHandle seedArray, bool useThreaded, - bool useAsyncComm, bool useBlockIds, const std::vector& blockIds) { @@ -55,10 +54,6 @@ void SetFilter(FilterType& filter, filter.SetSeeds(seedArray); filter.SetActiveField(fieldName); filter.SetUseThreadedAlgorithm(useThreaded); - if (useAsyncComm) - filter.SetUseAsynchronousCommunication(); - else - filter.SetUseSynchronousCommunication(); if (useBlockIds) filter.SetBlockIDs(blockIds); @@ -75,7 +70,6 @@ void TestPartitionedDataSet(vtkm::Id nPerRank, bool useGhost, FilterType fType, bool useThreaded, - bool useAsyncComm, bool useBlockIds, bool duplicateBlocks); diff --git a/vtkm/filter/flow/testing/UnitTestAdvectionAsynchronousMPI.cxx b/vtkm/filter/flow/testing/UnitTestAdvectionAsynchronousMPI.cxx deleted file mode 100644 index be6439f64b..0000000000 --- a/vtkm/filter/flow/testing/UnitTestAdvectionAsynchronousMPI.cxx +++ /dev/null @@ -1,59 +0,0 @@ -//============================================================================ -// 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 "TestingFlow.h" - -#include - -#include - -namespace -{ - -void DoTest() -{ - auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); - - FilterType filterType = PARTICLE_ADVECTION; - bool useAsyncComm = true; - - for (vtkm::Id nPerRank = 1; nPerRank < 3; ++nPerRank) - { - for (bool useGhost : { true, false }) - { - for (bool useThreaded : { true, false }) - { - for (bool useBlockIds : { true, false }) - { - //Run blockIds with and without block duplication. - if (useBlockIds && comm.size() > 1) - { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, true); - } - else - { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); - } - } - } - } - } -} - -} // anonymous namespace - -int UnitTestAdvectionAsynchronousMPI(int argc, char* argv[]) -{ - return vtkm::cont::testing::Testing::Run(DoTest, argc, argv); -} diff --git a/vtkm/filter/flow/testing/UnitTestAdvectionSynchronousMPI.cxx b/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx similarity index 71% rename from vtkm/filter/flow/testing/UnitTestAdvectionSynchronousMPI.cxx rename to vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx index a6363b7718..eaf96d9dd5 100644 --- a/vtkm/filter/flow/testing/UnitTestAdvectionSynchronousMPI.cxx +++ b/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx @@ -22,7 +22,6 @@ void DoTest() auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); FilterType filterType = PARTICLE_ADVECTION; - bool useAsyncComm = false; for (vtkm::Id nPerRank = 1; nPerRank < 3; ++nPerRank) { @@ -35,15 +34,12 @@ void DoTest() //Run blockIds with and without block duplication. if (useBlockIds && comm.size() > 1) { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, true); + TestPartitionedDataSet(nPerRank, useGhost, filterType, useThreaded, useBlockIds, false); + TestPartitionedDataSet(nPerRank, useGhost, filterType, useThreaded, useBlockIds, true); } else { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); + TestPartitionedDataSet(nPerRank, useGhost, filterType, useThreaded, useBlockIds, false); } } } @@ -53,7 +49,7 @@ void DoTest() } // anonymous namespace -int UnitTestAdvectionSynchronousMPI(int argc, char* argv[]) +int UnitTestAdvectionMPI(int argc, char* argv[]) { return vtkm::cont::testing::Testing::Run(DoTest, argc, argv); } diff --git a/vtkm/filter/flow/testing/UnitTestParticleMessengerMPI.cxx b/vtkm/filter/flow/testing/UnitTestParticleMessengerMPI.cxx deleted file mode 100644 index ed2a8a2492..0000000000 --- a/vtkm/filter/flow/testing/UnitTestParticleMessengerMPI.cxx +++ /dev/null @@ -1,298 +0,0 @@ -//============================================================================ -// 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 - -namespace -{ -using PCommType = std::pair>; -using MCommType = std::pair>; -using PCommType = std::pair>; -using PRecvCommType = std::pair>; - -class TestMessenger : public vtkm::filter::flow::internal::ParticleMessenger -{ -public: - TestMessenger(vtkmdiy::mpi::communicator& comm, - bool useAsyncComm, - const vtkm::filter::flow::internal::BoundsMap& bm, - int msgSz = 1, - int numParticles = 1, - int numBlockIds = 1) - : ParticleMessenger(comm, useAsyncComm, bm, msgSz, numParticles, numBlockIds) - { - } - - void GetBufferSizes(int numParticles, - int numBlockIds, - int msgSz, - std::size_t& pBuffSize, - std::size_t& mBuffSize) - { - pBuffSize = this->CalcParticleBufferSize(numParticles, numBlockIds); - mBuffSize = this->CalcMessageBufferSize(msgSz); - } - - void SendP(int dst, - const std::vector& p, - const std::vector>& bids) - { - std::vector data; - for (std::size_t i = 0; i < p.size(); i++) - data.push_back(PCommType(p[i], bids[i])); - - this->SendParticles(dst, data); - } - - void SendM(int dst, const std::vector& msg) { this->SendMsg(dst, { msg }); } - - void SendMAll(int msg) { this->SendAllMsg({ msg }); } - - bool ReceiveAnything(std::vector* msgs, - std::vector* recvParticles, - bool blockAndWait = false) - { - return this->RecvAny(msgs, recvParticles, blockAndWait); - } -}; - -void ValidateReceivedParticles( - int sendRank, - const std::vector& recvP, - const std::vector>& particles, - const std::vector>>& particleBlockIds) -{ - //Make sure the right number of particles were received from sender. - std::size_t numReqParticles = particles[static_cast(sendRank)].size(); - std::size_t numRecvParticles = recvP.size(); - VTKM_TEST_ASSERT(numReqParticles == numRecvParticles, "Wrong number of particles received."); - - //Make sure each particle is the same. - for (std::size_t i = 0; i < numRecvParticles; i++) - { - const auto& reqP = particles[static_cast(sendRank)][i]; - const auto& p = recvP[i].first; - - VTKM_TEST_ASSERT(p.GetPosition() == reqP.GetPosition(), - "Received particle has wrong Position."); - VTKM_TEST_ASSERT(p.GetTime() == reqP.GetTime(), "Received particle has wrong Time."); - VTKM_TEST_ASSERT(p.GetID() == reqP.GetID(), "Received particle has wrong ID."); - VTKM_TEST_ASSERT(p.GetNumberOfSteps() == reqP.GetNumberOfSteps(), - "Received particle has wrong NumSteps."); - - VTKM_TEST_ASSERT(p.GetPosition() == reqP.GetPosition() && p.GetTime() == reqP.GetTime() && - p.GetID() == reqP.GetID() && p.GetNumberOfSteps() == reqP.GetNumberOfSteps(), - "Received particle has wrong values."); - - const auto& reqBids = particleBlockIds[static_cast(sendRank)][i]; - const auto& bids = recvP[i].second; - VTKM_TEST_ASSERT(reqBids.size() == bids.size(), "Wrong number of particle block ids."); - for (std::size_t j = 0; j < bids.size(); j++) - VTKM_TEST_ASSERT(bids[j] == reqBids[j], "Wrong block Id."); - } -} - -void ValidateReceivedMessage(int sendRank, - const std::vector& recvMsg, - const std::vector>& messages) -{ - const auto& reqMsg = messages[static_cast(sendRank)]; - VTKM_TEST_ASSERT(recvMsg.size() == reqMsg.size(), "Wrong message size received"); - - for (std::size_t i = 0; i < recvMsg.size(); i++) - VTKM_TEST_ASSERT(reqMsg[i] == recvMsg[i], "Wrong message value received"); -} - -void TestParticleMessenger() -{ - auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); - - //Only works for 2 or more ranks. - if (comm.size() == 1) - return; - vtkm::filter::flow::internal::BoundsMap boundsMap; - - int maxMsgSz = 100; - int maxNumParticles = 128; - int maxNumBlockIds = 5 * comm.size(); - TestMessenger messenger( - comm, true, boundsMap, maxMsgSz / 2, maxNumParticles / 2, maxNumBlockIds / 2); - - //create some data. - std::vector> particles(comm.size()); - std::vector>> particleBlockIds(comm.size()); - std::vector> messages(comm.size()); - - std::random_device device; - std::default_random_engine generator(static_cast(83921)); - vtkm::FloatDefault v0(-100), v1(100); - std::uniform_real_distribution floatDist(v0, v1); - std::uniform_int_distribution idDist(0, 10000), bidDist(1, maxNumBlockIds); - std::uniform_int_distribution nPDist(1, maxNumParticles), nStepsDist(10, 100); - std::uniform_int_distribution msgSzDist(1, maxMsgSz); - - //initialize particles and messages. - std::size_t rank = static_cast(comm.rank()); - std::size_t numRanks = static_cast(comm.size()); - - vtkm::Id pid = 0; - for (std::size_t r = 0; r < numRanks; r++) - { - int nP = nPDist(generator); - std::vector pvec; - std::vector> blockIds; - for (int p = 0; p < nP; p++) - { - vtkm::Particle particle; - particle.SetPosition({ floatDist(generator), floatDist(generator), floatDist(generator) }); - particle.SetTime(floatDist(generator)); - particle.SetID(pid++); - particle.SetNumberOfSteps(nStepsDist(generator)); - pvec.push_back(particle); - - std::vector bids(bidDist(generator)); - for (auto& b : bids) - b = static_cast(idDist(generator)); - blockIds.push_back(bids); - } - - //set the particles for this rank. - particles[r] = pvec; - particleBlockIds[r] = blockIds; - - //set the messages for this rank. - std::vector msg(msgSzDist(generator)); - for (auto& m : msg) - m = idDist(generator); - messages[r] = msg; - } - - bool done = false; - - std::uniform_int_distribution rankDist(0, comm.size() - 1); - int particleRecvCtr = 0, msgRecvCtr = 0; - - constexpr int DONE_MSG = -100; - - while (!done) - { - int dst = rankDist(generator); - if (dst != comm.rank()) - { - std::vector sendP = particles[rank]; - std::vector> sendIds = particleBlockIds[rank]; - messenger.SendP(dst, sendP, sendIds); - } - - dst = rankDist(generator); - if (dst != comm.rank()) - messenger.SendM(dst, messages[rank]); - - std::vector msgData; - std::vector particleData; - if (messenger.ReceiveAnything(&msgData, &particleData)) - { - if (!msgData.empty()) - msgRecvCtr++; - if (!particleData.empty()) - particleRecvCtr++; - - //Validate what we received. - for (const auto& md : msgData) - { - int sendRank = md.first; - const auto& recvM = md.second; - - //check for done message. - if (sendRank == 0 && recvM.size() == 1 && recvM[0] == DONE_MSG) - done = true; - else - ValidateReceivedMessage(sendRank, recvM, messages); - } - - for (const auto& pd : particleData) - { - int sendRank = pd.first; - const auto& recvP = pd.second; - ValidateReceivedParticles(sendRank, recvP, particles, particleBlockIds); - } - - //We are done once rank0 receives at least 25 messages and particles. - if (rank == 0 && msgRecvCtr > 25 && particleRecvCtr > 25) - { - done = true; - messenger.SendMAll(DONE_MSG); - } - } - } - - comm.barrier(); -} - -void TestBufferSizes() -{ - //Make sure the buffer sizes are correct. - auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); - vtkm::filter::flow::internal::BoundsMap boundsMap; - - std::vector mSzs = { 1, 2, 3, 4, 5 }; - std::vector numPs = { 1, 2, 3, 4, 5 }; - std::vector numBids = { 0, 1, 2, 3, 4, 5 }; - - for (const auto& mSz : mSzs) - for (const auto& numP : numPs) - for (const auto& nBids : numBids) - { - TestMessenger messenger(comm, true, boundsMap); - - std::size_t pSize, mSize; - messenger.GetBufferSizes(numP, nBids, mSz, pSize, mSize); - - //Make sure message buffers are the right size. - int rank = 0; - vtkmdiy::MemoryBuffer mbM; - std::vector msg(mSz); - vtkmdiy::save(mbM, rank); - vtkmdiy::save(mbM, msg); - VTKM_TEST_ASSERT(mbM.size() == mSize, "Message buffer sizes not equal"); - - //Make sure particle buffers are the right size. - std::vector particleData; - for (int i = 0; i < numP; i++) - { - vtkm::Particle p; - std::vector bids(nBids, 0); - particleData.push_back(std::make_pair(p, bids)); - } - - vtkmdiy::MemoryBuffer mbP; - vtkmdiy::save(mbP, rank); - vtkmdiy::save(mbP, particleData); - VTKM_TEST_ASSERT(mbP.size() == pSize, "Particle buffer sizes not equal"); - } -} - -void TestParticleMessengerMPI() -{ - TestBufferSizes(); - TestParticleMessenger(); -} -} - -int UnitTestParticleMessengerMPI(int argc, char* argv[]) -{ - return vtkm::cont::testing::Testing::Run(TestParticleMessengerMPI, argc, argv); -} diff --git a/vtkm/filter/flow/testing/UnitTestPathlineAsynchronousMPI.cxx b/vtkm/filter/flow/testing/UnitTestPathlineMPI.cxx similarity index 71% rename from vtkm/filter/flow/testing/UnitTestPathlineAsynchronousMPI.cxx rename to vtkm/filter/flow/testing/UnitTestPathlineMPI.cxx index 5eabb9df8d..59fc358293 100644 --- a/vtkm/filter/flow/testing/UnitTestPathlineAsynchronousMPI.cxx +++ b/vtkm/filter/flow/testing/UnitTestPathlineMPI.cxx @@ -22,7 +22,6 @@ void DoTest() auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); FilterType filterType = PATHLINE; - bool useAsyncComm = true; for (vtkm::Id nPerRank = 1; nPerRank < 3; ++nPerRank) { @@ -35,15 +34,12 @@ void DoTest() //Run blockIds with and without block duplication. if (useBlockIds && comm.size() > 1) { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, true); + TestPartitionedDataSet(nPerRank, useGhost, filterType, useThreaded, useBlockIds, false); + TestPartitionedDataSet(nPerRank, useGhost, filterType, useThreaded, useBlockIds, true); } else { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); + TestPartitionedDataSet(nPerRank, useGhost, filterType, useThreaded, useBlockIds, false); } } } @@ -53,7 +49,7 @@ void DoTest() } // anonymous namespace -int UnitTestPathlineAsynchronousMPI(int argc, char* argv[]) +int UnitTestPathlineMPI(int argc, char* argv[]) { return vtkm::cont::testing::Testing::Run(DoTest, argc, argv); } diff --git a/vtkm/filter/flow/testing/UnitTestPathlineSynchronousMPI.cxx b/vtkm/filter/flow/testing/UnitTestPathlineSynchronousMPI.cxx deleted file mode 100644 index 1fe976c087..0000000000 --- a/vtkm/filter/flow/testing/UnitTestPathlineSynchronousMPI.cxx +++ /dev/null @@ -1,59 +0,0 @@ -//============================================================================ -// 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 "TestingFlow.h" - -#include - -#include - -namespace -{ - -void DoTest() -{ - auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); - - FilterType filterType = PATHLINE; - bool useAsyncComm = false; - - for (vtkm::Id nPerRank = 1; nPerRank < 3; ++nPerRank) - { - for (bool useGhost : { true, false }) - { - for (bool useThreaded : { true, false }) - { - for (bool useBlockIds : { true, false }) - { - //Run blockIds with and without block duplication. - if (useBlockIds && comm.size() > 1) - { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, true); - } - else - { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); - } - } - } - } - } -} - -} // anonymous namespace - -int UnitTestPathlineSynchronousMPI(int argc, char* argv[]) -{ - return vtkm::cont::testing::Testing::Run(DoTest, argc, argv); -} diff --git a/vtkm/filter/flow/testing/UnitTestStreamlineAsynchronousMPI.cxx b/vtkm/filter/flow/testing/UnitTestStreamlineMPI.cxx similarity index 71% rename from vtkm/filter/flow/testing/UnitTestStreamlineAsynchronousMPI.cxx rename to vtkm/filter/flow/testing/UnitTestStreamlineMPI.cxx index 26fad71aaf..ce7bb1ab85 100644 --- a/vtkm/filter/flow/testing/UnitTestStreamlineAsynchronousMPI.cxx +++ b/vtkm/filter/flow/testing/UnitTestStreamlineMPI.cxx @@ -22,7 +22,6 @@ void DoTest() auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); FilterType filterType = STREAMLINE; - bool useAsyncComm = true; for (vtkm::Id nPerRank = 1; nPerRank < 3; ++nPerRank) { @@ -35,15 +34,12 @@ void DoTest() //Run blockIds with and without block duplication. if (useBlockIds && comm.size() > 1) { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, true); + TestPartitionedDataSet(nPerRank, useGhost, filterType, useThreaded, useBlockIds, false); + TestPartitionedDataSet(nPerRank, useGhost, filterType, useThreaded, useBlockIds, true); } else { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); + TestPartitionedDataSet(nPerRank, useGhost, filterType, useThreaded, useBlockIds, false); } } } @@ -53,7 +49,7 @@ void DoTest() } // anonymous namespace -int UnitTestStreamlineAsynchronousMPI(int argc, char* argv[]) +int UnitTestStreamlineMPI(int argc, char* argv[]) { return vtkm::cont::testing::Testing::Run(DoTest, argc, argv); } diff --git a/vtkm/filter/flow/testing/UnitTestStreamlineSynchronousMPI.cxx b/vtkm/filter/flow/testing/UnitTestStreamlineSynchronousMPI.cxx deleted file mode 100644 index 9550c4dc95..0000000000 --- a/vtkm/filter/flow/testing/UnitTestStreamlineSynchronousMPI.cxx +++ /dev/null @@ -1,59 +0,0 @@ -//============================================================================ -// 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 "TestingFlow.h" - -#include - -#include - -namespace -{ - -void DoTest() -{ - auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); - - FilterType filterType = STREAMLINE; - bool useAsyncComm = false; - - for (vtkm::Id nPerRank = 1; nPerRank < 3; ++nPerRank) - { - for (bool useGhost : { true, false }) - { - for (bool useThreaded : { true, false }) - { - for (bool useBlockIds : { true, false }) - { - //Run blockIds with and without block duplication. - if (useBlockIds && comm.size() > 1) - { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, true); - } - else - { - TestPartitionedDataSet( - nPerRank, useGhost, filterType, useThreaded, useAsyncComm, useBlockIds, false); - } - } - } - } - } -} - -} // anonymous namespace - -int UnitTestStreamlineSynchronousMPI(int argc, char* argv[]) -{ - return vtkm::cont::testing::Testing::Run(DoTest, argc, argv); -} -- GitLab From 58838b792b2f11d41211d4ee607c55d241921b49 Mon Sep 17 00:00:00 2001 From: Dave Pugmire Date: Sun, 7 Jul 2024 15:51:07 -0400 Subject: [PATCH 09/73] Fix for non-mpi build. --- vtkm/filter/flow/internal/ParticleExchanger.h | 50 ++++++------------- 1 file changed, 15 insertions(+), 35 deletions(-) diff --git a/vtkm/filter/flow/internal/ParticleExchanger.h b/vtkm/filter/flow/internal/ParticleExchanger.h index b5a4b6a7d9..3da4eb7374 100644 --- a/vtkm/filter/flow/internal/ParticleExchanger.h +++ b/vtkm/filter/flow/internal/ParticleExchanger.h @@ -59,9 +59,21 @@ public: } private: - // pair(vector of particles, vector of blockIds) - //using ParticleCommType = std::pair, std::vector>; - // pair(particle, bids); + void SerialExchange(const std::vector& outData, + const std::unordered_map>& outBlockIDsMap, + std::vector& inData, + std::unordered_map>& inDataBlockIDsMap) + { + //Copy output to input. + for (const auto& p : outData) + { + const auto& bids = outBlockIDsMap.find(p.GetID())->second; + inData.emplace_back(p); + inDataBlockIDsMap[p.GetID()] = bids; + } + } + +#ifdef VTKM_ENABLE_MPI using ParticleCommType = std::pair>; void CleanupSendBuffers(bool checkRequests) @@ -85,22 +97,6 @@ private: if (err != MPI_SUCCESS) throw vtkm::cont::ErrorFilterExecution( "Error with MPI_Testsome in ParticleExchanger::CleanupSendBuffers"); - -#if 0 - if (num > 0) - { - std::size_t sz = static_cast(num); - for (std::size_t i = 0; i < sz; i++) - { - auto it = this->SendBuffers.find(requests[indices[i]]); - if (it == this->SendBuffers.end()) - throw vtkm::cont::ErrorFilterExecution("Error with requests in ParticleExchanger::CleanupSendBuffers"); - -// delete it->second; -// this->SendBuffers.erase(it->first); - } - } -#endif } void SendParticles(const std::vector& outData, @@ -210,22 +206,6 @@ private: } } - void SerialExchange(const std::vector& outData, - const std::unordered_map>& outBlockIDsMap, - std::vector& inData, - std::unordered_map>& inDataBlockIDsMap) - { - //Copy output to input. - for (const auto& p : outData) - { - const auto& bids = outBlockIDsMap.find(p.GetID())->second; - inData.emplace_back(p); - inDataBlockIDsMap[p.GetID()] = bids; - } - } - - -#ifdef VTKM_ENABLE_MPI MPI_Comm MPIComm; vtkm::Id NumRanks; vtkm::Id Rank; -- GitLab From 165ab3cefcfd0877bc96d860c2b23b4147d3a85b Mon Sep 17 00:00:00 2001 From: Dave Pugmire Date: Mon, 8 Jul 2024 08:14:35 -0400 Subject: [PATCH 10/73] Test non-threaded for dashboards. --- vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx b/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx index eaf96d9dd5..9a992ed8e5 100644 --- a/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx +++ b/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx @@ -29,6 +29,8 @@ void DoTest() { for (bool useThreaded : { true, false }) { + if (useThreaded) + continue; for (bool useBlockIds : { true, false }) { //Run blockIds with and without block duplication. -- GitLab From aed89094149124c991493735b3912e5997c113af Mon Sep 17 00:00:00 2001 From: "Gunther H. Weber" Date: Thu, 6 Jun 2024 14:46:58 -0700 Subject: [PATCH 11/73] Removed duplicate tests for contour tree --- vtkm/worklet/testing/CMakeLists.txt | 2 - ...gContourTreeUniformDistributedLoadArrays.h | 120 ------ .../UnitTestContourTreeUniformDistributed.cxx | 395 ------------------ 3 files changed, 517 deletions(-) delete mode 100644 vtkm/worklet/testing/TestingContourTreeUniformDistributedLoadArrays.h delete mode 100644 vtkm/worklet/testing/UnitTestContourTreeUniformDistributed.cxx diff --git a/vtkm/worklet/testing/CMakeLists.txt b/vtkm/worklet/testing/CMakeLists.txt index 8683de07d5..6e38c0e132 100644 --- a/vtkm/worklet/testing/CMakeLists.txt +++ b/vtkm/worklet/testing/CMakeLists.txt @@ -10,14 +10,12 @@ set(headers GenerateTestDataSets.h - TestingContourTreeUniformDistributedLoadArrays.h ) set(unit_tests UnitTestAverageByKey.cxx UnitTestBoundingIntervalHierarchy.cxx UnitTestCellDeepCopy.cxx - UnitTestContourTreeUniformDistributed.cxx UnitTestCosmoTools.cxx UnitTestDescriptiveStatistics.cxx UnitTestDispatcherBase.cxx diff --git a/vtkm/worklet/testing/TestingContourTreeUniformDistributedLoadArrays.h b/vtkm/worklet/testing/TestingContourTreeUniformDistributedLoadArrays.h deleted file mode 100644 index 290532cda6..0000000000 --- a/vtkm/worklet/testing/TestingContourTreeUniformDistributedLoadArrays.h +++ /dev/null @@ -1,120 +0,0 @@ -//============================================================================ -// 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 (c) 2018, The Regents of the University of California, through -// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals -// from the U.S. Dept. of Energy). All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// (1) Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// (2) Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// (3) Neither the name of the University of California, Lawrence Berkeley National -// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -// OF THE POSSIBILITY OF SUCH DAMAGE. -// -#ifndef vtk_m_worklet_testing_contourtree_distributed_load_arrays_h -#define vtk_m_worklet_testing_contourtree_distributed_load_arrays_h - -#include -#include - -namespace vtkm -{ -namespace worklet -{ -namespace testing -{ -namespace contourtree_distributed -{ - -// Types used in binary test files -typedef size_t FileSizeType; -typedef unsigned long long FileIndexType; -const FileIndexType FileIndexMask = 0x07FFFFFFFFFFFFFFLL; -typedef double FileDataType; - -inline void ReadIndexArray(std::ifstream& is, vtkm::cont::ArrayHandle& indexArray) -{ - FileSizeType sz; - is.read(reinterpret_cast(&sz), sizeof(sz)); - //std::cout << "Reading index array of size " << sz << std::endl; - indexArray.Allocate(sz); - auto writePortal = indexArray.WritePortal(); - - for (vtkm::Id i = 0; i < static_cast(sz); ++i) - { - FileIndexType x; - is.read(reinterpret_cast(&x), sizeof(x)); - // Covert from index type size in file (64 bit) to index type currently used by - // shifting the flag portion of the index accordingly - vtkm::Id shiftedFlagVal = (x & FileIndexMask) | - ((x & ~FileIndexMask) >> ((sizeof(FileIndexType) - sizeof(vtkm::Id)) << 3)); - writePortal.Set(i, shiftedFlagVal); - } -} - -inline void ReadIndexArrayVector(std::ifstream& is, - std::vector>& indexArrayVector) -{ - FileSizeType sz; - is.read(reinterpret_cast(&sz), sizeof(sz)); - //std::cout << "Reading vector of " << sz << " index arrays" << std::endl; - indexArrayVector.resize(sz); - - for (vtkm::Id i = 0; i < static_cast(sz); ++i) - { - ReadIndexArray(is, indexArrayVector[i]); - } -} - -template -inline void ReadDataArray(std::ifstream& is, vtkm::cont::ArrayHandle& dataArray) -{ - FileSizeType sz; - is.read(reinterpret_cast(&sz), sizeof(sz)); - //std::cout << "Reading data array of size " << sz << std::endl; - dataArray.Allocate(sz); - auto writePortal = dataArray.WritePortal(); - - for (vtkm::Id i = 0; i < static_cast(sz); ++i) - { - FileDataType x; - is.read(reinterpret_cast(&x), sizeof(x)); - //std::cout << "Read " << x << std::endl; - writePortal.Set( - i, - FieldType(x)); // Test data is stored as double but generally is also ok to be cast to float. - } -} - -} // namespace contourtree_distributed -} // namespace testing -} // namespace worklet -} // namespace vtkm - -#endif diff --git a/vtkm/worklet/testing/UnitTestContourTreeUniformDistributed.cxx b/vtkm/worklet/testing/UnitTestContourTreeUniformDistributed.cxx deleted file mode 100644 index e5c68258f0..0000000000 --- a/vtkm/worklet/testing/UnitTestContourTreeUniformDistributed.cxx +++ /dev/null @@ -1,395 +0,0 @@ -//============================================================================ -// 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 (c) 2018, The Regents of the University of California, through -// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals -// from the U.S. Dept. of Energy). All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// (1) Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// (2) Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// (3) Neither the name of the University of California, Lawrence Berkeley National -// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -// OF THE POSSIBILITY OF SUCH DAMAGE. -// -//============================================================================= -// -// This code is an extension of the algorithm presented in the paper: -// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. -// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. -// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization -// (LDAV), October 2016, Baltimore, Maryland. -// -// The PPP2 algorithm and software were jointly developed by -// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and -// Oliver Ruebel (LBNL) -//============================================================================== - -// #define DEBUG_PRINT -// #define PRINT_RESULT - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// clang-format off -VTKM_THIRDPARTY_PRE_INCLUDE -#include -VTKM_THIRDPARTY_POST_INCLUDE -// clang-format on - -namespace -{ -template -void LoadHierarchicalContourTree( - const char* filename, - vtkm::worklet::contourtree_distributed::HierarchicalContourTree& ht) -{ - using vtkm::worklet::testing::contourtree_distributed::ReadIndexArray; - using vtkm::worklet::testing::contourtree_distributed::ReadIndexArrayVector; - //template - using vtkm::worklet::testing::contourtree_distributed::ReadDataArray; //; - - std::ifstream is(filename, std::ios_base::binary); - ReadIndexArray(is, ht.RegularNodeGlobalIds); - ReadDataArray(is, ht.DataValues); - ReadIndexArray(is, ht.RegularNodeSortOrder); - ReadIndexArray(is, ht.Regular2Supernode); - ReadIndexArray(is, ht.Superparents); - ReadIndexArray(is, ht.Supernodes); - ReadIndexArray(is, ht.Superarcs); - ReadIndexArray(is, ht.Hyperparents); - ReadIndexArray(is, ht.Super2Hypernode); - ReadIndexArray(is, ht.WhichRound); - ReadIndexArray(is, ht.WhichIteration); - ReadIndexArray(is, ht.Hypernodes); - ReadIndexArray(is, ht.Hyperarcs); - ReadIndexArray(is, ht.Superchildren); - int nRounds; - is.read(reinterpret_cast(&nRounds), sizeof(nRounds)); - //std::cout << "nRounds = " << nRounds << std::endl; - ht.NumRounds = nRounds; - //ht.NumOwnedRegularVertices = 0; - ReadIndexArray(is, ht.NumRegularNodesInRound); - ReadIndexArray(is, ht.NumSupernodesInRound); - ReadIndexArray(is, ht.NumHypernodesInRound); - ReadIndexArray(is, ht.NumIterations); - ReadIndexArrayVector(is, ht.FirstSupernodePerIteration); - ReadIndexArrayVector(is, ht.FirstHypernodePerIteration); -} - -template -void TestContourTreeMeshCombine(const std::string& mesh1_filename, - const std::string& mesh2_filename, - const std::string& combined_filename) -{ - std::cout << "Testing combining meshes " << mesh1_filename << " " << mesh2_filename - << " with expected result " << combined_filename << std::endl; - - vtkm::worklet::contourtree_augmented::ContourTreeMesh contourTreeMesh1; - contourTreeMesh1.Load(mesh1_filename.c_str()); - vtkm::worklet::contourtree_augmented::ContourTreeMesh contourTreeMesh2; - contourTreeMesh2.Load(mesh2_filename.c_str()); - contourTreeMesh2.MergeWith(contourTreeMesh1); - // Result is written to contourTreeMesh2 - vtkm::worklet::contourtree_augmented::ContourTreeMesh combinedContourTreeMesh; - combinedContourTreeMesh.Load(combined_filename.c_str()); - VTKM_TEST_ASSERT( - test_equal_ArrayHandles(contourTreeMesh2.SortedValues, combinedContourTreeMesh.SortedValues)); - VTKM_TEST_ASSERT(test_equal_ArrayHandles(contourTreeMesh2.GlobalMeshIndex, - combinedContourTreeMesh.GlobalMeshIndex)); - VTKM_TEST_ASSERT(test_equal_ArrayHandles(contourTreeMesh2.GlobalMeshIndex, - combinedContourTreeMesh.GlobalMeshIndex)); - VTKM_TEST_ASSERT(test_equal_ArrayHandles(contourTreeMesh2.NeighborConnectivity, - combinedContourTreeMesh.NeighborConnectivity)); - VTKM_TEST_ASSERT(test_equal_ArrayHandles(contourTreeMesh2.NeighborOffsets, - combinedContourTreeMesh.NeighborOffsets)); - VTKM_TEST_ASSERT(contourTreeMesh2.NumVertices == combinedContourTreeMesh.NumVertices); - VTKM_TEST_ASSERT(contourTreeMesh2.MaxNeighbors == combinedContourTreeMesh.MaxNeighbors); -} - -void TestHierarchicalHyperSweeper() -{ - std::cout << "Testing HierarchicalHyperSweeper" << std::endl; - using vtkm::cont::testing::Testing; - using ContourTreeDataFieldType = vtkm::FloatDefault; - - // Test input - const int numBlocks = 4; - const char* filenames[numBlocks] = { "misc/8x9test_HierarchicalAugmentedTree_Block0.dat", - "misc/8x9test_HierarchicalAugmentedTree_Block1.dat", - "misc/8x9test_HierarchicalAugmentedTree_Block2.dat", - "misc/8x9test_HierarchicalAugmentedTree_Block3.dat" }; - vtkm::Id3 globalSize{ 9, 8, 1 }; - vtkm::Id3 sizes[numBlocks] = { { 5, 4, 1 }, { 5, 5, 1 }, { 5, 4, 1 }, { 5, 5, 1 } }; - vtkm::Id3 origins[numBlocks] = { { 0, 0, 0 }, { 0, 3, 0 }, { 4, 0, 0 }, { 4, 3, 0 } }; - vtkm::Id3 blockIndices[numBlocks] = { { 0, 0, 0 }, { 0, 1, 0 }, { 1, 0, 0 }, { 1, 1, 0 } }; - - // Expected output - vtkm::cont::ArrayHandle expectedIntrinsicVolume[numBlocks] = { - vtkm::cont::make_ArrayHandle({ 6, 9, 8, 24, 20, 1, 1 }), - vtkm::cont::make_ArrayHandle({ 6, 9, 8, 24, 20, 1, 1 }), - vtkm::cont::make_ArrayHandle({ 6, 9, 8, 24, 20, 1 }), - vtkm::cont::make_ArrayHandle({ 6, 9, 8, 24, 20, 1, 2 }) - }; - - vtkm::cont::ArrayHandle expectedDependentVolume[numBlocks] = { - vtkm::cont::make_ArrayHandle({ 6, 9, 18, 24, 46, 72, 1 }), - vtkm::cont::make_ArrayHandle({ 6, 9, 18, 24, 46, 72, 1 }), - vtkm::cont::make_ArrayHandle({ 6, 9, 18, 24, 46, 72 }), - vtkm::cont::make_ArrayHandle({ 6, 9, 18, 24, 46, 72, 2 }) - }; - - // Load trees - vtkm::worklet::contourtree_distributed::HierarchicalContourTree - hct[numBlocks]; - for (vtkm::Id blockNo = 0; blockNo < numBlocks; ++blockNo) - { - LoadHierarchicalContourTree(Testing::DataPath(filenames[blockNo]).c_str(), hct[blockNo]); -#ifdef DEBUG_PRINT - std::cout << hct[blockNo].DebugPrint("AfterLoad", __FILE__, __LINE__); -#endif - } - - // Create and add DIY blocks - auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); - vtkm::Id rank = comm.rank(); - - vtkmdiy::Master master(comm, - 1, // Use 1 thread, VTK-M will do the treading - -1 // All block in memory - ); - - // Set up connectivity - using RegularDecomposer = vtkmdiy::RegularDecomposer; - RegularDecomposer::BoolVector shareFace(3, true); - RegularDecomposer::BoolVector wrap(3, false); - RegularDecomposer::CoordinateVector ghosts(3, 1); - RegularDecomposer::DivisionsVector diyDivisions{ 2, 2, 1 }; // HARDCODED FOR TEST - - int numDims = 2; - vtkmdiy::DiscreteBounds diyBounds(2); - diyBounds.min[0] = diyBounds.min[1] = 0; - diyBounds.max[0] = static_cast(globalSize[0]); - diyBounds.max[1] = static_cast(globalSize[1]); - - RegularDecomposer decomposer( - numDims, diyBounds, numBlocks, shareFace, wrap, ghosts, diyDivisions); - - // ... coordinates of local blocks - std::vector vtkmdiyLocalBlockGids(numBlocks); - for (vtkm::Id bi = 0; bi < numBlocks; bi++) - { - RegularDecomposer::DivisionsVector diyCoords(static_cast(numDims)); - auto currentCoords = blockIndices[bi]; - for (vtkm::IdComponent d = 0; d < numDims; ++d) - { - diyCoords[d] = static_cast(currentCoords[d]); - } - vtkmdiyLocalBlockGids[static_cast(bi)] = - RegularDecomposer::coords_to_gid(diyCoords, diyDivisions); - } - - // Define which blocks live on which rank so that vtkmdiy can manage them - vtkmdiy::DynamicAssigner assigner(comm, comm.size(), numBlocks); - for (vtkm::Id bi = 0; bi < numBlocks; bi++) - { - assigner.set_rank(static_cast(rank), - static_cast(vtkmdiyLocalBlockGids[static_cast(bi)])); - } - vtkmdiy::fix_links(master, assigner); - - vtkm::worklet::contourtree_distributed::HyperSweepBlock* - localHyperSweeperBlocks[numBlocks]; - for (vtkm::Id blockNo = 0; blockNo < numBlocks; ++blockNo) - { - localHyperSweeperBlocks[blockNo] = - new vtkm::worklet::contourtree_distributed::HyperSweepBlock( - blockNo, - vtkmdiyLocalBlockGids[blockNo], - origins[blockNo], - sizes[blockNo], - globalSize, - hct[blockNo]); - master.add( - vtkmdiyLocalBlockGids[blockNo], localHyperSweeperBlocks[blockNo], new vtkmdiy::Link()); - } - - master.foreach ( - [](vtkm::worklet::contourtree_distributed::HyperSweepBlock* b, - const vtkmdiy::Master::ProxyWithLink&) { -#ifdef DEBUG_PRINT - std::cout << "Block " << b->GlobalBlockId << std::endl; - std::cout << b->HierarchicalContourTree.DebugPrint( - "Before initializing HyperSweeper", __FILE__, __LINE__); -#endif - // Create HyperSweeper - vtkm::worklet::contourtree_distributed::HierarchicalHyperSweeper - hyperSweeper( - b->GlobalBlockId, b->HierarchicalContourTree, b->IntrinsicVolume, b->DependentVolume); - -#ifdef DEBUG_PRINT - std::cout << "Block " << b->GlobalBlockId << std::endl; - std::cout << b->HierarchicalContourTree.DebugPrint( - "After initializing HyperSweeper", __FILE__, __LINE__); -#endif - - // Create mesh and initialize vertex counts - vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler idRelabeler{ b->Origin, - b->Size, - b->GlobalSize }; - - if (b->GlobalSize[2] <= 1) - { - vtkm::worklet::contourtree_augmented::DataSetMeshTriangulation2DFreudenthal mesh( - vtkm::Id2{ b->Size[0], b->Size[1] }); - hyperSweeper.InitializeIntrinsicVertexCount( - b->HierarchicalContourTree, mesh, idRelabeler, b->IntrinsicVolume); - } - else - { - // TODO/FIXME: For getting owned vertices, it should not make a difference if marching - // cubes or not. Verify. - vtkm::worklet::contourtree_augmented::DataSetMeshTriangulation3DFreudenthal mesh(b->Size); - hyperSweeper.InitializeIntrinsicVertexCount( - b->HierarchicalContourTree, mesh, idRelabeler, b->IntrinsicVolume); - } - -#ifdef DEBUG_PRINT - std::cout << "Block " << b->GlobalBlockId << std::endl; - std::cout << b->HierarchicalContourTree.DebugPrint( - "After initializing intrinsic vertex count", __FILE__, __LINE__); -#endif - - // Initialize dependentVolume by copy from intrinsicVolume - vtkm::cont::Algorithm::Copy(b->IntrinsicVolume, b->DependentVolume); - - // Perform the local hypersweep - hyperSweeper.LocalHyperSweep(); - -#ifdef DEBUG_PRINT - std::cout << "Block " << b->GlobalBlockId << std::endl; - std::cout << b->HierarchicalContourTree.DebugPrint( - "After local hypersweep", __FILE__, __LINE__); -#endif - }); - - // Reduce - // partners for merge over regular block grid - vtkmdiy::RegularSwapPartners partners( - decomposer, // domain decomposition - 2, // radix of k-ary reduction. - true // contiguous: true=distance doubling, false=distance halving - ); - vtkmdiy::reduce(master, - assigner, - partners, - vtkm::worklet::contourtree_distributed::CobmineHyperSweepBlockFunctor< - ContourTreeDataFieldType>{}); - -#ifdef PRINT_RESULT - // Print - vtkm::Id totalVolume = globalSize[0] * globalSize[1] * globalSize[2]; - master.foreach ( - [&totalVolume]( - vtkm::worklet::contourtree_distributed::HyperSweepBlock* b, - const vtkmdiy::Master::ProxyWithLink&) { - std::cout << "Block " << b->GlobalBlockId << std::endl; - std::cout << "=========" << std::endl; - vtkm::worklet::contourtree_augmented::PrintHeader(b->IntrinsicVolume.GetNumberOfValues(), - std::cout); - vtkm::worklet::contourtree_augmented::PrintIndices( - "Intrinsic Volume", b->IntrinsicVolume, -1, std::cout); - vtkm::worklet::contourtree_augmented::PrintIndices( - "Dependent Volume", b->DependentVolume, -1, std::cout); - - std::cout << b->HierarchicalContourTree.DebugPrint( - "Called from DumpVolumes", __FILE__, __LINE__); - std::cout << vtkm::worklet::contourtree_distributed::HierarchicalContourTree< - ContourTreeDataFieldType>::DumpVolumes(b->HierarchicalContourTree.Supernodes, - b->HierarchicalContourTree.Superarcs, - b->HierarchicalContourTree.RegularNodeGlobalIds, - totalVolume, - b->IntrinsicVolume, - b->DependentVolume); - }); -#endif - - // Compare to expected results - master.foreach ( - [&expectedIntrinsicVolume, &expectedDependentVolume]( - vtkm::worklet::contourtree_distributed::HyperSweepBlock* b, - const vtkmdiy::Master::ProxyWithLink&) { -#ifdef DEBUG_PRINT - vtkm::worklet::contourtree_augmented::PrintIndices( - "Intrinsic Volume", b->IntrinsicVolume, -1, std::cout); - vtkm::worklet::contourtree_augmented::PrintIndices( - "Expected Intrinsic Volume", expectedIntrinsicVolume[b->GlobalBlockId], -1, std::cout); - vtkm::worklet::contourtree_augmented::PrintIndices( - "Dependent Volume", b->DependentVolume, -1, std::cout); - vtkm::worklet::contourtree_augmented::PrintIndices( - "Expected Dependent Volume", expectedDependentVolume[b->GlobalBlockId], -1, std::cout); -#endif - VTKM_TEST_ASSERT(test_equal_portals(expectedIntrinsicVolume[b->GlobalBlockId].ReadPortal(), - b->IntrinsicVolume.ReadPortal())); - VTKM_TEST_ASSERT(test_equal_portals(expectedDependentVolume[b->GlobalBlockId].ReadPortal(), - b->DependentVolume.ReadPortal())); - }); - - // Clean-up - for (auto b : localHyperSweeperBlocks) - { - delete b; - } -} - -void TestContourTreeUniformDistributed() -{ - using vtkm::cont::testing::Testing; - TestContourTreeMeshCombine( - Testing::DataPath("misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh1.ctm"), - Testing::DataPath("misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh2.ctm"), - Testing::RegressionImagePath("5x6_7_MC_Rank0_Block0_Round1_CombinedMesh.ctm")); - - TestHierarchicalHyperSweeper(); -} - -} // anonymous namespace - -int UnitTestContourTreeUniformDistributed(int argc, char* argv[]) -{ - return vtkm::cont::testing::Testing::Run(TestContourTreeUniformDistributed, argc, argv); -} -- GitLab From 8d0c76599bccf46dbb335c8f3d63061f27a2ba25 Mon Sep 17 00:00:00 2001 From: "Gunther H. Weber" Date: Fri, 7 Jun 2024 14:50:32 -0700 Subject: [PATCH 12/73] Removed no longer required test LFS files. --- data/baseline/5x6_7_MC_Rank0_Block0_Round1_CombinedMesh.ctm | 3 --- .../misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh1.ctm | 3 --- .../misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh2.ctm | 3 --- data/data/misc/8x9test_HierarchicalAugmentedTree_Block0.dat | 3 --- data/data/misc/8x9test_HierarchicalAugmentedTree_Block1.dat | 3 --- data/data/misc/8x9test_HierarchicalAugmentedTree_Block2.dat | 3 --- data/data/misc/8x9test_HierarchicalAugmentedTree_Block3.dat | 3 --- 7 files changed, 21 deletions(-) delete mode 100644 data/baseline/5x6_7_MC_Rank0_Block0_Round1_CombinedMesh.ctm delete mode 100644 data/data/misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh1.ctm delete mode 100644 data/data/misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh2.ctm delete mode 100644 data/data/misc/8x9test_HierarchicalAugmentedTree_Block0.dat delete mode 100644 data/data/misc/8x9test_HierarchicalAugmentedTree_Block1.dat delete mode 100644 data/data/misc/8x9test_HierarchicalAugmentedTree_Block2.dat delete mode 100644 data/data/misc/8x9test_HierarchicalAugmentedTree_Block3.dat diff --git a/data/baseline/5x6_7_MC_Rank0_Block0_Round1_CombinedMesh.ctm b/data/baseline/5x6_7_MC_Rank0_Block0_Round1_CombinedMesh.ctm deleted file mode 100644 index 143be4cd8f..0000000000 --- a/data/baseline/5x6_7_MC_Rank0_Block0_Round1_CombinedMesh.ctm +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aa4623c6a5f1051038cfdafb9167a1e2625091063e5ee8c1247af45539189eea -size 2657 diff --git a/data/data/misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh1.ctm b/data/data/misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh1.ctm deleted file mode 100644 index 29df1903ab..0000000000 --- a/data/data/misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh1.ctm +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ef15b2a0e8ae45c80c1e55cc8aadb0bbd725d39cf2fb091c2b3dde9d601e3930 -size 1445 diff --git a/data/data/misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh2.ctm b/data/data/misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh2.ctm deleted file mode 100644 index d0c215acd1..0000000000 --- a/data/data/misc/5x6_7_MC_Rank0_Block0_Round1_BeforeCombineMesh2.ctm +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4ec4d071805a7300bf61aa2c017fe5c9892ad10df7495ef40c69799d58c84db8 -size 1522 diff --git a/data/data/misc/8x9test_HierarchicalAugmentedTree_Block0.dat b/data/data/misc/8x9test_HierarchicalAugmentedTree_Block0.dat deleted file mode 100644 index 26e36963dd..0000000000 --- a/data/data/misc/8x9test_HierarchicalAugmentedTree_Block0.dat +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c5c3afc7bdb0fa75bad78fb69ce0557c752b98cab6f000d757ef2706a4498576 -size 2116 diff --git a/data/data/misc/8x9test_HierarchicalAugmentedTree_Block1.dat b/data/data/misc/8x9test_HierarchicalAugmentedTree_Block1.dat deleted file mode 100644 index a2572ad258..0000000000 --- a/data/data/misc/8x9test_HierarchicalAugmentedTree_Block1.dat +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e31bdb91419a32e98dc1061ae6ce348f8fbc6c79b26a4e0bcd352d2b2e62b6a8 -size 2228 diff --git a/data/data/misc/8x9test_HierarchicalAugmentedTree_Block2.dat b/data/data/misc/8x9test_HierarchicalAugmentedTree_Block2.dat deleted file mode 100644 index a961412741..0000000000 --- a/data/data/misc/8x9test_HierarchicalAugmentedTree_Block2.dat +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ccbe5767ce622b4c0e5e93dd6f6243d5c76a5a6f07c1e5cf43fb2b5e2e069255 -size 2076 diff --git a/data/data/misc/8x9test_HierarchicalAugmentedTree_Block3.dat b/data/data/misc/8x9test_HierarchicalAugmentedTree_Block3.dat deleted file mode 100644 index b4e1f46d2a..0000000000 --- a/data/data/misc/8x9test_HierarchicalAugmentedTree_Block3.dat +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6e9f0913c17a3a12338d043c455c6e9cde51c72fa70d619d935fa4369effed45 -size 2316 -- GitLab From 3dca50053e029b3e5d573c2ad6b3e9d11c6f00fc Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 17 Jul 2024 14:06:37 -0400 Subject: [PATCH 13/73] ci: use the `vtkm` for macOS runners --- .gitlab/ci/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/ci/macos.yml b/.gitlab/ci/macos.yml index ca84569deb..5400084b91 100644 --- a/.gitlab/ci/macos.yml +++ b/.gitlab/ci/macos.yml @@ -97,7 +97,7 @@ test:macos_xcode14: .macos_build_tags: tags: - - vtk-m + - vtkm - macos-x86_64 - xcode-14.2 - nonconcurrent -- GitLab From 23bf92a51cfa598296592871c44ad6eb9abf7c2a Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Thu, 25 Jul 2024 15:03:50 -0400 Subject: [PATCH 14/73] Move the flow test data set header The header file `GenerateTestDataSets.h` was located in `vtkm/worklet/testing`. However, the only tests that used this header are in `vtkm/filter/flow/testing`. Thus, the header file is moved to where it is used. --- vtkm/{worklet => filter/flow}/testing/GenerateTestDataSets.h | 0 vtkm/filter/flow/testing/TestingFlow.cxx | 2 +- vtkm/filter/flow/testing/UnitTestLagrangianFilter.cxx | 2 +- vtkm/filter/flow/testing/UnitTestLagrangianStructuresFilter.cxx | 2 +- vtkm/filter/flow/testing/UnitTestStreamlineAMRMPI.cxx | 2 +- vtkm/filter/flow/testing/UnitTestStreamlineFilter.cxx | 2 +- vtkm/filter/flow/testing/UnitTestWorkletParticleAdvection.cxx | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename vtkm/{worklet => filter/flow}/testing/GenerateTestDataSets.h (100%) diff --git a/vtkm/worklet/testing/GenerateTestDataSets.h b/vtkm/filter/flow/testing/GenerateTestDataSets.h similarity index 100% rename from vtkm/worklet/testing/GenerateTestDataSets.h rename to vtkm/filter/flow/testing/GenerateTestDataSets.h diff --git a/vtkm/filter/flow/testing/TestingFlow.cxx b/vtkm/filter/flow/testing/TestingFlow.cxx index 6ece724d04..c1f8edf3db 100644 --- a/vtkm/filter/flow/testing/TestingFlow.cxx +++ b/vtkm/filter/flow/testing/TestingFlow.cxx @@ -15,8 +15,8 @@ #include #include #include +#include #include -#include vtkm::cont::ArrayHandle CreateConstantVectorField(vtkm::Id num, const vtkm::Vec3f& vec) { diff --git a/vtkm/filter/flow/testing/UnitTestLagrangianFilter.cxx b/vtkm/filter/flow/testing/UnitTestLagrangianFilter.cxx index ddb850c83b..572bc7a9b9 100644 --- a/vtkm/filter/flow/testing/UnitTestLagrangianFilter.cxx +++ b/vtkm/filter/flow/testing/UnitTestLagrangianFilter.cxx @@ -13,7 +13,7 @@ #include #include #include -#include +#include namespace { diff --git a/vtkm/filter/flow/testing/UnitTestLagrangianStructuresFilter.cxx b/vtkm/filter/flow/testing/UnitTestLagrangianStructuresFilter.cxx index d6b1a53c95..e44b226760 100644 --- a/vtkm/filter/flow/testing/UnitTestLagrangianStructuresFilter.cxx +++ b/vtkm/filter/flow/testing/UnitTestLagrangianStructuresFilter.cxx @@ -13,7 +13,7 @@ #include #include #include -#include +#include namespace auxiliary { diff --git a/vtkm/filter/flow/testing/UnitTestStreamlineAMRMPI.cxx b/vtkm/filter/flow/testing/UnitTestStreamlineAMRMPI.cxx index 32358e25fe..0aa7b613a8 100644 --- a/vtkm/filter/flow/testing/UnitTestStreamlineAMRMPI.cxx +++ b/vtkm/filter/flow/testing/UnitTestStreamlineAMRMPI.cxx @@ -14,8 +14,8 @@ #include #include #include +#include #include -#include namespace { diff --git a/vtkm/filter/flow/testing/UnitTestStreamlineFilter.cxx b/vtkm/filter/flow/testing/UnitTestStreamlineFilter.cxx index 75a7ddbfb2..4e24d59178 100644 --- a/vtkm/filter/flow/testing/UnitTestStreamlineFilter.cxx +++ b/vtkm/filter/flow/testing/UnitTestStreamlineFilter.cxx @@ -17,8 +17,8 @@ #include #include #include +#include #include -#include namespace { diff --git a/vtkm/filter/flow/testing/UnitTestWorkletParticleAdvection.cxx b/vtkm/filter/flow/testing/UnitTestWorkletParticleAdvection.cxx index dfca21a2b4..d13a529b3e 100644 --- a/vtkm/filter/flow/testing/UnitTestWorkletParticleAdvection.cxx +++ b/vtkm/filter/flow/testing/UnitTestWorkletParticleAdvection.cxx @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,6 @@ #include #include #include -#include #include -- GitLab From 9efc8712de1f5a3ef82c39b4e3f4118e085c35d3 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Fri, 26 Jul 2024 13:33:05 -0400 Subject: [PATCH 15/73] Add a form of CellInterpolate that operates on whole cell sets The initial implementation of `CellInterpolate` takes arguments that are expected from a topology map worklet. However, sometimes you want to interplate cells that are queried from locators or otherwise come from a `WholeCellSet` control signature argument. A new form of `CellInterpolate` is added to handle this case. --- docs/changelog/cell-interpolation.global.md | 8 ++ .../examples/GuideExampleCellOperations.cxx | 76 ++++++++++++++++--- docs/users-guide/working-with-cells.rst | 10 +++ vtkm/exec/CellInterpolate.h | 36 ++++++++- vtkm/exec/testing/UnitTestCellDerivative.cxx | 4 +- vtkm/exec/testing/UnitTestCellInterpolate.cxx | 60 ++++++++++++++- .../testing/UnitTestParametricCoordinates.cxx | 4 +- 7 files changed, 180 insertions(+), 18 deletions(-) create mode 100644 docs/changelog/cell-interpolation.global.md diff --git a/docs/changelog/cell-interpolation.global.md b/docs/changelog/cell-interpolation.global.md new file mode 100644 index 0000000000..a011bb601b --- /dev/null +++ b/docs/changelog/cell-interpolation.global.md @@ -0,0 +1,8 @@ +## Add a form of CellInterpolate that operates on whole cell sets + +The initial implementation of `CellInterpolate` takes arguments that are +expected from a topology map worklet. However, sometimes you want to +interplate cells that are queried from locators or otherwise come from a +`WholeCellSet` control signature argument. + +A new form of `CellInterpolate` is added to handle this case. diff --git a/docs/users-guide/examples/GuideExampleCellOperations.cxx b/docs/users-guide/examples/GuideExampleCellOperations.cxx index 17deb28870..78958de9f0 100644 --- a/docs/users-guide/examples/GuideExampleCellOperations.cxx +++ b/docs/users-guide/examples/GuideExampleCellOperations.cxx @@ -11,9 +11,10 @@ #include #include -#include +#include #include +#include #include #include @@ -25,7 +26,7 @@ namespace //// struct CellCenters : vtkm::worklet::WorkletVisitCellsWithPoints { - using ControlSignature = void(CellSetIn, + using ControlSignature = void(CellSetIn inputCells, FieldInPoint inputField, FieldOutCell outputField); using ExecutionSignature = void(CellShape, PointCount, _2, _3); @@ -52,6 +53,46 @@ struct CellCenters : vtkm::worklet::WorkletVisitCellsWithPoints //// END-EXAMPLE CellCenters //// +//// +//// BEGIN-EXAMPLE CellLookupInterp +//// +struct CellLookupInterp : vtkm::worklet::WorkletMapField +{ + using ControlSignature = void(WholeCellSetIn<> inputCells, + WholeArrayIn inputField, + FieldOut outputField); + using ExecutionSignature = void(InputIndex, _1, _2, _3); + using InputDomain = _3; + + template + VTKM_EXEC void operator()(vtkm::Id index, + const StructureType& structure, + const FieldInPortalType& inputField, + FieldOutType& outputField) const + { + // Normally you would use something like a locator to find the index to + // a cell that matches some query criteria. For demonstration purposes, + // we are just using a passed in index. + auto shape = structure.GetCellShape(index); + vtkm::IdComponent pointCount = structure.GetNumberOfIndices(index); + + vtkm::Vec3f center; + vtkm::ErrorCode status = + vtkm::exec::ParametricCoordinatesCenter(pointCount, shape, center); + if (status != vtkm::ErrorCode::Success) + { + this->RaiseError(vtkm::ErrorString(status)); + return; + } + + auto pointIndices = structure.GetIndices(index); + vtkm::exec::CellInterpolate(pointIndices, inputField, center, shape, outputField); + } +}; +//// +//// END-EXAMPLE CellLookupInterp +//// + void TryCellCenters() { std::cout << "Trying CellCenters worklet." << std::endl; @@ -62,14 +103,25 @@ void TryCellCenters() using ArrayType = vtkm::cont::ArrayHandle; ArrayType centers; - vtkm::worklet::DispatcherMapTopology dispatcher; - dispatcher.Invoke(dataSet.GetCellSet(), - dataSet.GetField("pointvar").GetData().AsArrayHandle(), - centers); - + vtkm::cont::Invoker invoke; + invoke(CellCenters{}, + dataSet.GetCellSet(), + dataSet.GetField("pointvar").GetData().AsArrayHandle(), + centers); vtkm::cont::printSummary_ArrayHandle(centers, std::cout); std::cout << std::endl; + VTKM_TEST_ASSERT(centers.GetNumberOfValues() == + dataSet.GetCellSet().GetNumberOfCells(), + "Bad number of cells."); + VTKM_TEST_ASSERT(test_equal(60.1875, centers.ReadPortal().Get(0)), "Bad first value."); + centers.Fill(0); + invoke(CellLookupInterp{}, + dataSet.GetCellSet(), + dataSet.GetField("pointvar").GetData().AsArrayHandle(), + centers); + vtkm::cont::printSummary_ArrayHandle(centers, std::cout); + std::cout << std::endl; VTKM_TEST_ASSERT(centers.GetNumberOfValues() == dataSet.GetCellSet().GetNumberOfCells(), "Bad number of cells."); @@ -122,11 +174,13 @@ void TryCellDerivatives() using ArrayType = vtkm::cont::ArrayHandle; vtkm::cont::ArrayHandle derivatives; + vtkm::cont::Invoker invoke; vtkm::worklet::DispatcherMapTopology dispatcher; - dispatcher.Invoke(dataSet.GetCellSet(), - dataSet.GetField("pointvar").GetData().AsArrayHandle(), - dataSet.GetCoordinateSystem().GetData(), - derivatives); + invoke(CellDerivatives{}, + dataSet.GetCellSet(), + dataSet.GetField("pointvar").GetData().AsArrayHandle(), + dataSet.GetCoordinateSystem().GetData(), + derivatives); vtkm::cont::printSummary_ArrayHandle(derivatives, std::cout); std::cout << std::endl; diff --git a/docs/users-guide/working-with-cells.rst b/docs/users-guide/working-with-cells.rst index a36d27afd2..0002a13e87 100644 --- a/docs/users-guide/working-with-cells.rst +++ b/docs/users-guide/working-with-cells.rst @@ -168,6 +168,16 @@ The :file:`vtkm/exec/CellInterpolate.h` header contains the function :func:`vtkm :file: GuideExampleCellOperations.cxx :caption: Interpolating field values to a cell's center. +The previous form of :func:`vtkm::exec::CellInterpolate` is typically used within a :class:`vtkm::worklet::WorkletVisitCellsWithPoints` to interpolate within cells provided by the worklet. +There is another form of :func:`vtkm::exec::CellInterpolate` that is used when provided with a cell set structure from a ``WholeCellSetIn``. +Using ``WholeCellSetIn`` is described in more detail in :secref:`globals:Whole Cell Sets`, but the summary is that you can get the shape and incident points of any cell in the mesh. +In this case, the alternate form of :func:`vtkm::exec::CellInterpolate` takes a ``Vec`` of the point indices for the cell and an array portal of all field values. + +.. doxygenfunction:: vtkm::exec::CellInterpolate(const IndicesVecType&, const FieldPortalType&, const vtkm::Vec&, CellShapeTag, typename FieldPortalType::ValueType&) + +.. load-example:: CellLookupInterp + :file: GuideExampleCellOperations.cxx + :caption: Interpolating field values in a cell queried from a global structure. ------------------------------ Derivatives diff --git a/vtkm/exec/CellInterpolate.h b/vtkm/exec/CellInterpolate.h index 3be4b50351..c65a10fa0b 100644 --- a/vtkm/exec/CellInterpolate.h +++ b/vtkm/exec/CellInterpolate.h @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -156,7 +158,7 @@ VTKM_EXEC vtkm::ErrorCode CellInterpolate(const vtkm::VecAxisAlignedPointCoordin /// \brief Interpolate a point field in a cell. /// /// Given the point field values for each node and the parametric coordinates -/// of a point within the cell, interpolates the field to that point. +/// of a location within the cell, interpolates the field to that location. /// /// @param[in] pointFieldValues A list of field values for each point in the cell. This /// usually comes from a `FieldInPoint` argument in a @@ -184,6 +186,38 @@ VTKM_EXEC vtkm::ErrorCode CellInterpolate(const FieldVecType& pointFieldValues, return status; } +//----------------------------------------------------------------------------- +/// @brief Interpolate a point field in a cell. +/// +/// Given the indices of the points for each node in a `Vec`, a portal to the point +/// field values, and the parametric coordinates of a location within the cell, interpolates +/// to that location. +/// +/// @param[in] pointIndices A list of point indices for each point in the cell. This +/// usually comes from a `GetIndices()` call on the structure object provided by +/// a `WholeCellSetIn` argument to a worklet. +/// @param[in] pointFieldPortal An array portal containing all the values in a point +/// field array. This usually comes from a `WholeArrayIn` worklet argument. +/// @param[in] parametricCoords The parametric coordinates where you want to get the +/// interpolaged field value for. +/// @param[in] shape A tag of type `CellShapeTag*` to identify the shape of the cell. +/// @param[out] result Value to store the interpolated field. +template +VTKM_EXEC vtkm::ErrorCode CellInterpolate(const IndicesVecType& pointIndices, + const FieldPortalType& pointFieldPortal, + const vtkm::Vec& parametricCoords, + CellShapeTag shape, + typename FieldPortalType::ValueType& result) +{ + return CellInterpolate(vtkm::make_VecFromPortalPermute(&pointIndices, pointFieldPortal), + parametricCoords, + shape, + result); +} + } } // namespace vtkm::exec diff --git a/vtkm/exec/testing/UnitTestCellDerivative.cxx b/vtkm/exec/testing/UnitTestCellDerivative.cxx index eed69eaa86..cc1b562d6f 100644 --- a/vtkm/exec/testing/UnitTestCellDerivative.cxx +++ b/vtkm/exec/testing/UnitTestCellDerivative.cxx @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include @@ -330,5 +330,5 @@ void TestDerivative() int UnitTestCellDerivative(int argc, char* argv[]) { - return vtkm::testing::Testing::Run(TestDerivative, argc, argv); + return vtkm::cont::testing::Testing::Run(TestDerivative, argc, argv); } diff --git a/vtkm/exec/testing/UnitTestCellInterpolate.cxx b/vtkm/exec/testing/UnitTestCellInterpolate.cxx index 5c951b161a..0b617d07e1 100644 --- a/vtkm/exec/testing/UnitTestCellInterpolate.cxx +++ b/vtkm/exec/testing/UnitTestCellInterpolate.cxx @@ -18,7 +18,7 @@ #include #include -#include +#include #define CHECK_CALL(call) \ VTKM_TEST_ASSERT((call) == vtkm::ErrorCode::Success, "Call resulted in error.") @@ -91,6 +91,49 @@ struct TestInterpolateFunctor "Interpolation at center not average value."); } + template + void DoTestWithIndices(CellShapeTag shape, + const IndexVecType& pointIndices, + const FieldPortalType& fieldValues) const + { + vtkm::IdComponent numPoints = pointIndices.GetNumberOfComponents(); + if (numPoints < 1) + { + return; + } + + FieldType averageValue = vtkm::TypeTraits::ZeroInitialization(); + for (vtkm::IdComponent pointIndex = 0; pointIndex < numPoints; pointIndex++) + { + averageValue = averageValue + fieldValues.Get(pointIndices[pointIndex]); + } + averageValue = static_cast(1.0 / numPoints) * averageValue; + + for (vtkm::IdComponent pointIndex = 0; pointIndex < numPoints; pointIndex++) + { + vtkm::Vec3f pcoord; + CHECK_CALL(vtkm::exec::ParametricCoordinatesPoint(numPoints, pointIndex, shape, pcoord)); + FieldType interpolatedValue; + CHECK_CALL( + vtkm::exec::CellInterpolate(pointIndices, fieldValues, pcoord, shape, interpolatedValue)); + + VTKM_TEST_ASSERT(test_equal(fieldValues.Get(pointIndices[pointIndex]), interpolatedValue), + "Interpolation at point not point value."); + } + + if (shape.Id != vtkm::CELL_SHAPE_POLY_LINE) + { + vtkm::Vec3f pcoord; + CHECK_CALL(vtkm::exec::ParametricCoordinatesCenter(numPoints, shape, pcoord)); + FieldType interpolatedValue; + CHECK_CALL( + vtkm::exec::CellInterpolate(pointIndices, fieldValues, pcoord, shape, interpolatedValue)); + + VTKM_TEST_ASSERT(test_equal(averageValue, interpolatedValue), + "Interpolation at center not average value."); + } + } + template void DoTest(CellShapeTag shape, vtkm::IdComponent numPoints) const { @@ -102,6 +145,19 @@ struct TestInterpolateFunctor } this->DoTestWithField(shape, fieldValues); + + vtkm::cont::ArrayHandle fieldArray; + fieldArray.Allocate(41); + SetPortal(fieldArray.WritePortal()); + + vtkm::VecVariable pointIndices; + for (vtkm::IdComponent pointIndex = 0; pointIndex < numPoints; pointIndex++) + { + vtkm::Id globalIndex = (7 + (13 * pointIndex)) % 41; + pointIndices.Append(globalIndex); + } + + this->DoTestWithIndices(shape, pointIndices, fieldArray.ReadPortal()); } template @@ -154,5 +210,5 @@ void TestInterpolate() int UnitTestCellInterpolate(int argc, char* argv[]) { - return vtkm::testing::Testing::Run(TestInterpolate, argc, argv); + return vtkm::cont::testing::Testing::Run(TestInterpolate, argc, argv); } diff --git a/vtkm/exec/testing/UnitTestParametricCoordinates.cxx b/vtkm/exec/testing/UnitTestParametricCoordinates.cxx index 31cbe91903..e3854733d3 100644 --- a/vtkm/exec/testing/UnitTestParametricCoordinates.cxx +++ b/vtkm/exec/testing/UnitTestParametricCoordinates.cxx @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include @@ -232,5 +232,5 @@ void TestAllPCoords() int UnitTestParametricCoordinates(int argc, char* argv[]) { - return vtkm::testing::Testing::Run(TestAllPCoords, argc, argv); + return vtkm::cont::testing::Testing::Run(TestAllPCoords, argc, argv); } -- GitLab From 273485a8eb28481d5d6d3777d59350e9bfe18ff0 Mon Sep 17 00:00:00 2001 From: Vicente Adolfo Bolea Sanchez Date: Fri, 2 Aug 2024 19:37:59 -0400 Subject: [PATCH 16/73] ci: update latest cmake to 3.30.2 --- .gitlab/ci/config/cmake.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab/ci/config/cmake.sh b/.gitlab/ci/config/cmake.sh index 17c9550991..7cfc5a5292 100755 --- a/.gitlab/ci/config/cmake.sh +++ b/.gitlab/ci/config/cmake.sh @@ -15,14 +15,14 @@ set -ex -version="${1:-3.23.4}" +version="${1:-3.30.2}" case "$( uname -s )" in Linux) readonly -A sumsByVersion=( # We require CMake >= 3.13 in the CI to support CUDA builds ['3.13.5']='e2fd0080a6f0fc1ec84647acdcd8e0b4019770f48d83509e6a5b0b6ea27e5864' - ['3.23.4']='3fbcbff85043d63a8a83c8bdf8bd5b1b2fd5768f922de7dc4443de7805a2670d' + ['3.30.2']='cdd7fb352605cee3ae53b0e18b5929b642900e33d6b0173e19f6d4f2067ebf16' ) shatool="sha256sum" sha256sum="${sumsByVersion[$version]}" @@ -31,7 +31,7 @@ case "$( uname -s )" in ;; Darwin) shatool="shasum -a 256" - sha256sum="98cac043cdf321caa4fd07f27da3316db6c8bc48c39997bf78e27e5c46c4eb68" + sha256sum="c6fdda745f9ce69bca048e91955c7d043ba905d6388a62e0ff52b681ac17183c" platform="macos" arch="universal" ;; -- GitLab From be6649803d199c33bb6abcaab57074dca066f1c0 Mon Sep 17 00:00:00 2001 From: Vicente Adolfo Bolea Sanchez Date: Fri, 2 Aug 2024 17:43:54 -0400 Subject: [PATCH 17/73] ci,thrust: fix typo thrust dependency with kokkos --- CMakeLists.txt | 14 ++++++++++++-- README.md | 1 + vtkm/cont/kokkos/internal/CMakeLists.txt | 8 +++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 561bee9835..46d1cfc37c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -264,11 +264,21 @@ include(VTKmWrappers) # By default: Set VTKm_ENABLE_KOKKOS_THRUST to ON if VTKm_ENABLE_KOKKOS is ON, otherwise # disable it (or if the user explicitly turns this option OFF) -cmake_dependent_option(VTKm_ENABLE_KOKKOS_THRUST "Enable Kokkos thrust support (only valid with CUDA and HIP)" - ON "VTKm_ENABLE_KOKKOS;Kokkos_ENABLE_CUDA OR Kokkos_ENABLE_HIP" OFF) +cmake_dependent_option( + VTKm_ENABLE_KOKKOS_THRUST + "Enable Kokkos thrust support (only valid with CUDA and HIP)" + ON + "VTKm_ENABLE_KOKKOS;Kokkos_ENABLE_CUDA OR Kokkos_ENABLE_HIP; NOT Kokkos_ENABLE_HIP AND CMAKE_VERSION VERSION_LESS 3.24" + OFF +) # CUDA already provides thrust if (VTKm_ENABLE_KOKKOS_THRUST AND TARGET vtkm_kokkos_hip) + if (CMAKE_VERSION VERSION_LESS 3.24) + message(FATAL_ERROR "VTKm_ENABLE_KOKKOS_THRUST=ON with HIP needs CMAKE >= 3.24") + endif() + # This policy is needed for LINK_ONLY to work in LINK_LIBRARIES. + cmake_policy(SET CMP0131 NEW) find_package(rocthrust REQUIRED CONFIG) endif() diff --git a/README.md b/README.md index aad908b955..8f5512a7e6 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ VTK-m Requires: + [CMake](http://www.cmake.org/download/) + CMake 3.12+ + CMake 3.13+ (for CUDA support) + + CMake 3.24+ (for ROCM+THRUST support) Optional dependencies are: diff --git a/vtkm/cont/kokkos/internal/CMakeLists.txt b/vtkm/cont/kokkos/internal/CMakeLists.txt index 7777005f3e..210d9ed25c 100644 --- a/vtkm/cont/kokkos/internal/CMakeLists.txt +++ b/vtkm/cont/kokkos/internal/CMakeLists.txt @@ -35,7 +35,13 @@ if (TARGET vtkm_kokkos) set_source_files_properties(${sources} TARGET_DIRECTORY vtkm_cont PROPERTIES LANGUAGE HIP) kokkos_compilation(SOURCE ${sources}) if (VTKm_ENABLE_KOKKOS_THRUST) - target_link_libraries(vtkm_cont PRIVATE $<$:roc::rocthrust>) + # rocthrust does not wrap its compile defs/ops/dirs with $<$. + # We need this workaround since we mix CXX and HIP source files in vtkm_cont. + target_link_libraries(vtkm_cont + PRIVATE + $<$:roc::rocthrust> + $ + ) endif() endif() -- GitLab From ca127802ff1ab0ad55e2de1bd727668cc3c4fd94 Mon Sep 17 00:00:00 2001 From: Abhishek Yenpure Date: Tue, 19 Dec 2023 15:10:55 -0800 Subject: [PATCH 18/73] Changing VTK-m clip tables Quad case 5: to avoid overlapping Fix to remedy https://gitlab.kitware.com/vtk/vtk-m/-/issues/808 This fix changes on Case 5 to use quads instead of triangles to remedy the airtightness of clipping. Case 10 -- which is isometric is left untouched as it may cause overlaps in clip and clip inverse intersection. This MR causes the output of VTK-m to be different than VisIt and VTK as they suffer similar problems described in the issue. --- vtkm/filter/contour/worklet/clip/ClipTables.h | 100 +++++++++--------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/vtkm/filter/contour/worklet/clip/ClipTables.h b/vtkm/filter/contour/worklet/clip/ClipTables.h index 5e3dd18c56..7860ae9a69 100644 --- a/vtkm/filter/contour/worklet/clip/ClipTables.h +++ b/vtkm/filter/contour/worklet/clip/ClipTables.h @@ -71,8 +71,8 @@ VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 ClipTablesData[] = { 9, 4, 1, 2, 103, 101, 5, 3, 101, 103, 100, 2, // Case : 5 -5, 3, 3, 2, 103, -5, 3, 1, 0, 101, +9, 4, 103, 101, 1, 2, +9, 4, 101, 103, 3, 0, 1, // Case : 6 9, 4, 0, 2, 103, 100, 1, // Case : 7 @@ -2365,58 +2365,58 @@ VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt16 ClipTablesIndices[] = { //Tri 21, 27, 34, 41, 47, 54, 60, 66, //cases 0 - 7 //Qua -67, 74, 86, 98, 105, 117, 128, 135, //cases 0 - 7 -141, 153, 160, 171, 177, 184, 190, 196, //cases 8 - 15 +67, 74, 86, 98, 105, 117, 130, 137, //cases 0 - 7 +143, 155, 162, 173, 179, 186, 192, 198, //cases 8 - 15 //Tet -197, 204, 213, 222, 231, 240, 249, 258, //cases 0 - 7 -265, 274, 283, 292, 299, 308, 315, 322, //cases 8 - 15 +199, 206, 215, 224, 233, 242, 251, 260, //cases 0 - 7 +267, 276, 285, 294, 301, 310, 317, 324, //cases 8 - 15 //Hex -323, 334, 397, 460, 479, 542, 618, 637, //cases 0 - 7 -698, 761, 780, 856, 917, 936, 997, 1058, //cases 8 - 15 -1069, 1132, 1151, 1227, 1288, 1329, 1401, 1473, //cases 16 - 23 -1534, 1610, 1671, 1712, 1747, 1819, 1880, 1933, //cases 24 - 31 -1980, 2043, 2119, 2138, 2199, 2275, 2316, 2377, //cases 32 - 39 -2412, 2453, 2525, 2597, 2658, 2730, 2783, 2844, //cases 40 - 47 -2891, 2910, 2971, 3032, 3043, 3115, 3168, 3229, //cases 48 - 55 -3276, 3348, 3409, 3462, 3509, 3526, 3541, 3556, //cases 56 - 63 -3565, 3628, 3669, 3745, 3817, 3836, 3908, 3969, //cases 64 - 71 -4030, 4106, 4178, 4219, 4272, 4333, 4394, 4429, //cases 72 - 79 -4476, 4552, 4624, 4665, 4718, 4790, 4807, 4860, //cases 80 - 87 -4875, 4916, 4969, 4994, 5013, 5066, 5081, 5100, //cases 88 - 95 -5113, 5132, 5204, 5265, 5326, 5387, 5440, 5451, //cases 96 - 103 -5498, 5570, 5587, 5640, 5655, 5716, 5731, 5778, //cases 104 - 111 -5787, 5848, 5909, 5944, 5991, 6052, 6067, 6114, //cases 112 - 119 -6123, 6176, 6191, 6210, 6223, 6238, 6251, 6264, //cases 120 - 127 -6271, 6334, 6410, 6451, 6523, 6599, 6640, 6712, //cases 128 - 135 -6765, 6784, 6845, 6917, 6978, 7039, 7074, 7135, //cases 136 - 143 -7182, 7201, 7262, 7334, 7395, 7467, 7520, 7537, //cases 144 - 151 -7552, 7613, 7624, 7677, 7724, 7785, 7832, 7847, //cases 152 - 159 -7856, 7932, 7973, 8045, 8098, 8139, 8164, 8217, //cases 160 - 167 -8236, 8308, 8361, 8378, 8393, 8446, 8465, 8480, //cases 168 - 175 -8493, 8554, 8589, 8650, 8697, 8750, 8769, 8784, //cases 176 - 183 -8797, 8858, 8905, 8920, 8929, 8944, 8957, 8970, //cases 184 - 191 -8977, 8996, 9068, 9140, 9157, 9218, 9271, 9332, //cases 192 - 199 -9347, 9408, 9469, 9522, 9537, 9548, 9595, 9642, //cases 200 - 207 -9651, 9712, 9773, 9826, 9841, 9902, 9917, 9932, //cases 208 - 215 -9945, 9980, 10027, 10046, 10059, 10106, 10115, 10128, //cases 216 - 223 -10135, 10196, 10249, 10310, 10325, 10360, 10379, 10426, //cases 224 - 231 -10439, 10500, 10515, 10530, 10543, 10590, 10603, 10612, //cases 232 - 239 -10619, 10630, 10677, 10724, 10733, 10780, 10793, 10802, //cases 240 - 247 -10809, 10856, 10865, 10878, 10885, 10894, 10901, 10908, //cases 248 - 255 +325, 336, 399, 462, 481, 544, 620, 639, //cases 0 - 7 +700, 763, 782, 858, 919, 938, 999, 1060, //cases 8 - 15 +1071, 1134, 1153, 1229, 1290, 1331, 1403, 1475, //cases 16 - 23 +1536, 1612, 1673, 1714, 1749, 1821, 1882, 1935, //cases 24 - 31 +1982, 2045, 2121, 2140, 2201, 2277, 2318, 2379, //cases 32 - 39 +2414, 2455, 2527, 2599, 2660, 2732, 2785, 2846, //cases 40 - 47 +2893, 2912, 2973, 3034, 3045, 3117, 3170, 3231, //cases 48 - 55 +3278, 3350, 3411, 3464, 3511, 3528, 3543, 3558, //cases 56 - 63 +3567, 3630, 3671, 3747, 3819, 3838, 3910, 3971, //cases 64 - 71 +4032, 4108, 4180, 4221, 4274, 4335, 4396, 4431, //cases 72 - 79 +4478, 4554, 4626, 4667, 4720, 4792, 4809, 4862, //cases 80 - 87 +4877, 4918, 4971, 4996, 5015, 5068, 5083, 5102, //cases 88 - 95 +5115, 5134, 5206, 5267, 5328, 5389, 5442, 5453, //cases 96 - 103 +5500, 5572, 5589, 5642, 5657, 5718, 5733, 5780, //cases 104 - 111 +5789, 5850, 5911, 5946, 5993, 6054, 6069, 6116, //cases 112 - 119 +6125, 6178, 6193, 6212, 6225, 6240, 6253, 6266, //cases 120 - 127 +6273, 6336, 6412, 6453, 6525, 6601, 6642, 6714, //cases 128 - 135 +6767, 6786, 6847, 6919, 6980, 7041, 7076, 7137, //cases 136 - 143 +7184, 7203, 7264, 7336, 7397, 7469, 7522, 7539, //cases 144 - 151 +7554, 7615, 7626, 7679, 7726, 7787, 7834, 7849, //cases 152 - 159 +7858, 7934, 7975, 8047, 8100, 8141, 8166, 8219, //cases 160 - 167 +8238, 8310, 8363, 8380, 8395, 8448, 8467, 8482, //cases 168 - 175 +8495, 8556, 8591, 8652, 8699, 8752, 8771, 8786, //cases 176 - 183 +8799, 8860, 8907, 8922, 8931, 8946, 8959, 8972, //cases 184 - 191 +8979, 8998, 9070, 9142, 9159, 9220, 9273, 9334, //cases 192 - 199 +9349, 9410, 9471, 9524, 9539, 9550, 9597, 9644, //cases 200 - 207 +9653, 9714, 9775, 9828, 9843, 9904, 9919, 9934, //cases 208 - 215 +9947, 9982, 10029, 10048, 10061, 10108, 10117, 10130, //cases 216 - 223 +10137, 10198, 10251, 10312, 10327, 10362, 10381, 10428, //cases 224 - 231 +10441, 10502, 10517, 10532, 10545, 10592, 10605, 10614, //cases 232 - 239 +10621, 10632, 10679, 10726, 10735, 10782, 10795, 10804, //cases 240 - 247 +10811, 10858, 10867, 10880, 10887, 10896, 10903, 10910, //cases 248 - 255 //Wdg -10909, 10918, 10934, 10950, 11006, 11022, 11078, 11134, //cases 0 - 7 -11143, 11159, 11170, 11231, 11278, 11339, 11386, 11401, //cases 8 - 15 -11410, 11426, 11487, 11498, 11545, 11606, 11621, 11668, //cases 16 - 23 -11677, 11733, 11780, 11827, 11836, 11851, 11864, 11877, //cases 24 - 31 -11884, 11900, 11961, 12022, 12037, 12048, 12095, 12142, //cases 32 - 39 -12151, 12207, 12254, 12269, 12282, 12329, 12338, 12351, //cases 40 - 47 -12358, 12414, 12429, 12476, 12489, 12536, 12549, 12558, //cases 48 - 55 -12565, 12574, 12583, 12592, 12599, 12608, 12615, 12622, //cases 56 - 63 +10911, 10920, 10936, 10952, 11008, 11024, 11080, 11136, //cases 0 - 7 +11145, 11161, 11172, 11233, 11280, 11341, 11388, 11403, //cases 8 - 15 +11412, 11428, 11489, 11500, 11547, 11608, 11623, 11670, //cases 16 - 23 +11679, 11735, 11782, 11829, 11838, 11853, 11866, 11879, //cases 24 - 31 +11886, 11902, 11963, 12024, 12039, 12050, 12097, 12144, //cases 32 - 39 +12153, 12209, 12256, 12271, 12284, 12331, 12340, 12353, //cases 40 - 47 +12360, 12416, 12431, 12478, 12491, 12538, 12551, 12560, //cases 48 - 55 +12567, 12576, 12585, 12594, 12601, 12610, 12617, 12624, //cases 56 - 63 //Pyr -12623, 12631, 12646, 12661, 12711, 12726, 12743, 12793, //cases 0 - 7 -12808, 12823, 12873, 12890, 12905, 12955, 12970, 12985, //cases 8 - 15 -12993, 13004, 13021, 13038, 13047, 13064, 13077, 13086, //cases 16 - 23 -13093, 13110, 13119, 13132, 13139, 13148, 13155, 13162, //cases 24 - 31 +12625, 12633, 12648, 12663, 12713, 12728, 12745, 12795, //cases 0 - 7 +12810, 12825, 12875, 12892, 12907, 12957, 12972, 12987, //cases 8 - 15 +12995, 13006, 13023, 13040, 13049, 13066, 13079, 13088, //cases 16 - 23 +13095, 13112, 13121, 13134, 13141, 13150, 13157, 13164, //cases 24 - 31 }; enum -- GitLab From ea44b4b50d80137b6cb007fcc0cdc1d9de312481 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Thu, 5 Sep 2024 15:02:53 -0400 Subject: [PATCH 19/73] Better document the creation of Field and CoordinateSystem The constructors for `vtkm::cont::Field` and `vtkm::cont::CoordinateSystem` were missing from the built user's guide. The construction of these classes from names, associations, and arrays are now provided in the documentation. Also added new versions of `AddField` and `AddCoordinateSystem` to `vtkm::cont::DataSet` that mimic the constructors. This adds some sytatic sugar so you can just emplace the field instead of constructing and passing. --- docs/changelog/document-field-creation.md | 10 ++++++++ docs/users-guide/dataset.rst | 12 +++++++++ vtkm/cont/DataSet.cxx | 14 +++++++++++ vtkm/cont/DataSet.h | 30 ++++++++++++++++++----- vtkm/cont/PartitionedDataSet.h | 16 +++++++++--- 5 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 docs/changelog/document-field-creation.md diff --git a/docs/changelog/document-field-creation.md b/docs/changelog/document-field-creation.md new file mode 100644 index 0000000000..9339623436 --- /dev/null +++ b/docs/changelog/document-field-creation.md @@ -0,0 +1,10 @@ +## Better document the creation of Field and CoordinateSystem + +The constructors for `vtkm::cont::Field` and `vtkm::cont::CoordinateSystem` +were missing from the built user's guide. The construction of these classes +from names, associations, and arrays are now provided in the documentation. + +Also added new versions of `AddField` and `AddCoordinateSystem` to +`vtkm::cont::DataSet` that mimic the constructors. This adds some sytatic +sugar so you can just emplace the field instead of constructing and +passing. diff --git a/docs/users-guide/dataset.rst b/docs/users-guide/dataset.rst index 207d0f7bd7..a4f53d3f41 100644 --- a/docs/users-guide/dataset.rst +++ b/docs/users-guide/dataset.rst @@ -474,6 +474,10 @@ Associations are identified by the :enum:`vtkm::cont::Field::Association` enumer .. doxygenenum:: vtkm::cont::Field::Association +A :class:`vtkm::cont::Field` class can be constructed by providing the name, association and data. + +.. doxygenfunction:: vtkm::cont::Field::Field(std::string, Association, const vtkm::cont::UnknownArrayHandle&) + The :class:`vtkm::cont::Field` class also has several convenience methods for querying the association. .. doxygenfunction:: vtkm::cont::Field::IsPointField @@ -515,6 +519,14 @@ The point coordinates can then be interpolated throughout the mesh. .. doxygenclass:: vtkm::cont::CoordinateSystem +Because a :class:`vtkm::cont::CoordinateSystem` is a field that is always associated with points, it can be constructed with just the name and the data. + +.. doxygenfunction:: vtkm::cont::CoordinateSystem::CoordinateSystem(std::string, const vtkm::cont::UnknownArrayHandle&) + +:class:`vtkm::cont::CoordinateSystem` also has a convenience constructor for creating a uniform mesh of points. + +.. doxygenfunction:: vtkm::cont::CoordinateSystem::CoordinateSystem(std::string, vtkm::Id3, vtkm::Vec3f, vtkm::Vec3f) + In addition to all the methods provided by the :class:`vtkm::cont::Field` superclass, the :class:`vtkm::cont::CoordinateSystem` also provides a :func:`vtkm::cont::CoordinateSystem::GetBounds` convenience method that returns a :class:`vtkm::Bounds` object giving the spatial bounds of the coordinate system. .. doxygenfunction:: vtkm::cont::CoordinateSystem::GetBounds diff --git a/vtkm/cont/DataSet.cxx b/vtkm/cont/DataSet.cxx index bc4e84cc26..23a0b311d8 100644 --- a/vtkm/cont/DataSet.cxx +++ b/vtkm/cont/DataSet.cxx @@ -102,6 +102,14 @@ void DataSet::AddField(const Field& field) this->Fields.AddField(field); } +void DataSet::AddField(const std::string& name, + vtkm::cont::Field::Association association, + const vtkm::cont::UnknownArrayHandle& data) +{ + this->AddField({ name, association, data }); +} + + vtkm::Id DataSet::GetNumberOfCells() const { return this->CellSet.GetNumberOfCells(); @@ -173,6 +181,12 @@ vtkm::IdComponent DataSet::AddCoordinateSystem(const vtkm::cont::CoordinateSyste return this->AddCoordinateSystem(cs.GetName()); } +vtkm::IdComponent DataSet::AddCoordinateSystem(const std::string& name, + const vtkm::cont::UnknownArrayHandle& data) +{ + return this->AddCoordinateSystem({ name, data }); +} + vtkm::IdComponent DataSet::AddCoordinateSystem(const std::string& pointFieldName) { // Check to see if we already have this coordinate system. diff --git a/vtkm/cont/DataSet.h b/vtkm/cont/DataSet.h index c0c21b8ae6..49814c4968 100644 --- a/vtkm/cont/DataSet.h +++ b/vtkm/cont/DataSet.h @@ -86,6 +86,16 @@ public: /// integer index. VTKM_CONT void AddField(const Field& field); + /// \brief Adds a field to the `DataSet`. + /// + /// Note that the indexing of fields is not the same as the order in which they are + /// added, and that adding a field can arbitrarily reorder the integer indexing of + /// all the fields. To retrieve a specific field, retrieve the field by name, not by + /// integer index. + VTKM_CONT void AddField(const std::string& name, + vtkm::cont::Field::Association association, + const vtkm::cont::UnknownArrayHandle& data); + ///@{ /// \brief Retrieves a field by index. /// @@ -221,14 +231,14 @@ public: VTKM_CONT void AddPointField(const std::string& fieldName, const vtkm::cont::UnknownArrayHandle& field) { - this->AddField(make_FieldPoint(fieldName, field)); + this->AddField(fieldName, vtkm::cont::Field::Association::Points, field); } template VTKM_CONT void AddPointField(const std::string& fieldName, const vtkm::cont::ArrayHandle& field) { - this->AddField(make_FieldPoint(fieldName, field)); + this->AddPointField(fieldName, vtkm::cont::UnknownArrayHandle{ field }); } template @@ -256,14 +266,14 @@ public: VTKM_CONT void AddCellField(const std::string& fieldName, const vtkm::cont::UnknownArrayHandle& field) { - this->AddField(make_FieldCell(fieldName, field)); + this->AddField(fieldName, vtkm::cont::Field::Association::Cells, field); } template VTKM_CONT void AddCellField(const std::string& fieldName, const vtkm::cont::ArrayHandle& field) { - this->AddField(make_FieldCell(fieldName, field)); + this->AddCellField(fieldName, vtkm::cont::UnknownArrayHandle{ field }); } template @@ -340,16 +350,24 @@ public: /// /// The coordinate system will also be added as a point field of the same name. /// - /// \returns the index assigned to the added coordinate system. + /// \returns the field index assigned to the added coordinate system. VTKM_CONT vtkm::IdComponent AddCoordinateSystem(const vtkm::cont::CoordinateSystem& cs); + /// \brief Adds a `CoordinateSystem` with the given name and data. + /// + /// The coordinate system will also be added as a point field of the same name. + /// + /// \returns the field index assigned to the added coordinate system. + VTKM_CONT vtkm::IdComponent AddCoordinateSystem(const std::string& name, + const vtkm::cont::UnknownArrayHandle& data); + /// \brief Marks the point field with the given name as a coordinate system. /// /// If no such point field exists or the point field is of the wrong format, an exception /// will be throw. /// - /// \returns the index assigned to the added coordinate system. + /// \returns the field index assigned to the added coordinate system. VTKM_CONT vtkm::IdComponent AddCoordinateSystem(const std::string& pointFieldName); VTKM_CONT diff --git a/vtkm/cont/PartitionedDataSet.h b/vtkm/cont/PartitionedDataSet.h index 51421acfe5..77cc765ea8 100644 --- a/vtkm/cont/PartitionedDataSet.h +++ b/vtkm/cont/PartitionedDataSet.h @@ -91,15 +91,25 @@ public: /// @brief Adds a field that is applied to the meta-partition structure. /// - /// The @a field must have a partition that applies across all partitions. + /// The `field` must have an association that applies across all partitions. VTKM_CONT void AddField(const Field& field) { this->Fields.AddField(field); } + /// @brief Adds a field that is applied to the meta-partition structure. + /// + /// The `field` must have an association that applies across all partitions. + VTKM_CONT void AddField(const std::string& name, + vtkm::cont::Field::Association association, + const vtkm::cont::UnknownArrayHandle& data) + { + this->AddField({ name, association, data }); + } + /// @brief Add a field with a global association. template VTKM_CONT void AddGlobalField(const std::string& fieldName, const vtkm::cont::ArrayHandle& field) { - this->AddField(vtkm::cont::Field(fieldName, vtkm::cont::Field::Association::Global, field)); + this->AddField(fieldName, vtkm::cont::Field::Association::Global, field); } template @@ -121,7 +131,7 @@ public: VTKM_CONT void AddPartitionsField(const std::string& fieldName, const vtkm::cont::ArrayHandle& field) { - this->AddField(vtkm::cont::Field(fieldName, vtkm::cont::Field::Association::Partitions, field)); + this->AddField(fieldName, vtkm::cont::Field::Association::Partitions, field); } template -- GitLab From cbb5871eaea8b91dedbee9556ce471d19d4044d5 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 17 Sep 2024 14:03:28 -0400 Subject: [PATCH 20/73] Consolidate flying edges device cases The flying edges code has two main implementations: one where it traverses the mesh in the X direction (for CPU) and one where it traverses the mesh in the Y direction (for GPU). There were several places in the code where the device was checked for the case to use. This resulted in several places where the "GPU" devices had to be listed. Improve the code by consolidate the check to a single piece of code in `FlyingEdgesHelpers.h` where the axis to sum structure is selected. Make the overloads based on this structure rather than directly on the devices. --- .../worklet/contour/FlyingEdgesPass1.h | 70 ++++++------------- .../worklet/contour/FlyingEdgesPass4.h | 13 ++-- 2 files changed, 29 insertions(+), 54 deletions(-) diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass1.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass1.h index cfea94c1d2..498855d7c1 100644 --- a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass1.h +++ b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass1.h @@ -45,8 +45,8 @@ namespace flying_edges * */ -template -inline VTKM_EXEC void write_edge(Device, +template +inline VTKM_EXEC void write_edge(SumXAxis, vtkm::Id write_index, WholeEdgeField& edges, vtkm::UInt8 edgeCase) @@ -55,7 +55,7 @@ inline VTKM_EXEC void write_edge(Device, } template -inline VTKM_EXEC void write_edge(vtkm::cont::DeviceAdapterTagCuda, +inline VTKM_EXEC void write_edge(SumYAxis, vtkm::Id write_index, WholeEdgeField& edges, vtkm::UInt8 edgeCase) @@ -98,7 +98,7 @@ struct ComputePass1 : public vtkm::worklet::WorkletVisitPointsWithCells vtkm::Id& axis_max, WholeEdgeField& edges, const WholeDataField& field, - Device device) const + Device) const { using AxisToSum = typename select_AxisToSum::type; @@ -129,7 +129,7 @@ struct ComputePass1 : public vtkm::worklet::WorkletVisitPointsWithCells edgeCase |= FlyingEdges3D::RightAbove; } - write_edge(device, startPos + (offset * i), edges, edgeCase); + write_edge(AxisToSum{}, startPos + (offset * i), edges, edgeCase); if (edgeCase == FlyingEdges3D::LeftAbove || edgeCase == FlyingEdges3D::RightAbove) { @@ -141,29 +141,19 @@ struct ComputePass1 : public vtkm::worklet::WorkletVisitPointsWithCells } } } - write_edge(device, startPos + (offset * end), edges, FlyingEdges3D::Below); + write_edge(AxisToSum{}, startPos + (offset * end), edges, FlyingEdges3D::Below); } }; struct launchComputePass1 { - template - VTKM_CONT bool LaunchXAxis(DeviceAdapterTag device, - const ComputePass1& worklet, - const vtkm::cont::ArrayHandle& inputField, - vtkm::cont::ArrayHandle& edgeCases, - vtkm::cont::CellSetStructured<2>& metaDataMesh2D, - Args&&... args) const + void FillEdgeCases(vtkm::cont::ArrayHandle&, SumXAxis) const { - vtkm::cont::Invoker invoke(device); - metaDataMesh2D = make_metaDataMesh2D(SumXAxis{}, worklet.PointDims); - - invoke(worklet, metaDataMesh2D, std::forward(args)..., edgeCases, inputField); - return true; + // Do nothing + } + void FillEdgeCases(vtkm::cont::ArrayHandle& edgeCases, SumYAxis) const + { + edgeCases.Fill(static_cast(FlyingEdges3D::Below)); } template - VTKM_CONT bool LaunchYAxis(DeviceAdapterTag device, - const ComputePass1& worklet, - const vtkm::cont::ArrayHandle& inputField, - vtkm::cont::ArrayHandle& edgeCases, - vtkm::cont::CellSetStructured<2>& metaDataMesh2D, - Args&&... args) const + VTKM_CONT bool operator()(DeviceAdapterTag device, + const ComputePass1& worklet, + const vtkm::cont::ArrayHandle& inputField, + vtkm::cont::ArrayHandle& edgeCases, + vtkm::cont::CellSetStructured<2>& metaDataMesh2D, + Args&&... args) const { + using AxisToSum = typename select_AxisToSum::type; + vtkm::cont::Invoker invoke(device); - metaDataMesh2D = make_metaDataMesh2D(SumYAxis{}, worklet.PointDims); + metaDataMesh2D = make_metaDataMesh2D(AxisToSum{}, worklet.PointDims); - edgeCases.Fill(static_cast(FlyingEdges3D::Below)); + this->FillEdgeCases(edgeCases, AxisToSum{}); invoke(worklet, metaDataMesh2D, std::forward(args)..., edgeCases, inputField); return true; } - - template - VTKM_CONT bool operator()(DeviceAdapterTag device, Args&&... args) const - { - return this->LaunchXAxis(device, std::forward(args)...); - } - - template - VTKM_CONT bool operator()(vtkm::cont::DeviceAdapterTagCuda device, Args&&... args) const - { - return this->LaunchYAxis(device, std::forward(args)...); - } - - template - VTKM_CONT bool operator()(vtkm::cont::DeviceAdapterTagKokkos device, Args&&... args) const - { - return this->LaunchYAxis(device, std::forward(args)...); - } }; } } diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h index 088f12dd9a..1deb99d9e0 100644 --- a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h +++ b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h @@ -168,21 +168,22 @@ struct launchComputePass4 } template - VTKM_CONT bool operator()(DeviceAdapterTag device, Args&&... args) const + VTKM_CONT bool Launch(SumXAxis, DeviceAdapterTag device, Args&&... args) const { return this->LaunchXAxis(device, std::forward(args)...); } - template - VTKM_CONT bool operator()(vtkm::cont::DeviceAdapterTagCuda device, Args&&... args) const + template + VTKM_CONT bool Launch(SumYAxis, DeviceAdapterTag device, Args&&... args) const { return this->LaunchYAxis(device, std::forward(args)...); } - template - VTKM_CONT bool operator()(vtkm::cont::DeviceAdapterTagKokkos device, Args&&... args) const + template + VTKM_CONT bool operator()(DeviceAdapterTag device, Args&&... args) const { - return this->LaunchYAxis(device, std::forward(args)...); + return this->Launch( + (typename select_AxisToSum::type){}, device, std::forward(args)...); } }; } -- GitLab From 24f3f0858951e4e1373a38399da23537107b4412 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 17 Sep 2024 14:51:52 -0400 Subject: [PATCH 21/73] Fixed winding of triangles of flying edges on GPUs The flying edges implementation has an optimization where it will traverse meshes in the Y direction rather than the X direction on the GPU. It created mostly correct results, but the triangles' winding was the opposite from the CPU. This was mostly problematic when normals were generated from the gradients. In this case, the gradient would point from the "back" of the face, and that can cause shading problems with some renderers. This has been fixed to make the windings consistent on the GPU with the CPU and the gradients. Fixes #825 --- docs/changelog/flipped-contour-tris.md | 11 ++++ .../testing/UnitTestContourFilterNormals.cxx | 51 ++++++++++++++++--- .../worklet/contour/FlyingEdgesHelpers.h | 8 +++ .../worklet/contour/FlyingEdgesPass4.h | 2 +- .../worklet/contour/FlyingEdgesPass4Common.h | 8 +-- .../worklet/contour/FlyingEdgesPass4X.h | 2 +- .../contour/FlyingEdgesPass4XWithNormals.h | 2 +- .../worklet/contour/FlyingEdgesPass4Y.h | 4 +- 8 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 docs/changelog/flipped-contour-tris.md diff --git a/docs/changelog/flipped-contour-tris.md b/docs/changelog/flipped-contour-tris.md new file mode 100644 index 0000000000..d643cf6fda --- /dev/null +++ b/docs/changelog/flipped-contour-tris.md @@ -0,0 +1,11 @@ +## Fixed winding of triangles of flying edges on GPUs + +The flying edges implementation has an optimization where it will traverse +meshes in the Y direction rather than the X direction on the GPU. It +created mostly correct results, but the triangles' winding was the opposite +from the CPU. This was mostly problematic when normals were generated from +the gradients. In this case, the gradient would point from the "back" of +the face, and that can cause shading problems with some renderers. + +This has been fixed to make the windings consistent on the GPU with the CPU +and the gradients. diff --git a/vtkm/filter/contour/testing/UnitTestContourFilterNormals.cxx b/vtkm/filter/contour/testing/UnitTestContourFilterNormals.cxx index be37c30577..e149bcbea4 100644 --- a/vtkm/filter/contour/testing/UnitTestContourFilterNormals.cxx +++ b/vtkm/filter/contour/testing/UnitTestContourFilterNormals.cxx @@ -9,6 +9,8 @@ //============================================================================ #include +#include +#include #include #include #include @@ -43,6 +45,37 @@ vtkm::cont::DataSet MakeNormalsTestDataSet() return dataSet; } +// Verify that the direction of the normals is consistent with the triangle winding. +void CheckWinding(const vtkm::cont::DataSet& contour) +{ + vtkm::cont::CellSetSingleType<> cellSet; + contour.GetCellSet().AsCellSet(cellSet); + + vtkm::cont::ArrayHandle coords; + contour.GetCoordinateSystem().GetData().AsArrayHandle(coords); + auto coordsPortal = coords.ReadPortal(); + + vtkm::cont::ArrayHandle normals; + contour.GetPointField("normals").GetData().AsArrayHandle(normals); + auto normalsPortal = normals.ReadPortal(); + + for (vtkm::Id triId = 0; triId < cellSet.GetNumberOfCells(); ++triId) + { + vtkm::Id3 pointIds; + cellSet.GetIndices(triId, pointIds); + + vtkm::Vec3f facetNormal = vtkm::TriangleNormal( + coordsPortal.Get(pointIds[0]), coordsPortal.Get(pointIds[1]), coordsPortal.Get(pointIds[2])); + for (vtkm::IdComponent i = 0; i < 3; ++i) + { + vtkm::Vec3f pointNormal = normalsPortal.Get(pointIds[i]); + vtkm::FloatDefault normalDirections = vtkm::Dot(facetNormal, pointNormal); + VTKM_TEST_ASSERT(normalDirections > 0, + "Triangle winding and computed normal pointing in different directions."); + } + } +} + void TestNormals(const vtkm::cont::DataSet& dataset, bool structured) { const vtkm::Id numVerts = 16; @@ -90,14 +123,14 @@ void TestNormals(const vtkm::cont::DataSet& dataset, bool structured) //When using the Y axis algorithm the cells are generated in a different //order. const vtkm::Vec3f fast_fe_y[numVerts] = { - { -0.243433f, -0.429741f, -0.869519f }, { 0.158904f, 0.164214f, -0.973542f }, - { -0.895292f, -0.390217f, -0.214903f }, { -0.895057f, 0.134692f, -0.425125f }, - { 0.829547f, -0.418793f, -0.36941f }, { 0.846705f, 0.425787f, -0.319054f }, - { 0.253811f, -0.853394f, -0.4553f }, { -0.216381f, 0.940084f, -0.263478f }, - { -0.848579f, -0.35602f, 0.391362f }, { -0.93948f, 0.252957f, 0.231065f }, - { 0.831549f, -0.472663f, 0.291744f }, { 0.910494f, 0.0298277f, 0.412446f }, - { -0.362862f, -0.815464f, 0.450944f }, { 0.107848f, 0.958544f, 0.263748f }, - { 0.135131f, -0.437674f, 0.888921f }, { -0.286251f, 0.172078f, 0.942576f } + { 0.243433f, 0.429741f, 0.869519f }, { -0.158904f, -0.164214f, 0.973542f }, + { 0.895292f, 0.390217f, 0.214903f }, { 0.895057f, -0.134692f, 0.425125f }, + { -0.829547f, 0.418793f, 0.36941f }, { -0.846705f, -0.425787f, 0.319054f }, + { -0.253811f, 0.853394f, 0.4553f }, { 0.216381f, -0.940084f, 0.263478f }, + { 0.848579f, 0.35602f, -0.391362f }, { 0.93948f, -0.252957f, -0.231065f }, + { -0.831549f, 0.472663f, -0.291744f }, { -0.910494f, -0.0298277f, -0.412446f }, + { 0.362862f, 0.815464f, -0.450944f }, { -0.107848f, -0.958544f, -0.263748f }, + { -0.135131f, 0.437674f, -0.888921f }, { 0.286251f, -0.172078f, -0.942576f } }; vtkm::cont::ArrayHandle normals; @@ -140,6 +173,7 @@ void TestNormals(const vtkm::cont::DataSet& dataset, bool structured) i); } } + CheckWinding(result); // Test the other normals generation method if (structured) @@ -176,6 +210,7 @@ void TestNormals(const vtkm::cont::DataSet& dataset, bool structured) i); } } + CheckWinding(result); } void TestContourNormals() diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdgesHelpers.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesHelpers.h index 7a03a0e46a..f6491d09b8 100644 --- a/vtkm/filter/contour/worklet/contour/FlyingEdgesHelpers.h +++ b/vtkm/filter/contour/worklet/contour/FlyingEdgesHelpers.h @@ -53,12 +53,20 @@ struct SumXAxis static constexpr vtkm::Id xindex = 0; static constexpr vtkm::Id yindex = 1; static constexpr vtkm::Id zindex = 2; + + static constexpr vtkm::IdComponent windingIndex0 = 0; + static constexpr vtkm::IdComponent windingIndex1 = 2; + static constexpr vtkm::IdComponent windingIndex2 = 1; }; struct SumYAxis { static constexpr vtkm::Id xindex = 1; static constexpr vtkm::Id yindex = 0; static constexpr vtkm::Id zindex = 2; + + static constexpr vtkm::IdComponent windingIndex0 = 0; + static constexpr vtkm::IdComponent windingIndex1 = 1; + static constexpr vtkm::IdComponent windingIndex2 = 2; }; template diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h index 1deb99d9e0..b2e1efc73c 100644 --- a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h +++ b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4.h @@ -183,7 +183,7 @@ struct launchComputePass4 VTKM_CONT bool operator()(DeviceAdapterTag device, Args&&... args) const { return this->Launch( - (typename select_AxisToSum::type){}, device, std::forward(args)...); + typename select_AxisToSum::type{}, device, std::forward(args)...); } }; } diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4Common.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4Common.h index 96aaf95ac8..79bf0978da 100644 --- a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4Common.h +++ b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4Common.h @@ -48,7 +48,7 @@ VTKM_EXEC inline bool case_includes_axes(vtkm::UInt8 const* const edgeUses) return (edgeUses[0] != 0 || edgeUses[4] != 0 || edgeUses[8] != 0); } -template +template VTKM_EXEC inline void generate_tris(vtkm::Id inputCellId, vtkm::UInt8 edgeCase, vtkm::UInt8 numTris, @@ -68,9 +68,9 @@ VTKM_EXEC inline void generate_tris(vtkm::Id inputCellId, //produced. By keeping the winding the same we make sure //that 'fast' normals are consistent with the marching //cells version - conn.Set(index, edgeIds[edges[edgeIndex]]); - conn.Set(index + 1, edgeIds[edges[edgeIndex + 2]]); - conn.Set(index + 2, edgeIds[edges[edgeIndex + 1]]); + conn.Set(index, edgeIds[edges[edgeIndex + AxisToSum::windingIndex0]]); + conn.Set(index + 1, edgeIds[edges[edgeIndex + AxisToSum::windingIndex1]]); + conn.Set(index + 2, edgeIds[edges[edgeIndex + AxisToSum::windingIndex2]]); index += 3; edgeIndex += 3; } diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4X.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4X.h index f6cddc9b8c..a1dbf72b64 100644 --- a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4X.h +++ b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4X.h @@ -124,7 +124,7 @@ struct ComputePass4X : public vtkm::worklet::WorkletVisitCellsWithPoints if (numTris > 0) { // Start by generating triangles for this case - generate_tris( + generate_tris( state.cellId, edgeCase, numTris, edgeIds, cell_tri_offset, conn, inputCellIds); // Now generate edgeIds and weights along voxel axes if needed. Remember to take diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4XWithNormals.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4XWithNormals.h index 678052299f..1b058cd86f 100644 --- a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4XWithNormals.h +++ b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4XWithNormals.h @@ -126,7 +126,7 @@ struct ComputePass4XWithNormals : public vtkm::worklet::WorkletVisitCellsWithPoi if (numTris > 0) { // Start by generating triangles for this case - generate_tris( + generate_tris( state.cellId, edgeCase, numTris, edgeIds, cell_tri_offset, conn, inputCellIds); // Now generate edgeIds and weights along voxel axes if needed. Remember to take diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4Y.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4Y.h index 08a16f3b44..46e0a57cae 100644 --- a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4Y.h +++ b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4Y.h @@ -15,10 +15,12 @@ #include +#include #include #include #include +#include namespace vtkm { @@ -120,7 +122,7 @@ struct ComputePass4Y : public vtkm::worklet::WorkletVisitCellsWithPoints if (numTris > 0) { // Start by generating triangles for this case - generate_tris( + generate_tris( state.cellId, edgeCase, numTris, edgeIds, cell_tri_offset, conn, inputCellIds); // Now generate edgeIds and weights along voxel axes if needed. Remember to take -- GitLab From 809127ed9e73d5926d534160c0de6cb7225cbbe9 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Mon, 7 Aug 2023 01:36:07 -0700 Subject: [PATCH 22/73] (Bug)fix to suppress off-block regular nodes in Augmented Tree --- .../ContourTreeUniformDistributed.cxx | 16 +++- .../HierarchicalAugmenter.h | 29 +++++- .../FindSuperparentForNecessaryNodesWorklet.h | 95 ++++++++++++++++++- 3 files changed, 128 insertions(+), 12 deletions(-) diff --git a/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx b/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx index 9992fd64ab..57203719ca 100644 --- a/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx +++ b/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx @@ -1138,11 +1138,17 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute( // ******** 3. Augment the hierarchical tree if requested ******** if (this->AugmentHierarchicalTree) { - master.foreach ( - [](DistributedContourTreeBlockData* blockData, const vtkmdiy::Master::ProxyWithLink&) { - blockData->HierarchicalAugmenter.Initialize( - blockData->GlobalBlockId, &blockData->HierarchicalTree, &blockData->AugmentedTree); - }); + master.foreach ([globalPointDimensions](DistributedContourTreeBlockData* blockData, + const vtkmdiy::Master::ProxyWithLink&) { + blockData->HierarchicalAugmenter.Initialize( + blockData->GlobalBlockId, + &blockData->HierarchicalTree, + &blockData->AugmentedTree, + blockData->BlockOrigin, // Origin of the data block + blockData->BlockSize, // Extends of the data block + globalPointDimensions // global point dimensions + ); + }); timingsStream << " " << std::setw(38) << std::left << "Initalize Hierarchical Trees" << ": " << timer.GetElapsedTime() << " seconds" << std::endl; diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h index a9ad55f7f1..203544500c 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h @@ -127,6 +127,11 @@ template class HierarchicalAugmenter { // class HierarchicalAugmenter public: + /// base mesh variable needs to determine whether a vertex is inside or outside of the block + vtkm::Id3 MeshBlockOrigin; + vtkm::Id3 MeshBlockSize; + vtkm::Id3 MeshGlobalSize; + /// the tree that it hypersweeps over vtkm::worklet::contourtree_distributed::HierarchicalContourTree* BaseTree; /// the tree that it is building @@ -198,7 +203,10 @@ public: void Initialize( vtkm::Id blockId, vtkm::worklet::contourtree_distributed::HierarchicalContourTree* inBaseTree, - vtkm::worklet::contourtree_distributed::HierarchicalContourTree* inAugmentedTree); + vtkm::worklet::contourtree_distributed::HierarchicalContourTree* inAugmentedTree, + vtkm::Id3 meshBlockOrigin, + vtkm::Id3 meshBockSize, + vtkm::Id3 meshGlobalSize); /// routine to prepare the set of attachment points to transfer void PrepareOutAttachmentPoints(vtkm::Id round); @@ -249,12 +257,18 @@ template void HierarchicalAugmenter::Initialize( vtkm::Id blockId, vtkm::worklet::contourtree_distributed::HierarchicalContourTree* baseTree, - vtkm::worklet::contourtree_distributed::HierarchicalContourTree* augmentedTree) + vtkm::worklet::contourtree_distributed::HierarchicalContourTree* augmentedTree, + vtkm::Id3 meshBlockOrigin, + vtkm::Id3 meshBockSize, + vtkm::Id3 meshGlobalSize) { // Initialize() // copy the parameters for use this->BlockId = blockId; this->BaseTree = baseTree; this->AugmentedTree = augmentedTree; + this->MeshBlockOrigin = meshBlockOrigin; + this->MeshBlockSize = meshBockSize; + this->MeshGlobalSize = meshGlobalSize; // now construct a list of all attachment points on the block // to do this, we construct an index array with all supernode ID's that satisfy: @@ -728,12 +742,13 @@ void HierarchicalAugmenter::CopyBaseRegularStructure() vtkm::worklet::contourtree_augmented::IdArrayType tempRegularNodesNeeded; // create the worklet vtkm::worklet::contourtree_distributed::hierarchical_augmenter:: - FindSuperparentForNecessaryNodesWorklet findSuperparentForNecessaryNodesWorklet; + FindSuperparentForNecessaryNodesWorklet findSuperparentForNecessaryNodesWorklet( + this->MeshBlockOrigin, this->MeshBlockSize, this->MeshGlobalSize); // Get a FindRegularByGlobal and FindSuperArcForUnknownNode execution object for our worklet auto findRegularByGlobal = this->AugmentedTree->GetFindRegularByGlobal(); auto findSuperArcForUnknownNode = this->AugmentedTree->GetFindSuperArcForUnknownNode(); - // excute the worklet + // execute the worklet this->Invoke(findSuperparentForNecessaryNodesWorklet, // the worklet to call // inputs this->BaseTree->RegularNodeGlobalIds, // input domain @@ -831,6 +846,12 @@ void HierarchicalAugmenter::CopyBaseRegularStructure() ); } + + // Reset the number of regular nodes in round 0 + vtkm::Id regularNodesInRound0 = + numTotalRegular - this->AugmentedTree->NumRegularNodesInRound.ReadPortal().Get(1); + this->AugmentedTree->NumRegularNodesInRound.WritePortal().Set(0, regularNodesInRound0); + // Finally, we resort the regular node sort order { vtkm::worklet::contourtree_distributed::PermuteComparator // hierarchical_contour_tree:: diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h index f4c7454789..27af1e05a4 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h @@ -81,9 +81,17 @@ public: using ExecutionSignature = void(InputIndex, _1, _2, _3, _4, _5, _6, _7, _8, _9); using InputDomain = _1; + /// Default Constructor VTKM_EXEC_CONT - FindSuperparentForNecessaryNodesWorklet() {} + FindSuperparentForNecessaryNodesWorklet(vtkm::Id3 meshBlockOrigin, + vtkm::Id3 meshBlockSize, + vtkm::Id3 meshGlobalSize) + : MeshBlockOrigin(meshBlockOrigin) + , MeshBlockSize(meshBlockSize) + , MeshGlobalSize(meshGlobalSize) + { + } /// operator() of the workelt template IsInMesh(globalRegularId)) + { + // Set to NO_SUCH_ELEMENT by default. By doing this in the worklet we an avoid having to + // initialize the output arrays first and we can use FieldIn instead of FieldInOut + regularSuperparentsValue = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + regularNodesNeededValue = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + } // if it fails this test, then it's already in tree - if (vtkm::worklet::contourtree_augmented::NoSuchElement(newRegularId)) + else if (vtkm::worklet::contourtree_augmented::NoSuchElement(newRegularId)) { // not yet in tree // since it's not in the tree, we want to find where it belongs // to do so, we need to find an "above" and "below" node for it. Since it exists in the old tree, it belongs to a superarc, and we @@ -200,7 +216,80 @@ public: */ } // operator()() -}; // FindSuperparentForNecessaryNodesWorklet + +private: + // Mesh data + vtkm::Id3 MeshBlockOrigin; + vtkm::Id3 MeshBlockSize; + vtkm::Id3 MeshGlobalSize; + + VTKM_EXEC + bool IsInMesh(vtkm::Id globalId) const + { // IsInMesh() + if (this->MeshGlobalSize[2] > 1) // 3D + { + // convert from global ID to global coords + vtkm::Id globalSliceSize = this->MeshGlobalSize[0] * this->MeshGlobalSize[1]; + vtkm::Id globalSlice = globalId / globalSliceSize; + vtkm::Id globalRow = globalId / this->MeshGlobalSize[0]; + vtkm::Id globalCol = globalId % this->MeshGlobalSize[0]; + + // test validity + if (globalSlice < this->MeshBlockOrigin[2]) + { + return false; + } + if (globalSlice >= this->MeshBlockOrigin[2] + this->MeshBlockSize[2]) + { + return false; + } + if (globalRow < this->MeshBlockOrigin[1]) + { + return false; + } + if (globalRow >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1]) + { + return false; + } + if (globalCol < this->MeshBlockOrigin[0]) + { + return false; + } + if (globalCol >= this->MeshBlockOrigin[0] + this->MeshBlockSize[0]) + { + return false; + } + // it's in the block - return true + return true; + } // end if 3D + else // 2D mesh + { + // convert from global ID to global coords + vtkm::Id globalRow = globalId / this->MeshGlobalSize[0]; + vtkm::Id globalCol = globalId % this->MeshGlobalSize[0]; + + // test validity + if (globalRow < this->MeshBlockOrigin[1]) + { + return false; + } + if (globalRow >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1]) + { + return false; + } + if (globalCol < this->MeshBlockOrigin[0]) + { + return false; + } + if (globalCol >= this->MeshBlockOrigin[0] + this->MeshBlockSize[0]) + { + return false; + } + // it's in the block - return true + return true; + } + } // IsInMesh() +}; // FindSuperparentForNecessaryNodesWorklet } // namespace hierarchical_augmenter } // namespace contourtree_distributed -- GitLab From 237b5ad664cb7b0da9fb979baafab0b9505847ff Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Mon, 7 Aug 2023 01:52:24 -0700 Subject: [PATCH 23/73] Add pre-simplification for contour tree Add pre-simplification as mitigation for memory usage and scaling issues when running distributed contour tree algorithm on highly complex data sets. Co-authored-by: Mingzhe Li --- data/baseline/5x6x7.presimplification.ct_txt | 3 + data/baseline/vanc.presimplification.ct_txt | 3 + .../ContourTreeApp.cxx | 15 + .../ContourTreeUniformDistributed.cxx | 389 +++++--- .../ContourTreeUniformDistributed.h | 15 +- .../SelectTopVolumeContoursFilter.cxx | 22 + .../internal/SelectTopVolumeContoursBlock.h | 2 + .../SelectTopVolumeContoursFunctor.cxx | 64 ++ ...stingContourTreeUniformDistributedFilter.h | 259 ++++- ...estContourTreeUniformDistributedFilter.cxx | 89 ++ .../HierarchicalVolumetricBranchDecomposer.h | 9 +- .../CollapseBranchesWorklet.h | 23 +- .../NotNoSuchElementPredicate.h | 13 + .../contourtree_augmented/PrintVectors.h | 4 +- .../worklet/contourtree_augmented/Types.h | 38 + .../DistributedContourTreeBlockData.h | 10 +- .../HierarchicalAugmenter.h | 895 +++++++++++++----- .../HierarchicalAugmenterFunctor.h | 2 - .../HierarchicalContourTree.h | 7 + .../HierarchicalHyperSweeper.h | 166 +++- .../hierarchical_augmenter/CMakeLists.txt | 3 + .../CreateSuperarcsData.h | 190 ++++ ...arcsSetFirstSupernodePerIterationWorklet.h | 148 +++ ...sUpdateFirstSupernodePerIterationWorklet.h | 104 ++ .../CreateSuperarcsWorklet.h | 754 +++++++++------ .../FindSuperparentForNecessaryNodesWorklet.h | 30 +- .../IsAttachementPointPredicate.h | 46 +- .../ResizeArraysBuildNewSupernodeIdsWorklet.h | 28 +- ...ateHyperstructureSetSuperchildrenWorklet.h | 91 +- .../hierarchical_hyper_sweeper/CMakeLists.txt | 2 + .../ComputeSuperarcTransferWeightsWorklet.h | 3 +- .../TransferTargetComperator.h | 32 +- .../TransferWeightsUpdateLHEWorklet.h | 41 +- .../TransferWeightsUpdateLHEWorkletRound2.h | 126 +++ .../TransferWeightsUpdateRHEWorklet.h | 27 +- .../TransferWeightsUpdateRHEWorkletRound2.h | 160 ++++ .../GetBranchVolumeWorklet.h | 4 +- 37 files changed, 3035 insertions(+), 782 deletions(-) create mode 100644 data/baseline/5x6x7.presimplification.ct_txt create mode 100644 data/baseline/vanc.presimplification.ct_txt create mode 100644 vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsData.h create mode 100644 vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsSetFirstSupernodePerIterationWorklet.h create mode 100644 vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsUpdateFirstSupernodePerIterationWorklet.h create mode 100644 vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateLHEWorkletRound2.h create mode 100644 vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateRHEWorkletRound2.h diff --git a/data/baseline/5x6x7.presimplification.ct_txt b/data/baseline/5x6x7.presimplification.ct_txt new file mode 100644 index 0000000000..5de211fa57 --- /dev/null +++ b/data/baseline/5x6x7.presimplification.ct_txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:65991b155badb52b1124f2d164043ea9c16c13c92f66212c26244b3ac5650de9 +size 58 diff --git a/data/baseline/vanc.presimplification.ct_txt b/data/baseline/vanc.presimplification.ct_txt new file mode 100644 index 0000000000..f7ac4ca022 --- /dev/null +++ b/data/baseline/vanc.presimplification.ct_txt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58db994f697d80bdf137fa879111f5c8da96b452fdd1745a7bae7d98e5e8068e +size 213 diff --git a/examples/contour_tree_distributed/ContourTreeApp.cxx b/examples/contour_tree_distributed/ContourTreeApp.cxx index b3ff965680..ed9ca59d27 100644 --- a/examples/contour_tree_distributed/ContourTreeApp.cxx +++ b/examples/contour_tree_distributed/ContourTreeApp.cxx @@ -220,6 +220,12 @@ int main(int argc, char* argv[]) } } + vtkm::Id presimplifyThreshold = 0; // Do not presimplify the hierachical contour tree + if (parser.hasOption("--presimplifyThreshold")) + { + presimplifyThreshold = std::stoi(parser.getOption("--presimplifyThreshold")); + } + bool useBoundaryExtremaOnly = true; if (parser.hasOption("--useFullBoundary")) { @@ -368,6 +374,10 @@ int main(int argc, char* argv[]) std::cout << "--eps= Floating point offset awary from the critical point. (default=0.00001)" << std::endl; + std::cout << "--presimplifyThreshold Integer volume threshold for presimplifying the tree" + << std::endl; + std::cout << " Default value is 0, indicating no presimplification" + << std::endl; std::cout << "--preSplitFiles Input data is already pre-split into blocks." << std::endl; std::cout << "--saveDot Save DOT files of the distributed contour tree " << std::endl << " computation (Default=False). " << std::endl; @@ -411,6 +421,7 @@ int main(int argc, char* argv[]) << " augmentHierarchicalTree=" << augmentHierarchicalTree << std::endl << " computeVolumetricBranchDecomposition=" << computeHierarchicalVolumetricBranchDecomposition << std::endl + << " presimplifyThreshold=" << presimplifyThreshold << std::endl << " saveOutputData=" << saveOutputData << std::endl << " forwardSummary=" << forwardSummary << std::endl << " nblocks=" << numBlocks << std::endl @@ -648,6 +659,10 @@ int main(int argc, char* argv[]) filter.SetUseBoundaryExtremaOnly(useBoundaryExtremaOnly); filter.SetUseMarchingCubes(useMarchingCubes); filter.SetAugmentHierarchicalTree(augmentHierarchicalTree); + if (presimplifyThreshold > 0) + { + filter.SetPresimplifyThreshold(presimplifyThreshold); + } filter.SetSaveDotFiles(saveDotFiles); filter.SetActiveField("values"); diff --git a/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx b/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx index 57203719ca..ad572e6b53 100644 --- a/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx +++ b/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx @@ -169,6 +169,7 @@ ContourTreeUniformDistributed::ContourTreeUniformDistributed(vtkm::cont::LogLeve : UseBoundaryExtremaOnly(true) , UseMarchingCubes(false) , AugmentHierarchicalTree(false) + , PresimplifyThreshold(0) , SaveDotFiles(false) , TimingsLogLevel(timingsLogLevel) , TreeLogLevel(treeLogLevel) @@ -585,7 +586,10 @@ inline VTKM_CONT void ContourTreeUniformDistributed::ComputeVolumeMetric( vtkmdiy::RegularSwapPartners& partners, const FieldType&, // dummy parameter to get the type std::stringstream& timingsStream, - std::vector& hierarchicalTreeOutputDataSet) + const vtkm::cont::PartitionedDataSet& input, + bool useAugmentedTree, + std::vector>& intrinsicVolumes, + std::vector>& dependentVolumes) { // TODO/FIXME: CONSIDER MOVING CONTENTS OF THIS METHOD TO SEPARATE FILTER vtkm::cont::Timer timer; @@ -610,11 +614,12 @@ inline VTKM_CONT void ContourTreeUniformDistributed::ComputeVolumeMetric( inputContourTreeMaster.foreach ( [&](DistributedContourTreeBlockData* currInBlock, const vtkmdiy::Master::ProxyWithLink&) { vtkm::Id blockNo = currInBlock->LocalBlockNo; - const vtkm::cont::DataSet& currDS = hierarchicalTreeOutputDataSet[blockNo]; + //const vtkm::cont::DataSet& currDS = hierarchicalTreeOutputDataSet[blockNo]; + auto currOriginalBlock = input.GetPartition(static_cast(blockNo)); // The block size and origin may be modified during the FanIn so we need to use the // size and origin from the original decomposition instead of looking it up in the currInBlock vtkm::Id3 pointDimensions, globalPointDimensions, globalPointIndexStart; - currDS.GetCellSet().CastAndCallForTypes( + currOriginalBlock.GetCellSet().CastAndCallForTypes( vtkm::worklet::contourtree_augmented::GetLocalAndGlobalPointDimensions(), pointDimensions, globalPointDimensions, @@ -625,15 +630,38 @@ inline VTKM_CONT void ContourTreeUniformDistributed::ComputeVolumeMetric( // to keep the pointer, as DIY will "own" it and delete it when no longer needed. // NOTE: Since we passed a "Destroy" function to DIY master, it will own the local data // blocks and delete them when done. - hierarchical_hyper_sweep_master.add( - currInBlock->GlobalBlockId, - new HyperSweepBlock(blockNo, - currInBlock->GlobalBlockId, - globalPointIndexStart, - pointDimensions, - globalPointDimensions, - *currInBlock->HierarchicalAugmenter.AugmentedTree), - new vtkmdiy::Link()); + + // If we are pre-simplifying the tree then we need to use the base tree and if we compute the + // final volume, then we need to use the augmented tree + // currInBlock->HierarchicalAugmenter is NOT initialized + // when this function is first called if pre-simplification is applied. + // currInBlock->HierarchicalAugmenter.AugmentedTree seems ok to remain, + // because it is only called during augmentation, + // in which the HierarchicalAugmenter is intialized. + auto hierarchicalTreeToProcess = useAugmentedTree + ? currInBlock->HierarchicalAugmenter.AugmentedTree + : &currInBlock->HierarchicalTree; + +#ifdef DEBUG_PRINT_HYPER_SWEEPER + { + std::stringstream debugStream; + debugStream << "Block " << blockNo << std::endl; + debugStream << hierarchicalTreeToProcess->DebugPrint( + "Choosing Hierarchical Tree To Process", __FILE__, __LINE__); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream); + } +#endif // DEBUG_PRINT_HYPER_SWEEPER + + + // Create HyperSweeper + hierarchical_hyper_sweep_master.add(currInBlock->GlobalBlockId, + new HyperSweepBlock(blockNo, + currInBlock->GlobalBlockId, + globalPointIndexStart, + pointDimensions, + globalPointDimensions, + *hierarchicalTreeToProcess), + new vtkmdiy::Link()); }); // Log time to copy the data to the HyperSweepBlock data objects @@ -648,94 +676,151 @@ inline VTKM_CONT void ContourTreeUniformDistributed::ComputeVolumeMetric( << ": " << timer.GetElapsedTime() << " seconds" << std::endl; timer.Start(); - hierarchical_hyper_sweep_master.foreach ([&](HyperSweepBlock* b, - const vtkmdiy::Master::ProxyWithLink&) { - std::stringstream localHypersweepTimingsStream; - vtkm::cont::Timer localHypersweepTimer; - localHypersweepTimer.Start(); + hierarchical_hyper_sweep_master.foreach ( + [&](HyperSweepBlock* b, const vtkmdiy::Master::ProxyWithLink&) { + std::stringstream localHypersweepTimingsStream; + vtkm::cont::Timer localHypersweepTimer; + localHypersweepTimer.Start(); // Create HyperSweeper -#ifdef DEBUG_PRINT - VTKM_LOG_S(this->TreeLogLevel, "Block " << b->GlobalBlockId); - VTKM_LOG_S(this->TreeLogLevel, - b->HierarchicalContourTree.DebugPrint( - "Before initializing HierarchicalHyperSweeper", __FILE__, __LINE__)); +#ifdef DEBUG_PRINT_HYPER_SWEEPER + { + std::stringstream debugStream; + const vtkm::Id nBlockVertices = + b->Size[0] * b->Size[1] * (b->GlobalSize[2] <= 1 ? 1 : b->Size[2]); + debugStream << std::endl; + debugStream << std::endl; + debugStream << std::endl; + debugStream << std::endl; + debugStream << "------------------------------" << std::endl; + debugStream << "Computing Contour Tree Volumes" << std::endl; + debugStream << "------------------------------" << std::endl; + debugStream << std::endl; + debugStream << std::endl; + debugStream << "Volumes Before Initialisation" << std::endl; + debugStream << "Block: " << b->GlobalBlockId << " Size: " << nBlockVertices << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(b->IntrinsicVolume.GetNumberOfValues(), + debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Intrinsic", b->IntrinsicVolume, -1, debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Dependent", b->DependentVolume, -1, debugStream); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); + } #endif - // Create the HierarchicalHypersweeper - vtkm::worklet::contourtree_distributed::HierarchicalHyperSweeper - hyperSweeper( - b->GlobalBlockId, b->HierarchicalContourTree, b->IntrinsicVolume, b->DependentVolume); - // Log the time - localHypersweepTimingsStream << " Create Hypersweeper (block=" << b->LocalBlockNo - << ") : " << localHypersweepTimer.GetElapsedTime() << " seconds" - << std::endl; - localHypersweepTimer.Start(); - // Create mesh and initialize vertex counts - vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler idRelabeler{ b->Origin, - b->Size, - b->GlobalSize }; + // Create the HierarchicalHypersweeper + vtkm::worklet::contourtree_distributed::HierarchicalHyperSweeper + hyperSweeper( + b->GlobalBlockId, b->HierarchicalContourTree, b->IntrinsicVolume, b->DependentVolume); + // Log the time + localHypersweepTimingsStream << " Create Hypersweeper (block=" << b->LocalBlockNo + << ") : " << localHypersweepTimer.GetElapsedTime() << " seconds" + << std::endl; + localHypersweepTimer.Start(); + + // Create mesh and initialize vertex counts + vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler idRelabeler{ b->Origin, + b->Size, + b->GlobalSize }; + +#ifdef DEBUG_PRINT_HYPER_SWEEPER + { + std::stringstream debugStream; + debugStream << "Computing Intrinsic Vertex Count" << std::endl; + debugStream << "Block: " << b->GlobalBlockId << " Size: " << nBlockVertices << std::endl; + VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); + } +#endif - if (b->GlobalSize[2] <= 1) - { - vtkm::worklet::contourtree_augmented::DataSetMeshTriangulation2DFreudenthal mesh( - vtkm::Id2{ b->Size[0], b->Size[1] }); - hyperSweeper.InitializeIntrinsicVertexCount( - b->HierarchicalContourTree, mesh, idRelabeler, b->IntrinsicVolume); - } - else - { - // For getting owned vertices, it does not make a difference if we are using marching cubes or not. - vtkm::worklet::contourtree_augmented::DataSetMeshTriangulation3DFreudenthal mesh(b->Size); - hyperSweeper.InitializeIntrinsicVertexCount( - b->HierarchicalContourTree, mesh, idRelabeler, b->IntrinsicVolume); - } + if (b->GlobalSize[2] <= 1) + { + vtkm::worklet::contourtree_augmented::DataSetMeshTriangulation2DFreudenthal mesh( + vtkm::Id2{ b->Size[0], b->Size[1] }); + hyperSweeper.InitializeIntrinsicVertexCount( + b->HierarchicalContourTree, mesh, idRelabeler, b->IntrinsicVolume); + } + else + { + // For getting owned vertices, it does not make a difference if we are using marching cubes or not. + vtkm::worklet::contourtree_augmented::DataSetMeshTriangulation3DFreudenthal mesh(b->Size); + hyperSweeper.InitializeIntrinsicVertexCount( + b->HierarchicalContourTree, mesh, idRelabeler, b->IntrinsicVolume); + } -#ifdef DEBUG_PRINT - VTKM_LOG_S(this->TreeLogLevel, "Block " << b->GlobalBlockId); - VTKM_LOG_S(this->TreeLogLevel, - b->HierarchicalContourTree.DebugPrint( - "After initializing intrinsic vertex count", __FILE__, __LINE__)); - std::ostringstream volumeStream; - vtkm::worklet::contourtree_augmented::PrintHeader(b->IntrinsicVolume.GetNumberOfValues(), - volumeStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "Intrinsic Volume", b->IntrinsicVolume, -1, volumeStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "Dependent Volume", b->DependentVolume, -1, volumeStream); - VTKM_LOG_S(this->TreeLogLevel, volumeStream.str()); - VTKM_LOG_S(this->TreeLogLevel, "FLUSH" << std::endl << std::flush); + // Initialize dependentVolume by copy from intrinsicVolume + vtkm::cont::Algorithm::Copy(b->IntrinsicVolume, b->DependentVolume); + +#ifdef DEBUG_PRINT_HYPER_SWEEPER + { + std::stringstream debugStream; + debugStream << "Intrinsic Volume Computed & Copied to Dependent" << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(b->IntrinsicVolume.GetNumberOfValues(), + debugStream); + vtkm::cont::ArrayHandle whichTreeSupernodeRegularIDs; + // we copy the HCT information to a temp array because b->HierarchicalContourTree is a const + vtkm::cont::ArrayHandle hctGRIds; + vtkm::cont::ArrayHandle hctSupernodes; + vtkm::cont::Algorithm::Copy(b->HierarchicalContourTree.RegularNodeGlobalIds, hctGRIds); + vtkm::cont::Algorithm::Copy(b->HierarchicalContourTree.Supernodes, hctSupernodes); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + hctGRIds, hctSupernodes, whichTreeSupernodeRegularIDs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Regular ID", whichTreeSupernodeRegularIDs, -1, debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Intrinsic", b->IntrinsicVolume, -1, debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Dependent", b->DependentVolume, -1, debugStream); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); + } #endif - // Initialize dependentVolume by copy from intrinsicVolume - vtkm::cont::Algorithm::Copy(b->IntrinsicVolume, b->DependentVolume); - // Log the time - localHypersweepTimingsStream << " Initalize Vertex Counts (block=" << b->LocalBlockNo - << ") : " << localHypersweepTimer.GetElapsedTime() << " seconds" - << std::endl; - localHypersweepTimer.Start(); + // Log the time + localHypersweepTimingsStream << " Initalize Vertex Counts (block=" << b->LocalBlockNo + << ") : " << localHypersweepTimer.GetElapsedTime() << " seconds" + << std::endl; + localHypersweepTimer.Start(); - // Perform the local hypersweep - hyperSweeper.LocalHyperSweep(); + // Perform the local hypersweep + hyperSweeper.LocalHyperSweep(); -#ifdef DEBUG_PRINT - VTKM_LOG_S(this->TreeLogLevel, "Block " << b->GlobalBlockId); - VTKM_LOG_S(this->TreeLogLevel, - b->HierarchicalContourTree.DebugPrint("After local hypersweep", __FILE__, __LINE__)); +#ifdef DEBUG_PRINT_HYPER_SWEEPER + { + std::stringstream debugStream; + debugStream << "Local Hypersweep Complete" << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(b->IntrinsicVolume.GetNumberOfValues(), + debugStream); + vtkm::cont::ArrayHandle whichTreeSupernodeRegularIDs; + // we copy the HCT information to a temp array because b->HierarchicalContourTree is a const + vtkm::cont::ArrayHandle hctGRIds; + vtkm::cont::ArrayHandle hctSupernodes; + vtkm::cont::Algorithm::Copy(b->HierarchicalContourTree.RegularNodeGlobalIds, hctGRIds); + vtkm::cont::Algorithm::Copy(b->HierarchicalContourTree.Supernodes, hctSupernodes); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + hctGRIds, hctSupernodes, whichTreeSupernodeRegularIDs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Regular ID", whichTreeSupernodeRegularIDs, debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Intrinsic", b->IntrinsicVolume, -1, debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Dependent", b->DependentVolume, -1, debugStream); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); + } #endif - // Log the local hypersweep time - localHypersweepTimingsStream << " Local Hypersweep (block=" << b->LocalBlockNo - << ") : " << localHypersweepTimer.GetElapsedTime() << " seconds" - << std::endl; - localHypersweepTimer.Start(); - // Log the timing stats we collected - VTKM_LOG_S(this->TimingsLogLevel, - std::endl - << " ------------ Compute Local Hypersweep (block=" << b->LocalBlockNo - << ") ------------" << std::endl - << localHypersweepTimingsStream.str()); - }); + // Log the local hypersweep time + localHypersweepTimingsStream << " Local Hypersweep (block=" << b->LocalBlockNo + << ") : " << localHypersweepTimer.GetElapsedTime() << " seconds" + << std::endl; + localHypersweepTimer.Start(); + + // Log the timing stats we collected + VTKM_LOG_S(this->TimingsLogLevel, + std::endl + << " ------------ Compute Local Hypersweep (block=" << b->LocalBlockNo + << ") ------------" << std::endl + << localHypersweepTimingsStream.str()); + }); // Log time for performing the local hypersweep timingsStream << " " << std::setw(38) << std::left << "Compute Local Hypersweep" @@ -755,33 +840,30 @@ inline VTKM_CONT void ContourTreeUniformDistributed::ComputeVolumeMetric( << ": " << timer.GetElapsedTime() << " seconds" << std::endl; timer.Start(); - // Print & add to output data set - //std::vector hierarchicalTreeAndVolumeOutputDataSet(localDataBlocks.size()); + // Add the intrinsic and dependent volumes to the output vectors + intrinsicVolumes.resize(inputContourTreeMaster.size()); + dependentVolumes.resize(inputContourTreeMaster.size()); hierarchical_hyper_sweep_master.foreach ( [&](HyperSweepBlock* b, const vtkmdiy::Master::ProxyWithLink&) { - vtkm::cont::Field intrinsicVolumeField( - "IntrinsicVolume", vtkm::cont::Field::Association::WholeDataSet, b->IntrinsicVolume); - hierarchicalTreeOutputDataSet[b->LocalBlockNo].AddField(intrinsicVolumeField); - vtkm::cont::Field dependentVolumeField( - "DependentVolume", vtkm::cont::Field::Association::WholeDataSet, b->DependentVolume); - hierarchicalTreeOutputDataSet[b->LocalBlockNo].AddField(dependentVolumeField); -#ifdef DEBUG_PRINT - VTKM_LOG_S(this->TreeLogLevel, "Block " << b->GlobalBlockId); - VTKM_LOG_S( - this->TreeLogLevel, - b->HierarchicalContourTree.DebugPrint("Called from DumpVolumes", __FILE__, __LINE__)); - std::ostringstream volumeStream; - vtkm::worklet::contourtree_augmented::PrintHeader(b->IntrinsicVolume.GetNumberOfValues(), - volumeStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "Intrinsic Volume", b->IntrinsicVolume, -1, volumeStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "Dependent Volume", b->DependentVolume, -1, volumeStream); - VTKM_LOG_S(this->TreeLogLevel, volumeStream.str()); + intrinsicVolumes[b->LocalBlockNo] = b->IntrinsicVolume; + dependentVolumes[b->LocalBlockNo] = b->DependentVolume; + +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + { + VTKM_LOG_S(this->TreeLogLevel, "Block " << b->GlobalBlockId); + /*VTKM_LOG_S( + this->TreeLogLevel, + b->HierarchicalContourTree.DebugPrint("Called from DumpVolumes", __FILE__, __LINE__));*/ + std::ostringstream volumeStream; + vtkm::worklet::contourtree_augmented::PrintHeader(b->IntrinsicVolume.GetNumberOfValues(), + volumeStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Intrinsic Volume", b->IntrinsicVolume, -1, volumeStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Dependent Volume", b->DependentVolume, -1, volumeStream); + VTKM_LOG_S(this->TreeLogLevel, volumeStream.str()); + } #endif - // Log the time for adding hypersweep data to the output dataset - timingsStream << " " << std::setw(38) << std::left << "Create Output Data (Hypersweep)" - << ": " << timer.GetElapsedTime() << " seconds" << std::endl; }); } @@ -885,6 +967,8 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute( newBlock->LocalBlockNo = bi; newBlock->BlockOrigin = globalPointIndexStart; newBlock->BlockSize = pointDimensions; + newBlock->FixedBlockOrigin = globalPointIndexStart; + newBlock->FixedBlockSize = pointDimensions; // Save local tree information for fan out; TODO/FIXME: Try to avoid copy newBlock->ContourTrees.push_back(this->LocalContourTrees[bi]); @@ -1135,18 +1219,58 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute( << ": " << timer.GetElapsedTime() << " seconds" << std::endl; timer.Start(); + + // Compute the volume for pre-simplification if we want to pre-simplify + // The dependent volumes from the unaugemented hierarchical tree are used for the pre-simplification + // as part of HierarchicalAugmenter.Initialize. + std::vector> unaugmentedDependentVolumes; + if (this->PresimplifyThreshold > 0) + { + // we don't need the unaugemented intrinsic Volumes for the pre-simplification, so we + // use a local variable that is being deleted automatically after the context + std::vector> unaugmentedIntrinsicVolumes; + // Compute the volume for the base hierarchical tree before augmentation in order to allow for pre-simplification. + this->ComputeVolumeMetric( + master, + assigner, + partners, + FieldType{}, + timingsStream, + input, + false, // use the unaugmented hierarchical tree (i.e., the base tree) for the volume computation + unaugmentedIntrinsicVolumes, + unaugmentedDependentVolumes); + timingsStream << " " << std::setw(38) << std::left << "Compute Volume for Presimplication" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + } + // ******** 3. Augment the hierarchical tree if requested ******** if (this->AugmentHierarchicalTree) { - master.foreach ([globalPointDimensions](DistributedContourTreeBlockData* blockData, - const vtkmdiy::Master::ProxyWithLink&) { + vtkm::Id localPresimplifyThreshold = this->PresimplifyThreshold; + master.foreach ([globalPointDimensions, localPresimplifyThreshold, unaugmentedDependentVolumes]( + DistributedContourTreeBlockData* blockData, + const vtkmdiy::Master::ProxyWithLink&) { + // if we don't presimplify then use a NULL pointer for the dependent volume used for pre-simplification + vtkm::worklet::contourtree_augmented::IdArrayType* volumeArrayForPresimplifiction = NULL; + // if we presimplify then get a pointer for the dependent volume for the current block + if (localPresimplifyThreshold > 0) + { + volumeArrayForPresimplifiction = + const_cast( + &unaugmentedDependentVolumes[blockData->LocalBlockNo]); + } + // Initialize the hierarchical augmenter blockData->HierarchicalAugmenter.Initialize( blockData->GlobalBlockId, &blockData->HierarchicalTree, &blockData->AugmentedTree, - blockData->BlockOrigin, // Origin of the data block - blockData->BlockSize, // Extends of the data block - globalPointDimensions // global point dimensions + blockData->FixedBlockOrigin, // Origin of the data block + blockData->FixedBlockSize, // Extends of the data block + globalPointDimensions, // global point dimensions + volumeArrayForPresimplifiction, // DependentVolume if we computed it or NULL if no presimplification is used + localPresimplifyThreshold // presimplify if threshold is > 0 ); }); @@ -1159,6 +1283,7 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute( partners, vtkm::worklet::contourtree_distributed::HierarchicalAugmenterFunctor{ this->TimingsLogLevel }); + // Clear all swap data as it is no longer needed master.foreach ( [](DistributedContourTreeBlockData* blockData, const vtkmdiy::Master::ProxyWithLink&) { @@ -1265,8 +1390,34 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute( if (this->AugmentHierarchicalTree) { - this->ComputeVolumeMetric( - master, assigner, partners, FieldType{}, timingsStream, hierarchicalTreeOutputDataSet); + std::vector> augmentedIntrinsicVolumes; + std::vector> augmentedDependentVolumes; + this->ComputeVolumeMetric(master, + assigner, + partners, + FieldType{}, + timingsStream, + input, + true, // use the augmented tree + augmentedIntrinsicVolumes, + augmentedDependentVolumes); + timer.Start(); + + master.foreach ( + [&](DistributedContourTreeBlockData* blockData, const vtkmdiy::Master::ProxyWithLink&) { + // Add the intrinsic and dependent volumes to the output data set + vtkm::cont::Field intrinsicVolumeField("IntrinsicVolume", + vtkm::cont::Field::Association::WholeDataSet, + augmentedIntrinsicVolumes[blockData->LocalBlockNo]); + hierarchicalTreeOutputDataSet[blockData->LocalBlockNo].AddField(intrinsicVolumeField); + vtkm::cont::Field dependentVolumeField("DependentVolume", + vtkm::cont::Field::Association::WholeDataSet, + augmentedDependentVolumes[blockData->LocalBlockNo]); + hierarchicalTreeOutputDataSet[blockData->LocalBlockNo].AddField(dependentVolumeField); + // Log the time for adding hypersweep data to the output dataset + timingsStream << " " << std::setw(38) << std::left << "Add Volume Output Data" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + }); } VTKM_LOG_S(this->TimingsLogLevel, diff --git a/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.h b/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.h index 23610971aa..525fa48148 100644 --- a/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.h +++ b/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.h @@ -118,6 +118,11 @@ public: this->AugmentHierarchicalTree = augmentHierarchicalTree; } + VTKM_CONT void SetPresimplifyThreshold(vtkm::Id presimplifyThreshold) + { + this->PresimplifyThreshold = presimplifyThreshold; + } + VTKM_CONT void SetBlockIndices(vtkm::Id3 blocksPerDim, const vtkm::cont::ArrayHandle& localBlockIndices) { @@ -127,6 +132,8 @@ public: VTKM_CONT bool GetAugmentHierarchicalTree() { return this->AugmentHierarchicalTree; } + VTKM_CONT vtkm::Id GetPresimplifyThreshold() { return this->PresimplifyThreshold; } + VTKM_CONT void SetSaveDotFiles(bool saveDotFiles) { this->SaveDotFiles = saveDotFiles; } VTKM_CONT bool GetSaveDotFiles() { return this->SaveDotFiles; } @@ -166,7 +173,10 @@ private: vtkmdiy::RegularSwapPartners& partners, const FieldType&, // dummy parameter to get the type std::stringstream& timingsStream, - std::vector& hierarchicalTreeOutputDataSet); + const vtkm::cont::PartitionedDataSet& input, + bool useAugmentedTree, + std::vector>& intrinsicVolumes, + std::vector>& dependentVolumes); /// /// Internal helper function that implements the actual functionality of PostExecute @@ -188,6 +198,9 @@ private: /// Augment hierarchical tree bool AugmentHierarchicalTree; + /// Threshold to use for volume pre-simplification + vtkm::Id PresimplifyThreshold; + /// Save dot files for all tree computations bool SaveDotFiles; diff --git a/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx b/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx index c7d0b8f312..f14a09e255 100644 --- a/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx +++ b/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx @@ -116,6 +116,14 @@ VTKM_CONT vtkm::cont::PartitionedDataSet SelectTopVolumeContoursFilter::DoExecut auto branchRootGRId = ds.GetField("BranchRootGRId").GetData().AsArrayHandle>(); + auto upperEndGRId = ds.GetField("UpperEndGlobalRegularIds") + .GetData() + .AsArrayHandle>(); + + auto lowerEndGRId = ds.GetField("LowerEndGlobalRegularIds") + .GetData() + .AsArrayHandle>(); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( branchRootGRId, topVolumeBranch, b->TopVolumeBranchRootGRId); @@ -128,6 +136,12 @@ VTKM_CONT vtkm::cont::PartitionedDataSet SelectTopVolumeContoursFilter::DoExecut vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( b->BranchSaddleEpsilon, topVolumeBranch, b->TopVolumeBranchSaddleEpsilon); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + upperEndGRId, topVolumeBranch, b->TopVolumeBranchUpperEndGRId); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + lowerEndGRId, topVolumeBranch, b->TopVolumeBranchLowerEndGRId); + auto resolveArray = [&](const auto& inArray) { using InArrayHandleType = std::decay_t; InArrayHandleType topVolBranchSaddleIsoValue; @@ -153,6 +167,14 @@ VTKM_CONT vtkm::cont::PartitionedDataSet SelectTopVolumeContoursFilter::DoExecut branch_top_volume_master.foreach ( [&](SelectTopVolumeContoursBlock* b, const vtkmdiy::Master::ProxyWithLink&) { + vtkm::cont::Field TopVolBranchUpperEndField("TopVolumeBranchUpperEnd", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeBranchUpperEndGRId); + outputDataSets[b->LocalBlockNo].AddField(TopVolBranchUpperEndField); + vtkm::cont::Field TopVolBranchLowerEndField("TopVolumeBranchLowerEnd", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeBranchLowerEndGRId); + outputDataSets[b->LocalBlockNo].AddField(TopVolBranchLowerEndField); vtkm::cont::Field TopVolBranchGRIdField("TopVolumeBranchGlobalRegularIds", vtkm::cont::Field::Association::WholeDataSet, b->TopVolumeBranchRootGRId); diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h index 7d08433edb..2d1ca6d677 100644 --- a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h +++ b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h @@ -86,6 +86,8 @@ struct SelectTopVolumeContoursBlock vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchVolume; vtkm::cont::UnknownArrayHandle TopVolumeBranchSaddleIsoValue; vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchSaddleEpsilon; + vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchUpperEndGRId; + vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchLowerEndGRId; // Destroy function allowing DIY to own blocks and clean them up after use static void Destroy(void* b) { delete static_cast(b); } diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx index 485c14d2e3..cba0501f4e 100644 --- a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx +++ b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx @@ -102,6 +102,8 @@ void SelectTopVolumeContoursFunctor::operator()( auto topVolBranchRootGRIdPortal = b->TopVolumeBranchRootGRId.ReadPortal(); auto topVolBranchVolumePortal = b->TopVolumeBranchVolume.ReadPortal(); auto topVolBranchSaddleEpsilonPortal = b->TopVolumeBranchSaddleEpsilon.ReadPortal(); + auto topVolBranchUpperEndPortal = b->TopVolumeBranchUpperEndGRId.ReadPortal(); + auto topVolBranchLowerEndPortal = b->TopVolumeBranchLowerEndGRId.ReadPortal(); vtkm::Id nBranches = topVolBranchRootGRIdPortal.GetNumberOfValues(); @@ -112,6 +114,10 @@ void SelectTopVolumeContoursFunctor::operator()( rp.enqueue(target, topVolBranchVolumePortal.Get(branch)); for (vtkm::Id branch = 0; branch < nBranches; ++branch) rp.enqueue(target, topVolBranchSaddleEpsilonPortal.Get(branch)); + for (vtkm::Id branch = 0; branch < nBranches; ++branch) + rp.enqueue(target, topVolBranchUpperEndPortal.Get(branch)); + for (vtkm::Id branch = 0; branch < nBranches; ++branch) + rp.enqueue(target, topVolBranchLowerEndPortal.Get(branch)); auto resolveArray = [&](const auto& inArray) { using InArrayHandleType = std::decay_t; @@ -199,6 +205,34 @@ void SelectTopVolumeContoursFunctor::operator()( // Replace with dequeuing ArrayHandles once bug is fixed. // rp.dequeue(ingid, incomingTopVolBranchSaddleEpsilon); + IdArrayType incomingTopVolBranchUpperEnd; + incomingTopVolBranchUpperEnd.Allocate(nIncoming); + auto incomingTopVolBranchUpperEndPortal = incomingTopVolBranchUpperEnd.WritePortal(); + for (vtkm::Id branch = 0; branch < nIncoming; ++branch) + { + vtkm::Id incomingTmpBranchUpperEnd; + rp.dequeue(ingid, incomingTmpBranchUpperEnd); + incomingTopVolBranchUpperEndPortal.Set(branch, incomingTmpBranchUpperEnd); + } + + // TODO/FIXME: This is a workaround for a bug in DIY/vtk-m. + // Replace with dequeuing ArrayHandles once bug is fixed. + // rp.dequeue(ingid, incomingTopVolBranchUpperEnd); + + IdArrayType incomingTopVolBranchLowerEnd; + incomingTopVolBranchLowerEnd.Allocate(nIncoming); + auto incomingTopVolBranchLowerEndPortal = incomingTopVolBranchLowerEnd.WritePortal(); + for (vtkm::Id branch = 0; branch < nIncoming; ++branch) + { + vtkm::Id incomingTmpBranchLowerEnd; + rp.dequeue(ingid, incomingTmpBranchLowerEnd); + incomingTopVolBranchLowerEndPortal.Set(branch, incomingTmpBranchLowerEnd); + } + + // TODO/FIXME: This is a workaround for a bug in DIY/vtk-m. + // Replace with dequeuing ArrayHandles once bug is fixed. + // rp.dequeue(ingid, incomingTopVolBranchLowerEnd); + auto resolveArray = [&](auto& inArray) { using InArrayHandleType = std::decay_t; using ValueType = typename InArrayHandleType::ValueType; @@ -246,10 +280,14 @@ void SelectTopVolumeContoursFunctor::operator()( IdArrayType mergedTopVolBranchGRId; IdArrayType mergedTopVolBranchVolume; IdArrayType mergedTopVolBranchSaddleEpsilon; + IdArrayType mergedTopVolBranchUpperEnd; + IdArrayType mergedTopVolBranchLowerEnd; InArrayHandleType mergedTopVolBranchSaddleIsoValue; mergedTopVolBranchGRId.Allocate(nIncoming + nSelf); mergedTopVolBranchVolume.Allocate(nIncoming + nSelf); mergedTopVolBranchSaddleEpsilon.Allocate(nIncoming + nSelf); + mergedTopVolBranchUpperEnd.Allocate(nIncoming + nSelf); + mergedTopVolBranchLowerEnd.Allocate(nIncoming + nSelf); mergedTopVolBranchSaddleIsoValue.Allocate(nIncoming + nSelf); vtkm::cont::Algorithm::CopySubRange( @@ -258,6 +296,10 @@ void SelectTopVolumeContoursFunctor::operator()( incomingTopVolBranchVolume, 0, nIncoming, mergedTopVolBranchVolume, 0); vtkm::cont::Algorithm::CopySubRange( incomingTopVolBranchSaddleEpsilon, 0, nIncoming, mergedTopVolBranchSaddleEpsilon, 0); + vtkm::cont::Algorithm::CopySubRange( + incomingTopVolBranchUpperEnd, 0, nIncoming, mergedTopVolBranchUpperEnd, 0); + vtkm::cont::Algorithm::CopySubRange( + incomingTopVolBranchLowerEnd, 0, nIncoming, mergedTopVolBranchLowerEnd, 0); vtkm::cont::Algorithm::CopySubRange( incomingTopVolBranchSaddleIsoValue, 0, nIncoming, mergedTopVolBranchSaddleIsoValue, 0); vtkm::cont::Algorithm::CopySubRange( @@ -266,6 +308,10 @@ void SelectTopVolumeContoursFunctor::operator()( b->TopVolumeBranchVolume, 0, nSelf, mergedTopVolBranchVolume, nIncoming); vtkm::cont::Algorithm::CopySubRange( b->TopVolumeBranchSaddleEpsilon, 0, nSelf, mergedTopVolBranchSaddleEpsilon, nIncoming); + vtkm::cont::Algorithm::CopySubRange( + b->TopVolumeBranchUpperEndGRId, 0, nSelf, mergedTopVolBranchUpperEnd, nIncoming); + vtkm::cont::Algorithm::CopySubRange( + b->TopVolumeBranchLowerEndGRId, 0, nSelf, mergedTopVolBranchLowerEnd, nIncoming); vtkm::cont::Algorithm::CopySubRange( inArray, 0, nSelf, mergedTopVolBranchSaddleIsoValue, nIncoming); @@ -289,6 +335,12 @@ void SelectTopVolumeContoursFunctor::operator()( IdArrayType permutedTopVolBranchSaddleEpsilon; vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( mergedTopVolBranchSaddleEpsilon, sortedBranchId, permutedTopVolBranchSaddleEpsilon); + IdArrayType permutedTopVolBranchUpperEnd; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + mergedTopVolBranchUpperEnd, sortedBranchId, permutedTopVolBranchUpperEnd); + IdArrayType permutedTopVolBranchLowerEnd; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + mergedTopVolBranchLowerEnd, sortedBranchId, permutedTopVolBranchLowerEnd); InArrayHandleType permutedTopVolBranchSaddleIsoValue; vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( mergedTopVolBranchSaddleIsoValue, sortedBranchId, permutedTopVolBranchSaddleIsoValue); @@ -318,6 +370,8 @@ void SelectTopVolumeContoursFunctor::operator()( IdArrayType mergedUniqueBranchGRId; IdArrayType mergedUniqueBranchVolume; IdArrayType mergedUniqueBranchSaddleEpsilon; + IdArrayType mergedUniqueBranchUpperEnd; + IdArrayType mergedUniqueBranchLowerEnd; InArrayHandleType mergedUniqueBranchSaddleIsoValue; vtkm::cont::Algorithm::CopyIf( @@ -326,6 +380,10 @@ void SelectTopVolumeContoursFunctor::operator()( permutedTopVolBranchVolume, oneIfUniqueBranch, mergedUniqueBranchVolume); vtkm::cont::Algorithm::CopyIf( permutedTopVolBranchSaddleEpsilon, oneIfUniqueBranch, mergedUniqueBranchSaddleEpsilon); + vtkm::cont::Algorithm::CopyIf( + permutedTopVolBranchUpperEnd, oneIfUniqueBranch, mergedUniqueBranchUpperEnd); + vtkm::cont::Algorithm::CopyIf( + permutedTopVolBranchLowerEnd, oneIfUniqueBranch, mergedUniqueBranchLowerEnd); vtkm::cont::Algorithm::CopyIf( permutedTopVolBranchSaddleIsoValue, oneIfUniqueBranch, mergedUniqueBranchSaddleIsoValue); @@ -356,6 +414,10 @@ void SelectTopVolumeContoursFunctor::operator()( 0, this->nSavedBranches, b->TopVolumeBranchSaddleEpsilon); + vtkm::cont::Algorithm::CopySubRange( + mergedUniqueBranchUpperEnd, 0, this->nSavedBranches, b->TopVolumeBranchUpperEndGRId); + vtkm::cont::Algorithm::CopySubRange( + mergedUniqueBranchLowerEnd, 0, this->nSavedBranches, b->TopVolumeBranchLowerEndGRId); // InArrayHandleType subRangeUniqueBranchSaddleIsoValue; inArray.Allocate(this->nSavedBranches); vtkm::cont::Algorithm::CopySubRange( @@ -368,6 +430,8 @@ void SelectTopVolumeContoursFunctor::operator()( vtkm::cont::Algorithm::Copy(mergedUniqueBranchVolume, b->TopVolumeBranchVolume); vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleEpsilon, b->TopVolumeBranchSaddleEpsilon); + vtkm::cont::Algorithm::Copy(mergedUniqueBranchUpperEnd, b->TopVolumeBranchUpperEndGRId); + vtkm::cont::Algorithm::Copy(mergedUniqueBranchLowerEnd, b->TopVolumeBranchLowerEndGRId); inArray.Allocate(nMergedUnique); vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleIsoValue, inArray); } diff --git a/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h b/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h index 909cf7076a..01e9321e55 100644 --- a/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h +++ b/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h @@ -231,6 +231,37 @@ inline std::vector ReadGroundTruth return result; } +inline void ReadGroundTruthBranchVolume(std::string filename, + std::vector& branchDirections, + std::vector& branchInnerEnds, + std::vector& branchVolumes) +{ + std::ifstream ct_file(filename); + if (!ct_file.is_open()) + { + throw vtkm::io::ErrorIO("Unable to open ground truth data file: " + filename); + } + // read the branch information line by line + vtkm::Id branchDirection; + while (ct_file >> branchDirection) + { + branchDirections.push_back(branchDirection); + vtkm::Id branchInnerEnd, branchVolume; + if (branchDirection != 0) + { + ct_file >> branchInnerEnd >> branchVolume; + branchInnerEnds.push_back(branchInnerEnd); + branchVolumes.push_back(branchVolume); + } + else + { + vtkm::Id branchLowerEnd, branchUpperEnd; + ct_file >> branchLowerEnd >> branchUpperEnd >> branchVolume; + // we do not store the main branch in the current check + } + } +} + inline vtkm::cont::PartitionedDataSet RunContourTreeDUniformDistributed( const vtkm::cont::DataSet& ds, std::string fieldName, @@ -241,7 +272,8 @@ inline vtkm::cont::PartitionedDataSet RunContourTreeDUniformDistributed( bool augmentHierarchicalTree, bool computeHierarchicalVolumetricBranchDecomposition, vtkm::Id3& globalSize, - bool passBlockIndices = true) + bool passBlockIndices = true, + const vtkm::Id presimplifyThreshold = 0) { // Get dimensions of data set vtkm::cont::CastAndCall( @@ -293,6 +325,10 @@ inline vtkm::cont::PartitionedDataSet RunContourTreeDUniformDistributed( filter.SetUseBoundaryExtremaOnly(true); filter.SetAugmentHierarchicalTree(augmentHierarchicalTree); filter.SetActiveField(fieldName); + if (presimplifyThreshold > 0) + { + filter.SetPresimplifyThreshold(presimplifyThreshold); + } auto result = filter.Execute(pds); if (computeHierarchicalVolumetricBranchDecomposition) @@ -878,6 +914,227 @@ inline void TestContourTreeFile(std::string ds_filename, } } +// Routine to verify the branch decomposition results from presimplification. +inline void VerifyContourTreePresimplificationOutput(std::string datasetName, + vtkm::cont::PartitionedDataSet& tp_result, + std::vector& gtBranchDirections, + std::vector& gtBranchInnerEnds, + std::vector& gtBranchVolumes, + int rank = 0, + const vtkm::Id presimplifyThreshold = 1) +{ + if (rank == 0) + { + // the top branches by volume are consistent across all blocks + auto tp_ds = tp_result.GetPartition(0); + auto topVolBranchUpperEndGRIds = tp_ds.GetField("TopVolumeBranchUpperEnd") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto topVolBranchLowerEndGRIds = tp_ds.GetField("TopVolumeBranchLowerEnd") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto topVolBranchVolume = tp_ds.GetField("TopVolumeBranchVolume") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto topVolBranchSaddleEpsilon = tp_ds.GetField("TopVolumeBranchSaddleEpsilon") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + vtkm::Id nSelectedBranches = topVolBranchUpperEndGRIds.GetNumberOfValues(); + std::vector gtSortedOrder, sortedOrder; + std::vector topVolBranchInnerEnds; + for (vtkm::Id branch = 0; branch < nSelectedBranches; branch++) + { + if (topVolBranchVolume.Get(branch) > presimplifyThreshold) + { + sortedOrder.push_back(branch); + } + if (topVolBranchSaddleEpsilon.Get(branch) < 0) + topVolBranchInnerEnds.push_back(topVolBranchUpperEndGRIds.Get(branch)); + else + topVolBranchInnerEnds.push_back(topVolBranchLowerEndGRIds.Get(branch)); + } + for (size_t branch = 0; branch < gtBranchVolumes.size(); branch++) + { + if (gtBranchVolumes.at(branch) > presimplifyThreshold) + gtSortedOrder.push_back(branch); + } + VTKM_TEST_ASSERT(sortedOrder.size() == gtSortedOrder.size(), + "Test failed: number of branches does not match for data set " + datasetName); + std::sort(sortedOrder.begin(), + sortedOrder.end(), + [topVolBranchInnerEnds, topVolBranchVolume, topVolBranchSaddleEpsilon]( + const vtkm::Id& lhs, const vtkm::Id& rhs) { + if (topVolBranchInnerEnds.at(lhs) < topVolBranchInnerEnds.at(rhs)) + return true; + if (topVolBranchInnerEnds.at(lhs) > topVolBranchInnerEnds.at(rhs)) + return false; + if (topVolBranchVolume.Get(lhs) < topVolBranchVolume.Get(rhs)) + return true; + if (topVolBranchVolume.Get(lhs) > topVolBranchVolume.Get(rhs)) + return false; + return (topVolBranchSaddleEpsilon.Get(lhs) < topVolBranchSaddleEpsilon.Get(rhs)); + }); + std::sort(gtSortedOrder.begin(), + gtSortedOrder.end(), + [gtBranchInnerEnds, gtBranchVolumes, gtBranchDirections](const vtkm::Id& lhs, + const vtkm::Id& rhs) { + if (gtBranchInnerEnds.at(lhs) < gtBranchInnerEnds.at(rhs)) + return true; + if (gtBranchInnerEnds.at(lhs) > gtBranchInnerEnds.at(rhs)) + return false; + if (gtBranchVolumes.at(lhs) < gtBranchVolumes.at(rhs)) + return true; + if (gtBranchVolumes.at(lhs) > gtBranchVolumes.at(rhs)) + return false; + return (gtBranchDirections.at(lhs) < gtBranchDirections.at(rhs)); + }); + + for (size_t branch = 0; branch < sortedOrder.size(); branch++) + { + VTKM_TEST_ASSERT(topVolBranchInnerEnds.at(sortedOrder.at(branch)) == + gtBranchInnerEnds.at(gtSortedOrder.at(branch)), + "Test failed: branch inner end does not match for data set " + datasetName); + + VTKM_TEST_ASSERT(topVolBranchVolume.Get(sortedOrder.at(branch)) == + gtBranchVolumes.at(gtSortedOrder.at(branch)), + "Test failed: branch volume does not match for data set " + datasetName); + + VTKM_TEST_ASSERT(topVolBranchSaddleEpsilon.Get(sortedOrder.at(branch)) == + gtBranchDirections.at(gtSortedOrder.at(branch)), + "Test failed: branch direction does not match for data set " + datasetName); + } + } +} + +// routine to run distributed contour tree and presimplification +inline void RunContourTreePresimplification(std::string fieldName, + vtkm::cont::DataSet& ds, + vtkm::cont::PartitionedDataSet& tp_result, + int nBlocks, + bool marchingCubes = false, + int rank = 0, + int size = 1, + bool passBlockIndices = true, + const vtkm::Id presimplifyThreshold = 1) +{ + vtkm::Id3 globalSize; + + vtkm::cont::PartitionedDataSet result = RunContourTreeDUniformDistributed(ds, + fieldName, + marchingCubes, + nBlocks, + rank, + size, + true, + true, + globalSize, + passBlockIndices, + presimplifyThreshold); + + // Compute branch decomposition + vtkm::cont::PartitionedDataSet bd_result; + + vtkm::filter::scalar_topology::DistributedBranchDecompositionFilter bd_filter; + bd_result = bd_filter.Execute(result); + + // Compute SelectTopVolumeContours + vtkm::filter::scalar_topology::SelectTopVolumeContoursFilter tp_filter; + + // numBranches needs to be large enough to include all branches + // numBranches < numSuperarcs < globalSize + tp_filter.SetSavedBranches(globalSize[0] * globalSize[1] * + (globalSize[2] > 1 ? globalSize[2] : 1)); + tp_result = tp_filter.Execute(bd_result); +} + +// routine to test contour tree presimplification +inline void TestContourTreePresimplification( + std::string datasetName, + std::string fieldName, + std::string gtbr_filename, + int nBlocks, + vtkm::cont::DataSet input_ds, // dataset if we pre-load the dataset + std::string ds_filename = "", // dataset file name if we load data from file + const vtkm::Id presimplifyThreshold = 1, + bool marchingCubes = false, + int rank = 0, + int size = 1, + bool passBlockIndices = true) +{ + if (rank == 0) + { + std::cout << "Testing ContourTreeUniformDistributed with " + << (marchingCubes ? "marching cubes" : "Freudenthal") << " mesh connectivity on \"" + << datasetName << "\" divided into " << nBlocks + << " blocks. Using presimplification threshold = " << presimplifyThreshold + << std::endl; + } + + // data set should either be loaded from file or passed as input. + // if we get a ds_filename, we load the dataset from file, which means that input_ds becomes useless. + // if the ds_filename is empty, we use input_ds. + // TODO/FIXME: is there a better way to do this? + vtkm::cont::DataSet ds; + if (!ds_filename.empty()) + { + if (rank == 0) + std::cout << "Loading data from " << ds_filename << std::endl; + vtkm::io::VTKDataSetReader reader(ds_filename); + try + { + ds = reader.ReadDataSet(); + } + catch (vtkm::io::ErrorIO& e) + { + std::string message("Error reading: "); + message += ds_filename; + message += ", "; + message += e.GetMessage(); + + VTKM_TEST_FAIL(message.c_str()); + } + } + else + ds = input_ds; + + // get the output of contour tree + presimplification + vtkm::cont::PartitionedDataSet tp_result; + RunContourTreePresimplification(fieldName, + ds, + tp_result, + nBlocks, + marchingCubes, + rank, + size, + passBlockIndices, + presimplifyThreshold); + + // get the ground truth from file + if (rank == 0) + { + std::vector gtBranchDirections; + std::vector gtBranchInnerEnds; + std::vector gtBranchVolumes; + + // load the ground truth branch decomposition by volume from file + ReadGroundTruthBranchVolume( + gtbr_filename, gtBranchDirections, gtBranchInnerEnds, gtBranchVolumes); + + // verify the contour tree presimplification output with ground truth + VerifyContourTreePresimplificationOutput(datasetName, + tp_result, + gtBranchDirections, + gtBranchInnerEnds, + gtBranchVolumes, + rank, + presimplifyThreshold); + } +} + } } } diff --git a/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx b/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx index 48e131c8fd..bf70052ac6 100644 --- a/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx +++ b/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx @@ -55,6 +55,7 @@ namespace { using vtkm::filter::testing::contourtree_uniform_distributed::TestContourTreeFile; +using vtkm::filter::testing::contourtree_uniform_distributed::TestContourTreePresimplification; using vtkm::filter::testing::contourtree_uniform_distributed:: TestContourTreeUniformDistributed5x6x7; using vtkm::filter::testing::contourtree_uniform_distributed::TestContourTreeUniformDistributed8x9; @@ -130,6 +131,94 @@ public: true, false, false); + + // tests for contour tree presimplification on 2D vanc dataset + TestContourTreePresimplification( + "vanc", // dataset name + "var", // field name + vtkm::cont::testing::Testing::RegressionImagePath( + "vanc.presimplification.ct_txt"), // ground truth file name + 2, // nBlocks + vtkm::cont::DataSet(), // empty, just to satisfy the function input format + vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), // dataset file path + 1 // presimplifyThreshold + ); + TestContourTreePresimplification( + "vanc", + "var", + vtkm::cont::testing::Testing::RegressionImagePath("vanc.presimplification.ct_txt"), + 4, + vtkm::cont::DataSet(), + vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), + 1); + TestContourTreePresimplification( + "vanc", + "var", + vtkm::cont::testing::Testing::RegressionImagePath("vanc.presimplification.ct_txt"), + 2, + vtkm::cont::DataSet(), + vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), + 4); + TestContourTreePresimplification( + "vanc", + "var", + vtkm::cont::testing::Testing::RegressionImagePath("vanc.presimplification.ct_txt"), + 4, + vtkm::cont::DataSet(), + vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), + 4); + + // test for contour tree presimplification on 3D 5x6x7 dataset + TestContourTreePresimplification( + "5x6x7", // dataset name + "pointvar", // field name + vtkm::cont::testing::Testing::RegressionImagePath( + "5x6x7.presimplification.ct_txt"), // ground truth file name + 2, // nBlocks + vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), // dataset preset, + "", // empty dataset filename, just to satisfy the function format + 2 // presimplifyThreshold + ); + TestContourTreePresimplification( + "5x6x7", + "pointvar", + vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), + 4, + vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), + "", + 2); + TestContourTreePresimplification( + "5x6x7", + "pointvar", + vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), + 8, + vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), + "", + 2); + TestContourTreePresimplification( + "5x6x7", + "pointvar", + vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), + 2, + vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), + "", + 4); + TestContourTreePresimplification( + "5x6x7", + "pointvar", + vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), + 4, + vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), + "", + 4); + TestContourTreePresimplification( + "5x6x7", + "pointvar", + vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), + 8, + vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), + "", + 4); } }; } diff --git a/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h b/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h index 35a8b86093..034be5d6e8 100644 --- a/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h +++ b/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h @@ -506,12 +506,19 @@ inline void HierarchicalVolumetricBranchDecomposer::CollapseBranches( vtkm::worklet::contourtree_distributed::FindSuperArcBetweenNodes findSuperArcBetweenNodes{ hierarchicalTreeSuperarcs }; + // Get the number of rounds + auto numRoundsArray = hierarchicalTreeDataSet.GetField("NumRounds") + .GetData() + .AsArrayHandle>(); + vtkm::Id numRounds = vtkm::cont::ArrayGetValue(0, numRoundsArray); using vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer:: CollapseBranchesWorklet; - this->Invoke(CollapseBranchesWorklet{}, // the worklet + CollapseBranchesWorklet collapseBranchesWorklet(numRounds); + this->Invoke(collapseBranchesWorklet, // the worklet this->BestUpSupernode, // input this->BestDownSupernode, // input + hierarchicalTreeSuperarcs, // input findRegularByGlobal, // input ExecutionObject findSuperArcBetweenNodes, // input ExecutionObject hierarchicalTreeRegular2Supernode, // input diff --git a/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/CollapseBranchesWorklet.h b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/CollapseBranchesWorklet.h index 498f7eb5f2..d16e8b1083 100644 --- a/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/CollapseBranchesWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/CollapseBranchesWorklet.h @@ -65,6 +65,7 @@ public: using ControlSignature = void( FieldIn bestUpSupernode, FieldIn bestDownSupernode, + FieldIn superarcs, // Execution objects from the hierarchical tree to use the FindRegularByGlobal function ExecObject findRegularByGlobal, // Execution objects from the hierarchical tree to use the FindSuperArcBetweenNodes, function @@ -72,12 +73,15 @@ public: WholeArrayIn hierarchicalTreeRegular2supernode, WholeArrayIn hierarchicalTreeWhichRound, WholeArrayInOut branchRoot); - using ExecutionSignature = void(InputIndex, _1, _2, _3, _4, _5, _6, _7); + using ExecutionSignature = void(InputIndex, _1, _2, _3, _4, _5, _6, _7, _8); using InputDomain = _1; /// Default Constructor VTKM_EXEC_CONT - CollapseBranchesWorklet() {} + CollapseBranchesWorklet(vtkm::Id numRounds) + : NumRounds(numRounds) + { + } /// operator() of the workelt template NumRounds) && + (vtkm::worklet::contourtree_augmented::NoSuchElement(superarcsId))) + { + return; + } + // if there is no best up, we're at an upper leaf and will not connect up two superarcs anyway, so we can skip the supernode if (vtkm::worklet::contourtree_augmented::NoSuchElement(bestUpSupernodeId)) { @@ -233,6 +250,8 @@ public: */ } // operator()() +private: + vtkm::Id NumRounds; }; // CollapseBranchesWorklet diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_augmented/NotNoSuchElementPredicate.h b/vtkm/filter/scalar_topology/worklet/contourtree_augmented/NotNoSuchElementPredicate.h index 3cb674c655..67b8f6f5de 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_augmented/NotNoSuchElementPredicate.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_augmented/NotNoSuchElementPredicate.h @@ -76,6 +76,19 @@ public: private: }; +//Simple functor to subset a VTKm ArrayHandle +class NoSuchElementPredicate +{ +public: + VTKM_EXEC_CONT + NoSuchElementPredicate() {} + + VTKM_EXEC_CONT + bool operator()(const vtkm::Id& vertexId) const { return NoSuchElement(vertexId); } + +private: +}; + } // namespace contourtree_augmented } // namespace worklet } // namespace vtkm diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_augmented/PrintVectors.h b/vtkm/filter/scalar_topology/worklet/contourtree_augmented/PrintVectors.h index 289bc686ba..76634060f0 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_augmented/PrintVectors.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_augmented/PrintVectors.h @@ -72,8 +72,8 @@ namespace contourtree_augmented { // local constants to allow changing the spacing as needed -constexpr int PRINT_WIDTH = 14; -constexpr int PREFIX_WIDTH = 30; +constexpr int PRINT_WIDTH = 18; +constexpr int PREFIX_WIDTH = 36; template void PrintValues(std::string label, diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_augmented/Types.h b/vtkm/filter/scalar_topology/worklet/contourtree_augmented/Types.h index e1d02f68b9..e31d660e93 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_augmented/Types.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_augmented/Types.h @@ -86,12 +86,27 @@ constexpr vtkm::Id IS_REGULAR = static_cast(2); constexpr vtkm::Id IS_SADDLE = static_cast(3); constexpr vtkm::Id IS_ATTACHMENT = static_cast(4); +// NOTE: 29/08/2024 +// After discussion between Mingzhe and Hamish, warning left in. +// The logic for the choice is that we are already using 5 bits out of 64, leaving us with 0.5 exa-indices available. +// Using an extra bit flag would reduce it to 0.25 exa-indices, which may pose a problem in the future. +// We therefore have chosen to reuse an existing bit flag, which is not used in the section of code in question. +// (HierarchicalHypersweeper.h: ComputeSuperarcTransferWeights()) +// WARNING 11/07/2023 +// TERMINAL_ELEMENT is primarily used for optimisation of memory access during pointer doubling operations +// We now need to distinguish between a supernode and superarc when sorting by superarc(node) IDs +// This only (at present) comes up when processing attachment points, which have null superarcs, so it +// is reasonable to reuse TERMINAL_ELEMENT for this purpose. However, we give it a separate macro name with +// the same value to aid comprehension +constexpr vtkm::Id TRANSFER_TO_SUPERARC = TERMINAL_ELEMENT; + // clang-format on using IdArrayType = vtkm::cont::ArrayHandle; using EdgePair = vtkm::Pair; // here EdgePair.first=low and EdgePair.second=high using EdgePairArray = vtkm::cont::ArrayHandle; // Array of edge pairs + // inline functions for retrieving flags or index VTKM_EXEC_CONT inline bool NoSuchElement(vtkm::Id flaggedIndex) @@ -143,6 +158,12 @@ inline bool NoFlagsSet(vtkm::Id flaggedIndex) return (flaggedIndex & ~INDEX_MASK) == 0; } // NoFlagsSet +// Helper function: to check that the TRANSFER_TO_SUPERARC flag is set +VTKM_EXEC_CONT +inline bool TransferToSuperarc(vtkm::Id flaggedIndex) +{ // transferToSuperarc() + return ((flaggedIndex & TRANSFER_TO_SUPERARC) != 0); +} // transferToSuperarc() // Debug helper function: Assert that an index array has no element with any flags set template @@ -225,6 +246,23 @@ inline std::string FlagString(vtkm::Id flaggedIndex) return fString; } // FlagString() + +// == comparison operator for edges +inline bool edgeEqual(const EdgePair& LHS, const EdgePair& RHS) +{ // operator == + + if (LHS.first != RHS.first) + { + return false; + } + if (LHS.second != RHS.second) + { + return false; + } + return true; +} // operator == + + class EdgeDataHeight { public: diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/DistributedContourTreeBlockData.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/DistributedContourTreeBlockData.h index 3ef88ae91e..93e82e5a1a 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/DistributedContourTreeBlockData.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/DistributedContourTreeBlockData.h @@ -76,10 +76,12 @@ template struct DistributedContourTreeBlockData { // Block metadata - int GlobalBlockId; // Global DIY id of this block - vtkm::Id LocalBlockNo; // Local block id on this rank - vtkm::Id3 BlockOrigin; // Origin of the data block - vtkm::Id3 BlockSize; // Extends of the data block + int GlobalBlockId; // Global DIY id of this block + vtkm::Id LocalBlockNo; // Local block id on this rank + vtkm::Id3 BlockOrigin; // Origin of the data block + vtkm::Id3 BlockSize; // Extends of the data block + vtkm::Id3 FixedBlockOrigin; // Origin unaffected by fan-in + vtkm::Id3 FixedBlockSize; // Extends unaffected by fan-in // Fan in data std::vector ContourTrees; diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h index 203544500c..dee8f012ac 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h @@ -101,6 +101,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -114,6 +117,10 @@ #include #include +#ifdef DEBUG_PRINT +#define DEBUG_PRINT_HIERARCHICAL_AUGMENTER +#define DEBUG_PRINT_HIERARCHICAL_CONTOUR_TREE +#endif namespace vtkm { @@ -178,7 +185,7 @@ public: /// these are essentially temporary local variables, but are placed here to make the DebugPrint() /// more comprehensive. They will be allocated where used - /// this one makes a list of attachment Ids and is used in sevral different places, so resize it when done + /// this one makes a list of attachment Ids and is used in several different places, so resize it when done vtkm::worklet::contourtree_augmented::IdArrayType AttachmentIds; /// tracks segments of attachment points by round vtkm::worklet::contourtree_augmented::IdArrayType FirstAttachmentPointInRound; @@ -206,7 +213,9 @@ public: vtkm::worklet::contourtree_distributed::HierarchicalContourTree* inAugmentedTree, vtkm::Id3 meshBlockOrigin, vtkm::Id3 meshBockSize, - vtkm::Id3 meshGlobalSize); + vtkm::Id3 meshGlobalSize, + vtkm::worklet::contourtree_augmented::IdArrayType* volumeArray = NULL, + vtkm::Id presimplifyThreshold = 0); /// routine to prepare the set of attachment points to transfer void PrepareOutAttachmentPoints(vtkm::Id round); @@ -228,7 +237,7 @@ public: /// transfer level of superstructure with insertions void CopySuperstructure(); /// reset the super Ids in the hyperstructure to the new values - void UpdateHyperstructure(); + void UpdateHyperstructure(vtkm::Id roundNumber); /// copy the remaining base level regular nodes void CopyBaseRegularStructure(); @@ -260,7 +269,9 @@ void HierarchicalAugmenter::Initialize( vtkm::worklet::contourtree_distributed::HierarchicalContourTree* augmentedTree, vtkm::Id3 meshBlockOrigin, vtkm::Id3 meshBockSize, - vtkm::Id3 meshGlobalSize) + vtkm::Id3 meshGlobalSize, + vtkm::worklet::contourtree_augmented::IdArrayType* volumeArray, + vtkm::Id presimplifyThreshold) { // Initialize() // copy the parameters for use this->BlockId = blockId; @@ -270,17 +281,125 @@ void HierarchicalAugmenter::Initialize( this->MeshBlockSize = meshBockSize; this->MeshGlobalSize = meshGlobalSize; +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + std::stringstream headStr; + headStr << "=======================" << std::endl; + headStr << "Initializing Block " << blockId << std::endl; + headStr << "=======================" << std::endl; + VTKM_LOG_S(vtkm::cont::LogLevel::Info, headStr.str()); +#endif + // now construct a list of all attachment points on the block + // except those under the presimplify threshold. The presimplification is + // handled in the IsAttachementPointPredicate + +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + bool presimplify = ((volumeArray != NULL) && (presimplifyThreshold > 0)); + std::stringstream presimpStr; + presimpStr << "Presimplification Threshold: " << presimplifyThreshold << std::endl; + presimpStr << "Volume Array: " << ((volumeArray != NULL) ? "T" : "F") << std::endl; + presimpStr << "Threshold: " << ((presimplifyThreshold > 0) ? "T" : "F") + << std::endl; + presimpStr << "Presimplify: " << (presimplify ? "T" : "F") << std::endl; + + if (presimplify) + { // presimplifying + vtkm::worklet::contourtree_augmented::PrintHeader((*volumeArray).GetNumberOfValues(), + presimpStr); + vtkm::worklet::contourtree_augmented::PrintIndices("Volumes: ", *volumeArray, -1, presimpStr); + vtkm::worklet::contourtree_augmented::PrintHeader(baseTree->Superparents.GetNumberOfValues(), + presimpStr); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Global Regular", baseTree->RegularNodeGlobalIds, -1, presimpStr); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Superparents", baseTree->Superparents, -1, presimpStr); + } // presimplifying + VTKM_LOG_S(vtkm::cont::LogLevel::Info, presimpStr.str()); +#endif + // to do this, we construct an index array with all supernode ID's that satisfy: // 1. superparent == NO_SUCH_ELEMENT (i.e. root of interior tree) // 2. round < nRounds (except the top level, where 1. indicates the tree root) - // initalize AttachementIds + // initialize AttachementIds { vtkm::worklet::contourtree_distributed::hierarchical_augmenter::IsAttachementPointPredicate - isAttachementPointPredicate( - this->BaseTree->Superarcs, this->BaseTree->WhichRound, this->BaseTree->NumRounds); + isAttachementPointPredicate(this->BaseTree->Superarcs, + this->BaseTree->WhichRound, + this->BaseTree->NumRounds, + volumeArray, + presimplifyThreshold); auto tempSupernodeIndex = vtkm::cont::ArrayHandleIndex(this->BaseTree->Supernodes.GetNumberOfValues()); + +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + // This debug routine is a serial implementation of the IsAttachmentPointPredicate + // with necessary debug output in the intermediate steps. + { + std::stringstream isAttachmentStream; + vtkm::cont::Algorithm::Copy(tempSupernodeIndex, this->AttachmentIds); + isAttachmentStream << "Block: " << blockId << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(this->AttachmentIds.GetNumberOfValues(), + isAttachmentStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Attachment ID", this->AttachmentIds, -1, isAttachmentStream); + + auto supernodes = this->BaseTree->Supernodes.ReadPortal(); + auto superarcs = this->BaseTree->Superarcs.ReadPortal(); + auto whichRound = this->BaseTree->WhichRound.ReadPortal(); + auto volumeArr = + presimplify ? volumeArray->ReadPortal() : this->BaseTree->WhichRound.ReadPortal(); + auto regNodeGlobalIds = this->BaseTree->RegularNodeGlobalIds.ReadPortal(); + auto attachmentIds = this->AttachmentIds.WritePortal(); + for (vtkm::Id supernode = 0; supernode < this->AttachmentIds.GetNumberOfValues(); supernode++) + { // per supernode + isAttachmentStream << "Processing supernode " << supernode << std::endl; + isAttachmentStream << "Regular ID " << supernodes.Get(supernode) << std::endl; + isAttachmentStream << "Global Regular ID " + << regNodeGlobalIds.Get(supernodes.Get(supernode)) << std::endl; + + // an attachment point is defined by having no superarc (NO_SUCH_ELEMENT) and not being in the final round (where this indicates the global root) + if (vtkm::worklet::contourtree_augmented::NoSuchElement(superarcs.Get(supernode)) && + (whichRound.Get(supernode) < baseTree->NumRounds)) + // passes the predicate - nothing further needed + { // passes the predicate + isAttachmentStream << "Attachment Point: it passed the first test" << std::endl; + if (presimplify) + { + isAttachmentStream << "Volume: " << volumeArr.Get(supernode) << std::endl; + isAttachmentStream << "Threshold: " << presimplifyThreshold << std::endl; + } + + // suppress if it's volume is at or below the threshold + if (presimplify && volumeArr.Get(supernode) <= presimplifyThreshold) + { // below threshold + attachmentIds.Set(supernode, vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT); + isAttachmentStream << "Failed Second Test" << std::endl; + } // below threshold + else + isAttachmentStream << "Volume Greater than Threshold: it passed the second test" + << std::endl; + isAttachmentStream << "Block: " << blockId << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(attachmentIds.GetNumberOfValues(), + isAttachmentStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Attachment ID", this->AttachmentIds, -1, isAttachmentStream); + } // passes the predicate + else + { // fails + // reset the value + attachmentIds.Set(supernode, vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT); + } // fails + } // per supernode + + isAttachmentStream << "Block: " << blockId << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(this->AttachmentIds.GetNumberOfValues(), + isAttachmentStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Attachment ID", this->AttachmentIds, -1, isAttachmentStream); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, isAttachmentStream.str()); + } +#endif + vtkm::cont::Algorithm::CopyIf( // first a list of all of the supernodes tempSupernodeIndex, @@ -294,6 +413,18 @@ void HierarchicalAugmenter::Initialize( // being in the final round (where this indicates the global root) defined by the condition // if (noSuchElement(baseTree->superarcs[supernode]) && (baseTree->whichRound[supernode] < baseTree->nRounds)) isAttachementPointPredicate); + +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + { + std::stringstream debugStream; + debugStream << "Block: " << blockId << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(this->AttachmentIds.GetNumberOfValues(), + debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Attachment ID", this->AttachmentIds, -1, debugStream); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); + } +#endif } // we now resize the working arrays @@ -333,7 +464,7 @@ void HierarchicalAugmenter::Initialize( // get the ascending flag from the superparent's superarc and transfer to the superparent // Array decorator to add the IS_ASCENDING flag to our superparent, i.e., // if (isAscending(baseTree->superarcs[superparent])){ superparent |= IS_ASCENDING; } - // TODO: When using the superparents ArrayHandlePermutation in the ArrayHandleDecorator the compiler + // NOTE: When using the superparents ArrayHandlePermutation in the ArrayHandleDecorator the compiler // has issues discovering the StorageType when calling Copy(isAscendingSuperparentArr, superparents) // by copying superparents to an actual array in tempArrSuperparents we can avoid this issue, // at the cost of an extra copy. @@ -347,6 +478,31 @@ void HierarchicalAugmenter::Initialize( vtkm::cont::Algorithm::Copy(isAscendingSuperparentArr, this->Superparents); } +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + { + std::stringstream debugStream; + debugStream << "Block: " << blockId << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(this->AttachmentIds.GetNumberOfValues(), + debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Attachment ID", this->AttachmentIds, -1, debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Global Regular ID", this->GlobalRegularIds, -1, debugStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "Data Value", this->DataValues, -1, debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Supernode ID", this->SupernodeIds, -1, debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Superparent ID ", this->Superparents, -1, debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Superparent Round", this->SuperparentRounds, -1, debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Which Round", this->WhichRounds, -1, debugStream); + debugStream << std::endl; + VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); + } +#endif + // clean up memory this->AttachmentIds.ReleaseResources(); } // Initialize() @@ -422,6 +578,16 @@ void HierarchicalAugmenter::RetrieveInAttachmentPoints() vtkm::Id numIncomingAttachments = InData.GlobalRegularIds.GetNumberOfValues(); vtkm::Id numTotalAttachments = numAttachmentsCurrently + numIncomingAttachments; +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + { + std::stringstream debugStream; + debugStream << "nAttachmentsCurrently: " << numAttachmentsCurrently << std::endl; + debugStream << "nIncomingAttachments: " << numIncomingAttachments << std::endl; + debugStream << "nTotalAttachments: " << numTotalAttachments << std::endl; + VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); + } +#endif + // I. resize the existing arrays vtkm::worklet::contourtree_augmented::ResizeVector( this->GlobalRegularIds, numTotalAttachments, static_cast(0)); @@ -490,19 +656,50 @@ void HierarchicalAugmenter::ReleaseSwapArrays() } // ReleaseSwapArrays() -// routine to reconstruct a hierarchical tree using the augmenting supernodes +// routine to reconstruct a hierarchical tree using the augmenting supernodes. +// Allowing pre-simplification needs the superstructure and hyperstructure to be done one layer +// at a time. This means lifting the code from CopySuperstructure up to here CopyHyperstructure() +// can actually be left the way it is - copying the entire hyperstructure, as the augmentation +// process doesn't change it. It might be sensible to change it anyway, just to make the code better organised template void HierarchicalAugmenter::BuildAugmentedTree() { // BuildAugmentedTree() // 1. Prepare the data structures for filling in, copying in basic information & organising the attachment points this->PrepareAugmentedTree(); +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint(std::string("Block ") + std::to_string(this->BlockId) + + std::string(" Step 1: Augmented Tree Prepared"), + __FILE__, + __LINE__)); +#endif + // 2. Copy the hyperstructure, using the old super IDs for now this->CopyHyperstructure(); - // 3. Copy the superstructure, inserting additional points as we do - this->CopySuperstructure(); - // 4. Update the hyperstructure to use the new super IDs - this->UpdateHyperstructure(); - // 5. Copy the remaining regular structure at the bottom level, setting up the regular sort order in the process +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint(std::string("Block ") + std::to_string(this->BlockId) + + std::string(" Step 2: Hyperstructure Copied"), + __FILE__, + __LINE__)); +#endif + + // 3. Copy superstructure one round at a time, updating the hyperstructure as well + // (needed to permit search for superarcs) + // Loop from the top down: + for (vtkm::Id roundNumber = this->BaseTree->NumRounds; roundNumber >= 0; roundNumber--) + { // per round + // start by retrieving list of old supernodes from the tree (except for attachment points) + this->RetrieveOldSupernodes(roundNumber); + // since we know the number of attachment points, we can now allocate space for the level + // and set up arrays for sorting the supernodes + this->ResizeArrays(roundNumber); + // now we create the superarcs for the round in the new tree + this->CreateSuperarcs(roundNumber); + // finally, we update the hyperstructure for the round in the new tree + this->UpdateHyperstructure(roundNumber); + } // per round + // 4. Copy the remaining regular structure at the bottom level, setting up the regular sort order in the process this->CopyBaseRegularStructure(); } // BuildAugmentedTree() @@ -518,6 +715,10 @@ void HierarchicalAugmenter::PrepareAugmentedTree() // segments with identical superparent round, which is all we need for now vtkm::cont::Algorithm::Copy( vtkm::cont::ArrayHandleIndex(this->GlobalRegularIds.GetNumberOfValues()), this->AttachmentIds); +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint("Attachment Points List Constructed", __FILE__, __LINE__)); +#endif // 1a. We now need to suppress duplicates, { // Sort the attachement Ids @@ -525,10 +726,27 @@ void HierarchicalAugmenter::PrepareAugmentedTree() AttachmentSuperparentAndIndexComparator attachmentSuperparentAndIndexComparator( this->SuperparentRounds, this->GlobalRegularIds, this->SupernodeIds); vtkm::cont::Algorithm::Sort(this->AttachmentIds, attachmentSuperparentAndIndexComparator); + +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint("Attachment Points Sorted on Superparent Round", __FILE__, __LINE__)); + const vtkm::Id nAttachmentDupIds = this->AttachmentIds.GetNumberOfValues(); +#endif + // Remove the duplicate values using GlobalRegularIds[AttachmentIds] for checking for equality vtkm::worklet::contourtree_distributed::hierarchical_augmenter::AttachmentIdsEqualComparator attachmentIdsEqualComparator(this->GlobalRegularIds); vtkm::cont::Algorithm::Unique(this->AttachmentIds, attachmentIdsEqualComparator); + +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + { + std::stringstream debugStream; + debugStream << "Block " << this->BlockId << ": reducing attachment point list from size " + << nAttachmentDupIds << " to size " << this->AttachmentIds.GetNumberOfValues() + << std::endl; + VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); + } +#endif } // 2. Set up array with bounds for subsegments @@ -539,6 +757,14 @@ void HierarchicalAugmenter::PrepareAugmentedTree() this->BaseTree->NumRounds + 2), this->FirstAttachmentPointInRound); +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint(std::string("FirstAttachment Array resized to ") + + std::to_string(this->BaseTree->NumRounds + 2), + __FILE__, + __LINE__)); +#endif + // Now do a parallel set operation { vtkm::worklet::contourtree_distributed::hierarchical_augmenter:: @@ -556,9 +782,9 @@ void HierarchicalAugmenter::PrepareAugmentedTree() firstAttachmentPointInRoundPortal.Set(this->BaseTree->NumRounds + 1, this->AttachmentIds.GetNumberOfValues()); -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER VTKM_LOG_S(vtkm::cont::LogLevel::Info, - this->DebugPrint("First Attachment Point Set Where Possible", __FILE__, __LINE__)); + DebugPrint("First Attachment Point Set Where Possible", __FILE__, __LINE__)); #endif // Now clean up by looping through the rounds (serially - this is logarithmic at worst) // We loop backwards so that the next up propagates downwards @@ -574,7 +800,7 @@ void HierarchicalAugmenter::PrepareAugmentedTree() } } // per round -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint("Subsegments Identified", __FILE__, __LINE__)); #endif // 3. Initialise an array to keep track of the mapping from old supernode ID to new supernode ID @@ -583,10 +809,8 @@ void HierarchicalAugmenter::PrepareAugmentedTree() this->BaseTree->Supernodes.GetNumberOfValues()), this->NewSupernodeIds); -#ifdef DEBUG_PRINT - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - this->DebugPrint("Augmented Tree Prepared", __FILE__, __LINE__)); - +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint("Augmented Tree Prepared", __FILE__, __LINE__)); #endif } // PrepareAugmentedTree() @@ -637,68 +861,106 @@ void HierarchicalAugmenter::CopyHyperstructure() this->AugmentedTree->FirstHypernodePerIteration[roundNumber]); } // per round -#ifdef DEBUG_PRINT + // WARNING 28/05/2023: Since this resize is for the full hyperstructure, it should be safe to put here. + // Unless of course, anything relies on the sizes: but they were 0, so it is unlikely. + // A search for hyperarcs.size() & hypernodes.size() in this unit confirmed that nothing uses them. + // Nevertheless, set them all to NO_SUCH_ELEMENT out of paranoia + // 5. Reset hypernodes, hyperarcs and superchildren using supernode IDs + // The hyperstructure is unchanged, but uses old supernode IDs + vtkm::worklet::contourtree_augmented::ResizeVector( + this->AugmentedTree->Hypernodes, // resize array + this->BaseTree->Hypernodes.GetNumberOfValues(), // new size + vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT // set all elements to this value + ); + vtkm::worklet::contourtree_augmented::ResizeVector( + this->AugmentedTree->Hyperarcs, // resize array + this->BaseTree->Hyperarcs.GetNumberOfValues(), // new size + vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT // set all elements to this value + ); + vtkm::worklet::contourtree_augmented::ResizeVector( + this->AugmentedTree->Superchildren, // resize array + this->BaseTree->Superchildren.GetNumberOfValues(), // new size + 0 // set all elements to this value + ); + +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint("Hyperstructure Copied", __FILE__, __LINE__)); #endif } // CopyHyperstructure() - -// transfer level of superstructure with insertions -template -void HierarchicalAugmenter::CopySuperstructure() -{ // CopySuperstructure() - - // Loop from the top down: - for (vtkm::Id roundNumber = this->BaseTree->NumRounds; roundNumber >= 0; roundNumber--) - { // per round - // start by retrieving list of old supernodes from the tree (except for attachment points) - this->RetrieveOldSupernodes(roundNumber); - // since we know the number of attachment points, we can now allocate space for the level - // and set up arrays for sorting the supernodes - this->ResizeArrays(roundNumber); - // now we create the superarcs for the round in the new tree - this->CreateSuperarcs(roundNumber); - } // per round -#ifdef DEBUG_PRINT - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - this->DebugPrint("Superstructure Copied", __FILE__, __LINE__)); -#endif -} // CopySuperstructure() - - // reset the super IDs in the hyperstructure to the new values template -void HierarchicalAugmenter::UpdateHyperstructure() +void HierarchicalAugmenter::UpdateHyperstructure(vtkm::Id roundNumber) { // UpdateHyperstructure() - - // 5. Reset hypernodes, hyperarcs and superchildren using supernode IDs - // The hyperstructure is unchanged, but uses old supernode IDs + // now that the superstructure is known, we can find the new supernode IDs for all + // of the old hypernodes at this level and update.Wwe want to update the entire round + // at once, so we would like to use the firstHypernodePerIteration array. + vtkm::Id startIndex = + vtkm::cont::ArrayGetValue(0, this->AugmentedTree->FirstHypernodePerIteration[roundNumber]); + vtkm::Id stopIndex = vtkm::cont::ArrayGetValue( + vtkm::cont::ArrayGetValue(roundNumber, this->AugmentedTree->NumIterations), + this->AugmentedTree->FirstHypernodePerIteration[roundNumber]); + vtkm::Id selectSize = stopIndex - startIndex; { vtkm::worklet::contourtree_distributed::hierarchical_augmenter:: UpdateHyperstructureSetHyperarcsAndNodesWorklet updateHyperstructureSetHyperarcsAndNodesWorklet; - this->Invoke( - updateHyperstructureSetHyperarcsAndNodesWorklet, - this->BaseTree->Hypernodes, // input - this->BaseTree->Hyperarcs, // input - this->NewSupernodeIds, // input - this->AugmentedTree->Hypernodes, // output (the array is automatically resized here) - this->AugmentedTree->Hyperarcs // output (the array is automatically resized here) + // Create ArrayHandleViews of the subrange of the input and output arrays we need to process + auto baseTreeHypernodesView = + vtkm::cont::make_ArrayHandleView(this->BaseTree->Hypernodes, startIndex, selectSize); + auto baseTreeHyperarcsView = + vtkm::cont::make_ArrayHandleView(this->BaseTree->Hyperarcs, startIndex, selectSize); + auto augmentedTreeHypernodesView = + vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Hypernodes, startIndex, selectSize); + auto augmentedTreeHyperarcsView = + vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Hyperarcs, startIndex, selectSize); + // Invoke the worklet with the subset view of our arrays + this->Invoke(updateHyperstructureSetHyperarcsAndNodesWorklet, + baseTreeHypernodesView, // input + baseTreeHyperarcsView, // input + this->NewSupernodeIds, // input + augmentedTreeHypernodesView, // output (the array is automatically resized here) + augmentedTreeHyperarcsView // output (the array is automatically resized here) ); } // finally, find the number of superchildren as the delta between the // super ID and the next hypernode's super ID + // This is slightly tricky. Multiple supernodes may share the same hyperparent. { + vtkm::Id superchildrenStartIndex = + vtkm::cont::ArrayGetValue(0, this->AugmentedTree->FirstSupernodePerIteration[roundNumber]); + vtkm::Id superchildrenStopIndex = vtkm::cont::ArrayGetValue( + vtkm::cont::ArrayGetValue(roundNumber, this->AugmentedTree->NumIterations), + this->AugmentedTree->FirstSupernodePerIteration[roundNumber]); + vtkm::Id superchildrenSelectSize = superchildrenStopIndex - superchildrenStartIndex; + vtkm::Id extraSelectSize = ((superchildrenStartIndex + superchildrenSelectSize) < + this->AugmentedTree->Hyperparents.GetNumberOfValues()) + ? superchildrenSelectSize + 1 + : superchildrenSelectSize; + + // Because we use ArrayHandleView to select the size of array for the array, + // the index of the entry in the worklet is NOT the actual array index. + // We need to send the starting index of supernodes into the worklet. vtkm::worklet::contourtree_distributed::hierarchical_augmenter:: UpdateHyperstructureSetSuperchildrenWorklet updateHyperstructureSetSuperchildrenWorklet( - this->AugmentedTree->Supernodes.GetNumberOfValues()); - this->Invoke( - updateHyperstructureSetSuperchildrenWorklet, - this->AugmentedTree->Hypernodes, // input - this->AugmentedTree->Superchildren // output (the array is automatically resized here) + this->AugmentedTree->Supernodes.GetNumberOfValues(), superchildrenStartIndex); + // As above, we need to create views of the relevant subranges of our arrays + auto augmentedTreeSuperarcsView = vtkm::cont::make_ArrayHandleView( + this->AugmentedTree->Superarcs, superchildrenStartIndex, superchildrenSelectSize); + auto augmentedTreeHyperparentsView = vtkm::cont::make_ArrayHandleView( + this->AugmentedTree->Hyperparents, superchildrenStartIndex, extraSelectSize); + + this->Invoke(updateHyperstructureSetSuperchildrenWorklet, + this->AugmentedTree->Hypernodes, // input + augmentedTreeSuperarcsView, // input + augmentedTreeHyperparentsView, // inpu + this->AugmentedTree->Superchildren // output ); } +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint("Hyperstructure Updated", __FILE__, __LINE__)); +#endif } // UpdateHyperstructure() @@ -716,7 +978,7 @@ void HierarchicalAugmenter::CopyBaseRegularStructure() vtkm::cont::Algorithm::Sort(this->AugmentedTree->RegularNodeSortOrder, permuteComparator); } -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint("Regular Node Sorter Sorted", __FILE__, __LINE__)); #endif @@ -763,6 +1025,20 @@ void HierarchicalAugmenter::CopyBaseRegularStructure() this->RegularSuperparents, // output tempRegularNodesNeeded // output. will be CopyIf'd to this->RegularNodesNeeded ); + +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + { + std::stringstream debugStream; + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint("Regular Node Superparents Found", __FILE__, __LINE__)); + vtkm::worklet::contourtree_augmented::PrintHeader(tempRegularNodesNeeded.GetNumberOfValues(), + debugStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "RegularNodesNeeded", tempRegularNodesNeeded, -1, debugStream); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); + } +#endif + // We now compress to get the set of nodes to transfer. I.e., remove all // NO_SUCH_ELEMENT entires and copy the values to keep to our proper arrays vtkm::worklet::contourtree_augmented::NotNoSuchElementPredicate notNoSuchElementPredicate; @@ -774,6 +1050,11 @@ void HierarchicalAugmenter::CopyBaseRegularStructure() ); } +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint("Regular Node List Compressed", __FILE__, __LINE__)); +#endif + // resize the regular arrays to fit vtkm::Id numRegNeeded = this->RegularNodesNeeded.GetNumberOfValues(); vtkm::Id numExistingRegular = this->AugmentedTree->RegularNodeGlobalIds.GetNumberOfValues(); @@ -846,10 +1127,10 @@ void HierarchicalAugmenter::CopyBaseRegularStructure() ); } - // Reset the number of regular nodes in round 0 + /* ----- MINGZHE: DEBUG ----- */ vtkm::Id regularNodesInRound0 = - numTotalRegular - this->AugmentedTree->NumRegularNodesInRound.ReadPortal().Get(1); + this->AugmentedTree->NumRegularNodesInRound.ReadPortal().Get(0) + numRegNeeded; this->AugmentedTree->NumRegularNodesInRound.WritePortal().Set(0, regularNodesInRound0); // Finally, we resort the regular node sort order @@ -858,6 +1139,12 @@ void HierarchicalAugmenter::CopyBaseRegularStructure() permuteComparator(this->AugmentedTree->RegularNodeGlobalIds); vtkm::cont::Algorithm::Sort(this->AugmentedTree->RegularNodeSortOrder, permuteComparator); } + +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint("Base Regular Structure Copied", __FILE__, __LINE__)); +#endif + } // CopyBaseRegularStructure() @@ -868,26 +1155,21 @@ void HierarchicalAugmenter::RetrieveOldSupernodes(vtkm::Id roundNumbe { // RetrieveOldSupernodes() // a. Transfer supernodes from same level of old tree minus attachment points, storing by global regular ID not regular ID // Use compression to get the set of supernode IDs that we want to keep - // TODO PERFORMANCE STATISTICS: - // the # of supernodes at each level minus the # of kept supernodes gives us the # of attachment points we lose at this level - // in addition to this, the firstAttachmentPointInRound array gives us the # of attachment points we gain at this level + // Previously, it made the hard assumption that all attachment points were transferred & used that to suppress + // them. Now it can do that no longer, which gives us a choice. We can pass in the threshold & volume array + // and test here, but that's not ideal as it does the same test in multiple places. Alternatively, we can + // look up whether the supernode is already present in the structure, which has an associated search cost. + // BUT, we have an array called newSupernodeIDs for the purpose already, so that's how we'll do it. + vtkm::Id supernodeIndexBase = vtkm::cont::ArrayGetValue(0, this->BaseTree->FirstSupernodePerIteration[roundNumber]); vtkm::cont::ArrayHandleCounting supernodeIdVals( supernodeIndexBase, // start 1, // step this->BaseTree->NumSupernodesInRound.ReadPortal().Get(roundNumber)); - // the test for whether to keep it is: - // a1. at the top level, keep everything - if (!(roundNumber < this->BaseTree->NumRounds)) - { - vtkm::cont::Algorithm::Copy(supernodeIdVals, this->KeptSupernodes); - } - // a2. at lower levels, keep them if the superarc is NO_SUCH_ELEMENT - else + { // Reset this-KeptSupernodes to the right size and initalize with NO_SUCH_ELEMENT. - // TODO: Check if a simple free and allocate without initalizing the array is sufficient vtkm::cont::Algorithm::Copy( // Create const array to copy vtkm::cont::ArrayHandleConstant( @@ -895,15 +1177,16 @@ void HierarchicalAugmenter::RetrieveOldSupernodes(vtkm::Id roundNumbe this->BaseTree->NumSupernodesInRound.ReadPortal().Get(roundNumber)), // target array this->KeptSupernodes); + // Create the predicate for the CopyIf - vtkm::worklet::contourtree_augmented::NotNoSuchElementPredicate notNoSuchElementPredicate; + vtkm::worklet::contourtree_augmented::NoSuchElementPredicate NoSuchElementPredicate; // Copy supernodeId to this->KeptSupernodes vtkm::cont::Algorithm::CopyIf( // first we generate a list of supernodeIds supernodeIdVals, // Stencil with baseTree->superarcs[supernodeID] vtkm::cont::make_ArrayHandleView( - this->BaseTree->Superarcs, supernodeIndexBase, this->KeptSupernodes.GetNumberOfValues()), + this->NewSupernodeIds, supernodeIndexBase, this->KeptSupernodes.GetNumberOfValues()), // And the CopyIf compresses the array to eliminate unnecssary elements // save to this->KeptSupernodes this->KeptSupernodes, @@ -911,10 +1194,10 @@ void HierarchicalAugmenter::RetrieveOldSupernodes(vtkm::Id roundNumbe // are all points that suffice the condition // vtkm::Id supernodeID = keptSupernode + supernodeIndexBase; // !noSuchElement(baseTree->superarcs[supernodeID]); - notNoSuchElementPredicate); + NoSuchElementPredicate); } -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Round ") + std::to_string(roundNumber) + std::string(" Old Supernodes Retrieved"), @@ -1001,7 +1284,7 @@ void HierarchicalAugmenter::ResizeArrays(vtkm::Id roundNumber) vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT); } -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER VTKM_LOG_S( vtkm::cont::LogLevel::Info, DebugPrint(std::string("Round ") + std::to_string(roundNumber) + std::string(" Arrays Resized"), @@ -1025,7 +1308,7 @@ void HierarchicalAugmenter::ResizeArrays(vtkm::Id roundNumber) vtkm::worklet::contourtree_augmented::ResizeVector( this->SupernodeIdSet, numSupernodesThisLevel, static_cast(0)); } -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Round ") + std::to_string(roundNumber) + std::string(" Sorter Set Resized"), @@ -1034,6 +1317,9 @@ void HierarchicalAugmenter::ResizeArrays(vtkm::Id roundNumber) #endif // b. Transfer attachment points for level into new supernode array + // NOTE: this means the set of attachment points that we have determined by swapping + // need to be inserted onto a superarc at this level. All of them should be from + // lower levels originally, but are being moved up to this level for insertion // to copy them in, we use the existing array of attachment point IDs by round { vtkm::Id firstAttachmentPointInRoundCurrent = @@ -1074,7 +1360,7 @@ void HierarchicalAugmenter::ResizeArrays(vtkm::Id roundNumber) supernodeIdsPermuted, 0, supernodeIdsPermuted.GetNumberOfValues(), this->SupernodeIdSet, 0); } -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Round ") + std::to_string(roundNumber) + std::string(" Attachment Points Transferred"), @@ -1082,7 +1368,10 @@ void HierarchicalAugmenter::ResizeArrays(vtkm::Id roundNumber) __LINE__)); #endif - // Now we copy in the kept supernodes + // Now we copy in the kept supernodes: this used to mean only the non-attachment points + // now it includes the attachment points at this level that the simplification removed + // so they need to be put back where they were + // However, that means that all of them do exist in the base tree, so we can copy from there { auto oldRegularIdArr = vtkm::cont::make_ArrayHandlePermutation(this->KeptSupernodes, // index @@ -1125,7 +1414,7 @@ void HierarchicalAugmenter::ResizeArrays(vtkm::Id roundNumber) numInsertedSupernodes); } -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Round ") + std::to_string(roundNumber) + std::string(" Kept Supernodes Transferred"), @@ -1142,7 +1431,7 @@ void HierarchicalAugmenter::ResizeArrays(vtkm::Id roundNumber) vtkm::cont::Algorithm::Sort(this->SupernodeSorter, attachmentAndSupernodeComparator); } -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Round ") + std::to_string(roundNumber) + std::string(" Sorter Set Sorted"), @@ -1156,18 +1445,27 @@ void HierarchicalAugmenter::ResizeArrays(vtkm::Id roundNumber) ResizeArraysBuildNewSupernodeIdsWorklet resizeArraysBuildNewSupernodeIdsWorklet( numSupernodesAlready); auto supernodeIndex = vtkm::cont::ArrayHandleIndex(this->SupernodeSorter.GetNumberOfValues()); - auto supernodeIdSetPermuted = - vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->SupernodeIdSet); + // auto supernodeIdSetPermuted = + // vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->SupernodeIdSet); + auto globalRegularIdSetPermuted = + vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->GlobalRegularIdSet); + auto findRegularByGlobal = this->BaseTree->GetFindRegularByGlobal(); this->Invoke( resizeArraysBuildNewSupernodeIdsWorklet, supernodeIndex, // input domain. We only need the index because supernodeIdSetPermuted already does the permute - supernodeIdSetPermuted, // input input supernodeIDSet permuted by supernodeSorter to allow for FieldIn + // supernodeIdSetPermuted, // input input supernodeIDSet permuted by supernodeSorter to allow for FieldIn + globalRegularIdSetPermuted, + findRegularByGlobal, + this->BaseTree->Regular2Supernode, this ->NewSupernodeIds // output/input (both are necessary since not all values will be overwritten) ); + + // Add const ExecObjectType1& findRegularByGlobal, + // Add baseTree->regular2supernode } -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Round ") + std::to_string(roundNumber) + std::string(" Sorting Arrays Built"), @@ -1182,160 +1480,232 @@ template void HierarchicalAugmenter::CreateSuperarcs(vtkm::Id roundNumber) { // CreateSuperarcs() // retrieve the ID number of the first supernode at this level +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint(std::string("Round ") + std::to_string(roundNumber) + + std::string(" Starting CreateSuperarcs()"), + __FILE__, + __LINE__)); +#endif + + vtkm::Id currNumIterations = + vtkm::cont::ArrayGetValue(roundNumber, this->AugmentedTree->NumIterations); vtkm::Id numSupernodesAlready = vtkm::cont::ArrayGetValue(0, this->AugmentedTree->FirstSupernodePerIteration[roundNumber]); - // e. Connect superarcs for the level & set hyperparents & superchildren count, whichRound, whichIteration, super2hypernode - { // START scope for e. to delete temporary variables - // Note: The original PPP algorithm performed all operations listed in this block - // in a single parralel for loop. Many of those operations were smart array - // copies. So to simplfy the worklet and to make more effective use of - // VTKm algorithm, a large number of copy operations have been extracted from - // the loop and are performed here via combinations of fancy array handles and - // vtkm::cont::Algorithm::Copy operations. - - // Define the new supernode and regular Id. Both are actually the same here since we are - // augmenting the tree here, but for clarity we define them as separate variables. - // At all levels above 0, we used to keep regular vertices in case - // they are attachment points. After augmentation, we don't need to. - // Instead, at all levels above 0, the regular nodes in each round - // are identical to the supernodes. In order to avoid confusion, - // we will copy the Id into a separate variable - vtkm::cont::ArrayHandleCounting newSupernodeId( - numSupernodesAlready, // start - static_cast(1), // step - this->SupernodeSorter.GetNumberOfValues() // number of values - ); - auto newRegularId = newSupernodeId; - // define the superparentOldSuperId - auto permutedSuperparentSet = - vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->SuperparentSet); - auto superparentOldSuperId = vtkm::cont::make_ArrayHandleTransform( - permutedSuperparentSet, vtkm::worklet::contourtree_augmented::MaskedIndexFunctor()); - - // set the supernode's regular Id. Set: augmentedTree->supernodes[newSupernodeID] = newRegularID; - auto permutedAugmentedTreeSupernodes = - vtkm::cont::make_ArrayHandlePermutation(newSupernodeId, this->AugmentedTree->Supernodes); - vtkm::cont::Algorithm::Copy(newRegularId, permutedAugmentedTreeSupernodes); - - // Run the worklet for more complex operations - { // START block for CreateSuperarcsWorklet - // create the worklet - vtkm::worklet::contourtree_distributed::hierarchical_augmenter::CreateSuperarcsWorklet - createSuperarcsWorklet( - numSupernodesAlready, - this->BaseTree->NumRounds, - vtkm::cont::ArrayGetValue(roundNumber, this->AugmentedTree->NumIterations), - roundNumber, - this->AugmentedTree->Supernodes.GetNumberOfValues()); - // create fancy arrays needed to allow use of FieldIn for worklet parameters - auto permutedGlobalRegularIdSet = - vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->GlobalRegularIdSet); - auto augmentedTreeSuperarcsView = - vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Superarcs, - numSupernodesAlready, - this->SupernodeSorter.GetNumberOfValues()); - auto augmentedTreeSuper2HypernodeView = - vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Super2Hypernode, - numSupernodesAlready, - this->SupernodeSorter.GetNumberOfValues()); - // invoke the worklet - this->Invoke(createSuperarcsWorklet, // the worklet - this->SupernodeSorter, // input domain - this->SuperparentSet, // input - this->BaseTree->Superarcs, // input - this->NewSupernodeIds, // input - this->BaseTree->Supernodes, // input - this->BaseTree->RegularNodeGlobalIds, // input - permutedGlobalRegularIdSet, // input - this->BaseTree->Super2Hypernode, // input - this->BaseTree->WhichIteration, // input - augmentedTreeSuperarcsView, // output - this->AugmentedTree->FirstSupernodePerIteration[roundNumber], // input/output - augmentedTreeSuper2HypernodeView // output - ); - } // END block for CreateSuperarcsWorklet - - // setting the hyperparent is straightforward since the hyperstructure is preserved - // we take the superparent (which is guaranteed to be in the baseTree), find it's hyperparent and use that - // Set: augmentedTree->hyperparents[newSupernodeID] = baseTree->hyperparents[superparentOldSuperID]; - auto permutedAugmentedTreeHyperparents = - vtkm::cont::make_ArrayHandlePermutation(newSupernodeId, this->AugmentedTree->Hyperparents); - auto permutedBaseTreeHyperparents = - vtkm::cont::make_ArrayHandlePermutation(superparentOldSuperId, this->BaseTree->Hyperparents); - vtkm::cont::Algorithm::Copy(permutedBaseTreeHyperparents, permutedAugmentedTreeHyperparents); - - // which round and iteration carry over - // Set: augmentedTree->whichRound[newSupernodeID] = baseTree->whichRound[superparentOldSuperID]; - auto permutedAugmentedTreeWhichRound = - vtkm::cont::make_ArrayHandlePermutation(newSupernodeId, this->AugmentedTree->WhichRound); - auto permutedBaseTreeWhichRound = - vtkm::cont::make_ArrayHandlePermutation(superparentOldSuperId, this->BaseTree->WhichRound); - vtkm::cont::Algorithm::Copy(permutedBaseTreeWhichRound, permutedAugmentedTreeWhichRound); - - // Set: augmentedTree->whichIteration[newSupernodeID] = baseTree->whichIteration[superparentOldSuperID]; - auto permutedAugmentedTreeWhichIteration = - vtkm::cont::make_ArrayHandlePermutation(newSupernodeId, this->AugmentedTree->WhichIteration); - auto permutedBaseTreeWhichIterationPortal = vtkm::cont::make_ArrayHandlePermutation( - superparentOldSuperId, this->BaseTree->WhichIteration); - vtkm::cont::Algorithm::Copy(permutedBaseTreeWhichIterationPortal, - permutedAugmentedTreeWhichIteration); - - // now we deal with the regular-sized arrays. In the following supernodeSetIndex is simply supernodeSorterPortal.Get(supernode); - // copy the global regular Id and data value - // Set: augmentedTree->regularNodeGlobalIDs[newRegularID] = globalRegularIDSet[supernodeSetIndex]; - auto permutedAugmentedTreeRegularNodeGlobalIds = vtkm::cont::make_ArrayHandlePermutation( - newRegularId, this->AugmentedTree->RegularNodeGlobalIds); + // e. Connect superarcs for the level & set hyperparents & superchildren count, whichRound, whichIteration, super2hypernode + // 24/05/2023: Expansion of comment to help debug. + // At this point, we know that all higher rounds are correctly constructed, and that any attachment points that survived simplification + // have already been inserted in a higher round. + + // The sort should have resulted in the supernodes being segmented along old superarcs. Most supernodes should be in a segment of length + // 1, and should be their own superparent in the sort array. But we can't readily test that, because other supernodes may also have them + // as the superparent. + + // This loop will principally determine the superarc for each supernode. For this, the rules break down to: + // 1. If the supernode is the global root, connect it nowhere + // 2. If the supernode is the last in the set of all supernodes in this round, treat it as the end of a segment + // 3. If the supernode is the last in a segment by superarc, connect it to the target of its superparent in the old tree, using the new supernode ID + // 4. Otherwise, connect to the new supernode ID of the next supernode in the segment + + // In each case, we will need to preserve the ascending / descending flag + + // We will also have to set the first supernode per iteration - if possible, in a separate loop + + // We need this to determine which supernodes are inserted and which are attached (see below) + vtkm::Id numInsertedSupernodes = + (vtkm::cont::ArrayGetValue(roundNumber + 1, this->FirstAttachmentPointInRound) - + vtkm::cont::ArrayGetValue(roundNumber, this->FirstAttachmentPointInRound)); + + { // START Call CreateSuperarcsWorklet (scope to delete temporary variables) + // create the worklet + vtkm::worklet::contourtree_distributed::hierarchical_augmenter::CreateSuperarcsWorklet< + FieldType> + createSuperarcsWorklet( + numSupernodesAlready, this->BaseTree->NumRounds, numInsertedSupernodes, roundNumber); + + // create fancy arrays needed to allow use of FieldIn for worklet parameters + + // permutedSupernodeIdSet may be NO_SUCH_ELEMENT if not already in the base tree + // We cannot use it for permutation index + // For any array using permutedSupernodeIdSet as indices, we put them into the data exec object. + auto permutedSupernodeIdSet = + vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->SupernodeIdSet); + auto permutedGlobalRegularIdSet = vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->GlobalRegularIdSet); - vtkm::cont::Algorithm::Copy(permutedGlobalRegularIdSet, - permutedAugmentedTreeRegularNodeGlobalIds); - // SetL augmentedTree->dataValues[newRegularID] = dataValueSet[supernodeSetIndex]; - auto permutedAugmentedTreeDataValues = - vtkm::cont::make_ArrayHandlePermutation(newRegularId, this->AugmentedTree->DataValues); auto permutedDataValueSet = vtkm::cont::make_ArrayHandlePermutation(this->SupernodeSorter, this->DataValueSet); - vtkm::cont::Algorithm::Copy(permutedDataValueSet, permutedAugmentedTreeDataValues); - - // the sort order will be dealt with later - // since all of these nodes are supernodes, they will be their own superparent, which means that: - // a. the regular2node can be set immediately - // Set: augmentedTree->regular2supernode[newRegularID] = newSupernodeID; - auto permutedAugmentedTreeRegular2Supernode = - vtkm::cont::make_ArrayHandlePermutation(newRegularId, this->AugmentedTree->Regular2Supernode); - vtkm::cont::Algorithm::Copy(newSupernodeId, permutedAugmentedTreeRegular2Supernode); - - // b. as can the superparent - // Set: augmentedTree->superparents[newRegularID] = newSupernodeID; - auto permutedAugmentedTreeSuperparents = - vtkm::cont::make_ArrayHandlePermutation(newRegularId, this->AugmentedTree->Superparents); - vtkm::cont::Algorithm::Copy(newSupernodeId, permutedAugmentedTreeSuperparents); - } // END scope for e. to delete temporary variables - - // We have one last bit of cleanup to do. If there were attachment points, then the round in which they transfer has been removed - // While it is possible to turn this into a null round, it is better to reduce the iteration count by one and resize the arrays - // To do this, we access the *LAST* element written and check to see whether it is in the final iteration (according to the base tree) + + // Create a view of the range of this->AugmentedTree->Superarcs that will be updated by the worklet + // so that we can use FieldOut as the type in the worklet + auto augmentedTreeSuperarcsView = vtkm::cont::make_ArrayHandleView( + this->AugmentedTree->Superarcs, + numSupernodesAlready, // start view here + this->SupernodeSorter.GetNumberOfValues() // select this many values + ); + + auto augmentedTreeHyperparentsView = vtkm::cont::make_ArrayHandleView( + this->AugmentedTree->Hyperparents, + numSupernodesAlready, // start view here + this->SupernodeSorter.GetNumberOfValues() // select this many values + ); + auto augmentedTreeSuper2HypernodeView = vtkm::cont::make_ArrayHandleView( + this->AugmentedTree->Super2Hypernode, + numSupernodesAlready, // start view here + this->SupernodeSorter.GetNumberOfValues() // select this many values + ); + auto augmentedTreeWhichRoundView = vtkm::cont::make_ArrayHandleView( + this->AugmentedTree->WhichRound, + numSupernodesAlready, // start view here + this->SupernodeSorter.GetNumberOfValues() // select this many values + ); + auto augmentedTreeWhichIterationView = vtkm::cont::make_ArrayHandleView( + this->AugmentedTree->WhichIteration, + numSupernodesAlready, // start view here + this->SupernodeSorter.GetNumberOfValues() // select this many values + ); + auto augmentedTreeRegularNodeGlobalIdsView = vtkm::cont::make_ArrayHandleView( + this->AugmentedTree->RegularNodeGlobalIds, + numSupernodesAlready, // start view here + this->SupernodeSorter.GetNumberOfValues() // select this many values + ); + auto augmentedTreeDataValuesView = vtkm::cont::make_ArrayHandleView( + this->AugmentedTree->DataValues, + numSupernodesAlready, // start view here + this->SupernodeSorter.GetNumberOfValues() // select this many values + ); + auto augmentedTreeRegular2SupernodeView = vtkm::cont::make_ArrayHandleView( + this->AugmentedTree->Regular2Supernode, + numSupernodesAlready, // start view here + this->SupernodeSorter.GetNumberOfValues() // select this many values + ); + auto augmentedTreeSuperparentsView = vtkm::cont::make_ArrayHandleView( + this->AugmentedTree->Superparents, + numSupernodesAlready, // start view here + this->SupernodeSorter.GetNumberOfValues() // select this many values + ); + + // Required execution objects to call other functions + auto findSuperArcForUnknownNode = this->AugmentedTree->GetFindSuperArcForUnknownNode(); + + // Execution object used to encapsulate data form the BaseTree to avoid the limit of 20 input parameters per worklet + auto createSuperarcsDataExecObj = + vtkm::worklet::contourtree_distributed::hierarchical_augmenter::CreateSuperarcsDataExec( + this->BaseTree->Hyperparents, + this->BaseTree->WhichRound, + this->BaseTree->WhichIteration, + this->BaseTree->Supernodes, + this->BaseTree->Superarcs, + this->BaseTree->Superparents, + this->BaseTree->Super2Hypernode, + this->BaseTree->Hypernodes, + this->SuperparentSet, + this->NewSupernodeIds); + + // invoke the worklet + this->Invoke(createSuperarcsWorklet, // the worklet + // inputs + this->SupernodeSorter, // input domain (WholeArrayIn) + permutedSupernodeIdSet, // input (FieldIn) + permutedGlobalRegularIdSet, // input (FieldIn) + permutedDataValueSet, // input (FieldIn) + findSuperArcForUnknownNode, // input (Execution object) + createSuperarcsDataExecObj, // input (Execution object with BaseTreeData + // Outputs + this->AugmentedTree->Supernodes, // input/output + augmentedTreeSuperarcsView, // output + augmentedTreeHyperparentsView, // output + augmentedTreeSuper2HypernodeView, // output + augmentedTreeWhichRoundView, // output + augmentedTreeWhichIterationView, // output + augmentedTreeRegularNodeGlobalIdsView, // output + augmentedTreeDataValuesView, // output + augmentedTreeRegular2SupernodeView, // output + augmentedTreeSuperparentsView // output + ); + } // END Call CreateSuperarcsWorklet + +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint(std::string("Round ") + std::to_string(roundNumber) + + std::string(" Details Filled in For Supernodes "), + __FILE__, + __LINE__)); +#endif + + // Now, in order to set the first supernode per iteration, we do an additional loop + // We are guaranteed that all supernodes at this level are implicitly sorted by iteration, so we test for ends of segments + // NOTE that we do this after the previous loop, since we depend on a value that it has set + { + vtkm::cont::ArrayHandleCounting tempIndex(1, 1, currNumIterations - 1); + vtkm::worklet::contourtree_distributed::hierarchical_augmenter:: + CreateSuperarcsSetFirstSupernodePerIterationWorklet + createSuperarcsSetFirstSupernodePerIterationWorklet(numSupernodesAlready); + vtkm::cont::ArrayHandleIndex tempSupernodeIndex(this->SupernodeSorter.GetNumberOfValues()); + this->Invoke(createSuperarcsSetFirstSupernodePerIterationWorklet, + tempSupernodeIndex, + this->AugmentedTree->WhichIteration, // input + this->AugmentedTree->FirstSupernodePerIteration[roundNumber] // input/output + ); + } + + // since there's an extra entry in the firstSupernode array as a sentinel, set it + vtkm::worklet::contourtree_augmented::IdArraySetValue( + currNumIterations, // index + this->AugmentedTree->Supernodes.GetNumberOfValues(), // new value + this->AugmentedTree->FirstSupernodePerIteration[roundNumber] // array + ); + // The following loop should be safe in parallel since there should never be two zeros in sequence, i.e., the next + // entry after a zero will always be valid, regardless of execution order + // This was added because in rare cases there are no supernodes transferred in an iteration, for example because there + // are no available upper leaves to prune. If this is case, we are guaranteed that there will be available lower leaves + // so the next iteration will have a non-zero number. We had a major bug from this, and it's cropped back up in the + // Hierarchical Augmentation, so I'm expanding the comment just in case. + { + vtkm::cont::ArrayHandleCounting tempIndex(1, 1, currNumIterations - 1); + vtkm::worklet::contourtree_distributed::hierarchical_augmenter:: + CreateSuperarcsUpdateFirstSupernodePerIterationWorklet + createSuperarcsUpdateFirstSupernodePerIterationWorklet; + this->Invoke(createSuperarcsUpdateFirstSupernodePerIterationWorklet, + tempIndex, // input index + this->AugmentedTree->FirstSupernodePerIteration[roundNumber] // input/output + ); + } + + // We have one last bit of cleanup to do. If there were attachment points, + // then the round in which they transfer has been removed + // While it is possible to turn this into a null round, it is better to + // reduce the iteration count by one and resize the arrays + // To do this, we access the *LAST* element written and check to see whether + // it is in the final iteration (according to the base tree) // But there might be *NO* supernodes in the round, so we check first - vtkm::Id iterationArraySize = - vtkm::cont::ArrayGetValue(roundNumber, this->AugmentedTree->NumIterations); - if (iterationArraySize > 0) + if (currNumIterations > 0) { // at least one iteration vtkm::Id lastSupernodeThisLevel = this->AugmentedTree->Supernodes.GetNumberOfValues() - 1; vtkm::Id lastIterationThisLevel = vtkm::worklet::contourtree_augmented::MaskedIndex( vtkm::cont::ArrayGetValue(lastSupernodeThisLevel, this->AugmentedTree->WhichIteration)); - // if there were no attachment points, it will be in the last iteration: if there were attachment points, it will be in the previous one - if (lastIterationThisLevel < iterationArraySize - 1) + // if there were no attachment points, it will be in the last iteration: if there were + // attachment points, it will be in the previous one + if (lastIterationThisLevel < currNumIterations - 1) { // attachment point round was removed // decrement the iteration count (still with an extra element as sentinel) + vtkm::Id iterationArraySize = currNumIterations; + // decrease iterations by 1. I.e,: augmentedTree->nIterations[roundNo]--; vtkm::worklet::contourtree_augmented::IdArraySetValue( - roundNumber, iterationArraySize - 1, this->AugmentedTree->NumIterations); + roundNumber, // index + currNumIterations - 1, // new value + this->AugmentedTree->NumIterations // array + ); // shrink the supernode array this->AugmentedTree->FirstSupernodePerIteration[roundNumber].Allocate( iterationArraySize, vtkm::CopyFlag::On); // shrink array but keep values vtkm::worklet::contourtree_augmented::IdArraySetValue( - iterationArraySize - 1, - this->AugmentedTree->Supernodes.GetNumberOfValues(), - this->AugmentedTree->FirstSupernodePerIteration[roundNumber]); + iterationArraySize - 1, // index + this->AugmentedTree->Supernodes.GetNumberOfValues(), // new value + this->AugmentedTree->FirstSupernodePerIteration[roundNumber] // array + ); + // for the hypernode array, the last iteration is guaranteed not to have hyperarcs by construction // so the last iteration will already have the correct sentinel value, and we just need to shrink the array this->AugmentedTree->FirstHypernodePerIteration[roundNumber].Allocate( @@ -1343,6 +1713,14 @@ void HierarchicalAugmenter::CreateSuperarcs(vtkm::Id roundNumber) } // attachment point round was removed } // at least one iteration +#ifdef DEBUG_PRINT_HIERARCHICAL_AUGMENTER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint(std::string("Round ") + std::to_string(roundNumber) + + std::string(" Superarcs Created "), + __FILE__, + __LINE__)); +#endif + // in the interests of debug, we resize the sorting array to zero here, // even though we will re-resize them in the next function this->SupernodeSorter.ReleaseResources(); @@ -1360,7 +1738,7 @@ std::string HierarchicalAugmenter::DebugPrint(std::string message, long lineNum) { // DebugPrint() std::stringstream resultStream; - resultStream << std::endl; + resultStream << "%" << std::endl; resultStream << "----------------------------------------" << std::endl; resultStream << std::setw(30) << std::left << fileName << ":" << std::right << std::setw(4) << lineNum << std::endl; @@ -1368,13 +1746,16 @@ std::string HierarchicalAugmenter::DebugPrint(std::string message, << std::endl; resultStream << "----------------------------------------" << std::endl; +#ifdef DEBUG_PRINT_HIERARCHICAL_CONTOUR_TREE resultStream << this->BaseTree->DebugPrint( (message + std::string(" Base Tree")).c_str(), fileName, lineNum); resultStream << this->AugmentedTree->DebugPrint( (message + std::string(" Augmented Tree")).c_str(), fileName, lineNum); +#endif resultStream << "========================================" << std::endl; resultStream << "Local List of Attachment Points" << std::endl; - vtkm::worklet::contourtree_augmented::PrintHeader(this->GlobalRegularIds.GetNumberOfValues()); + vtkm::worklet::contourtree_augmented::PrintHeader(this->GlobalRegularIds.GetNumberOfValues(), + resultStream); vtkm::worklet::contourtree_augmented::PrintIndices( "Global Regular Ids", this->GlobalRegularIds, -1, resultStream); vtkm::worklet::contourtree_augmented::PrintValues( @@ -1390,7 +1771,7 @@ std::string HierarchicalAugmenter::DebugPrint(std::string message, resultStream << std::endl; resultStream << "Outgoing Attachment Points" << std::endl; vtkm::worklet::contourtree_augmented::PrintHeader( - this->OutData.GlobalRegularIds.GetNumberOfValues()); + this->OutData.GlobalRegularIds.GetNumberOfValues(), resultStream); vtkm::worklet::contourtree_augmented::PrintIndices( "Out Global Regular Ids", this->OutData.GlobalRegularIds, -1, resultStream); vtkm::worklet::contourtree_augmented::PrintValues( @@ -1404,37 +1785,45 @@ std::string HierarchicalAugmenter::DebugPrint(std::string message, vtkm::worklet::contourtree_augmented::PrintIndices( "Out WhichRounds", this->OutData.WhichRounds, -1, resultStream); resultStream << std::endl; - resultStream << "Incoming Attachment Points" << std::endl; - vtkm::worklet::contourtree_augmented::PrintHeader( - this->InData.GlobalRegularIds.GetNumberOfValues()); - vtkm::worklet::contourtree_augmented::PrintIndices( - "In Global Regular Ids", this->InData.GlobalRegularIds, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintValues( - "In Data Values", this->InData.DataValues, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "In Supernode Ids", this->InData.SupernodeIds, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "In Superparents", this->InData.Superparents, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "In Superparent Rounds", this->InData.SuperparentRounds, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "In WhichRounds", this->InData.WhichRounds, -1, resultStream); - resultStream << std::endl; + // we only output the incoming attachment points in the data exchange debug print + if (message.find(std::string("In Attachment Points Received")) != std::string::npos) + { + resultStream << "Incoming Attachment Points" << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader( + this->InData.GlobalRegularIds.GetNumberOfValues(), resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "In Global Regular Ids", this->InData.GlobalRegularIds, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "In Data Values", this->InData.DataValues, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "In Supernode Ids", this->InData.SupernodeIds, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "In Superparents", this->InData.Superparents, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "In Superparent Rounds", this->InData.SuperparentRounds, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "In WhichRounds", this->InData.WhichRounds, -1, resultStream); + resultStream << std::endl; + } resultStream << "Holding Arrays" << std::endl; vtkm::worklet::contourtree_augmented::PrintHeader( - this->FirstAttachmentPointInRound.GetNumberOfValues()); + this->FirstAttachmentPointInRound.GetNumberOfValues(), resultStream); vtkm::worklet::contourtree_augmented::PrintIndices( "First Attach / Rd", this->FirstAttachmentPointInRound, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintHeader(this->AttachmentIds.GetNumberOfValues()); + vtkm::worklet::contourtree_augmented::PrintHeader(this->AttachmentIds.GetNumberOfValues(), + resultStream); vtkm::worklet::contourtree_augmented::PrintIndices( "AttachmentIds", this->AttachmentIds, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintHeader(this->NewSupernodeIds.GetNumberOfValues()); + vtkm::worklet::contourtree_augmented::PrintHeader(this->NewSupernodeIds.GetNumberOfValues(), + resultStream); vtkm::worklet::contourtree_augmented::PrintIndices( "New Supernode Ids", this->NewSupernodeIds, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintHeader(this->KeptSupernodes.GetNumberOfValues()); + vtkm::worklet::contourtree_augmented::PrintHeader(this->KeptSupernodes.GetNumberOfValues(), + resultStream); vtkm::worklet::contourtree_augmented::PrintIndices( "Kept Supernodes", this->KeptSupernodes, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintHeader(this->SupernodeSorter.GetNumberOfValues()); + vtkm::worklet::contourtree_augmented::PrintHeader(this->SupernodeSorter.GetNumberOfValues(), + resultStream); vtkm::worklet::contourtree_augmented::PrintIndices( "Supernode Sorter", this->SupernodeSorter, -1, resultStream); vtkm::worklet::contourtree_augmented::PrintIndices( @@ -1448,7 +1837,8 @@ std::string HierarchicalAugmenter::DebugPrint(std::string message, resultStream << std::endl; resultStream << std::endl; - vtkm::worklet::contourtree_augmented::PrintHeader(this->SupernodeSorter.GetNumberOfValues()); + vtkm::worklet::contourtree_augmented::PrintHeader(this->SupernodeSorter.GetNumberOfValues(), + resultStream); vtkm::worklet::contourtree_augmented::PrintIndices( "Supernode Id", this->SupernodeSorter, -1, resultStream); vtkm::worklet::contourtree_augmented::PrintArrayHandle( @@ -1474,7 +1864,8 @@ std::string HierarchicalAugmenter::DebugPrint(std::string message, resultStream << std::endl; resultStream << std::endl; - vtkm::worklet::contourtree_augmented::PrintHeader(this->RegularSuperparents.GetNumberOfValues()); + vtkm::worklet::contourtree_augmented::PrintHeader(this->RegularSuperparents.GetNumberOfValues(), + resultStream); vtkm::worklet::contourtree_augmented::PrintIndices( "RegularNodesNeeded", this->RegularNodesNeeded, -1, resultStream); vtkm::worklet::contourtree_augmented::PrintIndices( diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h index bd2dcdcba4..af2662b1a3 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h @@ -64,7 +64,6 @@ VTKM_THIRDPARTY_PRE_INCLUDE VTKM_THIRDPARTY_POST_INCLUDE // clang-format on - namespace vtkm { namespace worklet @@ -125,7 +124,6 @@ public: if (target.gid != selfid) { // Send to partner blockData->HierarchicalAugmenter.PrepareOutAttachmentPoints(round); - // TODO/FIXME: Correct function? Correct round? rp.enqueue(target, blockData->HierarchicalAugmenter.OutData); // Note: HierarchicalAugmenter.ReleaseSwapArrays() does not necessarily delete the // arrays. Rather, it releases the reference to them. If, for example, the data diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h index acd5eca2b1..a996d42928 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h @@ -1054,6 +1054,13 @@ void HierarchicalContourTree::AddToVTKMDataSet(vtkm::cont::DataSet& d ds.AddField(firstSupernodePerIterationOffsetsField); // TODO/FIXME: It seems we may only need the counts for the first iteration, so check, which // information we actually need. + // Add the number of rounds as an array of length 1 + vtkm::cont::ArrayHandle tempNumRounds; + tempNumRounds.Allocate(1); + vtkm::worklet::contourtree_augmented::IdArraySetValue(0, this->NumRounds, tempNumRounds); + vtkm::cont::Field numRoundsField( + "NumRounds", vtkm::cont::Field::Association::WholeDataSet, tempNumRounds); + ds.AddField(numRoundsField); } } // namespace contourtree_distributed diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h index da7126a9b0..be34f7bdf1 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h @@ -91,8 +91,14 @@ #include #include #include +#include #include +#include +#ifdef DEBUG_PRINT +#define DEBUG_PRINT_HYPER_SWEEPER +#define DEBUG_PRINT_HIERARCHICAL_CONTOUR_TREE +#endif // DEBUG_PRINT namespace vtkm { @@ -236,16 +242,15 @@ void HierarchicalHyperSweeper::InitializeI { // InitializeIntrinsicVertexCount() // I. Call the mesh to get a list of all regular vertices belonging to the block by global Id vtkm::worklet::contourtree_augmented::IdArrayType globalIds; - // TODO/FIXME: Even though the virtual function on DataSetMesh was removed in commit + // NOTE: Even though the virtual function on DataSetMesh was removed in commit // 93730495813f7b85e59d4a5dae2076977787fd78, this should call the correct function - // since MeshType is templated and should have the appropriate type. Verify that - // this is indeed correct. + // since MeshType is templated and should have the appropriate type. baseBlock.GetOwnedVerticesByGlobalId(localToGlobalIdRelabeler, globalIds); // and store the size for later reference //hierarchicalTree.NumOwnedRegularVertices = globalIds.GetNumberOfValues(); this->NumOwnedRegularVertices = globalIds.GetNumberOfValues(); -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HYPER_SWEEPER { std::stringstream debugStream; debugStream << std::endl << "Owned Regular Vertex List" << std::endl; @@ -270,9 +275,10 @@ void HierarchicalHyperSweeper::InitializeI ); } -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HYPER_SWEEPER { std::stringstream debugStream; + debugStream << std::endl; vtkm::worklet::contourtree_augmented::PrintIndices( "Superparents", superparents, -1, debugStream); VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); @@ -282,9 +288,10 @@ void HierarchicalHyperSweeper::InitializeI // III. Sort the superparent Ids & count the copies of each vtkm::cont::Algorithm ::Sort(superparents); -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HYPER_SWEEPER { std::stringstream debugStream; + debugStream << std::endl; vtkm::worklet::contourtree_augmented::PrintIndices("Sorted SP", superparents, -1, debugStream); VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); } @@ -307,9 +314,10 @@ void HierarchicalHyperSweeper::InitializeI superarcRegularCounts // output ); // and that is that -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HYPER_SWEEPER { std::stringstream debugStream; + debugStream << std::endl; vtkm::worklet::contourtree_augmented::PrintIndices( "SuperarcRegularCounts", superarcRegularCounts, -1, debugStream); VTKM_LOG_S(vtkm::cont::LogLevel::Info, debugStream.str()); @@ -322,7 +330,7 @@ void HierarchicalHyperSweeper::InitializeI template void HierarchicalHyperSweeper::LocalHyperSweep() { // LocalHyperSweep() -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HYPER_SWEEPER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) + std::string(" Starting Local HyperSweep"), @@ -333,7 +341,7 @@ void HierarchicalHyperSweeper::LocalHyperS // I. Iterate over all rounds of the hyperstructure for (vtkm::Id round = 0; round <= this->HierarchicalTree.NumRounds; round++) { // per round -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HYPER_SWEEPER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) + std::string(" Round ") + std::to_string(round) + @@ -347,7 +355,7 @@ void HierarchicalHyperSweeper::LocalHyperS .ReadPortal(); // TODO/FIXME: Use portal? Or something more efficient? for (vtkm::Id iteration = 0; iteration < numIterationsPortal.Get(round); iteration++) { // per iteration -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HYPER_SWEEPER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) + std::string(" Round ") + std::to_string(round) + @@ -366,7 +374,7 @@ void HierarchicalHyperSweeper::LocalHyperS // call the routine that computes the dependent weights for each superarc in that range this->ComputeSuperarcDependentWeights(round, iteration, firstSupernode, lastSupernode); -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HYPER_SWEEPER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) + std::string(" Round ") + std::to_string(round) + @@ -378,7 +386,7 @@ void HierarchicalHyperSweeper::LocalHyperS // now call the routine that computes the weights to be transferred and the superarcs to which they transfer this->ComputeSuperarcTransferWeights(round, iteration, firstSupernode, lastSupernode); -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HYPER_SWEEPER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) + std::string(" Round ") + std::to_string(round) + @@ -391,7 +399,7 @@ void HierarchicalHyperSweeper::LocalHyperS // transfer the weights this->TransferWeights(round, iteration, firstSupernode, lastSupernode); -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HYPER_SWEEPER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) + std::string(" Round ") + std::to_string(round) + @@ -402,7 +410,7 @@ void HierarchicalHyperSweeper::LocalHyperS #endif } // per iteration -#ifdef DEBUG_PRINT +#ifdef DEBUG_PRINT_HYPER_SWEEPER VTKM_LOG_S(vtkm::cont::LogLevel::Info, DebugPrint(std::string("Hypersweep Block ") + std::to_string(BlockId) + std::string(" Round ") + std::to_string(round) + @@ -530,6 +538,9 @@ void HierarchicalHyperSweeper::ComputeSupe } // scope ComputeSuperarcTransferWeightsWorklet // 5. Now we need to sort the transfer targets into contiguous segments + // NOTE 11/07/2023 + // We have now got a flag of ATTACHMENT_POINT_TRANSFER whose effect is to separate out transfers to + // the superarc from transfers to the supernode { // create view of superSortPermute[firstSupernode, lastSupernode) for sorting vtkm::cont::ArrayHandleView @@ -573,11 +584,26 @@ void HierarchicalHyperSweeper::ComputeSupe // routine to transfer the weights template void HierarchicalHyperSweeper::TransferWeights( - vtkm::Id, // round, // Kept parameters in case we need it for debugging. - vtkm::Id, // iteration, // Kept parameters in case we need it for debugging. +#ifdef DEBUG_PRINT_HYPER_SWEEPER + vtkm::Id round, // Kept parameters in case we need it for debugging. + vtkm::Id iteration, // Kept parameters in case we need it for debugging. +#else + vtkm::Id, + vtkm::Id, +#endif vtkm::Id firstSupernode, vtkm::Id lastSupernode) { // TransferWeights() + // WARNING 11/07/2023 + // This code was originally written on the assumption that the hierarchical tree had been augmented by the attachment points. + // As a result, it assumed that no attachment points remained. + // It is now being used for partially augmented versions due to pre-simplification, for which the correct treatment is to + // transfer not as dependent weight, but as intrinsic weight. Note that this ONLY applies to attachment points: if the + // subtree attaches at a proper supernode in the ancestor level, it should still be treated as dependent weight. The logic + // behind this is that an attachment point is regular with respect to the superarc along which it inserts. Adding it as + // dependent weight means that it is treated as *OUTSIDE* the superarc in the reverse sweep (or equivalent computation) + // Treating it as dependent weight means that both ends of the superarc end up with the correct value. + // 7. Now perform a segmented prefix sum vtkm::Id numSupernodesToProcess = lastSupernode - firstSupernode; // Same as std::partial_sum(valuePrefixSum.begin() + firstSupernode, valuePrefixSum.begin() + lastSupernode, valuePrefixSum.begin() + firstSupernode); @@ -587,10 +613,8 @@ void HierarchicalHyperSweeper::TransferWei valuePrefixSumView(this->ValuePrefixSum, // subset ValuePrefixSum firstSupernode, // start at firstSupernode numSupernodesToProcess); // until lastSuperNode (not inclued) - // TODO: If it is safe to use the same array as input and output for ScanInclusive then this code should be updated to avoid the extra copy - // In this case our traget array is the same as our source array. For safety we - // store the values of our prefix sum in a temporary arrya and then copy the values - // back into our valuePrefixSumView at the end + // For safety we store the values of our prefix sum in a temporary array + // and then copy the values back into our valuePrefixSumView at the end vtkm::worklet::contourtree_augmented::IdArrayType tempScanInclusiveTarget; tempScanInclusiveTarget.Allocate(numSupernodesToProcess); // Compute the partial sum for DependentValues[firstSuperNode, lastSupernode) and write to ValuePrefixSum[firstSuperNode, lastSupernode) @@ -600,8 +624,24 @@ void HierarchicalHyperSweeper::TransferWei vtkm::cont::Algorithm::Copy(tempScanInclusiveTarget, valuePrefixSumView); } +#ifdef DEBUG_PRINT_HYPER_SWEEPER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint(std::string("Hypersweep Block ") + std::to_string(this->BlockId) + + std::string(" Round ") + std::to_string(round) + + std::string(" Step 1 Iteration ") + std::to_string(iteration) + + std::string(" Starting Weight Transfer"), + __FILE__, + __LINE__)); +#endif // DEBUG_PRINT_HYPER_SWEEPER + // 7a. and 7b. { + // WARNING 11/07/2023 + // Before dealing with attachment points, we just transferred by segments. We now have + // the possibility of transferring some weight at an attachment point, + // and some not. To avoid write conflicts, we treat this as two passes: one for attachment + // points, one for all others. This means duplicating 7a/7b, sadly. + // 7a. Find the RHE of each group and transfer the prefix sum weight // Note that we do not compute the transfer weight separately, we add it in place instead // Instantiate the worklet @@ -622,6 +662,16 @@ void HierarchicalHyperSweeper::TransferWei this->DependentValues); } +#ifdef DEBUG_PRINT_HYPER_SWEEPER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint(std::string("Hypersweep Block ") + std::to_string(this->BlockId) + + std::string(" Round ") + std::to_string(round) + + std::string(" Step 1 Iteration ") + std::to_string(iteration) + + std::string(" Non-Attachment LHE Completed"), + __FILE__, + __LINE__)); +#endif // DEBUG_PRINT_HYPER_SWEEPER + { VTKM_ASSERT(firstSupernode + 1 + numSupernodesToProcess - 1 <= this->SortedTransferTarget.GetNumberOfValues()); @@ -634,7 +684,7 @@ void HierarchicalHyperSweeper::TransferWei auto valuePrefixSumPreviousValueView = vtkm::cont::make_ArrayHandleView( this->ValuePrefixSum, firstSupernode, numSupernodesToProcess - 1); - // 7b. Now find the LHE of each group and subtract out the prior weight + // 7b (non-attachment). Now find the LHE of each group and subtract out the prior weight. vtkm::worklet::contourtree_distributed::hierarchical_hyper_sweeper:: TransferWeightsUpdateLHEWorklet transferWeightsUpdateLHEWorklet; this->Invoke(transferWeightsUpdateLHEWorklet, @@ -643,6 +693,74 @@ void HierarchicalHyperSweeper::TransferWei valuePrefixSumPreviousValueView, this->DependentValues); } + +#ifdef DEBUG_PRINT_HYPER_SWEEPER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint(std::string("Hypersweep Block ") + std::to_string(this->BlockId) + + std::string(" Round ") + std::to_string(round) + + std::string(" Step 1 Iteration ") + std::to_string(iteration) + + std::string(" Non-Attachment RHE Completed"), + __FILE__, + __LINE__)); +#endif + + // 7a (attachment). Find the RHE of each group and transfer the prefix sum weight + // Note that we do not compute the transfer weight separately, we add it in place instead + { + auto supernodeIndex = + vtkm::cont::make_ArrayHandleCounting(firstSupernode, vtkm::Id{ 1 }, numSupernodesToProcess); + auto valuePrefixSumView = vtkm::cont::make_ArrayHandleView( + this->ValuePrefixSum, firstSupernode, numSupernodesToProcess); + + vtkm::worklet::contourtree_distributed::hierarchical_hyper_sweeper:: + TransferWeightsUpdateRHEWorkletRound2 transferWeightsUpdateRHEWorkletRound2(lastSupernode); + // Invoke the worklet + this->Invoke(transferWeightsUpdateRHEWorkletRound2, // worklet + supernodeIndex, // input counting array [firstSupernode, lastSupernode) + this->SortedTransferTarget, + valuePrefixSumView, // input view of valuePrefixSum[firstSupernode, lastSupernode) + this->IntrinsicValues, + this->DependentValues); + } + +#ifdef DEBUG_PRINT_HYPER_SWEEPER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint(std::string("Hypersweep Block ") + std::to_string(this->BlockId) + + std::string(" Round ") + std::to_string(round) + + std::string(" Step 1 Iteration ") + std::to_string(iteration) + + std::string(" Attachment LHE Completed"), + __FILE__, + __LINE__)); +#endif + + // 7b (i). Now find the LHE of each group and subtract out the prior weight. + { + auto sortedTransferTargetView = vtkm::cont::make_ArrayHandleView( + this->SortedTransferTarget, firstSupernode + 1, numSupernodesToProcess - 1); + auto sortedTransferTargetShiftedView = vtkm::cont::make_ArrayHandleView( + this->SortedTransferTarget, firstSupernode, numSupernodesToProcess - 1); + auto valuePrefixSumPreviousValueView = vtkm::cont::make_ArrayHandleView( + this->ValuePrefixSum, firstSupernode, numSupernodesToProcess - 1); + + vtkm::worklet::contourtree_distributed::hierarchical_hyper_sweeper:: + TransferWeightsUpdateLHEWorkletRound2 transferWeightsUpdateLHEWorkletRound2; + this->Invoke(transferWeightsUpdateLHEWorkletRound2, + sortedTransferTargetView, + sortedTransferTargetShiftedView, + valuePrefixSumPreviousValueView, + this->IntrinsicValues, + this->DependentValues); + } + +#ifdef DEBUG_PRINT_HYPER_SWEEPER + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + DebugPrint(std::string("Hypersweep Block ") + std::to_string(this->BlockId) + + std::string(" Round ") + std::to_string(round) + + std::string(" Step 1 Iteration ") + std::to_string(iteration) + + std::string(" Attachment RHE Completed"), + __FILE__, + __LINE__)); +#endif } // TransferWeights() @@ -659,6 +777,12 @@ std::string HierarchicalHyperSweeper::Debu resultStream << std::setw(30) << std::left << fileName << ":" << std::right << std::setw(4) << lineNum << std::endl; resultStream << std::left << message << std::endl; + +#ifdef DEBUG_PRINT_HIERARCHICAL_CONTOUR_TREE + resultStream << this->HierarchicalTree.DebugPrint( + (message + std::string(" Hierarchical Tree")).c_str(), fileName, lineNum); +#endif + resultStream << "Hypersweep Value Array Contains: " << std::endl; resultStream << "----------------------------------------" << std::endl; resultStream << std::endl; diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt index 56a3b61c1d..79a50d0a81 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt @@ -23,6 +23,9 @@ set(headers AttachmentAndSupernodeComparator.h ResizeArraysBuildNewSupernodeIdsWorklet.h CreateSuperarcsWorklet.h + CreateSuperarcsData.h + CreateSuperarcsSetFirstSupernodePerIterationWorklet.h + CreateSuperarcsUpdateFirstSupernodePerIterationWorklet.h HierarchicalAugmenterInOutData.h ) diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsData.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsData.h new file mode 100644 index 0000000000..40d64a2210 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsData.h @@ -0,0 +1,190 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +// This header contains an Execution Object used to pass a arrays to the +// CreateSuperarcsWorklet to overcome the limitation of 20 input parameters for a worklet + +#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_create_superarcs_data_h +#define vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_create_superarcs_data_h + +#include + +#include + +namespace vtkm +{ +namespace worklet +{ +namespace contourtree_distributed +{ +namespace hierarchical_augmenter +{ + + +class CreateSuperarcsData +{ +public: + // Sort indicies types + using IndicesPortalType = vtkm::worklet::contourtree_augmented::IdArrayType::ReadPortalType; + + VTKM_EXEC_CONT + CreateSuperarcsData() {} + + VTKM_CONT + CreateSuperarcsData( + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeHyperparents, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeWhichRound, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeWhichIteration, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeSupernodes, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeSuperarcs, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeSuperparents, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeSuper2Hypernode, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeHypernodes, + const vtkm::worklet::contourtree_augmented::IdArrayType& superparentSet, + const vtkm::worklet::contourtree_augmented::IdArrayType& newSupernodeIds, + vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) + { + this->BaseTreeHyperparents = baseTreeHyperparents.PrepareForInput(device, token); + this->BaseTreeWhichRound = baseTreeWhichRound.PrepareForInput(device, token); + this->BaseTreeWhichIteration = baseTreeWhichIteration.PrepareForInput(device, token); + this->BaseTreeSupernodes = baseTreeSupernodes.PrepareForInput(device, token); + this->BaseTreeSuperarcs = baseTreeSuperarcs.PrepareForInput(device, token); + this->BaseTreeSuperparents = baseTreeSuperparents.PrepareForInput(device, token); + this->BaseTreeSuper2Hypernode = baseTreeSuper2Hypernode.PrepareForInput(device, token); + this->BaseTreeHypernodes = baseTreeHypernodes.PrepareForInput(device, token); + this->SuperparentSet = superparentSet.PrepareForInput(device, token); + this->NewSupernodeIds = newSupernodeIds.PrepareForInput(device, token); + } + +public: + IndicesPortalType BaseTreeHyperparents; + IndicesPortalType BaseTreeWhichRound; + IndicesPortalType BaseTreeWhichIteration; + IndicesPortalType BaseTreeSupernodes; + IndicesPortalType BaseTreeSuperarcs; + IndicesPortalType BaseTreeSuperparents; + IndicesPortalType BaseTreeSuper2Hypernode; + IndicesPortalType BaseTreeHypernodes; + IndicesPortalType SuperparentSet; + IndicesPortalType NewSupernodeIds; +}; + + +class CreateSuperarcsDataExec : public vtkm::cont::ExecutionObjectBase +{ +public: + VTKM_EXEC_CONT + CreateSuperarcsDataExec( + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeHyperparents, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeWhichRound, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeWhichIteration, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeSupernodes, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeSuperarcs, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeSuperparents, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeSuper2Hypernode, + const vtkm::worklet::contourtree_augmented::IdArrayType& baseTreeHypernodes, + const vtkm::worklet::contourtree_augmented::IdArrayType& superparentSet, + const vtkm::worklet::contourtree_augmented::IdArrayType& newSupernodeIds) + : BaseTreeHyperparents(baseTreeHyperparents) + , BaseTreeWhichRound(baseTreeWhichRound) + , BaseTreeWhichIteration(baseTreeWhichIteration) + , BaseTreeSupernodes(baseTreeSupernodes) + , BaseTreeSuperarcs(baseTreeSuperarcs) + , BaseTreeSuperparents(baseTreeSuperparents) + , BaseTreeSuper2Hypernode(baseTreeSuper2Hypernode) + , BaseTreeHypernodes(baseTreeHypernodes) + , SuperparentSet(superparentSet) + , NewSupernodeIds(newSupernodeIds) + { + } + + VTKM_CONT + CreateSuperarcsData PrepareForExecution(vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) const + { + return CreateSuperarcsData(BaseTreeHyperparents, + BaseTreeWhichRound, + BaseTreeWhichIteration, + BaseTreeSupernodes, + BaseTreeSuperarcs, + BaseTreeSuperparents, + BaseTreeSuper2Hypernode, + BaseTreeHypernodes, + SuperparentSet, + NewSupernodeIds, + device, + token); + } + +private: + // Whole array data used from the BaseTree in CreateSuperarcsWorklet + const vtkm::worklet::contourtree_augmented::IdArrayType& BaseTreeHyperparents; + const vtkm::worklet::contourtree_augmented::IdArrayType& BaseTreeWhichRound; + const vtkm::worklet::contourtree_augmented::IdArrayType& BaseTreeWhichIteration; + const vtkm::worklet::contourtree_augmented::IdArrayType& BaseTreeSupernodes; + const vtkm::worklet::contourtree_augmented::IdArrayType& BaseTreeSuperarcs; + const vtkm::worklet::contourtree_augmented::IdArrayType& BaseTreeSuperparents; + const vtkm::worklet::contourtree_augmented::IdArrayType& BaseTreeSuper2Hypernode; + const vtkm::worklet::contourtree_augmented::IdArrayType& BaseTreeHypernodes; + const vtkm::worklet::contourtree_augmented::IdArrayType& SuperparentSet; + const vtkm::worklet::contourtree_augmented::IdArrayType& NewSupernodeIds; +}; + + + +} // namespace hierarchical_augmenter +} // namespace contourtree_distributed +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsSetFirstSupernodePerIterationWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsSetFirstSupernodePerIterationWorklet.h new file mode 100644 index 0000000000..1345551008 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsSetFirstSupernodePerIterationWorklet.h @@ -0,0 +1,148 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_create_auperarcs_set_first_supernode_per_iteration_worklet_h +#define vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_create_auperarcs_set_first_supernode_per_iteration_worklet_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace contourtree_distributed +{ +namespace hierarchical_augmenter +{ + +/// Worklet used in HierarchicalAugmenter::UpdateHyperstructure to set the hyperarcs and hypernodes +class CreateSuperarcsSetFirstSupernodePerIterationWorklet : public vtkm::worklet::WorkletMapField +{ +public: + /// Control signature for the worklet + using ControlSignature = void(FieldIn supernodeIndex, + WholeArrayIn augmentedTreeWhichIteration, + WholeArrayInOut augmentedTreeFirstSupernodePerIteration); + using ExecutionSignature = void(_1, _2, _3); + using InputDomain = _1; + + // Default Constructor + VTKM_EXEC_CONT + CreateSuperarcsSetFirstSupernodePerIterationWorklet(vtkm::Id numSupernodesAlready) + : NumSupernodesAlready(numSupernodesAlready) + { + } + + template + VTKM_EXEC void operator()( + const vtkm::Id& supernode, // index in supernodeSorter + // const vtkm::Id& supernodeSetindex, // supernodeSorter[supernode] + const InFieldPortalType& augmentedTreeWhichIterationPortal, + const InOutFieldPortalType& augmentedTreeFirstSupernodePerIterationPortal) const + { // operator()() + // per supernode in the set + // retrieve the index from the sorting index array (Done on input)(NOT USED) + // indexType supernodeSetIndex = supernodeSorter[supernode]; + + // work out the new supernode ID + vtkm::Id newSupernodeId = this->NumSupernodesAlready + supernode; + + // The 0th element sets the first element in the zeroth iteration + if (supernode == 0) + { + augmentedTreeFirstSupernodePerIterationPortal.Set(0, newSupernodeId); + } + // otherwise, mismatch to the left identifies a new iteration + else + { + if (vtkm::worklet::contourtree_augmented::MaskedIndex( + augmentedTreeWhichIterationPortal.Get(newSupernodeId)) != + vtkm::worklet::contourtree_augmented::MaskedIndex( + augmentedTreeWhichIterationPortal.Get(newSupernodeId - 1))) + { // start of segment + augmentedTreeFirstSupernodePerIterationPortal.Set( + vtkm::worklet::contourtree_augmented::MaskedIndex( + augmentedTreeWhichIterationPortal.Get(newSupernodeId)), + newSupernodeId); + } // start of segmen + } + + /* + #pragma omp parallel for + for (indexType supernode = 0; supernode < supernodeSorter.size(); supernode++) + { // per supernode in the set + // retrieve the index from the sorting index array + indexType supernodeSetIndex = supernodeSorter[supernode]; + + // work out the new supernode ID + indexType newSupernodeID = nSupernodesAlready + supernode; + + // The 0th element sets the first element in the zeroth iteration + if (supernode == 0) + augmentedTree->firstSupernodePerIteration[roundNo][0] = newSupernodeID; + // otherwise, mismatch to the left identifies a new iteration + else + { + if (augmentedTree->whichIteration[newSupernodeID] != augmentedTree->whichIteration[newSupernodeID-1]) + augmentedTree->firstSupernodePerIteration[roundNo][maskedIndex(augmentedTree->whichIteration[newSupernodeID])] = newSupernodeID; + } + } // per supernode in the set + */ + + } // operator()() + +private: + vtkm::Id NumSupernodesAlready; + + +}; // CreateSuperarcsSetFirstSupernodePerIterationWorklet + +} // namespace hierarchical_augmenter +} // namespace contourtree_distributed +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsUpdateFirstSupernodePerIterationWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsUpdateFirstSupernodePerIterationWorklet.h new file mode 100644 index 0000000000..386c743993 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsUpdateFirstSupernodePerIterationWorklet.h @@ -0,0 +1,104 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_create_auperarcs_update_first_supernode_per_iteration_worklet_h +#define vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_create_auperarcs_update_first_supernode_per_iteration_worklet_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace contourtree_distributed +{ +namespace hierarchical_augmenter +{ + +/// Worklet used in HierarchicalAugmenter::UpdateHyperstructure to set the hyperarcs and hypernodes +class CreateSuperarcsUpdateFirstSupernodePerIterationWorklet : public vtkm::worklet::WorkletMapField +{ +public: + /// Control signature for the worklet + using ControlSignature = void(FieldIn indexArray, + WholeArrayInOut augmentedTreeFirstSupernodePerIteration); + using ExecutionSignature = void(_1, _2); + using InputDomain = _1; + + // Default Constructor + VTKM_EXEC_CONT + CreateSuperarcsUpdateFirstSupernodePerIterationWorklet() {} + + template + VTKM_EXEC void operator()( + const vtkm::Id& iteration, + const InOutFieldPortalType& augmentedTreeFirstSupernodePerIterationPortal) const + { // operator()() + if (augmentedTreeFirstSupernodePerIterationPortal.Get(iteration) == 0) + { + augmentedTreeFirstSupernodePerIterationPortal.Set( + iteration, augmentedTreeFirstSupernodePerIterationPortal.Get(iteration + 1)); + } + + /* + for (indexType iteration = 1; iteration < augmentedTree->nIterations[roundNo]; ++iteration) + { + if (augmentedTree->firstSupernodePerIteration[roundNo][iteration] == 0) + { + augmentedTree->firstSupernodePerIteration[roundNo][iteration] = + augmentedTree->firstSupernodePerIteration[roundNo][iteration+1]; + } + } + */ + } // operator()() +}; // CreateSuperarcsUpdateFirstSupernodePerIterationWorklet + +} // namespace hierarchical_augmenter +} // namespace contourtree_distributed +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.h index 32bcc60375..8a674115f5 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.h @@ -61,6 +61,7 @@ namespace hierarchical_augmenter /// Worklet used to implement the main part of HierarchicalAugmenter::CreateSuperarcs /// Connect superarcs for the level & set hyperparents & superchildren count, whichRound, /// whichIteration, super2hypernode +template class CreateSuperarcsWorklet : public vtkm::worklet::WorkletMapField { public: @@ -69,342 +70,537 @@ public: /// Control signature for the worklet /// @param[in] supernodeSorter input domain. We need access to InputIndex and InputIndex+1, /// therefore this is a WholeArrayIn transfer. - /// @param[in] superparentSet WholeArrayIn because we need access to superparentSet[supernodeSorter[InputIndex]] - /// and superparentSet[supernodeSorter[InputIndex+1]]. - /// @param[in] baseTreeSuperarcs WholeArrayIn because we need access to baseTreeSuperarcsPortal.Get(superparentOldSuperId) - /// While this could be done with fancy array magic, it would require a sequence of multiple - /// fancy arrays and would likely not be cheaper then computing things in the worklet. - /// @param[in] newSupernodeIds WholeArrayIn because we need to access newSupernodeIdsPortal.Get(oldTargetSuperId) - /// where oldTargetSuperId is the unmasked baseTreeSuperarcsPortal.Get(superparentOldSuperId) - /// @param[in] baseTreeSupernodes WholeArrayIn because we need to access baseTreeSupernodesPortal.Get(superparentOldSuperId); - /// @param[in] baseTreeRegularNodeGlobalIds WholeArrayIn because we need to access - /// baseTreeRegularNodeGlobalIdsPortal.Get(superparentOldSuperId); - /// @param[in] globalRegularIdSet FieldInd. Permute globalRegularIdSet with supernodeSorter in order to allow this to be a FieldIn. - /// @param[in] baseTreeSuper2Hypernode WholeArrayIn because we need to access - /// baseTreeSuper2HypernodePortal.Get(superparentOldSuperId) - /// @param[in] baseTreeWhichIteration WholeArrayIn because we need to access baseTreeWhichIterationPortal.Get(superparentOldSuperId) - /// and baseTreeWhichIterationPortal.Get(superparentOldSuperId+1) - /// @param[in] augmentedTreeSuperarcsView output view of this->AugmentedTree->Superarcs with + /// @param[in] supernodeIdSetPermuted Field in of supernodeIdSet permuted by the supernodeSorter array to + /// allow us to use FieldIn + /// @param[in] globalRegularIdSetPermuted Field in of globalRegularIdSet permuted by supernodeSorter array to + /// allow use of FieldIn + /// @param[in] dataValueSetPermuted Field in of dataValyeSet permuted by supernodeSorter array to + /// allow use of FieldIn + /// @param[in] ExecObject findSuperArcForUnknownNode Execute object in to find the superarc of arbitrary node + /// @param[in] ExecObject createSuperarcsData Data object in, storing many BaseTree arrays + /// @param[out, in] augmentedTreeSupernodes this->AugmentedTree->Supernodes array + /// @param[out] augmentedTreeSuperarcsView output view of this->AugmentedTree->Superarcs with /// vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Superarcs, /// numSupernodesAlready, this->SupernodeSorter.GetNumberOfValues()). /// By using this view allows us to do this one as a FieldOut and it effectively the /// same as accessing the array at the newSuppernodeId location. - /// @param[in] augmentedTreeFirstSupernodePerIteration WholeArrayInOut because we need to update multiple locations. - /// In is used to preseve original values. Set to augmentedTree->firstSupernodePerIteration[roundNumber]. - /// @param[in] augmentedTreeSuper2hypernode FieldOut. Output view of this->AugmentedTree->Super2Hypernode + /// @param[out] augmentedTreeHyperparentsView output view of this->AugmentedTree->Hyperparents with + /// vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Hyperparents, + /// numSupernodesAlready, this->SupernodeSorter.GetNumberOfValues()). + /// By using this view allows us to do this one as a FieldOut and it effectively the + /// same as accessing the array at the newSuppernodeId location. + /// @param[out] augmentedTreeSuper2HypernodeView output view of this->AugmentedTree->Super2Hypernode with /// vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Super2Hypernode, /// numSupernodesAlready, this->SupernodeSorter.GetNumberOfValues()). /// By using this view allows us to do this one as a FieldOut and it effectively the /// same as accessing the array at the newSuppernodeId location. + /// @param[out] augmentedTreeWhichRoundView output view of this->AugmentedTree->WhichRound with + /// vtkm::cont::make_ArrayHandleView(this->AugmentedTree->WhichRound, + /// numSupernodesAlready, this->SupernodeSorter.GetNumberOfValues()). + /// By using this view allows us to do this one as a FieldOut and it effectively the + /// same as accessing the array at the newSuppernodeId location. + /// @param[out] augmentedTreeWhichIterationView output view of this->AugmentedTree->WhichIteration with + /// vtkm::cont::make_ArrayHandleView(this->AugmentedTree->WhichIteration, + /// numSupernodesAlready, this->SupernodeSorter.GetNumberOfValues()). + /// By using this view allows us to do this one as a FieldOut and it effectively the + /// same as accessing the array at the newSuppernodeId location. + /// @param[out] augmentedTreeRegularNodeGlobalIdsView output view of this->AugmentedTree->RegularNodeGlobalIds with + /// vtkm::cont::make_ArrayHandleView(this->AugmentedTree->RegularNodeGlobalIds, + /// numSupernodesAlready, this->SupernodeSorter.GetNumberOfValues()). + /// By using this view allows us to do this one as a FieldOut and it effectively the + /// same as accessing the array at the newRegularId location. + /// @param[out] augmentedTreeDataValuesView output view of this->AugmentedTree->DataValues with + /// vtkm::cont::make_ArrayHandleView(this->AugmentedTree->DataValues, + /// numSupernodesAlready, this->SupernodeSorter.GetNumberOfValues()). + /// By using this view allows us to do this one as a FieldOut and it effectively the + /// same as accessing the array at the newRegularId location. + /// @param[out] augmentedTreeRegular2SupernodeView output view of this->AugmentedTree->Regular2Supernode with + /// vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Regular2Supernode, + /// numSupernodesAlready, this->SupernodeSorter.GetNumberOfValues()). + /// By using this view allows us to do this one as a FieldOut and it effectively the + /// same as accessing the array at the newRegularId location. + /// @param[out] augmentedTreeSuperparentsView output view of this->AugmentedTree->Superparents with + /// vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Superparents, + /// numSupernodesAlready, this->SupernodeSorter.GetNumberOfValues()). + /// By using this view allows us to do this one as a FieldOut and it effectively the + /// same as accessing the array at the newRegularId location. + using ControlSignature = void( + // Inputs WholeArrayIn supernodeSorter, - WholeArrayIn superparentSet, // input - WholeArrayIn baseTreeSuperarcs, // input - WholeArrayIn newSupernodeIds, // input - WholeArrayIn baseTreeSupernodes, // input - WholeArrayIn baseTreeRegularNodeGlobalIds, // input - FieldIn globalRegularIdSet, // input - WholeArrayIn baseTreeSuper2Hypernode, // input - WholeArrayIn baseTreeWhichIteration, // input - FieldOut augmentedTreeSuperarcsView, // output - WholeArrayInOut augmentedTreeFirstSupernodePerIteration, // input/output - FieldOut augmentedTreeSuper2hypernode // ouput - ); - using ExecutionSignature = void(InputIndex, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12); + FieldIn supernodeIdSetPermuted, + FieldIn globalRegularIdSetPermuted, + FieldIn dataValueSetPermuted, + ExecObject findSuperArcForUnknownNode, + ExecObject createSuperarcsData, + // Outputs + WholeArrayInOut augmentedTreeSupernodes, + FieldOut augmentedTreeSuperarcsView, + FieldOut augmentedTreeHyperparentsView, + FieldOut augmentedTreeSuper2Hypernode, + FieldOut augmentedTreeWhichRoundView, + FieldOut augmentedTreeWhichIterationView, + FieldOut augmentedTreeRegularNodeGlobalIdsView, + FieldOut augmentedTreeDataValuesView, + FieldOut augmentedTreeRegular2SupernodeView, + FieldOut augmentedTreeSuperparentsViews); + using ExecutionSignature = + void(InputIndex, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16); + using InputDomain = _1; /// Default Constructor /// @param[in] numSupernodesAlready Set to vtkm::cont::ArrayGetValue(0, this->AugmentedTree->FirstSupernodePerIteration[roundNumber]); /// @param[in] baseTreeNumRounds Set to this->BaseTree->NumRounds - /// @param[in] augmentedTreeNumIterations Set to vtkm::cont::ArrayGetValue(roundNumber, this->AugmentedTree->NumIterations); - /// @param[in] roundNumber Set the current round - /// @param[in] numAugmentedTreeSupernodes Set to augmentedTreeSupernodes this->AugmentedTree->Supernodes.GetNumberOfValues(); + /// @param[in] numInsertedSupernodes Set to numInsertedSupernodes VTKM_EXEC_CONT CreateSuperarcsWorklet(const vtkm::Id& numSupernodesAlready, const vtkm::Id& baseTreeNumRounds, - const vtkm::Id& augmentedTreeNumIterations, - const vtkm::Id& roundNumber, - const vtkm::Id& numAugmentedTreeSupernodes) + const vtkm::Id& numInsertedSupernodes, + const vtkm::Id& roundNo) : NumSupernodesAlready(numSupernodesAlready) , BaseTreeNumRounds(baseTreeNumRounds) - , AugmentedTreeNumIterations(augmentedTreeNumIterations) - , RoundNumber(roundNumber) - , NumAugmentedTreeSupernodes(numAugmentedTreeSupernodes) + , NumInsertedSupernodes(numInsertedSupernodes) + , RoundNo(roundNo) { } /// operator() of the workelt - template + template + //typename InOutDataFieldPortalType> + //typename InOutFieldPortalType, + //typename ExecObjectTypeData, + //typename ExecObjType> VTKM_EXEC void operator()( + // Inputs const vtkm::Id& supernode, // InputIndex of supernodeSorter const InFieldPortalType& supernodeSorterPortal, - const InFieldPortalType& superparentSetPortal, - const InFieldPortalType& baseTreeSuperarcsPortal, - const InFieldPortalType& newSupernodeIdsPortal, - const InFieldPortalType& baseTreeSupernodesPortal, - const InFieldPortalType& baseTreeRegularNodeGlobalIdsPortal, - const vtkm::Id& globalRegularIdSetValue, - const InFieldPortalType& baseTreeSuper2HypernodePortal, - const InFieldPortalType& baseTreeWhichIterationPortal, - vtkm::Id& augmentedTreeSuperarcsValue, // same as augmentedTree->superarcs[newSupernodeId] - const InOutFieldPortalType& - augmentedTreeFirstSupernodePerIterationPortal, // augmentedTree->firstSupernodePerIteration[roundNumber] - vtkm::Id& augmentedTreeSuper2hypernodeValue) const + const vtkm::Id& oldSupernodeId, // supernodeIdSet[supernodeSorterPortal.Get(supernode)] + const vtkm::Id& + globalRegularIdSetValue, // globalRegularIdSet[supernodeSorterPortal.Get(supernode)]]; + const FieldType& dataValueSetValue, // dataValueSet[supernodeSorterPortal.Get(supernode)]]; + const ExecObjType& + findSuperArcForUnknownNode, // Execution object to call FindSuperArcForUnknownNode + const ExecObjectTypeData& + createSuperarcsData, // Execution object of collect BaseTree data array + // Outputs + const InOutFieldPortalType& augmentedTreeSupernodesPortal, + vtkm::Id& augmentedTreeSuperarcsValue, // set value for AugmentedTree->Superarcs[newSupernodeId] + vtkm::Id& + augmentedTreeHyperparentsValue, // set value for AugmentedTree->Hyperparents[newSupernodeId] + vtkm::Id& + augmentedTreeSuper2HypernodeValue, // set value for AugmentedTree->Super2Hypernode[newSupernodeId] + vtkm::Id& augmentedTreeWhichRoundValue, // AugmentedTree->WhichRound[newSupernodeId] + vtkm::Id& augmentedTreeWhichIterationValue, // AugmentedTree->WhichIteration[newSupernodeId] + vtkm::Id& + augmentedTreeRegularNodeGlobalIdsValue, // AugmentedTree->RegularNodeGlobalIds[newRegularID] + FieldType& augmentedTreeDataValuesValue, // AugmentedTree->DataValues[newRegularID] + vtkm::Id& augmentedTreeRegular2SupernodeValue, // AugmentedTree->Regular2Supernode[newRegularID] + vtkm::Id& augmentedTreeSuperparentsValue // AugmentedTree->Superparents[newRegularID] + ) const { // per supernode in the set // retrieve the index from the sorting index array vtkm::Id supernodeSetIndex = supernodeSorterPortal.Get(supernode); - // work out the new supernode Id. We have this defined on the outside as a fancy array handle, - // however, using the fancy handle here would not really make a performance differnce and - // computing it here is more readable + // work out the new supernode ID vtkm::Id newSupernodeId = this->NumSupernodesAlready + supernode; - // NOTE: The newRegularId is no longer needed here since all parts - // that used it in the worklet have been moved outside - // vtkm::Id newRegularId = newSupernodeId; - - // NOTE: This part has been moved out of the worklet and is performed using standard vtkm copy constructs - // // setting the supernode's regular Id is now trivial - // augmentedTreeSupernodesPortal.Set(newSupernodeId, newRegularId); - - // retrieve the ascending flag from the superparent - vtkm::Id superparentSetVal = superparentSetPortal.Get(supernodeSetIndex); - // get the ascending flag from the parent - bool superarcAscends = vtkm::worklet::contourtree_augmented::IsAscending(superparentSetVal); - // strip the ascending flag from the superparent. - vtkm::Id superparentOldSuperId = - vtkm::worklet::contourtree_augmented::MaskedIndex(superparentSetVal); - - // setting the superarc is done the usual way. Our sort routine has ended up - // with the supernodes arranged in either ascending or descending order - // inwards along the parent superarc (as expressed by the superparent Id). - // Each superarc except the last in the segment points to the next one: - // the last one points to the target of the original superarc. - // first test to see if we're the last in the array - if (supernode == supernodeSorterPortal.GetNumberOfValues() - 1) - { // last in the array - // special case for root of entire tree at end of top level - if (RoundNumber == this->BaseTreeNumRounds) + // and the old supernode ID + // vtkm::Id oldSupernodeId = supernodeIDSet[supernodeSetIndex]; Extracted on call and provides as input + + // At all levels above 0, we used to keep regular vertices in case they are attachment points. + // After augmentation, we don't need to. + // Instead, at all levels above 0, the regular nodes in each round are identical to the supernodes + // In order to avoid confusion, we will copy the ID into a separate variable + vtkm::Id newRegularId = newSupernodeId; + + // setting the supernode's regular ID is now trivial + augmentedTreeSupernodesPortal.Set(newSupernodeId, newRegularId); + + // retrieve the old superID of the superparent. This is slightly tricky, as we have four classes of supernodes: + // 1. the root of the entire tree + // 2. attachment points not being inserted. In this case, the supernode ID is stored in the superparentSet + // array, not the superparent for insertion purposes + // 3. attachment points being inserted. In this case, the superparent is stored in the superparentSet array + // 4. "ordinary" supernodes, where the superparent is the same as the supernode ID anyway + // + // Note that an attachment point gets inserted into a parent superarc. But the attachment point itself has + // a NULL superarc, because it's only a virtual insertion. + // This means that such an attachment superarc cannot be the superparent of any other attachment point + // It is therefore reasonable to deal with 1. & 2 separately. 3. & 4. then combine together + + // first we test for the root of the tree + if ((this->RoundNo == BaseTreeNumRounds) && + (supernode == supernodeSorterPortal.GetNumberOfValues() - 1)) + { // root of the tree + // note that oldSupernodeID is guaranteed not to be NO_SUCH_ELEMENT, as the root is in every tree + // set the super arrays + augmentedTreeSuperarcsValue = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + // hyperstructure carries over, so we use the same hyperparent as before + augmentedTreeHyperparentsValue = createSuperarcsData.BaseTreeHyperparents.Get(oldSupernodeId); + // and set the hypernode ID + augmentedTreeSuper2HypernodeValue = + createSuperarcsData.BaseTreeSuper2Hypernode.Get(oldSupernodeId); + // and the round and iteration + augmentedTreeWhichRoundValue = createSuperarcsData.BaseTreeWhichRound.Get(oldSupernodeId); + augmentedTreeWhichIterationValue = + createSuperarcsData.BaseTreeWhichIteration.Get(oldSupernodeId); + // and set the relevant regular arrays + augmentedTreeRegularNodeGlobalIdsValue = globalRegularIdSetValue; + augmentedTreeDataValuesValue = dataValueSetValue; + // for the root, these always point to itself + augmentedTreeRegular2SupernodeValue = newSupernodeId; + augmentedTreeSuperparentsValue = newSupernodeId; + } // root of the tree + // now deal with unsimplified attachment points, which we can identify because they were in the "kept" batch, not the "inserted" batch, + // and this is given away by the index into the set of supernodes to be added + // and the fact that the superarc is NO_SUCH_ELEMENT + else if ((supernodeSetIndex >= this->NumInsertedSupernodes) && + (vtkm::worklet::contourtree_augmented::NoSuchElement( + createSuperarcsData.BaseTreeSuperarcs.Get(oldSupernodeId)))) + { // preserved attachment point + // note that oldSupernodeID is guaranteed not to be NO_SUCH_ELEMENT, as the supernode came from this block originally + // set the superarc to NO_SUCH_ELEMENT, as before + augmentedTreeSuperarcsValue = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + // hyperstructure carries over, so we use the same hyperparent as before + // the "if" clauses guarantee the oldSupernodeId not to be NO_SUCH_ELEMENT. + // We cannot prepare the array permutation outside the worklet, or the guarantee does not hold. + augmentedTreeHyperparentsValue = createSuperarcsData.BaseTreeHyperparents.Get(oldSupernodeId); + // attachment points are never hypernodes anyway, so set it directly + augmentedTreeSuper2HypernodeValue = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + // and the round and iteration + augmentedTreeWhichRoundValue = createSuperarcsData.BaseTreeWhichRound.Get(oldSupernodeId); + augmentedTreeWhichIterationValue = + createSuperarcsData.BaseTreeWhichIteration.Get(oldSupernodeId); + // and set the relevant regular arrays + augmentedTreeRegularNodeGlobalIdsValue = globalRegularIdSetValue; + augmentedTreeDataValuesValue = dataValueSetValue; + // for a preserved attachment point, this always points to itself + augmentedTreeRegular2SupernodeValue = newSupernodeId; + // the superparent is the tricky one, as the old one may have been broken up by insertions at a higher level + + // Here, what we need to do is a search in the augmented tree to find which superarc to attach to. This is necessary + // because the old superarc it attached to may have been broken up. + // We are guaranteed that there is one, and that it only uses the higher levels of the augmented tree, + // so the fact that we are partially constructed doesn't get in the way. To do this, we need supernodes + // known to be in the higher level that are above and below the supernode. + // Since the point was an attachment point in the base tree, that means that there is a higher round superarc + // it inserts into. Moreover, the algorithm ALWAYS inserts a supernode at or above its original round, so + // we can guarantee that both ends of the parent are in the higher levels. Which means we only need to work + // out which end is higher. + + // the "if" clauses guarantee the oldSupernodeId not to be NO_SUCH_ELEMENT. + // However, we cannot prepare the array permutation outside the worklet, or the guarantee does not hold. + // indexType oldRegularID = baseTree->supernodes[oldSupernodeID]; + // indexType oldSuperFrom = baseTree->superparents[oldRegularID]; + // indexType oldSuperTo = baseTree->superarcs[oldSuperFrom]; + vtkm::Id oldRegularId = createSuperarcsData.BaseTreeSupernodes.Get(oldSupernodeId); + vtkm::Id oldSuperFromValue = createSuperarcsData.BaseTreeSuperparents.Get(oldRegularId); + vtkm::Id oldSuperToValue = createSuperarcsData.BaseTreeSuperarcs.Get(oldSuperFromValue); + + // retrieve the ascending flag + bool ascendingSuperarc = vtkm::worklet::contourtree_augmented::IsAscending(oldSuperToValue); + // and mask out the flags + vtkm::Id oldSuperToMaskedIndex = + vtkm::worklet::contourtree_augmented::MaskedIndex(oldSuperToValue); + + // since we haven't set up the regular search array yet, we can't use that + // instead, we know that the two supernodes must be in the new tree, so we retrieve their new super IDs + // and convert them to regular + + // retrieve their new super IDs + vtkm::Id newSuperFrom = createSuperarcsData.NewSupernodeIds.Get(oldSuperFromValue); + vtkm::Id newSuperTo = createSuperarcsData.NewSupernodeIds.Get(oldSuperToMaskedIndex); + + // convert to regular IDs (which is what the FindSuperArcForUnknownNode() routine assumes) + vtkm::Id newRegularFrom = augmentedTreeSupernodesPortal.Get(newSuperFrom); + vtkm::Id newRegularTo = augmentedTreeSupernodesPortal.Get(newSuperTo); + + // the new superparent after the search + vtkm::Id newSuperparentId = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + + // depending on the ascending flag + if (ascendingSuperarc) { - augmentedTreeSuperarcsValue = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + newSuperparentId = findSuperArcForUnknownNode.FindSuperArcForUnknownNode( + globalRegularIdSetValue, dataValueSetValue, newRegularTo, newRegularFrom); } else - { // not the tree root - // retrieve the target of the superarc from the base tree (masking to strip out the ascending flag) - vtkm::Id oldTargetSuperId = vtkm::worklet::contourtree_augmented::MaskedIndex( - baseTreeSuperarcsPortal.Get(superparentOldSuperId)); - // convert to a new supernode Id - vtkm::Id newTargetSuperId = newSupernodeIdsPortal.Get(oldTargetSuperId); - // add the ascending flag back in and store in the array - augmentedTreeSuperarcsValue = newTargetSuperId | - (superarcAscends ? vtkm::worklet::contourtree_augmented::IS_ASCENDING : 0x00); - } // not the tree root - // since there's an extra entry in the firstSupernode array as a sentinel, set it - augmentedTreeFirstSupernodePerIterationPortal.Set(this->AugmentedTreeNumIterations, - NumAugmentedTreeSupernodes); - } // last in the array - else if (superparentOldSuperId != - vtkm::worklet::contourtree_augmented::MaskedIndex( - superparentSetPortal.Get(supernodeSorterPortal.Get(supernode + 1)))) - { // last in the segment - // retrieve the target of the superarc from the base tree (masking to strip out the ascending flag) - vtkm::Id oldTargetSuperId = vtkm::worklet::contourtree_augmented::MaskedIndex( - baseTreeSuperarcsPortal.Get(superparentOldSuperId)); - // convert to a new supernode Id - vtkm::Id newTargetSuperId = newSupernodeIdsPortal.Get(oldTargetSuperId); - // add the ascending flag back in and store in the array - augmentedTreeSuperarcsValue = newTargetSuperId | - (superarcAscends ? vtkm::worklet::contourtree_augmented::IS_ASCENDING : 0x00); - - // since we're the last in the segment, we check to see if we are at the end of an iteration - vtkm::Id iterationNumber = vtkm::worklet::contourtree_augmented::MaskedIndex( - baseTreeWhichIterationPortal.Get(superparentOldSuperId)); - vtkm::Id iterationNumberOfNext = vtkm::worklet::contourtree_augmented::MaskedIndex( - baseTreeWhichIterationPortal.Get(superparentOldSuperId + 1)); - - if (iterationNumber != iterationNumberOfNext) - { // boundary of iterations - // If so, we set the "firstSupernodePerIteration" for the next - augmentedTreeFirstSupernodePerIterationPortal.Set(iterationNumberOfNext, - newSupernodeId + 1); - } // boundary of iterations - } // last in the segment - else - { // not last in the segment - // the target is always the next one, so just store it with the ascending flag - augmentedTreeSuperarcsValue = (newSupernodeId + 1) | - (superarcAscends ? vtkm::worklet::contourtree_augmented::IS_ASCENDING : 0x00); - } // not last in the segment - - // set the first supernode in the first iteration to the beginning of the round - augmentedTreeFirstSupernodePerIterationPortal.Set(0, this->NumSupernodesAlready); - - - // NOTE: This part has been moved out of the worklet and is performed using standard vtkm copy constructs - // // setting the hyperparent is straightforward since the hyperstructure is preserved - // // we take the superparent (which is guaranteed to be in the baseTree), find it's hyperparent and use that - // augmentedTreeHyperparentsPortal.Set(newSupernodeId, baseTreeHyperparentsPortal.Get(superparentOldSuperId)); - - // NOTE: This part could potentially be made a separate worklet but it does not seem necessary - // similarly, the super2hypernode should carry over, but it's harder to test because of the attachment points which - // do not have valid old supernode Ids. Instead, we check their superparent's regular global Id against them: if it - // matches, then it must be the start of the superarc, in which case it does have an old Id, and we can then use the - // existing hypernode Id - vtkm::Id superparentOldRegularId = baseTreeSupernodesPortal.Get(superparentOldSuperId); - vtkm::Id superparentGlobalId = baseTreeRegularNodeGlobalIdsPortal.Get(superparentOldRegularId); - // Here: globalRegularIdSetValue is the same as globalRegularIdSetPortal.Get(supernodeSetIndex) - if (superparentGlobalId == globalRegularIdSetValue) - { - // augmentedTreeSuper2hypernodePortal.Set(newSupernodeId, baseTreeSuper2HypernodePortal.Get(superparentOldSuperId)); - augmentedTreeSuper2hypernodeValue = baseTreeSuper2HypernodePortal.Get(superparentOldSuperId); - } + { + newSuperparentId = findSuperArcForUnknownNode.FindSuperArcForUnknownNode( + globalRegularIdSetValue, dataValueSetValue, newRegularFrom, newRegularTo); + } + + // attachment points use the superparent to store the superarc they insert onto + augmentedTreeSuperparentsValue = newSuperparentId; + + } // preserved attachment point else - { - // augmentedTreeSuper2hypernodePortal.Set(newSupernodeId, vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT); - augmentedTreeSuper2hypernodeValue = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; - } - - // NOTE: This part has been moved out of the worklet and is performed using standard vtkm copy constructs - // // which round and iteration carry over - // augmentedTreeWhichRoundPortal.Set(newSupernodeId, baseTreeWhichRoundPortal.Get(superparentOldSuperId)); - // augmentedTreeWhichIterationPortal.Set(newSupernodeId, baseTreeWhichIterationPortal.Get(superparentOldSuperId)); - - // now we deal with the regular-sized arrays - - // NOTE: This part has been moved out of the worklet and is performed using standard vtkm copy constructs - // // copy the global regular Id and data value - // augmentedTreeRegularNodeGlobalIdsPortal.Set(newRegularId, globalRegularIdSetPortal.Get(supernodeSetIndex)); - // augmentedTreeDataValuesPortal.Set(newRegularId, dataValueSetPortal.Get(supernodeSetIndex)); - - // NOTE: This part has been moved out of the worklet and is performed using standard vtkm copy constructs - // // the sort order will be dealt with later - // // since all of these nodes are supernodes, they will be their own superparent, which means that: - // // a. the regular2node can be set immediately - // augmentedTreeRegular2SupernodePortal.Set(newRegularId, newSupernodeId); - // // b. as can the superparent - // augmentedTreeSuperparentsPortal.Set(newRegularId, newSupernodeId); - - // In serial this worklet implements the following operation - /* - for (vtkm::Id supernode = 0; supernode < supernodeSorter.size(); supernode++) - { // per supernode in the set - // retrieve the index from the sorting index array - vtkm::Id supernodeSetIndex = supernodeSorter[supernode]; - - // work out the new supernode ID - vtkm::Id newSupernodeID = numSupernodesAlready + supernode; - - // At all levels above 0, we used to keep regular vertices in case they are attachment points. After augmentation, we don't need to. - // Instead, at all levels above 0, the regular nodes in each round are identical to the supernodes - // In order to avoid confusion, we will copy the ID into a separate variable - vtkm::Id newRegularID = newSupernodeID; - - // setting the supernode's regular ID is now trivial - augmentedTree->supernodes [newSupernodeID] = newRegularID; - - // retrieve the ascending flag from the superparent - bool superarcAscends = isAscending(superparentSet[supernodeSetIndex]); - - // strip the ascending flag from the superparent - vtkm::Id superparentOldSuperID = maskedIndex(superparentSet[supernodeSetIndex]); + { // raised attachment point or "ordinary" supernodes + // Since all of the superparents must be in the base tree, we can now retrieve the target + vtkm::Id superparentOldSuperId = vtkm::worklet::contourtree_augmented::MaskedIndex( + createSuperarcsData.SuperparentSet.Get(supernodeSetIndex)); + + vtkm::Id oldTargetSuperId = createSuperarcsData.BaseTreeSuperarcs.Get(superparentOldSuperId); + + // and break it into a target and flags + bool ascendingSuperarc = vtkm::worklet::contourtree_augmented::IsAscending(oldTargetSuperId); + // NOTE: if the target was NO_SUCH_ELEMENT, this will hold 0 + oldTargetSuperId = vtkm::worklet::contourtree_augmented::MaskedIndex(oldTargetSuperId); + + // and another boolean for whether we are the last element in a segment + bool isLastInSegment = false; + + // end of the entire array counts as last in segment + if (supernode == supernodeSorterPortal.GetNumberOfValues() - 1) + { + isLastInSegment = true; + } + // otherwise, check for a mismatch in the sorting superparent which indicates the end of a segment + else if (vtkm::worklet::contourtree_augmented::MaskedIndex( + createSuperarcsData.SuperparentSet.Get(supernodeSetIndex)) != + vtkm::worklet::contourtree_augmented::MaskedIndex( + createSuperarcsData.SuperparentSet.Get(supernodeSorterPortal.Get(supernode + 1)))) + { + isLastInSegment = true; + } // setting the superarc is done the usual way. Our sort routine has ended up with the supernodes arranged in either ascending or descending order // inwards along the parent superarc (as expressed by the superparent ID). Each superarc except the last in the segment points to the next one: // the last one points to the target of the original superarc. - // first test to see if we're the last in the array - if (supernode == supernodeSorter.size() - 1) - { // last in the array - // special case for root of entire tree at end of top level - if (roundNumber == baseTree->nRounds) - { - augmentedTree->superarcs[newSupernodeID] = NO_SUCH_ELEMENT; - } - else - { // not the tree root - // retrieve the target of the superarc from the base tree (masking to strip out the ascending flag) - vtkm::Id oldTargetSuperID = maskedIndex(baseTree->superarcs[superparentOldSuperID]); - // convert to a new supernode ID - vtkm::Id newTargetSuperID = newSupernodeIDs[oldTargetSuperID]; - // add the ascending flag back in and store in the array - augmentedTree->superarcs[newSupernodeID] = newTargetSuperID | (superarcAscends ? IS_ASCENDING : 0x00); - } // not the tree root - // since there's an extra entry in the firstSupernode array as a sentinel, set it - augmentedTree->firstSupernodePerIteration[roundNumber][augmentedTree->nIterations[roundNumber]] = augmentedTree->supernodes.size(); - } // last in the array - else if (superparentOldSuperID != maskedIndex(superparentSet[supernodeSorter[supernode+1]])) - { // last in the segment - // retrieve the target of the superarc from the base tree (masking to strip out the ascending flag) - vtkm::Id oldTargetSuperID = maskedIndex(baseTree->superarcs[superparentOldSuperID]); - // convert to a new supernode ID - vtkm::Id newTargetSuperID = newSupernodeIDs[oldTargetSuperID]; - // add the ascending flag back in and store in the array - augmentedTree->superarcs[newSupernodeID] = newTargetSuperID | (superarcAscends ? IS_ASCENDING : 0x00); - - // since we're the last in the segment, we check to see if we are at the end of an iteration - vtkm::Id iterationNumber = maskedIndex(baseTree->whichIteration[superparentOldSuperID]); - vtkm::Id iterationNumberOfNext = maskedIndex(baseTree->whichIteration[superparentOldSuperID + 1]); - - if (iterationNumber != iterationNumberOfNext) - { // boundary of iterations - // If so, we set the "firstSupernodePerIteration" for the next - augmentedTree->firstSupernodePerIteration[roundNumber][iterationNumberOfNext] = newSupernodeID + 1; - } // boundary of iterations - } // last in the segment + if (isLastInSegment) + { // last in segment + // we take the old target of the superarc (in old supernode IDs) and convert it to a new supernode ID + augmentedTreeSuperarcsValue = createSuperarcsData.NewSupernodeIds.Get(oldTargetSuperId) | + (ascendingSuperarc ? vtkm::worklet::contourtree_augmented::IS_ASCENDING : 0x00); + } // last in segment else - { // not last in the segment + { // not last in segment // the target is always the next one, so just store it with the ascending flag - augmentedTree->superarcs[newSupernodeID] = (newSupernodeID+1) | (superarcAscends ? IS_ASCENDING : 0x00); - } // not last in the segment - - // set the first supernode in the first iteration to the beginning of the round - augmentedTree->firstSupernodePerIteration[roundNumber][0] = numSupernodesAlready; - - // setting the hyperparent is straightforward since the hyperstructure is preserved - // we take the superparent (which is guaranteed to be in the baseTree), find it's hyperparent and use that - augmentedTree->hyperparents [newSupernodeID] = baseTree->hyperparents [superparentOldSuperID]; - - // similarly, the super2hypernode should carry over, but it's harder to test because of the attachment points which - // do not have valid old supernode IDs. Instead, we check their superparent's regular global ID against them: if it - // matches, then it must be the start of the superarc, in which case it does have an old ID, and we can then use the - // existing hypernode ID - vtkm::Id superparentOldRegularID = baseTree->supernodes[superparentOldSuperID]; - vtkm::Id superparentGlobalID = baseTree->regularNodeGlobalIDs[superparentOldRegularID]; - if (superparentGlobalID == globalRegularIDSet[supernodeSetIndex]) + augmentedTreeSuperarcsValue = (newSupernodeId + 1) | + (ascendingSuperarc ? vtkm::worklet::contourtree_augmented::IS_ASCENDING : 0x00); + } // not last in segment + + // first we identify the hyperarc on which the superarc sits + // this will be visible in the old base tree, since hyperstructure carries over + vtkm::Id oldHyperparent = createSuperarcsData.BaseTreeHyperparents.Get(superparentOldSuperId); + + // hyperstructure carries over, so we use the same hyperparent as the superparent + augmentedTreeHyperparentsValue = oldHyperparent; + + // retrieve the hyperparent's old supernode ID & convert to a new one, then test it + if (createSuperarcsData.NewSupernodeIds.Get( + createSuperarcsData.BaseTreeHypernodes.Get(oldHyperparent)) == newSupernodeId) { - augmentedTree->super2hypernode [newSupernodeID] = baseTree->super2hypernode[superparentOldSuperID]; + augmentedTreeSuper2HypernodeValue = oldHyperparent; } else { - augmentedTree->super2hypernode [newSupernodeID] = NO_SUCH_ELEMENT; + augmentedTreeSuper2HypernodeValue = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; } - // which round and iteration carry over - augmentedTree->whichRound [newSupernodeID] = baseTree->whichRound[superparentOldSuperID]; - augmentedTree->whichIteration [newSupernodeID] = baseTree->whichIteration[superparentOldSuperID]; - - // now we deal with the regular-sized arrays + // round and iteration are set from the superparent, since we are raising to its level + augmentedTreeWhichRoundValue = + createSuperarcsData.BaseTreeWhichRound.Get(superparentOldSuperId); + augmentedTreeWhichIterationValue = + createSuperarcsData.BaseTreeWhichIteration.Get(superparentOldSuperId); + // and set the relevant regular arrays + augmentedTreeRegularNodeGlobalIdsValue = globalRegularIdSetValue; + augmentedTreeDataValuesValue = dataValueSetValue; + // for all supernodes, this points to itself + augmentedTreeRegular2SupernodeValue = newSupernodeId; + // and since we're inserted, so does this + augmentedTreeSuperparentsValue = newSupernodeId; + } // raised attachment point or "ordinary" supernodes + + /* Original PPP2 code + for (indexType supernode = 0; supernode < supernodeSorter.size(); supernode++) + { // per supernode in the set + // retrieve the index from the sorting index array + indexType supernodeSetIndex = supernodeSorter[supernode]; + + // work out the new supernode ID + indexType newSupernodeID = nSupernodesAlready + supernode; + + // and the old supernode ID + // NB: May be NO_SUCH_ELEMENT if not already in the base tree + indexType oldSupernodeID = supernodeIDSet[supernodeSetIndex]; + + // At all levels above 0, we used to keep regular vertices in case they are attachment points. After augmentation, we don't need to. + // Instead, at all levels above 0, the regular nodes in each round are identical to the supernodes + // In order to avoid confusion, we will copy the ID into a separate variable + indexType newRegularID = newSupernodeID; + + // setting the supernode's regular ID is now trivial + augmentedTree->supernodes [newSupernodeID] = newRegularID; + + // retrieve the old superID of the superparent. This is slightly tricky, as we have four classes of supernodes: + // 1. the root of the entire tree + // 2. attachment points not being inserted. In this case, the supernode ID is stored in the superparentSet array, not the superparent for insertion purposes + // 3. attachment points being inserted. In this case, the superparent is stored in the superparentSet array + // 4. "ordinary" supernodes, where the superparent is the same as the supernode ID anyway + // + // Note that an attachment point gets inserted into a parent superarc. But the attachment point itself has a NULL superarc, because it's only a virtual insertion + // This means that such an attachment superarc cannot be the superparent of any other attachment point + // It is therefore reasonable to deal with 1. & 2 separately. 3. & 4. then combine together + + // first we test for the root of the tree + if ((roundNo == baseTree->nRounds) && (supernode == supernodeSorter.size() - 1)) + { // root of the tree + // note that oldSupernodeID is guaranteed not to be NO_SUCH_ELEMENT, as the root is in every tree + // set the super arrays + augmentedTree->superarcs [newSupernodeID] = NO_SUCH_ELEMENT; + // hyperstructure carries over, so we use the same hyperparent as before + augmentedTree->hyperparents [newSupernodeID] = baseTree->hyperparents[oldSupernodeID]; + // and set the hypernode ID + augmentedTree->super2hypernode [newSupernodeID] = baseTree->super2hypernode[oldSupernodeID]; + // and the round and iteration + augmentedTree->whichRound [newSupernodeID] = baseTree->whichRound[oldSupernodeID]; + augmentedTree->whichIteration [newSupernodeID] = baseTree->whichIteration[oldSupernodeID]; + // and set the relevant regular arrays + augmentedTree->regularNodeGlobalIDs [newRegularID] = globalRegularIDSet[supernodeSetIndex]; + augmentedTree->dataValues [newRegularID] = dataValueSet[supernodeSetIndex]; + // for the root, these always point to itself + augmentedTree->regular2supernode [newRegularID] = newSupernodeID; + augmentedTree->superparents [newRegularID] = newSupernodeID; + } // root of the tree + // now deal with unsimplified attachment points, which we can identify because they were in the "kept" batch, not the "inserted" batch, + // and this is given away by the index into the set of supernodes to be added + // and the fact that the superarc is NO_SUCH_ELEMENT + else if ((supernodeSetIndex >= nInsertedSupernodes) && (noSuchElement(baseTree->superarcs[oldSupernodeID]))) + { // preserved attachment point + // note that oldSupernodeID is guaranteed not to be NO_SUCH_ELEMENT, as the supernode came from this block originally + // set the superarc to NO_SUCH_ELEMENT, as before + augmentedTree->superarcs [newSupernodeID] = NO_SUCH_ELEMENT; + // hyperstructure carries over, so we use the same hyperparent as before + augmentedTree->hyperparents [newSupernodeID] = baseTree->hyperparents[oldSupernodeID]; + // attachment points are never hypernodes anyway, so set it directly + augmentedTree->super2hypernode [newSupernodeID] = NO_SUCH_ELEMENT; + // and the round and iteration + augmentedTree->whichRound [newSupernodeID] = baseTree->whichRound[oldSupernodeID]; + augmentedTree->whichIteration [newSupernodeID] = baseTree->whichIteration[oldSupernodeID]; + // and set the relevant regular arrays + augmentedTree->regularNodeGlobalIDs [newRegularID] = globalRegularIDSet[supernodeSetIndex]; + augmentedTree->dataValues [newRegularID] = dataValueSet[supernodeSetIndex]; + // for a preserved attachment point, this always points to itself + augmentedTree->regular2supernode [newRegularID] = newSupernodeID; + // the superparent is the tricky one, as the old one may have been broken up by insertions at a higher level + + // Here, what we need to do is a search in the augmented tree to find which superarc to attach to. This is necessary + // because the old superarc it attached to may have been broken up. + // We are guaranteed that there is one, and that it only uses the higher levels of the augmented tree, + // so the fact that we are partially constructed doesn't get in the way. To do this, we need supernodes + // known to be in the higher level that are above and below the supernode. + // Since the point was an attachment point in the base tree, that means that there is a higher round superarc + // it inserts into. Moreover, the algorithm ALWAYS inserts a supernode at or above its original round, so + // we can guarantee that both ends of the parent are in the higher levels. Which means we only need to work + // out which end is higher. + indexType oldRegularID = baseTree->supernodes[oldSupernodeID]; + indexType oldSuperFrom = baseTree->superparents[oldRegularID]; + indexType oldSuperTo = baseTree->superarcs[oldSuperFrom]; + // retrieve the ascending flag + bool ascendingSuperarc = isAscending(oldSuperTo); + // and mask out the flags + oldSuperTo = maskedIndex(oldSuperTo); + + // since we haven't set up the regular search array yet, we can't use that + // instead, we know that the two supernodes must be in the new tree, so we retrieve their new super IDs + // and convert them to regular + + // retrieve their new super IDs + indexType newSuperFrom = newSupernodeIDs[oldSuperFrom]; + indexType newSuperTo = newSupernodeIDs[oldSuperTo]; + + // convert to regular IDs (which is what the FindSuperArcForUnknownNode() routine assumes) + indexType newRegularFrom = augmentedTree->supernodes[newSuperFrom]; + indexType newRegularTo = augmentedTree->supernodes[newSuperTo]; + + // the new superparent after the search + indexType newSuperparentID = NO_SUCH_ELEMENT; + + // depending on the ascending flag + if (ascendingSuperarc) + newSuperparentID = augmentedTree->FindSuperArcForUnknownNode(globalRegularIDSet[supernodeSetIndex],dataValueSet[supernodeSetIndex], newRegularTo, newRegularFrom); + else + newSuperparentID = augmentedTree->FindSuperArcForUnknownNode(globalRegularIDSet[supernodeSetIndex],dataValueSet[supernodeSetIndex], newRegularFrom, newRegularTo); + + // attachment points use the superparent to store the superarc they insert onto + augmentedTree->superparents [newRegularID] = newSuperparentID; + + } // preserved attachment point + else + { // raised attachment point or "ordinary" supernodes + // Since all of the superparents must be in the base tree, we can now retrieve the target + indexType superparentOldSuperID = maskedIndex(superparentSet[supernodeSetIndex]); + indexType oldTargetSuperID = baseTree->superarcs[superparentOldSuperID]; + // and break it into a target and flags + bool ascendingSuperarc = isAscending(oldTargetSuperID); + // NOTE: if the target was NO_SUCH_ELEMENT, this will hold 0 + oldTargetSuperID = maskedIndex(oldTargetSuperID); + + // and another boolean for whether we are the last element in a segment + bool isLastInSegment = false; + + // end of the entire array counts as last in segment + if (supernode == supernodeSorter.size() - 1) + isLastInSegment = true; + // otherwise, check for a mismatch in the sorting superparent which indicates the end of a segment + else if (maskedIndex(superparentSet[supernodeSetIndex]) != maskedIndex(superparentSet[supernodeSorter[supernode+1]])) + isLastInSegment = true; + + // setting the superarc is done the usual way. Our sort routine has ended up with the supernodes arranged in either ascending or descending order + // inwards along the parent superarc (as expressed by the superparent ID). Each superarc except the last in the segment points to the next one: + // the last one points to the target of the original superarc. + if (isLastInSegment) + { // last in segment + // we take the old target of the superarc (in old supernode IDs) and convert it to a new supernode ID + augmentedTree->superarcs[newSupernodeID] = newSupernodeIDs[oldTargetSuperID] | (ascendingSuperarc ? IS_ASCENDING : 0x00); + } // last in segment + else + { // not last in segment + // the target is always the next one, so just store it with the ascending flag + augmentedTree->superarcs[newSupernodeID] = (newSupernodeID+1) | (ascendingSuperarc ? IS_ASCENDING : 0x00); + } // not last in segment + + // first we identify the hyperarc on which the superarc sits + // this will be visible in the old base tree, since hyperstructure carries over + indexType oldHyperparent = baseTree->hyperparents[superparentOldSuperID]; + + // hyperstructure carries over, so we use the same hyperparent as the superparent + augmentedTree->hyperparents [newSupernodeID] = oldHyperparent; + // retrieve the hyperparent's old supernode ID & convert to a new one, then test it + if (newSupernodeIDs[baseTree->hypernodes[oldHyperparent]] == newSupernodeID) + augmentedTree->super2hypernode [newSupernodeID] = oldHyperparent; + else + augmentedTree->super2hypernode [newSupernodeID] = NO_SUCH_ELEMENT; + + // round and iteration are set from the superparent, since we are raising to its level + augmentedTree->whichRound [newSupernodeID] = baseTree->whichRound[superparentOldSuperID]; + augmentedTree->whichIteration [newSupernodeID] = baseTree->whichIteration[superparentOldSuperID]; + // and set the relevant regular arrays + augmentedTree->regularNodeGlobalIDs [newRegularID] = globalRegularIDSet[supernodeSetIndex]; + augmentedTree->dataValues [newRegularID] = dataValueSet[supernodeSetIndex]; + // for all supernodes, this points to itself + augmentedTree->regular2supernode [newRegularID] = newSupernodeID; + // and since we're inserted, so does this + augmentedTree->superparents [newRegularID] = newSupernodeID; + } // raised attachment point or "ordinary" supernodes + + } // per supernode in the set + */ - // copy the global regular ID and data value - augmentedTree->regularNodeGlobalIDs [newRegularID] = globalRegularIDSet[supernodeSetIndex]; - augmentedTree->dataValues [newRegularID] = dataValueSet[supernodeSetIndex]; - // the sort order will be dealt with later - // since all of these nodes are supernodes, they will be their own superparent, which means that: - // a. the regular2node can be set immediately - augmentedTree->regular2supernode [newRegularID] = newSupernodeID; - // b. as can the superparent - augmentedTree->superparents [newRegularID] = newSupernodeID; - } // per supernode in the set - */ } // operator()() private: const vtkm::Id NumSupernodesAlready; const vtkm::Id BaseTreeNumRounds; - const vtkm::Id AugmentedTreeNumIterations; - const vtkm::Id RoundNumber; - const vtkm::Id NumAugmentedTreeSupernodes; + const vtkm::Id NumInsertedSupernodes; + const vtkm::Id RoundNo; }; // CreateSuperarcsWorklet diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h index 27af1e05a4..fbc48c5dec 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h @@ -229,33 +229,32 @@ private: if (this->MeshGlobalSize[2] > 1) // 3D { // convert from global ID to global coords - vtkm::Id globalSliceSize = this->MeshGlobalSize[0] * this->MeshGlobalSize[1]; - vtkm::Id globalSlice = globalId / globalSliceSize; - vtkm::Id globalRow = globalId / this->MeshGlobalSize[0]; - vtkm::Id globalCol = globalId % this->MeshGlobalSize[0]; + vtkm::Id3 pos{ globalId % this->MeshGlobalSize[0], + (globalId / this->MeshGlobalSize[0]) % this->MeshGlobalSize[1], + globalId / (this->MeshGlobalSize[0] * this->MeshGlobalSize[1]) }; // test validity - if (globalSlice < this->MeshBlockOrigin[2]) + if (pos[2] < this->MeshBlockOrigin[2]) { return false; } - if (globalSlice >= this->MeshBlockOrigin[2] + this->MeshBlockSize[2]) + if (pos[2] >= this->MeshBlockOrigin[2] + this->MeshBlockSize[2]) { return false; } - if (globalRow < this->MeshBlockOrigin[1]) + if (pos[1] < this->MeshBlockOrigin[1]) { return false; } - if (globalRow >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1]) + if (pos[1] >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1]) { return false; } - if (globalCol < this->MeshBlockOrigin[0]) + if (pos[0] < this->MeshBlockOrigin[0]) { return false; } - if (globalCol >= this->MeshBlockOrigin[0] + this->MeshBlockSize[0]) + if (pos[0] >= this->MeshBlockOrigin[0] + this->MeshBlockSize[0]) { return false; } @@ -265,23 +264,22 @@ private: else // 2D mesh { // convert from global ID to global coords - vtkm::Id globalRow = globalId / this->MeshGlobalSize[0]; - vtkm::Id globalCol = globalId % this->MeshGlobalSize[0]; + vtkm::Id2 pos{ globalId % this->MeshGlobalSize[0], globalId / this->MeshGlobalSize[0] }; // test validity - if (globalRow < this->MeshBlockOrigin[1]) + if (pos[1] < this->MeshBlockOrigin[1]) { return false; } - if (globalRow >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1]) + if (pos[1] >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1]) { return false; } - if (globalCol < this->MeshBlockOrigin[0]) + if (pos[0] < this->MeshBlockOrigin[0]) { return false; } - if (globalCol >= this->MeshBlockOrigin[0] + this->MeshBlockSize[0]) + if (pos[0] >= this->MeshBlockOrigin[0] + this->MeshBlockSize[0]) { return false; } diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/IsAttachementPointPredicate.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/IsAttachementPointPredicate.h index dd79e4ce7c..a85ccfd949 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/IsAttachementPointPredicate.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/IsAttachementPointPredicate.h @@ -81,27 +81,50 @@ public: const vtkm::worklet::contourtree_augmented::IdArrayType& superarcs, const vtkm::worklet::contourtree_augmented::IdArrayType& whichRound, const vtkm::Id numRounds, + vtkm::worklet::contourtree_augmented::IdArrayType* volumeArray, + vtkm::Id presimplifyThreshold, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) : SuperarcsPortal(superarcs.PrepareForInput(device, token)) , WhichRoundPortal(whichRound.PrepareForInput(device, token)) , NumRounds(numRounds) + , PresimplifyThreshold(presimplifyThreshold) { // constructor + this->Presimplify = ((volumeArray != NULL) && (presimplifyThreshold > 0)); + // If we presimplify then store the volumeArray. Otherwise we don't need to volume array and we + // set it to another portal, just to make sure the variable is being initalized with something + this->VolumeArrayPortal = + this->Presimplify ? volumeArray->PrepareForInput(device, token) : this->WhichRoundPortal; } // constructor // () operator - gets called to do comparison VTKM_EXEC bool operator()(const vtkm::Id& supernode) const { // operator() - return ( - vtkm::worklet::contourtree_augmented::NoSuchElement(this->SuperarcsPortal.Get(supernode)) && - (this->WhichRoundPortal.Get(supernode) < this->NumRounds)); + // an attachment point is defined by having no superarc (NO_SUCH_ELEMENT) and not being in + // the final round (where this indicates the global root) + bool predicate = + (vtkm::worklet::contourtree_augmented::NoSuchElement(this->SuperarcsPortal.Get(supernode)) && + (this->WhichRoundPortal.Get(supernode) < this->NumRounds)); + // if we pass this check then we need to also check that the supernode passes the pre-simplification threshold + if (predicate && this->Presimplify) + { + // suppress if it's volume is at or below the threshold + if (this->VolumeArrayPortal.Get(supernode) <= this->PresimplifyThreshold) + { // below threshold + predicate = false; // do not keep attachement point below the simplification threshold + } // below threshold + } + return predicate; } // operator() private: IdPortalType SuperarcsPortal; IdPortalType WhichRoundPortal; const vtkm::Id NumRounds; + bool Presimplify; + IdPortalType VolumeArrayPortal; + vtkm::Id PresimplifyThreshold; }; // IsAttachementPointPredicateImpl @@ -113,24 +136,35 @@ public: VTKM_CONT IsAttachementPointPredicate(const vtkm::worklet::contourtree_augmented::IdArrayType& superarcs, const vtkm::worklet::contourtree_augmented::IdArrayType& whichRound, - const vtkm::Id numRounds) + const vtkm::Id numRounds, + vtkm::worklet::contourtree_augmented::IdArrayType* volumeArray = NULL, + vtkm::Id presimplifyThreshold = 0) : Superarcs(superarcs) , WhichRound(whichRound) , NumRounds(numRounds) + , VolumeArray(volumeArray) + , PresimplifyThreshold(presimplifyThreshold) { } VTKM_CONT IsAttachementPointPredicateImpl PrepareForExecution(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) const { - return IsAttachementPointPredicateImpl( - this->Superarcs, this->WhichRound, this->NumRounds, device, token); + return IsAttachementPointPredicateImpl(this->Superarcs, + this->WhichRound, + this->NumRounds, + this->VolumeArray, + this->PresimplifyThreshold, + device, + token); } private: vtkm::worklet::contourtree_augmented::IdArrayType Superarcs; vtkm::worklet::contourtree_augmented::IdArrayType WhichRound; const vtkm::Id NumRounds; + vtkm::worklet::contourtree_augmented::IdArrayType* VolumeArray; + vtkm::Id PresimplifyThreshold; }; // IsAttachementPointPredicate } // namespace hierarchical_augmenter diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/ResizeArraysBuildNewSupernodeIdsWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/ResizeArraysBuildNewSupernodeIdsWorklet.h index 0903f85f40..9278548446 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/ResizeArraysBuildNewSupernodeIdsWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/ResizeArraysBuildNewSupernodeIdsWorklet.h @@ -65,12 +65,16 @@ public: /// Control signature for the worklet using ControlSignature = void( FieldIn supernodeIndex, // input domain ArrayHandleIndex(SupernodeSorter.GetNumberOfValues()) + // FieldIn supernodeIdSetPermuted, // input supernodeIDSet permuted by supernodeSorter to allow for FieldIn FieldIn - supernodeIdSetPermuted, // input supernodeIDSet permuted by supernodeSorter to allow for FieldIn + globalRegularIdSet, // input globalRegularIdSet permuted by supernodeSorter to allow for FieldIn + ExecObject findRegularByGlobal, + WholeArrayIn baseTreeRegular2Supernode, WholeArrayInOut newSupernodeIds // output/input (both are necessary since not all valyes will be overwritten) ); - using ExecutionSignature = void(_1, _2, _3); + + using ExecutionSignature = void(_1, _2, _3, _4, _5); using InputDomain = _1; // Default Constructor @@ -80,10 +84,13 @@ public: { } - template + template VTKM_EXEC void operator()( - const vtkm::Id& supernode, // InputIndex of supernodeSorter - const vtkm::Id& oldSupernodeId, // same as supernodeIDSet[supernodeSetIndex]; + const vtkm::Id& supernode, // InputIndex of supernodeSorter + // const vtkm::Id& oldSupernodeId, // same as supernodeIDSet[supernodeSetIndex]; + const vtkm::Id& globalRegularIdSetValue, // same as globalRegularIDSet[supernodeSetIndex] + const ExecObjectType& findRegularByGlobal, + const InFieldPortalType& baseTreeRegular2SupernodePortal, const InOutFieldPortalType& newSupernodeIdsPortal) const { // per supernode @@ -96,6 +103,17 @@ public: // that if it came from another block it will be set to NO_SUCH_ELEMENT // vtkm::Id oldSupernodeId set on input since we use ArrayHandlePermutation to // shuffle supernodeIDSet by supernodeSorter; + // TODO/WARNING: Logic error in that comment for presimplified trees, but not for the original version. See RetrieveOldSupernodes() for why. + + // TODO/WARNING: We substitute a search in the old hierarchical tree for the supernode. If it is present, then we fill in it's entry in the + + // newSupernodeIDs array. If not, we're happy. + vtkm::Id oldRegularId = findRegularByGlobal.FindRegularByGlobal(globalRegularIdSetValue); + vtkm::Id oldSupernodeId = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + if (!vtkm::worklet::contourtree_augmented::NoSuchElement(oldRegularId)) + { + oldSupernodeId = baseTreeRegular2SupernodePortal.Get(oldRegularId); + } // and write to the lookup array if (!vtkm::worklet::contourtree_augmented::NoSuchElement(oldSupernodeId)) diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/UpdateHyperstructureSetSuperchildrenWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/UpdateHyperstructureSetSuperchildrenWorklet.h index c81154f9ef..e25c63f703 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/UpdateHyperstructureSetSuperchildrenWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/UpdateHyperstructureSetSuperchildrenWorklet.h @@ -66,65 +66,82 @@ class UpdateHyperstructureSetSuperchildrenWorklet : public vtkm::worklet::Workle public: /// Control signature for the worklet using ControlSignature = void( - WholeArrayIn augmentedTreeHypernodes, // input (we need both this and the next value) - FieldOut augmentedTreeSuperchildren // output + WholeArrayIn augmentedTreeHypernodes, // input (we need both this and the next value) + FieldIn augmentedTreeSuperarcs, // input + WholeArrayIn augmentedTreeHyperparents, // input + WholeArrayInOut augmentedTreeSuperchildren // output ); - using ExecutionSignature = void(InputIndex, _1, _2); - using InputDomain = _1; + using ExecutionSignature = void(InputIndex, _1, _2, _3, _4); + using InputDomain = _2; // Default Constructor VTKM_EXEC_CONT - UpdateHyperstructureSetSuperchildrenWorklet(const vtkm::Id& augmentedTreeNumSupernodes) + UpdateHyperstructureSetSuperchildrenWorklet(const vtkm::Id& augmentedTreeNumSupernodes, + const vtkm::Id& supernodeStartIndex) : AugmentedTreeNumSupernodes(augmentedTreeNumSupernodes) + , AugmentedTreeSupernodeStartIndex(supernodeStartIndex) { } - template - VTKM_EXEC void operator()( - const vtkm::Id& hypernode, - const InFieldPortalType& augmentedTreeHypernodesPortal, - vtkm::Id& - augmentedTreeSuperchildrenValue // same as augmentedTree->superchildren[InputIndex] = ... - ) const + template + VTKM_EXEC void operator()(const vtkm::Id& supernode, + const InFieldPortalType1& augmentedTreeHypernodesPortal, + const vtkm::Id& augmentedTreeSuperarcsValue, + const InFieldPortalType2& augmentedTreeHyperparentsPortal, + const OutFieldPortalType& augmentedTreeSuperchildrenPortal) const { - // per hypernode - // retrieve the new superId - vtkm::Id superId = augmentedTreeHypernodesPortal.Get(hypernode); - // and the next one over - vtkm::Id nextSuperId; - if (hypernode == augmentedTreeHypernodesPortal.GetNumberOfValues() - 1) + // per supernode + // attachment points have NULL superarcs and are skipped + if (vtkm::worklet::contourtree_augmented::NoSuchElement(augmentedTreeSuperarcsValue)) { - nextSuperId = this->AugmentedTreeNumSupernodes; + return; } - else + // we are now guaranteed to have a valid hyperparent + vtkm::Id hyperparent = augmentedTreeHyperparentsPortal.Get(supernode); + vtkm::Id hyperparentSuperId = augmentedTreeHypernodesPortal.Get(hyperparent); + + // we could be at the end of the array, so test explicitly + if (this->AugmentedTreeSupernodeStartIndex + supernode == this->AugmentedTreeNumSupernodes - 1) { - nextSuperId = augmentedTreeHypernodesPortal.Get(hypernode + 1); + // this means that we are the end of the segment and can subtract the hyperparent's super ID to get the number of superchildren + augmentedTreeSuperchildrenPortal.Set(hyperparent, + this->AugmentedTreeNumSupernodes - hyperparentSuperId); } - // the difference is the number of superchildren - augmentedTreeSuperchildrenValue = nextSuperId - superId; - + // otherwise, if our hyperparent is different from our neighbor's, we are the end of the segment + else if (hyperparent != augmentedTreeHyperparentsPortal.Get(supernode + 1)) + { + // again, subtract to get the number + augmentedTreeSuperchildrenPortal.Set( + hyperparent, supernode + this->AugmentedTreeSupernodeStartIndex + 1 - hyperparentSuperId); + } + // per supernode // In serial this worklet implements the following operation /* - for (vtkm::Id hypernode = 0; hypernode < augmentedTree->hypernodes.size(); hypernode++) - { // per hypernode - // retrieve the new super ID - vtkm::Id superID = augmentedTree->hypernodes[hypernode]; - // and the next one over - vtkm::Id nextSuperID; - if (hypernode == augmentedTree->hypernodes.size() - 1) - nextSuperID = augmentedTree->supernodes.size(); - else - nextSuperID = augmentedTree->hypernodes[hypernode+1]; - // the difference is the number of superchildren - augmentedTree->superchildren[hypernode] = nextSuperID - superID; - } // per hypernode + for (indexType supernode = augmentedTree->firstSupernodePerIteration[roundNo][0]; supernode < augmentedTree->firstSupernodePerIteration[roundNo][augmentedTree->nIterations[roundNo]]; supernode++) + { // per supernode + // attachment points have NULL superarcs and are skipped + if (noSuchElement(augmentedTree->superarcs[supernode])) + continue; + // we are now guaranteed to have a valid hyperparent + indexType hyperparent = augmentedTree->hyperparents[supernode]; + indexType hyperparentSuperID = augmentedTree->hypernodes[hyperparent]; + // we could be at the end of the array, so test explicitly + if (supernode == augmentedTree->supernodes.size() - 1) + // this means that we are the end of the segment and can subtract the hyperparent's super ID to get the number of superchildren + augmentedTree->superchildren[hyperparent] = augmentedTree->supernodes.size() - hyperparentSuperID; + // otherwise, if our hyperparent is different from our neighbor's, we are the end of the segment + else if (hyperparent != augmentedTree->hyperparents[supernode + 1]) + // again, subtract to get the number + augmentedTree->superchildren[hyperparent] = supernode + 1 - hyperparentSuperID; + } // per supernode */ } // operator()() private: const vtkm::Id AugmentedTreeNumSupernodes; + const vtkm::Id AugmentedTreeSupernodeStartIndex; }; // UpdateHyperstructureSetSuperchildrenWorklet } // namespace hierarchical_augmenter diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/CMakeLists.txt b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/CMakeLists.txt index eb302fd40c..18b089ddb4 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/CMakeLists.txt +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/CMakeLists.txt @@ -17,6 +17,8 @@ set(headers TransferTargetComperator.h TransferWeightsUpdateRHEWorklet.h TransferWeightsUpdateLHEWorklet.h + TransferWeightsUpdateRHEWorkletRound2.h + TransferWeightsUpdateLHEWorkletRound2.h ) vtkm_declare_headers(${headers}) diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/ComputeSuperarcTransferWeightsWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/ComputeSuperarcTransferWeightsWorklet.h index b885cb1162..93882ac2b5 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/ComputeSuperarcTransferWeightsWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/ComputeSuperarcTransferWeightsWorklet.h @@ -116,7 +116,8 @@ public: else { // attachment point // set the transfer target - transferTarget = hierarchicalTreeSuperparentsPortal.Get(supernodeRegularId); + transferTarget = hierarchicalTreeSuperparentsPortal.Get(supernodeRegularId) | + vtkm::worklet::contourtree_augmented::TRANSFER_TO_SUPERARC; } // attachment point } // null superarc else diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferTargetComperator.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferTargetComperator.h index 2f703c9816..7fd4358ae9 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferTargetComperator.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferTargetComperator.h @@ -89,13 +89,22 @@ public: // while the index time in PPP is unsigned. Thus, for PPP "regular" indices are always // smaller that NO_SUCH_ELEMENT, while with the signed vtkm::Id, NO_SUCH_ELEMENT is // negative and the order is not as intented. - // TODO/FIXME: Verify this implementation is correct. - // TODO/FIXME: Is there a better way to do this? auto leftVal = this->SuperarcPortal.Get(left); auto rightVal = this->SuperarcPortal.Get(right); + + // Elements with TRANSFER_TO_SUPERARC as a heading flag should have higher "value order" than other elements without it. + // Although TRANSFER_TO_SUPERARC is positive, a safer way to do this is to extract the heading indicator explicitly. + bool isLeftTransferTo = vtkm::worklet::contourtree_augmented::TransferToSuperarc(leftVal); + bool isRightTransferTo = vtkm::worklet::contourtree_augmented::TransferToSuperarc(rightVal); + + // If both elements are NO_SUCH_ELEMENT, we compare the index order to guarantee a determined order + // It is not necessary to enforce the order, but a fixed order is beneficial for debug purposes if (vtkm::worklet::contourtree_augmented::NoSuchElement(leftVal)) { - return false; + if (vtkm::worklet::contourtree_augmented::NoSuchElement(rightVal)) + return left < right; + else + return false; } else if (vtkm::worklet::contourtree_augmented::NoSuchElement(rightVal)) { @@ -103,8 +112,21 @@ public: } else { - return vtkm::worklet::contourtree_augmented::MaskedIndex(leftVal) < - vtkm::worklet::contourtree_augmented::MaskedIndex(rightVal); + if (isLeftTransferTo && !isRightTransferTo) + return false; + else if (!isLeftTransferTo && isRightTransferTo) + return true; + else + { + vtkm::Id leftMaskedVal = vtkm::worklet::contourtree_augmented::MaskedIndex(leftVal); + vtkm::Id rightMaskedVal = vtkm::worklet::contourtree_augmented::MaskedIndex(rightVal); + if (leftMaskedVal < rightMaskedVal) + return true; + if (leftMaskedVal > rightMaskedVal) + return false; + // tiebreaker using the ID for debug purpose + return left < right; + } } } // operator() diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateLHEWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateLHEWorklet.h index 0d5c1ca981..86c8cb8588 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateLHEWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateLHEWorklet.h @@ -74,7 +74,7 @@ public: FieldIn sortedTransferTargetShiftedView, FieldIn valuePrefixSumShiftedView, WholeArrayInOut dependentValuesPortal); - using ExecutionSgnature = void(_1, _2, _3, _4); + using ExecutionSignature = void(_1, _2, _3, _4); template VTKM_EXEC void operator()(const vtkm::Id& sortedTransferTargetValue, @@ -88,30 +88,27 @@ public: { return; } - if (sortedTransferTargetValue != sortedTransferTargetPreviousValue) - { - auto originalValue = dependentValuesPortal.Get(sortedTransferTargetValue); - dependentValuesPortal.Set(sortedTransferTargetValue, - originalValue - valuePrefixSumPreviousValue); - } + // we need to separate out the flag for attachment points + bool superarcTransfer = + vtkm::worklet::contourtree_augmented::TransferToSuperarc(sortedTransferTargetValue); + vtkm::Id superarcOrNodeId = + vtkm::worklet::contourtree_augmented::MaskedIndex(sortedTransferTargetValue); - // In serial this worklet implements the following operation - /* - for (vtkm::Id supernode = firstSupernode + 1; supernode < lastSupernode; supernode++) - { // per supernode - // ignore any that point at NO_SUCH_ELEMENT - if (noSuchElement(sortedTransferTarget[supernode])) - continue; + // ignore the transfers for attachment points + if (superarcTransfer) + { + return; + } - // the LHE at 0 is special - it subtracts zero. In practice, since NO_SUCH_ELEMENT will sort low, this will never - // occur, but let's keep the logic strict - if (sortedTransferTarget[supernode] != sortedTransferTarget[supernode-1]) - { // LHE not 0 - dependentValues[sortedTransferTarget[supernode]] -= valuePrefixSum[supernode-1]; - } // LHE not 0 - } // per supernode - */ + // the LHE at 0 is special - it subtracts zero. In practice, since NO_SUCH_ELEMENT will sort low, this will never + // occur, but let's keep the logic strict + auto originalValue = dependentValuesPortal.Get(superarcOrNodeId); + // Outside the worklet, we have excluded the condition where supernode == firstSupernode using shift + if (sortedTransferTargetValue != sortedTransferTargetPreviousValue) + { // LHE not 0 + dependentValuesPortal.Set(superarcOrNodeId, originalValue - valuePrefixSumPreviousValue); + } } // operator()() }; // TransferWeightsUpdateLHEWorklet diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateLHEWorkletRound2.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateLHEWorkletRound2.h new file mode 100644 index 0000000000..9a0eade302 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateLHEWorkletRound2.h @@ -0,0 +1,126 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_hyper_sweeper_transfer_weights_update_lhe_worklet_round2_h +#define vtk_m_worklet_contourtree_distributed_hierarchical_hyper_sweeper_transfer_weights_update_lhe_worklet_round2_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace contourtree_distributed +{ +namespace hierarchical_hyper_sweeper +{ + +/// Worklet used in HierarchicalHyperSweeper.TransferWeights(...) to implement +/// step 7b. Now find the LHE of each group and subtract out the prior weight +class TransferWeightsUpdateLHEWorkletRound2 : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void(FieldIn sortedTransferTargetPortal, + FieldIn sortedTransferTargetShiftedView, + FieldIn valuePrefixSumShiftedView, + WholeArrayInOut intrinsicValues, + WholeArrayInOut dependentValues); + using ExecutionSignature = void(_1, _2, _3, _4, _5); + + template + VTKM_EXEC void operator()(const vtkm::Id& sortedTransferTargetValue, + const vtkm::Id& sortedTransferTargetPreviousValue, + const vtkm::Id& valuePrefixSumPreviousValue, + InOutPortalType& intrinsicValuesPortal, + InOutPortalType& dependentValuesPortal) const + { + // per supernode + // ignore any that point at NO_SUCH_ELEMENT + if (vtkm::worklet::contourtree_augmented::NoSuchElement(sortedTransferTargetValue)) + { + return; + } + + // we need to separate out the flag for attachment points + bool superarcTransfer = + vtkm::worklet::contourtree_augmented::TransferToSuperarc(sortedTransferTargetValue); + vtkm::Id superarcOrNodeId = + vtkm::worklet::contourtree_augmented::MaskedIndex(sortedTransferTargetValue); + + // ignore the transfers for attachment points + if (!superarcTransfer) + { + return; + } + + // the LHE at 0 is special - it subtracts zero. In practice, since NO_SUCH_ELEMENT will sort low, this will never + // occur, but let's keep the logic strict + auto originalIntrinsicValue = intrinsicValuesPortal.Get(superarcOrNodeId); + auto originalDependentValue = dependentValuesPortal.Get(superarcOrNodeId); + // Outside the worklet, we have excluded the condition where supernode == firstSupernode using shift + if (sortedTransferTargetValue != sortedTransferTargetPreviousValue) + { // LHE not 0 + intrinsicValuesPortal.Set(superarcOrNodeId, + originalIntrinsicValue - valuePrefixSumPreviousValue); + dependentValuesPortal.Set(superarcOrNodeId, + originalDependentValue - valuePrefixSumPreviousValue); + } + } // operator()() +}; // TransferWeightsUpdateLHEWorklet + +} // namespace hierarchical_hyper_sweeper +} // namespace contourtree_distributed +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateRHEWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateRHEWorklet.h index f1265b88a3..5c33c288c6 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateRHEWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateRHEWorklet.h @@ -100,8 +100,18 @@ public: if ((supernode == this->LastSupernode - 1) || (transferTarget != sortedTransferTargetPortal.Get(supernode + 1))) { // RHE of segment - auto originalValue = dependentValuesPortal.Get(transferTarget); - dependentValuesPortal.Set(transferTarget, originalValue + valuePrefixSum); + // we need to separate out the flag for attachment points + bool superarcTransfer = + vtkm::worklet::contourtree_augmented::TransferToSuperarc(transferTarget); + vtkm::Id superarcOrNodeId = + vtkm::worklet::contourtree_augmented::MaskedIndex(transferTarget); + // we ignore attachment points + if (superarcTransfer) + { + return; + } + auto originalValue = dependentValuesPortal.Get(superarcOrNodeId); + dependentValuesPortal.Set(superarcOrNodeId, originalValue + valuePrefixSum); } // RHE of segment } @@ -113,10 +123,19 @@ public: if (noSuchElement(sortedTransferTarget[supernode])) continue; - // the RHE of each segment transfers its weight (including all irrelevant prefixes) if ((supernode == lastSupernode - 1) || (sortedTransferTarget[supernode] != sortedTransferTarget[supernode+1])) { // RHE of segment - dependentValues[sortedTransferTarget[supernode]] += valuePrefixSum[supernode]; + // WARNING 11/07/2023 + // we need to separate out the flag for attachment points + bool superarcTransfer = transferToSuperarc(sortedTransferTarget[supernode]); + indexType superarcOrNodeID = maskedIndex(sortedTransferTarget[supernode]); + + // we ignore attachment points + if (superarcTransfer) + continue; + + // transfer as dependent weight + dependentValues[superarcOrNodeID] += valuePrefixSum[supernode]; } // RHE of segment } // per supernode */ diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateRHEWorkletRound2.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateRHEWorkletRound2.h new file mode 100644 index 0000000000..12b196bf8d --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/TransferWeightsUpdateRHEWorkletRound2.h @@ -0,0 +1,160 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_hyper_sweeper_transfer_weights_update_rhe_worklet_round2_h +#define vtk_m_worklet_contourtree_distributed_hierarchical_hyper_sweeper_transfer_weights_update_rhe_worklet_round2_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace contourtree_distributed +{ +namespace hierarchical_hyper_sweeper +{ + +/// Worklet used in HierarchicalHyperSweeper.TransferWeights(...) to implement +/// step 7a. Find the RHE of each group and transfer the prefix sum weight. +/// Note that we do not compute the transfer weight separately, we add it in place instead +class TransferWeightsUpdateRHEWorkletRound2 : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = + void(FieldIn supernodeIndex, // input counting array [firstSupernode, lastSupernode) + WholeArrayIn sortedTransferTarget, + FieldIn valuePrefixSumView, // input view of valuePrefixSum[firstSupernode, lastSupernode) + WholeArrayInOut intrinsicValuesPortal, + WholeArrayInOut dependentValuesPortal); + using ExecutionSignature = void(_1, _2, _3, _4, _5); + + // Default Constructor + VTKM_EXEC_CONT + TransferWeightsUpdateRHEWorkletRound2(const vtkm::Id& lastSupernode) + : LastSupernode(lastSupernode) + { + } + + template + VTKM_EXEC void operator()(const vtkm::Id& supernode, + const InPortalType& sortedTransferTargetPortal, + const vtkm::Id& valuePrefixSum, // same as valuePrefixSum[supernode] + OutPortalType& intrinsicValuesPortal, + OutPortalType& dependentValuesPortal) const + { + // per supernode + // ignore any that point at NO_SUCH_ELEMENT + vtkm::Id transferTarget = sortedTransferTargetPortal.Get(supernode); + if (!vtkm::worklet::contourtree_augmented::NoSuchElement(transferTarget)) + { + // the RHE of each segment transfers its weight (including all irrelevant prefixes) + if ((supernode == this->LastSupernode - 1) || + (transferTarget != sortedTransferTargetPortal.Get(supernode + 1))) + { // RHE of segment + // we need to separate out the flag for attachment points + bool superarcTransfer = + vtkm::worklet::contourtree_augmented::TransferToSuperarc(transferTarget); + vtkm::Id superarcOrNodeId = + vtkm::worklet::contourtree_augmented::MaskedIndex(transferTarget); + // we ignore attachment points + if (!superarcTransfer) + { + return; + } + // we modify both intrinsic and dependent values + auto originalIntrinsicValue = intrinsicValuesPortal.Get(superarcOrNodeId); + intrinsicValuesPortal.Set(superarcOrNodeId, originalIntrinsicValue + valuePrefixSum); + auto originalDependentValue = dependentValuesPortal.Get(superarcOrNodeId); + dependentValuesPortal.Set(superarcOrNodeId, originalDependentValue + valuePrefixSum); + } // RHE of segment + } + + // In serial this worklet implements the following operation + /* + for (indexType supernode = firstSupernode; supernode < lastSupernode; supernode++) + { // per supernode + // ignore any that point at NO_SUCH_ELEMENT + if (noSuchElement(sortedTransferTarget[supernode])) + continue; + + // the RHE of each segment transfers its weight (including all irrelevant prefixes) + if ((supernode == lastSupernode - 1) || (sortedTransferTarget[supernode] != sortedTransferTarget[supernode+1])) + { // RHE of segment + // we need to separate out the flag for attachment points + bool superarcTransfer = transferToSuperarc(sortedTransferTarget[supernode]); + indexType superarcOrNodeID = maskedIndex(sortedTransferTarget[supernode]); + // ignore the transfers for non-attachment points + if (!superarcTransfer) + continue; + + // we modify both intrinsic and dependent values + intrinsicValues[superarcOrNodeID] += valuePrefixSum[supernode]; + dependentValues[superarcOrNodeID] += valuePrefixSum[supernode]; + } // RHE of segment + + } // per supernode + */ + } // operator()() + +private: + const vtkm::Id LastSupernode; + +}; // TransferWeightsUpdateRHEWorklet + +} // namespace hierarchical_hyper_sweeper +} // namespace contourtree_distributed +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h index 4d808f8edf..6d79508862 100644 --- a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h @@ -113,13 +113,13 @@ public: if (isLowerLeaf) return contourtree_augmented::IsAscending(upperDirection) ? upperDependent - : totalVolume - upperDependent + upperIntrinsic; + : totalVolume - upperDependent + upperIntrinsic - 1; // if the branch is a maximum-saddle branch // if the lower end superarc direction is pointing down, then true; otherwise, false if (isUpperLeaf) return !contourtree_augmented::IsAscending(lowerDirection) ? lowerDependent - : totalVolume - lowerDependent + lowerIntrinsic; + : totalVolume - lowerDependent + lowerIntrinsic - 1; // in case of fallout, should never reach return 0; -- GitLab From 8f3e86170d114dfb83860deccb450843cbb2b56f Mon Sep 17 00:00:00 2001 From: "Gunther H. Weber" Date: Thu, 26 Sep 2024 18:43:21 -0700 Subject: [PATCH 24/73] Minor refactoring of tests for contour tree. --- ...stingContourTreeUniformDistributedFilter.h | 95 +++++++++++-------- ...estContourTreeUniformDistributedFilter.cxx | 23 +---- ...stDistributedBranchDecompositionFilter.cxx | 7 ++ 3 files changed, 64 insertions(+), 61 deletions(-) diff --git a/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h b/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h index 01e9321e55..a1f3513bfa 100644 --- a/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h +++ b/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h @@ -1051,19 +1051,18 @@ inline void RunContourTreePresimplification(std::string fieldName, tp_result = tp_filter.Execute(bd_result); } -// routine to test contour tree presimplification -inline void TestContourTreePresimplification( - std::string datasetName, - std::string fieldName, - std::string gtbr_filename, - int nBlocks, - vtkm::cont::DataSet input_ds, // dataset if we pre-load the dataset - std::string ds_filename = "", // dataset file name if we load data from file - const vtkm::Id presimplifyThreshold = 1, - bool marchingCubes = false, - int rank = 0, - int size = 1, - bool passBlockIndices = true) +// routine to test contour tree presimplification when data set is +// already in memory +inline void TestContourTreePresimplification(std::string datasetName, + std::string fieldName, + std::string gtbr_filename, + int nBlocks, + vtkm::cont::DataSet input_ds, + const vtkm::Id presimplifyThreshold = 1, + bool marchingCubes = false, + int rank = 0, + int size = 1, + bool passBlockIndices = true) { if (rank == 0) { @@ -1074,37 +1073,10 @@ inline void TestContourTreePresimplification( << std::endl; } - // data set should either be loaded from file or passed as input. - // if we get a ds_filename, we load the dataset from file, which means that input_ds becomes useless. - // if the ds_filename is empty, we use input_ds. - // TODO/FIXME: is there a better way to do this? - vtkm::cont::DataSet ds; - if (!ds_filename.empty()) - { - if (rank == 0) - std::cout << "Loading data from " << ds_filename << std::endl; - vtkm::io::VTKDataSetReader reader(ds_filename); - try - { - ds = reader.ReadDataSet(); - } - catch (vtkm::io::ErrorIO& e) - { - std::string message("Error reading: "); - message += ds_filename; - message += ", "; - message += e.GetMessage(); - - VTKM_TEST_FAIL(message.c_str()); - } - } - else - ds = input_ds; - // get the output of contour tree + presimplification vtkm::cont::PartitionedDataSet tp_result; RunContourTreePresimplification(fieldName, - ds, + input_ds, tp_result, nBlocks, marchingCubes, @@ -1135,6 +1107,47 @@ inline void TestContourTreePresimplification( } } +inline void TestContourTreePresimplification(std::string datasetName, + std::string fieldName, + std::string gtbr_filename, + int nBlocks, + std::string ds_filename, // dataset file name + const vtkm::Id presimplifyThreshold = 1, + bool marchingCubes = false, + int rank = 0, + int size = 1, + bool passBlockIndices = true) +{ + vtkm::cont::DataSet ds; + if (rank == 0) + std::cout << "Loading data from " << ds_filename << std::endl; + vtkm::io::VTKDataSetReader reader(ds_filename); + try + { + ds = reader.ReadDataSet(); + } + catch (vtkm::io::ErrorIO& e) + { + std::string message("Error reading: "); + message += ds_filename; + message += ", "; + message += e.GetMessage(); + + VTKM_TEST_FAIL(message.c_str()); + } + + TestContourTreePresimplification(datasetName, + fieldName, + gtbr_filename, + nBlocks, + ds, + presimplifyThreshold, + marchingCubes, + rank, + size, + passBlockIndices); +} + } } } diff --git a/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx b/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx index bf70052ac6..316e930dc0 100644 --- a/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx +++ b/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx @@ -59,8 +59,6 @@ using vtkm::filter::testing::contourtree_uniform_distributed::TestContourTreePre using vtkm::filter::testing::contourtree_uniform_distributed:: TestContourTreeUniformDistributed5x6x7; using vtkm::filter::testing::contourtree_uniform_distributed::TestContourTreeUniformDistributed8x9; -using vtkm::filter::testing::contourtree_uniform_distributed:: - TestContourTreeUniformDistributedBranchDecomposition8x9; class TestContourTreeUniformDistributedFilter { @@ -74,11 +72,6 @@ public: TestContourTreeUniformDistributed8x9(8); TestContourTreeUniformDistributed8x9(16); - TestContourTreeUniformDistributedBranchDecomposition8x9(2); - TestContourTreeUniformDistributedBranchDecomposition8x9(4); - TestContourTreeUniformDistributedBranchDecomposition8x9(8); - TestContourTreeUniformDistributedBranchDecomposition8x9(16); - TestContourTreeUniformDistributed5x6x7(2, false); TestContourTreeUniformDistributed5x6x7(4, false); TestContourTreeUniformDistributed5x6x7(8, false); @@ -137,9 +130,8 @@ public: "vanc", // dataset name "var", // field name vtkm::cont::testing::Testing::RegressionImagePath( - "vanc.presimplification.ct_txt"), // ground truth file name - 2, // nBlocks - vtkm::cont::DataSet(), // empty, just to satisfy the function input format + "vanc.presimplification.ct_txt"), // ground truth file name + 2, // nBlocks vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), // dataset file path 1 // presimplifyThreshold ); @@ -148,7 +140,6 @@ public: "var", vtkm::cont::testing::Testing::RegressionImagePath("vanc.presimplification.ct_txt"), 4, - vtkm::cont::DataSet(), vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), 1); TestContourTreePresimplification( @@ -156,7 +147,6 @@ public: "var", vtkm::cont::testing::Testing::RegressionImagePath("vanc.presimplification.ct_txt"), 2, - vtkm::cont::DataSet(), vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), 4); TestContourTreePresimplification( @@ -164,7 +154,6 @@ public: "var", vtkm::cont::testing::Testing::RegressionImagePath("vanc.presimplification.ct_txt"), 4, - vtkm::cont::DataSet(), vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), 4); @@ -176,8 +165,7 @@ public: "5x6x7.presimplification.ct_txt"), // ground truth file name 2, // nBlocks vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), // dataset preset, - "", // empty dataset filename, just to satisfy the function format - 2 // presimplifyThreshold + 2 // presimplifyThreshold ); TestContourTreePresimplification( "5x6x7", @@ -185,7 +173,6 @@ public: vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), 4, vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), - "", 2); TestContourTreePresimplification( "5x6x7", @@ -193,7 +180,6 @@ public: vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), 8, vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), - "", 2); TestContourTreePresimplification( "5x6x7", @@ -201,7 +187,6 @@ public: vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), 2, vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), - "", 4); TestContourTreePresimplification( "5x6x7", @@ -209,7 +194,6 @@ public: vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), 4, vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), - "", 4); TestContourTreePresimplification( "5x6x7", @@ -217,7 +201,6 @@ public: vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), 8, vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), - "", 4); } }; diff --git a/vtkm/filter/scalar_topology/testing/UnitTestDistributedBranchDecompositionFilter.cxx b/vtkm/filter/scalar_topology/testing/UnitTestDistributedBranchDecompositionFilter.cxx index 9f9cd3e190..fb750175eb 100644 --- a/vtkm/filter/scalar_topology/testing/UnitTestDistributedBranchDecompositionFilter.cxx +++ b/vtkm/filter/scalar_topology/testing/UnitTestDistributedBranchDecompositionFilter.cxx @@ -56,12 +56,19 @@ namespace { using vtkm::cont::testing::Testing; using vtkm::filter::testing::contourtree_uniform_distributed::TestContourTreeFile; +using vtkm::filter::testing::contourtree_uniform_distributed:: + TestContourTreeUniformDistributedBranchDecomposition8x9; class TestDistributedBranchDecompositionFilter { public: void operator()() const { + TestContourTreeUniformDistributedBranchDecomposition8x9(2); + TestContourTreeUniformDistributedBranchDecomposition8x9(4); + TestContourTreeUniformDistributedBranchDecomposition8x9(8); + TestContourTreeUniformDistributedBranchDecomposition8x9(16); + TestContourTreeFile(Testing::DataPath("rectilinear/vanc.vtk"), "var", Testing::RegressionImagePath("vanc.branch_compile.ct_txt"), -- GitLab From f5a95046a8e0e5ec0f64300cba617e9d64cbcef5 Mon Sep 17 00:00:00 2001 From: "Gunther H. Weber" Date: Fri, 27 Sep 2024 12:08:27 -0700 Subject: [PATCH 25/73] Disabled a few contour tree tests to reduce time needed --- ...estContourTreeUniformDistributedFilter.cxx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx b/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx index 316e930dc0..180280f58c 100644 --- a/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx +++ b/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx @@ -50,6 +50,13 @@ // Oliver Ruebel (LBNL) //============================================================================== +// NOTE: To save test time, we reduced test coverage on September 2th 2024. The +// tests still running should be sufficient to uncovoer any issues due to VTK-m +// changes. However, if we continue development of the contour tree algorithm, +// we should re-enable the additional tests as they have revealed problems in +// the past. +// #define ENABLE_ADDITIONAL_TESTS + #include "TestingContourTreeUniformDistributedFilter.h" namespace @@ -66,20 +73,27 @@ public: void operator()() const { using vtkm::cont::testing::Testing; +#ifdef ENABLE_ADDITIONAL_TESTS TestContourTreeUniformDistributed8x9(2); // TestContourTreeUniformDistributed8x9(3); CRASH??? TestContourTreeUniformDistributed8x9(4); +#endif TestContourTreeUniformDistributed8x9(8); TestContourTreeUniformDistributed8x9(16); +#ifdef ENABLE_ADDITIONAL_TESTS TestContourTreeUniformDistributed5x6x7(2, false); TestContourTreeUniformDistributed5x6x7(4, false); +#endif TestContourTreeUniformDistributed5x6x7(8, false); TestContourTreeUniformDistributed5x6x7(16, false); +#ifdef ENABLE_ADDITIONAL_TESTS TestContourTreeUniformDistributed5x6x7(2, true); TestContourTreeUniformDistributed5x6x7(4, true); +#endif TestContourTreeUniformDistributed5x6x7(8, true); TestContourTreeUniformDistributed5x6x7(16, true); +#ifdef ENABLE_ADDITIONAL_TESTS TestContourTreeFile(Testing::DataPath("rectilinear/vanc.vtk"), "var", Testing::RegressionImagePath("vanc.ct_txt"), @@ -88,6 +102,7 @@ public: "var", Testing::RegressionImagePath("vanc.ct_txt"), 4); +#endif TestContourTreeFile(Testing::DataPath("rectilinear/vanc.vtk"), "var", Testing::RegressionImagePath("vanc.ct_txt"), @@ -126,6 +141,7 @@ public: false); // tests for contour tree presimplification on 2D vanc dataset +#ifdef ENABLE_ADDITIONAL_TESTS TestContourTreePresimplification( "vanc", // dataset name "var", // field name @@ -135,6 +151,7 @@ public: vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), // dataset file path 1 // presimplifyThreshold ); +#endif TestContourTreePresimplification( "vanc", "var", @@ -142,6 +159,7 @@ public: 4, vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), 1); +#ifdef ENABLE_ADDITIONAL_TESTS TestContourTreePresimplification( "vanc", "var", @@ -149,6 +167,7 @@ public: 2, vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), 4); +#endif TestContourTreePresimplification( "vanc", "var", @@ -158,6 +177,7 @@ public: 4); // test for contour tree presimplification on 3D 5x6x7 dataset +#ifdef ENABLE_ADDITIONAL_TESTS TestContourTreePresimplification( "5x6x7", // dataset name "pointvar", // field name @@ -174,6 +194,7 @@ public: 4, vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), 2); +#endif TestContourTreePresimplification( "5x6x7", "pointvar", @@ -181,6 +202,7 @@ public: 8, vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), 2); +#ifdef ENABLE_ADDITIONAL_TESTS TestContourTreePresimplification( "5x6x7", "pointvar", @@ -195,6 +217,7 @@ public: 4, vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), 4); +#endif TestContourTreePresimplification( "5x6x7", "pointvar", -- GitLab From 010544279ebe4a1390318167657603e302d07edb Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 24 Sep 2024 10:15:03 -0400 Subject: [PATCH 26/73] Add documentation for all of the DataSet methods --- docs/users-guide/dataset.rst | 225 +++++++++++++++++- docs/users-guide/examples/CMakeLists.txt | 1 + .../examples/GuideExampleDataSetCreation.cxx | 72 ++++++ .../examples/GuideExampleFields.cxx | 121 ++++++++++ docs/users-guide/provided-filters.rst | 12 +- vtkm/CellClassification.h | 26 ++ vtkm/exec/BoundaryState.h | 8 +- 7 files changed, 451 insertions(+), 14 deletions(-) create mode 100644 docs/users-guide/examples/GuideExampleFields.cxx diff --git a/docs/users-guide/dataset.rst b/docs/users-guide/dataset.rst index a4f53d3f41..d518af78df 100644 --- a/docs/users-guide/dataset.rst +++ b/docs/users-guide/dataset.rst @@ -18,6 +18,19 @@ In addition to the base :class:`vtkm::cont::DataSet`, |VTKm| provides :class:`vt A :class:`vtkm::cont::PartitionedDataSet` is implemented as a collection of :class:`vtkm::cont::DataSet` objects. Partitioned data sets are described later in :secref:`dataset:Partitioned Data Sets`. +As will be seen throughout this chapter, there is a lot of variability in the structure that can be represented in a :class:`vtkm::cont::DataSet`. +To get a human-readable synopsis of its contents, use the :func:`vtkm::cont::DataSet::PrintSummary` method. +This is particularly helpful for debugging. + +.. doxygenfunction:: vtkm::cont::DataSet::PrintSummary + +The :func:`vtkm::cont::DataSet::PrintSummary` method takes a C++ output stream to direct the description. +Usually ``std::cout`` is provided to direct the output to the terminal. + +.. load-example:: DataSetPrintSummary + :file: GuideExampleDataSetCreation.cxx + :caption: Printing a summary of a :class:`vtkm::cont::DataSet`. + ------------------------------ Building Data Sets @@ -258,6 +271,27 @@ The following (somewhat contrived) example defines fields for a uniform grid tha :file: GuideExampleDataSetCreation.cxx :caption: Adding fields to a :class:`vtkm::cont::DataSet`. +Copying Data Sets +============================== + +It is sometimes the case where you want to derive one :class:`vtkm::cont::DataSet` from another. +In this case, you might need to copy the information from one object to another. +To copy all the information from one :class:`vtkm::cont::DataSet` to another, simply use the assignment operator. + +.. load-example:: DataSetCopyOperator + :file: GuideExampleDataSetCreation.cxx + :caption: Copying a :class:`vtkm::cont::DataSet` with the copy operator. + +Sometimes it is desirable to copy the structure of a :class:`vtkm::cont::DataSet` without copying the entire data. +That is, you wish to use the same geometry but have different information about the physical properties. +This can be done with the :func:`vtkm::cont::DataSet::CopyStructure` method. + +.. doxygenfunction:: vtkm::cont::DataSet::CopyStructure + +.. load-example:: DataSetCopyStructure + :file: GuideExampleDataSetCreation.cxx + :caption: Copying the structure of a :class:`vtkm::cont::DataSet`. + ------------------------------ Cell Sets @@ -276,20 +310,33 @@ A cell set determines the topological structure of the data in a data set. .. doxygenclass:: vtkm::cont::CellSet :members: +A :class:`vtkm::cont::DataSet` holds a :class:`vtkm::cont::CellSet` structure to define the cells it contains. +This cell set can be set or retrieved from a :class:`vtkm::cont::DataSet` object. + +.. doxygenfunction:: vtkm::cont::DataSet::SetCellSet +.. doxygenfunction:: vtkm::cont::DataSet::GetCellSet() +.. doxygenfunction:: vtkm::cont::DataSet::GetCellSet() const + +Cell sets are returned from a data set wrapped in a :class:`vtkm::cont::UnknownCellSet`, which is documented in :secref:`dataset:Unknown Cell Sets`. + 3D cells are made up of *points*, *edges*, and *faces*. (2D cells have only points and edges, and 1D cells have only points.) :numref:`fig:CellTopology` shows the relationship between a cell's shape and these topological elements. The arrangement of these points, edges, and faces is defined by the *shape* of the cell, which prescribes a specific ordering of each. The basic cell shapes provided by |VTKm| are discussed in detail in :chapref:`working-with-cells:Working with Cells`. -.. todo:: Add cell shape reference above. - .. figure:: images/CellConstituents.png :width: 50% :name: fig:CellTopology The relationship between a cell shape and its topological elements (points, edges, and faces). +The number of points and cells can be retrieved from the :func:`vtkm::cont::CellSet::GetNumberOfPoints` and :func:`vtkm::cont::CellSet::GetNumberOfCells` methods, respectively. +The :class:`vtkm::cont::DataSet` class contains convenience methods to get the number of points or cells without retrieving the cell set. + +.. doxygenfunction:: vtkm::cont::DataSet::GetNumberOfPoints +.. doxygenfunction:: vtkm::cont::DataSet::GetNumberOfCells + There are multiple ways to express the connections of a cell set, each with different benefits and restrictions. These different cell set types are managed by different cell set classes in |VTKm|. All |VTKm| cell set classes @@ -453,6 +500,9 @@ Fields are often used to describe physical properties such as pressure, temperat Fields are represented in a |VTKm| data set as an array where each value is associated with a particular element type of a mesh (such as points or cells). This association of field values to mesh elements and the structure of the cell set determines how the field is interpolated throughout the space of the mesh. +Field Class +============================== + Fields are manged by the :class:`vtkm::cont::Field` class. .. doxygenclass:: vtkm::cont::Field @@ -504,6 +554,47 @@ The :class:`vtkm::cont::Field` class also has several convenience methods for qu The :class:`vtkm::cont::UnknownArrayHandle` then has to be converted to a :class:`vtkm::cont::ArrayHandle` of the proper type as described in :chapref:`unknown-array-handle:Unknown Array Handles`. Once the proper :class:`vtkm::cont::ArrayHandle` is retrieved, the data can finally be accessed through an array portal as described in :secref:`basic-array-handles:Array Portals`. +Managing Data Set Fields +============================== + +:secref:`dataset:Add Fields` describes the convenient :func:`vtkm::cont::DataSet::AddPointField` and :func:`vtkm::cont::DataSet::AddCellField` methods for adding fields to a :class:`vtkm::cont::DataSet` from an array. +Fields can be added more generally by passing a :class:`vtkm::cont::Field` object or by providing a :enum:`vtkm::cont::Field::Association`. + +.. doxygenfunction:: vtkm::cont::DataSet::AddField(const Field&) +.. doxygenfunction:: vtkm::cont::DataSet::AddField(const std::string&, vtkm::cont::Field::Association, const vtkm::cont::UnknownArrayHandle&) + +A :class:`vtkm::cont::Field` can be retrieved from a :class:`vtkm::cont::DataSet` by name and an optional association. + +.. doxygenfunction:: vtkm::cont::DataSet::GetField(const std::string&, vtkm::cont::Field::Association) const +.. doxygenfunction:: vtkm::cont::DataSet::GetField(const std::string&, vtkm::cont::Field::Association) +.. doxygenfunction:: vtkm::cont::DataSet::GetPointField(const std::string&) const +.. doxygenfunction:: vtkm::cont::DataSet::GetPointField(const std::string&) +.. doxygenfunction:: vtkm::cont::DataSet::GetCellField(const std::string&) const +.. doxygenfunction:: vtkm::cont::DataSet::GetCellField(const std::string&) + +The number of fields in a :class:`vtkm::cont::DataSet` is returned by :func:`vtkm::cont::DataSet::GetNumberOfFields`. + +.. doxygenfunction:: vtkm::cont::DataSet::GetNumberOfFields + +It is possible to iterate over all fields of a :class:`vtkm::cont::DataSet` by quering the number of fields and then retrieving the fields by index. + +.. doxygenfunction:: vtkm::cont::DataSet::GetField(vtkm::Id) const +.. doxygenfunction:: vtkm::cont::DataSet::GetField(vtkm::Id) + +.. load-example:: IterateFields + :file: GuideExampleDataSetCreation.cxx + :caption: Iterating over all the fields in a :class:`vtkm::cont::DataSet`. + +.. commonerrors:: + Avoid retrieving fields by index unless doing simple iterations like this. + The ordering of the fields can change so under some circumstances you may get different :class:`vtkm::cont::Field` objects for the same index. + +:func:`vtkm::cont::DataSet::GetField` and the related methods will throw an exception if the :class:`vtkm::cont::DataSet` does not contain the requested field. +You can test whether a :class:`vtkm::cont::DataSet` has a field without having an exception thrown using one of the variations of :func:`vtkm::cont::DataSet::HasField`. + +.. doxygenfunction:: vtkm::cont::DataSet::HasField +.. doxygenfunction:: vtkm::cont::DataSet::HasPointField +.. doxygenfunction:: vtkm::cont::DataSet::HasCellField ------------------------------ Coordinate Systems @@ -517,6 +608,12 @@ A coordinate system determines the location of a mesh's elements in space. The spatial location is described by providing a 3D vector at each point that gives the coordinates there. The point coordinates can then be interpolated throughout the mesh. +Coordinate System Class +============================== + +Coordinate systems are managed by :class:`vtkm::cont::CoordinateSystem`, which is a subclass of :class:`vtkm::cont::Field`. +This is because a coordinate system is conceptually just a field with some special properties. + .. doxygenclass:: vtkm::cont::CoordinateSystem Because a :class:`vtkm::cont::CoordinateSystem` is a field that is always associated with points, it can be constructed with just the name and the data. @@ -531,15 +628,45 @@ In addition to all the methods provided by the :class:`vtkm::cont::Field` superc .. doxygenfunction:: vtkm::cont::CoordinateSystem::GetBounds +Managing Data Set Coordinate Systems +======================================== + It is typical for a :class:`vtkm::cont::DataSet` to have one coordinate system defined, but it is possible to define multiple coordinate systems. This is helpful when there are multiple ways to express coordinates. -For example, positions in geographic may be expressed as Cartesian coordinates or as latitude-longitude coordinates. +For example, planetary positions may be expressed as Cartesian coordinates or as latitude-longitude coordinates. Both are valid and useful in different ways. It is also valid to have a :class:`vtkm::cont::DataSet` with no coordinate system. This is useful when the structure is not rooted in physical space. For example, if the cell set is representing a graph structure, there might not be any physical space that has meaning for the graph. +Similar to regular fields, coordinate systems can be added to a :class:`vtkm::cont::DataSet` by either constructing a :class:`vtkm::cont::CoordinateSystem` or by providing the array and field information. + +.. doxygenfunction:: vtkm::cont::DataSet::AddCoordinateSystem(const vtkm::cont::CoordinateSystem&) +.. doxygenfunction:: vtkm::cont::DataSet::AddCoordinateSystem(const std::string&, const vtkm::cont::UnknownArrayHandle&) + +Because coordinate systems are part of the list of fields, an existing point field can be marked as a coordinate system by just providing its name. + +.. doxygenfunction:: vtkm::cont::DataSet::AddCoordinateSystem(const std::string&) + +A :class:`vtkm::cont::CoordinateSystem` can be retrieved from a :class:`vtkm::cont::DataSet` by name. + +.. doxygenfunction:: vtkm::cont::DataSet::GetCoordinateSystem(const std::string&) const + +:func:`vtkm::cont::DataSet::GetCoordianteSystem` will throw an exception if the :class:`vtkm::cont::DataSet` does not contain the requested field. +You can test whether a :class:`vtkm::cont::DataSet` has a field without having an exception thrown by using :func:`vtkm::cont::DataSet::HasCoordinateSystem`. + +.. doxygenfunction:: vtkm::cont::DataSet::HasCoordinateSystem + +Coordiante systems can also be retrieved by index. +Because most ``DataSet``'s contain exactly one coordiante system, it is common to pick the coordinate system at index 0, which is the default argument. + +.. doxygenfunction:: vtkm::cont::DataSet::GetCoordinateSystem(vtkm::Id) const + +It is also possible to iterate over all coordinate systems by retrieving the number of coordinate systems. + +.. doxygenfunction:: vtkm::cont::DataSet::GetNumberOfCoordinateSystems + ------------------------------ Partitioned Data Sets @@ -601,3 +728,95 @@ In both cases, the :func:`vtkm::cont::Filter::Execute` method is called on the f .. load-example:: FilterPartitionedDataSet :file: GuideExampleDataSetCreation.cxx :caption: Applying a filter to multi block data. + + +----------------------------------- +Cell Classification and Ghost Cells +----------------------------------- + +.. index:: + single: ghost cell + single: cell; ghost + single: halo cell + single: cell; halo + +One of the challenges of managing data that is divided into partitions, such as in a :class:`vtkm::cont::PartitionedDataSet` or across MPI ranks, is dealing with the boundary between partitions. +A cell on the boundary on one partition will not have the connection information to an adjacent cell in a neighboring partition. +This can cause a problem with many of the filtering operations in |VTKm| that operate on each partition independently. + +A simple remedy to many of the issues with this missing connectivity information is the introduction of *ghost cells* (sometimes also known as halo cells). +A ghost cell is one that is included in a :class:`vtkm::cont::DataSet`, but should not be considered part of the partition. +It is assumed a ghost cell can be removed from the data as it is repeated information. +However, it is provided so that algorithms using information across cell neighbors will get that information. + +There exist some filters in |VTKm| to manipulate ghost cells such as those described in :secref:`provided-filters:Ghost Cell Removal`, :secref:`provided-filters:Ghost Cell Classification`, and :secref:`provided-filters:AMR Arrays`. +The following operations document how to add and use ghost cell information. + +Ghost Cell Fields +============================== + +Each :class:`vtkm::cont::DataSet` can contain a special cell field that provides for each cell a flag identifying the cell as normal, ghost, or other properties. + +.. doxygenfunction:: vtkm::cont::DataSet::GetGhostCellField +.. doxygenfunction:: vtkm::cont::DataSet::HasGhostCellField +.. doxygenfunction:: vtkm::cont::DataSet::SetGhostCellField(const vtkm::cont::Field&) +.. doxygenfunction:: vtkm::cont::DataSet::SetGhostCellField(const std::string&, const vtkm::cont::UnknownArrayHandle&) + +Like coordinate systems, ghost cell fields are stored with the list of all fields within a :class:`vtkm::cont::DataSet` and are identified by the name of the field. +So, the ghost cell field can be specified by providing the name of an existing field. + +.. doxygenfunction:: vtkm::cont::DataSet::SetGhostCellField(const std::string&) + +The name of the field used for ghost cells is set independently of the field. + +.. doxygenfunction:: vtkm::cont::DataSet::GetGhostCellFieldName +.. doxygenfunction:: vtkm::cont::DataSet::SetGhostCellFieldName + +The :class:`vtkm::cont::DataSet` may name a ghost cell field that does not exist. +In this case, :func:`vtkm::cont::DataSet::HasGhostCellField` will report no ghost cell field, but the name will still exist. +If a cell field with this name is later added to the :class:`vtkm::cont::DataSet`, it will automatically become the ghost cell field. +Likewise, because the name of the ghost cell field already exists, a ghost cell field can be created by just providing the array without the name. + +.. doxygenfunction:: vtkm::cont::DataSet::SetGhostCellField(const vtkm::cont::UnknownArrayHandle&) + +|VTKm| also specifies a "global" cell field name. +All :class:`vtkm::cont::DataSet` objects will be born with this global cell field name. + +.. doxygenfunction:: vtkm::cont::GetGlobalGhostCellFieldName +.. doxygenfunction:: vtkm::cont::SetGlobalGhostCellFieldName + +This global cell field name makes it easier to work with other libraries or data sources that have a particular naming convention for ghost cells. + +.. didyouknow:: + The default global ghost cell name is ``vtkGhostCells``. + This follows the convention of the VTK visualization library. + +Cell Classification Flags +============================== + +The ghost cell field is typically a field of :type:`vtkm::UInt8` values. +Each value is treated as bit flags specifying the classification of the cell. +The interpretation of the ghost cell field flags is determined by :class:`vtkm::CellClassification`. + +.. doxygenclass:: vtkm::CellClassification + +.. index:: + double: cell; blanking + double: cell; invalid + +:class:`vtkm::CellClassification` behaves like a scoped enum, but values of type :class:`vtkm::CellClassification` can be used interchangeably with :type:`vtkm::UInt8`. +This simplifies working with classification flags. +Valid classification flags can be the or-ing of any of the following flags. + +.. doxygenenumvalue:: vtkm::CellClassification::Normal +.. doxygenenumvalue:: vtkm::CellClassification::Ghost +.. doxygenenumvalue:: vtkm::CellClassification::Invalid +.. doxygenenumvalue:: vtkm::CellClassification::Blanked + +.. didyouknow:: + Like the default ghost cell field name, the :class:`vtkm::CellClassification` flags follow the same flags used in the VTK library. + This allows data to be more easily imported between the two libraries. + +.. load-example:: SettingGhostCells + :file: GuideExampleFields.cxx + :caption: Using :class:`vtkm::CellClassification` to establish ghost and blanked cells. diff --git a/docs/users-guide/examples/CMakeLists.txt b/docs/users-guide/examples/CMakeLists.txt index a08a18b77d..507a5ce0c5 100644 --- a/docs/users-guide/examples/CMakeLists.txt +++ b/docs/users-guide/examples/CMakeLists.txt @@ -46,6 +46,7 @@ set(examples_device GuideExampleCellOperations.cxx GuideExampleDataSetCreation.cxx GuideExampleErrorHandling.cxx + GuideExampleFields.cxx GuideExampleFilterDataSetWithField.cxx GuideExampleGenerateMeshConstantShape.cxx GuideExampleSimpleAlgorithm.cxx diff --git a/docs/users-guide/examples/GuideExampleDataSetCreation.cxx b/docs/users-guide/examples/GuideExampleDataSetCreation.cxx index ad480e00b7..9a5f6eb3f8 100644 --- a/docs/users-guide/examples/GuideExampleDataSetCreation.cxx +++ b/docs/users-guide/examples/GuideExampleDataSetCreation.cxx @@ -166,6 +166,14 @@ void CreateUniformGrid() //// //// END-EXAMPLE UnknownCellSetResetCellSetList //// + + //// + //// BEGIN-EXAMPLE DataSetPrintSummary + //// + dataSet.PrintSummary(std::cout); + //// + //// END-EXAMPLE DataSetPrintSummary + //// } void CreateUniformGridCustomOriginSpacing() @@ -450,6 +458,46 @@ void CreateExplicitGridIterative() "Wrong number of cells on point 7"); } +//// +//// BEGIN-EXAMPLE DataSetCopyOperator +//// +vtkm::cont::DataSet AddFieldsExample(const vtkm::cont::DataSet& input) +{ + vtkm::cont::DataSet output; + output = input; + + // Add interesting fields... + + return output; +} +//// +//// END-EXAMPLE DataSetCopyOperator +//// + +//// +//// BEGIN-EXAMPLE DataSetCopyStructure +//// +vtkm::cont::DataSet RemoveFieldExample(const vtkm::cont::DataSet& input, + const std::string& fieldToRemove) +{ + vtkm::cont::DataSet output; + output.CopyStructure(input); + + for (vtkm::IdComponent fieldId = 0; fieldId < input.GetNumberOfFields(); ++fieldId) + { + vtkm::cont::Field field = input.GetField(fieldId); + if (field.GetName() != fieldToRemove) + { + output.AddField(field); + } + } + + return output; +} +//// +//// END-EXAMPLE DataSetCopyStructure +//// + void AddFieldData() { std::cout << "Add field data." << std::endl; @@ -513,6 +561,30 @@ void AddFieldData() //// //// END-EXAMPLE AddFieldData //// + + //// + //// BEGIN-EXAMPLE IterateFields + //// + std::cout << "Fields in data:"; + for (vtkm::IdComponent fieldId = 0; fieldId < dataSet.GetNumberOfFields(); ++fieldId) + { + vtkm::cont::Field field = dataSet.GetField(fieldId); + std::cout << " " << field.GetName(); + } + std::cout << std::endl; + //// + //// END-EXAMPLE IterateFields + //// + + vtkm::cont::DataSet copy1 = AddFieldsExample(dataSet); + VTKM_TEST_ASSERT(copy1.GetNumberOfFields() == dataSet.GetNumberOfFields()); + VTKM_TEST_ASSERT(copy1.GetNumberOfPoints() == dataSet.GetNumberOfPoints()); + VTKM_TEST_ASSERT(copy1.GetNumberOfCells() == dataSet.GetNumberOfCells()); + + vtkm::cont::DataSet copy2 = RemoveFieldExample(dataSet, "boundary_cells"); + VTKM_TEST_ASSERT(copy2.GetNumberOfFields() == dataSet.GetNumberOfFields() - 1); + VTKM_TEST_ASSERT(copy2.GetNumberOfPoints() == dataSet.GetNumberOfPoints()); + VTKM_TEST_ASSERT(copy2.GetNumberOfCells() == dataSet.GetNumberOfCells()); } void CreateCellSetPermutation() diff --git a/docs/users-guide/examples/GuideExampleFields.cxx b/docs/users-guide/examples/GuideExampleFields.cxx new file mode 100644 index 0000000000..b0c5ef59de --- /dev/null +++ b/docs/users-guide/examples/GuideExampleFields.cxx @@ -0,0 +1,121 @@ +//============================================================================ +// 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 + +namespace +{ + +//// +//// BEGIN-EXAMPLE SettingGhostCells +//// +struct SetGhostCells : vtkm::worklet::WorkletCellNeighborhood +{ + using ControlSignature = void(CellSetIn cellSet, + WholeArrayIn blankedRegions, + FieldOut ghostCells); + using ExecutionSignature = _3(_2, Boundary); + + template + VTKM_EXEC vtkm::UInt8 operator()(const BlankedRegionsPortal& blankedRegions, + const vtkm::exec::BoundaryState& location) const + { + vtkm::UInt8 cellClassification = vtkm::CellClassification::Normal; + + // Mark cells at boundary as ghost cells. + if (!location.IsRadiusInBoundary(1)) + { + cellClassification |= vtkm::CellClassification::Ghost; + } + + // Mark cells inside specified regions as blanked. + for (vtkm::Id brIndex = 0; brIndex < blankedRegions.GetNumberOfValues(); ++brIndex) + { + vtkm::RangeId3 blankedRegion = blankedRegions.Get(brIndex); + if (blankedRegion.Contains(location.GetCenterIndex())) + { + cellClassification |= vtkm::CellClassification::Blanked; + } + } + + return cellClassification; + } +}; + +void MakeGhostCells(vtkm::cont::DataSet& dataset, + const std::vector blankedRegions) +{ + vtkm::cont::Invoker invoke; + vtkm::cont::ArrayHandle ghostCells; + + invoke(SetGhostCells{}, + dataset.GetCellSet(), + vtkm::cont::make_ArrayHandle(blankedRegions, vtkm::CopyFlag::Off), + ghostCells); + + dataset.SetGhostCellField(ghostCells); +} +//// +//// END-EXAMPLE SettingGhostCells +//// + +void DoGhostCells() +{ + std::cout << "Do ghost cells\n"; + vtkm::cont::DataSetBuilderUniform dataSetBuilder; + vtkm::cont::DataSet dataset = dataSetBuilder.Create({ 11, 11, 11 }); + MakeGhostCells( + dataset, { { { 0, 5 }, { 0, 5 }, { 0, 5 } }, { { 5, 10 }, { 5, 10 }, { 5, 10 } } }); + + vtkm::cont::ArrayHandle ghostCells; + dataset.GetGhostCellField().GetData().AsArrayHandle(ghostCells); + auto ghosts = ghostCells.ReadPortal(); + vtkm::Id numGhosts = 0; + vtkm::Id numBlanked = 0; + for (vtkm::Id cellId = 0; cellId < ghostCells.GetNumberOfValues(); ++cellId) + { + vtkm::UInt8 flags = ghosts.Get(cellId); + if ((flags & vtkm::CellClassification::Ghost) == vtkm::CellClassification::Ghost) + { + ++numGhosts; + } + if ((flags & vtkm::CellClassification::Blanked) == vtkm::CellClassification::Blanked) + { + ++numBlanked; + } + } + std::cout << "Num ghosts: " << numGhosts << "\n"; + std::cout << "Num blanked: " << numBlanked << "\n"; + VTKM_TEST_ASSERT(numGhosts == 488); + VTKM_TEST_ASSERT(numBlanked == 250); +} + +void Run() +{ + DoGhostCells(); +} + +} // anonymous namespace + +int GuideExampleFields(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(Run, argc, argv); +} diff --git a/docs/users-guide/provided-filters.rst b/docs/users-guide/provided-filters.rst index 3f83bcc942..cd0c9bd221 100644 --- a/docs/users-guide/provided-filters.rst +++ b/docs/users-guide/provided-filters.rst @@ -422,8 +422,7 @@ Ghost Cell Removal The :class:`vtkm::filter::entity_extraction::GhostCellRemove` filter is used to remove cells from a data set according to a cell centered field that specifies whether a cell is a regular cell or a ghost cell. By default, the filter will get the ghost cell information that is registered in the input :class:`vtkm::cont::DataSet`, but it also possible to specify an arbitrary field for this purpose. - -.. todo:: Better document how ghost cells work in |VTKm| (somewhere). +Ghost cell information is documented in :secref:`dataset:Cell Classification and Ghost Cells`. .. doxygenclass:: vtkm::filter::entity_extraction::GhostCellRemove :members: @@ -866,8 +865,7 @@ Ghost Cell Classification The :class:`vtkm::filter::mesh_info::GhostCellClassify` filter determines which cells should be considered ghost cells in a structured data set. The ghost cells are expected to be on the border. - -.. todo:: Document ``vtkm::CellClassification``. +Ghost cell information is documented in :secref:`dataset:Cell Classification and Ghost Cells`. .. doxygenclass:: vtkm::filter::mesh_info::GhostCellClassify :members: @@ -990,9 +988,9 @@ The :class:`vtkm::filter::multi_block::AmrArrays` filter determines the hierarch :members: .. didyouknow:: - The names of the generated field arrays arrays (e.g. ``vtkAmrLevel``) are chosen to be compatible with the equivalent arrays in VTK. - This is why they use the prefix of "vtk" instead of "vtkm". - Likewise, the flags used for ``vtkGhostType`` are compatible with VTK. + The names of the generated field arrays arrays (e.g. ``vtkAmrLevel``) are chosen to be compatible with the equivalent arrays in VTK. + This is why they use the prefix of "vtk" instead of "vtkm". + Likewise, the flags used for ``vtkGhostType`` are compatible with VTK. Merge Data Sets ------------------------------ diff --git a/vtkm/CellClassification.h b/vtkm/CellClassification.h index 6f001c9a7b..2072eea58b 100644 --- a/vtkm/CellClassification.h +++ b/vtkm/CellClassification.h @@ -46,6 +46,32 @@ public: Unused5 = 1 << 6, }; + /// @var vtkm::UInt8 Normal + /// @brief Value used for a normal cell. + /// + /// This value is the clearing of any cell classification flags. This identifies + /// the cells as a "normal" cell without any special or exclusionary properties. + + /// @var vtkm::UInt8 Ghost + /// @brief Flag used for a ghost cell. + /// + /// This flag indicates the associated cell is repeated information from a different + /// partition. The ghost cell is provided to give data from cells in neighboring + /// partitions. This allows operations to correctly compute neighborhood information + /// without explicit communications. Ghost cells are typically removed for rendering. + + /// @var vtkm::UInt8 Invalid + /// @brief Flag used for an invalid cell. + + /// @var vtkm::UInt8 Blanked + /// @brief Flag used for a cell that should not be considered part of the data. + /// + /// A blanked cell should be ignored from the data. Cells with this flag should + /// be treated as if they were not declared. Blanked cells are primarily used + /// in structured cell sets to remove parts of the interior of the mesh volume that + /// could not otherwise be removed. Blanked cells are common in AMR structures + /// to indicate cells that are further refined in deeper levels. + VTKM_EXEC constexpr CellClassification(vtkm::UInt8 flags = vtkm::UInt8{ Normal }) : Flags(flags) { diff --git a/vtkm/exec/BoundaryState.h b/vtkm/exec/BoundaryState.h index bde079c89f..dd75f96c75 100644 --- a/vtkm/exec/BoundaryState.h +++ b/vtkm/exec/BoundaryState.h @@ -42,7 +42,6 @@ struct BoundaryState /// VTKM_EXEC const vtkm::Id3& GetCenterIndex() const { return this->IJK; } - ///@{ /// Returns true if a neighborhood of the given radius is contained within the bounds of the cell /// set in the X, Y, or Z direction. Returns false if the neighborhood extends outside of the /// boundary of the data in the X, Y, or Z direction. @@ -57,17 +56,18 @@ struct BoundaryState VTKM_ASSERT(radius >= 0); return (((this->IJK[0] - radius) >= 0) && ((this->IJK[0] + radius) < this->PointDimensions[0])); } + /// @copydoc IsRadiusInXBoundary VTKM_EXEC bool IsRadiusInYBoundary(vtkm::IdComponent radius) const { VTKM_ASSERT(radius >= 0); return (((this->IJK[1] - radius) >= 0) && ((this->IJK[1] + radius) < this->PointDimensions[1])); } + /// @copydoc IsRadiusInXBoundary VTKM_EXEC bool IsRadiusInZBoundary(vtkm::IdComponent radius) const { VTKM_ASSERT(radius >= 0); return (((this->IJK[2] - radius) >= 0) && ((this->IJK[2] + radius) < this->PointDimensions[2])); } - ///@} /// Returns true if a neighborhood of the given radius is contained within the bounds /// of the cell set. Returns false if the neighborhood extends outside of the boundary of the @@ -84,7 +84,6 @@ struct BoundaryState this->IsRadiusInZBoundary(radius); } - ///@{ /// Returns true if the neighbor at the specified @a offset is contained /// within the bounds of the cell set in the X, Y, or Z direction. Returns /// false if the neighbor falls outside of the boundary of the data in the X, @@ -94,15 +93,16 @@ struct BoundaryState { return (((this->IJK[0] + offset) >= 0) && ((this->IJK[0] + offset) < this->PointDimensions[0])); } + /// @copydoc IsNeighborInXBoundary VTKM_EXEC bool IsNeighborInYBoundary(vtkm::IdComponent offset) const { return (((this->IJK[1] + offset) >= 0) && ((this->IJK[1] + offset) < this->PointDimensions[1])); } + /// @copydoc IsNeighborInXBoundary VTKM_EXEC bool IsNeighborInZBoundary(vtkm::IdComponent offset) const { return (((this->IJK[2] + offset) >= 0) && ((this->IJK[2] + offset) < this->PointDimensions[2])); } - ///@} /// Returns true if the neighbor at the specified offset vector is contained /// within the bounds of the cell set. Returns false if the neighbor falls -- GitLab From c5a8de92c0a6985c5fbc27aab2dc0cd5767853f4 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 8 Oct 2024 10:39:59 -0400 Subject: [PATCH 27/73] Simplify CellLocatorBase `CellLocatorBase` used to use CRTP. However, this pattern is unnecessary as the only real subclass it calls is `Build`, which does not need templating. The base class does not have to call the `PrepareForExecution` method, so it can provide its own features to derived classes more easily. Also moved `CellLocatorBase` out of the `internal` namespace. Although it provides little benefit other than a base class, it will make documenting its methods easier. --- docs/changelog/simplify-cell-locator-base.md | 10 +++ vtkm/cont/CMakeLists.txt | 2 + vtkm/cont/CellLocatorBase.cxx | 31 +++++++ vtkm/cont/CellLocatorBase.h | 89 +++++++++++++++++++ .../CellLocatorBoundingIntervalHierarchy.h | 10 +-- vtkm/cont/CellLocatorGeneral.h | 8 +- vtkm/cont/CellLocatorRectilinearGrid.h | 11 +-- vtkm/cont/CellLocatorTwoLevel.h | 10 +-- vtkm/cont/CellLocatorUniformBins.h | 10 +-- vtkm/cont/CellLocatorUniformGrid.h | 10 +-- vtkm/cont/internal/CMakeLists.txt | 1 - vtkm/cont/internal/CellLocatorBase.h | 75 ---------------- 12 files changed, 150 insertions(+), 117 deletions(-) create mode 100644 docs/changelog/simplify-cell-locator-base.md create mode 100644 vtkm/cont/CellLocatorBase.cxx create mode 100644 vtkm/cont/CellLocatorBase.h delete mode 100644 vtkm/cont/internal/CellLocatorBase.h diff --git a/docs/changelog/simplify-cell-locator-base.md b/docs/changelog/simplify-cell-locator-base.md new file mode 100644 index 0000000000..a8c192d362 --- /dev/null +++ b/docs/changelog/simplify-cell-locator-base.md @@ -0,0 +1,10 @@ +## Simplify CellLocatorBase + +`CellLocatorBase` used to use CRTP. However, this pattern is unnecessary as +the only real subclass it calls is `Build`, which does not need templating. +The base class does not have to call the `PrepareForExecution` method, so +it can provide its own features to derived classes more easily. + +Also moved `CellLocatorBase` out of the `internal` namespace. Although it +provides little benefit other than a base class, it will make documenting +its methods easier. diff --git a/vtkm/cont/CMakeLists.txt b/vtkm/cont/CMakeLists.txt index 0c19f40a6c..1ae50480fe 100644 --- a/vtkm/cont/CMakeLists.txt +++ b/vtkm/cont/CMakeLists.txt @@ -58,6 +58,7 @@ set(headers BoundsCompute.h BoundsGlobalCompute.h CastAndCall.h + CellLocatorBase.h CellLocatorBoundingIntervalHierarchy.h CellLocatorChooser.h CellLocatorGeneral.h @@ -140,6 +141,7 @@ set(sources BitField.cxx BoundsCompute.cxx BoundsGlobalCompute.cxx + CellLocatorBase.cxx CellLocatorGeneral.cxx CellLocatorPartitioned.cxx CellLocatorRectilinearGrid.cxx diff --git a/vtkm/cont/CellLocatorBase.cxx b/vtkm/cont/CellLocatorBase.cxx new file mode 100644 index 0000000000..ed0c61c497 --- /dev/null +++ b/vtkm/cont/CellLocatorBase.cxx @@ -0,0 +1,31 @@ +//============================================================================ +// 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 + +namespace vtkm +{ +namespace cont +{ + +void CellLocatorBase::Update() const +{ + if (this->Modified) + { + // Although the data of the derived class may change, the logical state + // of the class should not. Thus, we will instruct the compiler to relax + // the const constraint. + const_cast(this)->Build(); + this->Modified = false; + } +} + +} +} // namespace vtkm::cont diff --git a/vtkm/cont/CellLocatorBase.h b/vtkm/cont/CellLocatorBase.h new file mode 100644 index 0000000000..bb85fcfccd --- /dev/null +++ b/vtkm/cont/CellLocatorBase.h @@ -0,0 +1,89 @@ +//============================================================================ +// 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. +//============================================================================ +#ifndef vtk_m_cont_internal_CellLocatorBase_h +#define vtk_m_cont_internal_CellLocatorBase_h + +#include + +#include +#include +#include +#include +#include + +namespace vtkm +{ +namespace cont +{ + +/// \brief Base class for all `CellLocator` classes. +/// +/// `CellLocatorBase` uses the curiously recurring template pattern (CRTP). Subclasses +/// must provide their own type for the template parameter. Subclasses must implement +/// `Build()` and `PrepareForExecution()` methods. +/// +/// If a derived class changes its state in a way that invalidates its internal search +/// structure, it should call the protected `SetModified()` method. This will alert the +/// base class to rebuild the structure on the next call to `Update()`. +class VTKM_CONT_EXPORT CellLocatorBase : public vtkm::cont::ExecutionObjectBase +{ + vtkm::cont::UnknownCellSet CellSet; + vtkm::cont::CoordinateSystem Coords; + mutable bool Modified = true; + +public: + virtual ~CellLocatorBase() = default; + + /// @brief Specify the `CellSet` defining the structure of the cells being searched. + /// + /// This is typically retrieved from the `vtkm::cont::DataSet::GetCellSet()` method. + VTKM_CONT const vtkm::cont::UnknownCellSet& GetCellSet() const { return this->CellSet; } + /// @copydoc GetCellSet + VTKM_CONT void SetCellSet(const vtkm::cont::UnknownCellSet& cellSet) + { + this->CellSet = cellSet; + this->SetModified(); + } + + /// @brief Specify the `CoordinateSystem` defining the location of the cells. + /// + /// This is typically retrieved from the `vtkm::cont::DataSet::GetCoordinateSystem()` method. + VTKM_CONT const vtkm::cont::CoordinateSystem& GetCoordinates() const { return this->Coords; } + /// @copydoc GetCoordinates + VTKM_CONT void SetCoordinates(const vtkm::cont::CoordinateSystem& coords) + { + this->Coords = coords; + this->SetModified(); + } + + /// @brief Build the search structure used to look up cells. + /// + /// This method must be called after the cells and coordiantes are specified with + /// `SetCellSet()` and `SetCoordinates()`, respectively. + /// The method must also be called before it is used with a worklet. + /// Before building the search structure `Update()` checks to see if the structure is + /// already built and up to date. If so, the method quickly returns. + /// Thus, it is good practice to call `Update()` before each use in a worklet. + /// + /// Although `Update()` is called from the control environment, it lauches jobs in the + /// execution environment to quickly build the search structure. + VTKM_CONT void Update() const; + +protected: + void SetModified() { this->Modified = true; } + bool GetModified() const { return this->Modified; } + + virtual void Build() = 0; +}; + +} +} // vtkm::cont::internal + +#endif //vtk_m_cont_internal_CellLocatorBase_h diff --git a/vtkm/cont/CellLocatorBoundingIntervalHierarchy.h b/vtkm/cont/CellLocatorBoundingIntervalHierarchy.h index c890110f5c..7598c2567b 100644 --- a/vtkm/cont/CellLocatorBoundingIntervalHierarchy.h +++ b/vtkm/cont/CellLocatorBoundingIntervalHierarchy.h @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include @@ -27,11 +27,8 @@ namespace vtkm namespace cont { -class VTKM_CONT_EXPORT CellLocatorBoundingIntervalHierarchy - : public vtkm::cont::internal::CellLocatorBase +class VTKM_CONT_EXPORT CellLocatorBoundingIntervalHierarchy : public vtkm::cont::CellLocatorBase { - using Superclass = vtkm::cont::internal::CellLocatorBase; - public: using SupportedCellSets = VTKM_DEFAULT_CELL_SET_LIST; @@ -80,8 +77,7 @@ private: vtkm::cont::ArrayHandle Nodes; vtkm::cont::ArrayHandle ProcessedCellIds; - friend Superclass; - VTKM_CONT void Build(); + VTKM_CONT void Build() override; struct MakeExecObject; }; diff --git a/vtkm/cont/CellLocatorGeneral.h b/vtkm/cont/CellLocatorGeneral.h index 85b4d63f63..6d3d7cf5fc 100644 --- a/vtkm/cont/CellLocatorGeneral.h +++ b/vtkm/cont/CellLocatorGeneral.h @@ -38,11 +38,8 @@ namespace cont /// Also note that `CellLocatorGeneral` can add a significant amount of code inside /// of worklet that uses it, and this might cause some issues with some compilers. /// -class VTKM_CONT_EXPORT CellLocatorGeneral - : public vtkm::cont::internal::CellLocatorBase +class VTKM_CONT_EXPORT CellLocatorGeneral : public vtkm::cont::CellLocatorBase { - using Superclass = vtkm::cont::internal::CellLocatorBase; - public: using ContLocatorList = vtkm::List LocatorImpl; - friend Superclass; - VTKM_CONT void Build(); + VTKM_CONT void Build() override; struct PrepareFunctor; }; diff --git a/vtkm/cont/CellLocatorRectilinearGrid.h b/vtkm/cont/CellLocatorRectilinearGrid.h index 2ced485e98..a0d925e212 100644 --- a/vtkm/cont/CellLocatorRectilinearGrid.h +++ b/vtkm/cont/CellLocatorRectilinearGrid.h @@ -10,7 +10,7 @@ #ifndef vtkm_cont_CellLocatorRectilinearGrid_h #define vtkm_cont_CellLocatorRectilinearGrid_h -#include +#include #include @@ -19,11 +19,8 @@ namespace vtkm namespace cont { -class VTKM_CONT_EXPORT CellLocatorRectilinearGrid - : public vtkm::cont::internal::CellLocatorBase +class VTKM_CONT_EXPORT CellLocatorRectilinearGrid : public vtkm::cont::CellLocatorBase { - using Superclass = vtkm::cont::internal::CellLocatorBase; - using Structured2DType = vtkm::cont::CellSetStructured<2>; using Structured3DType = vtkm::cont::CellSetStructured<3>; // Might want to handle cartesian product of both Float32 and Float64. @@ -48,8 +45,8 @@ private: vtkm::Id RowSize; bool Is3D = true; - friend Superclass; - VTKM_CONT void Build(); +protected: + VTKM_CONT void Build() override; }; } //namespace cont diff --git a/vtkm/cont/CellLocatorTwoLevel.h b/vtkm/cont/CellLocatorTwoLevel.h index 8fceca777a..a756a1ef15 100644 --- a/vtkm/cont/CellLocatorTwoLevel.h +++ b/vtkm/cont/CellLocatorTwoLevel.h @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include @@ -40,11 +40,8 @@ namespace cont /// Javor Kalojanov, Markus Billeter, and Philipp Slusallek. "Two-Level Grids for Ray Tracing /// on GPUs." _Computer Graphics Forum_, 2011, pages 307-314. DOI 10.1111/j.1467-8659.2011.01862.x /// -class VTKM_CONT_EXPORT CellLocatorTwoLevel - : public vtkm::cont::internal::CellLocatorBase +class VTKM_CONT_EXPORT CellLocatorTwoLevel : public vtkm::cont::CellLocatorBase { - using Superclass = vtkm::cont::internal::CellLocatorBase; - template using CellSetContToExec = typename CellSetCont::template ExecConnectivityType #include -#include +#include #include #include @@ -35,11 +35,8 @@ namespace cont /// produces a more efficient search structure, especially for GPUs where memory /// access patterns are critical to performance. -class VTKM_CONT_EXPORT CellLocatorUniformBins - : public vtkm::cont::internal::CellLocatorBase +class VTKM_CONT_EXPORT CellLocatorUniformBins : public vtkm::cont::CellLocatorBase { - using Superclass = vtkm::cont::internal::CellLocatorBase; - template using CellSetContToExec = typename CellSetCont::template ExecConnectivityType +#include #include @@ -19,11 +19,8 @@ namespace vtkm namespace cont { -class VTKM_CONT_EXPORT CellLocatorUniformGrid - : public vtkm::cont::internal::CellLocatorBase +class VTKM_CONT_EXPORT CellLocatorUniformGrid : public vtkm::cont::CellLocatorBase { - using Superclass = vtkm::cont::internal::CellLocatorBase; - public: using LastCell = vtkm::exec::CellLocatorUniformGrid::LastCell; @@ -39,8 +36,7 @@ private: vtkm::Vec3f MaxPoint; bool Is3D = true; - friend Superclass; - VTKM_CONT void Build(); + VTKM_CONT void Build() override; }; } } // vtkm::cont diff --git a/vtkm/cont/internal/CMakeLists.txt b/vtkm/cont/internal/CMakeLists.txt index 74c4459cec..e85bbb6e39 100644 --- a/vtkm/cont/internal/CMakeLists.txt +++ b/vtkm/cont/internal/CMakeLists.txt @@ -16,7 +16,6 @@ set(headers ArrayTransfer.h Buffer.h CastInvalidValue.h - CellLocatorBase.h ConnectivityExplicitInternals.h ConvertNumComponentsToOffsetsTemplate.h DeviceAdapterAlgorithmGeneral.h diff --git a/vtkm/cont/internal/CellLocatorBase.h b/vtkm/cont/internal/CellLocatorBase.h deleted file mode 100644 index 8b9a72545d..0000000000 --- a/vtkm/cont/internal/CellLocatorBase.h +++ /dev/null @@ -1,75 +0,0 @@ -//============================================================================ -// 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. -//============================================================================ -#ifndef vtk_m_cont_internal_CellLocatorBase_h -#define vtk_m_cont_internal_CellLocatorBase_h - -#include - -#include -#include -#include -#include - -namespace vtkm -{ -namespace cont -{ -namespace internal -{ - -/// \brief Base class for all `CellLocator` classes. -/// -/// `CellLocatorBase` uses the curiously recurring template pattern (CRTP). Subclasses -/// must provide their own type for the template parameter. Subclasses must implement -/// `Update` and `PrepareForExecution` methods. -/// -template -class VTKM_ALWAYS_EXPORT CellLocatorBase : public vtkm::cont::ExecutionObjectBase -{ - vtkm::cont::UnknownCellSet CellSet; - vtkm::cont::CoordinateSystem Coords; - mutable bool Modified = true; - -public: - const vtkm::cont::UnknownCellSet& GetCellSet() const { return this->CellSet; } - - void SetCellSet(const vtkm::cont::UnknownCellSet& cellSet) - { - this->CellSet = cellSet; - this->SetModified(); - } - - const vtkm::cont::CoordinateSystem& GetCoordinates() const { return this->Coords; } - - void SetCoordinates(const vtkm::cont::CoordinateSystem& coords) - { - this->Coords = coords; - this->SetModified(); - } - - void Update() const - { - if (this->Modified) - { - static_cast(const_cast(this))->Build(); - this->Modified = false; - } - } - -protected: - void SetModified() { this->Modified = true; } - bool GetModified() const { return this->Modified; } -}; - -} -} -} // vtkm::cont::internal - -#endif //vtk_m_cont_internal_CellLocatorBase_h -- GitLab From 05166afb0daa67cec0fcd1fdad82332189f3b443 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Wed, 9 Oct 2024 06:15:51 -0400 Subject: [PATCH 28/73] Simplify PointLocatorBase `PointLocatorBase` used to use CRTP. However, this pattern is unnecessary as the only real subclass it calls is Build, which does not need templating. The base class does not have to call the `PrepareForExecution` method, so it can provide its own features to derived classes more easily. Also moved `PointLocatorBase` out of the `internal` namespace. Although it provides little benefit other than a base class, it will make documenting its methods easier. --- docs/changelog/simplify-cell-locator-base.md | 10 ------ docs/changelog/simplify-locator-bases.md | 11 ++++++ vtkm/cont/CMakeLists.txt | 2 ++ vtkm/cont/PointLocatorBase.cxx | 31 ++++++++++++++++ vtkm/cont/{internal => }/PointLocatorBase.h | 38 +++++++++++--------- vtkm/cont/PointLocatorSparseGrid.h | 10 ++---- vtkm/cont/internal/CMakeLists.txt | 1 - 7 files changed, 69 insertions(+), 34 deletions(-) delete mode 100644 docs/changelog/simplify-cell-locator-base.md create mode 100644 docs/changelog/simplify-locator-bases.md create mode 100644 vtkm/cont/PointLocatorBase.cxx rename vtkm/cont/{internal => }/PointLocatorBase.h (53%) diff --git a/docs/changelog/simplify-cell-locator-base.md b/docs/changelog/simplify-cell-locator-base.md deleted file mode 100644 index a8c192d362..0000000000 --- a/docs/changelog/simplify-cell-locator-base.md +++ /dev/null @@ -1,10 +0,0 @@ -## Simplify CellLocatorBase - -`CellLocatorBase` used to use CRTP. However, this pattern is unnecessary as -the only real subclass it calls is `Build`, which does not need templating. -The base class does not have to call the `PrepareForExecution` method, so -it can provide its own features to derived classes more easily. - -Also moved `CellLocatorBase` out of the `internal` namespace. Although it -provides little benefit other than a base class, it will make documenting -its methods easier. diff --git a/docs/changelog/simplify-locator-bases.md b/docs/changelog/simplify-locator-bases.md new file mode 100644 index 0000000000..55ce5c3b77 --- /dev/null +++ b/docs/changelog/simplify-locator-bases.md @@ -0,0 +1,11 @@ +## Simplify CellLocatorBase and PointLocatorBase + +`CellLocatorBase` and `PointLocatorBase` used to use CRTP. However, this +pattern is unnecessary as the only method in the subclass they call is +`Build`, which does not need templating. The base class does not have to +call the `PrepareForExecution` method, so it can provide its own features +to derived classes more easily. + +Also moved `CellLocatorBase` and `PointLocatorBase` out of the `internal` +namespace. Although they provide little benefit other than a base class, it +will make documenting its methods easier. diff --git a/vtkm/cont/CMakeLists.txt b/vtkm/cont/CMakeLists.txt index 1ae50480fe..6499853705 100644 --- a/vtkm/cont/CMakeLists.txt +++ b/vtkm/cont/CMakeLists.txt @@ -110,6 +110,7 @@ set(headers MergePartitionedDataSet.h ParticleArrayCopy.h PartitionedDataSet.h + PointLocatorBase.h PointLocatorSparseGrid.h RuntimeDeviceInformation.h RuntimeDeviceTracker.h @@ -173,6 +174,7 @@ set(sources Logging.cxx RuntimeDeviceTracker.cxx PartitionedDataSet.cxx + PointLocatorBase.cxx Storage.cxx Token.cxx TryExecute.cxx diff --git a/vtkm/cont/PointLocatorBase.cxx b/vtkm/cont/PointLocatorBase.cxx new file mode 100644 index 0000000000..a4b37c769d --- /dev/null +++ b/vtkm/cont/PointLocatorBase.cxx @@ -0,0 +1,31 @@ +//============================================================================ +// 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 + +namespace vtkm +{ +namespace cont +{ + +void PointLocatorBase::Update() const +{ + if (this->Modified) + { + // Although the data of the derived class may change, the logical state + // of the class should not. Thus, we will instruct the compiler to relax + // the const constraint. + const_cast(this)->Build(); + this->Modified = false; + } +} + +} +} // namespace vtkm::cont diff --git a/vtkm/cont/internal/PointLocatorBase.h b/vtkm/cont/PointLocatorBase.h similarity index 53% rename from vtkm/cont/internal/PointLocatorBase.h rename to vtkm/cont/PointLocatorBase.h index 3d53760112..43c017409b 100644 --- a/vtkm/cont/internal/PointLocatorBase.h +++ b/vtkm/cont/PointLocatorBase.h @@ -20,45 +20,51 @@ namespace vtkm { namespace cont { -namespace internal -{ -/// \brief Base class for all `PointLocator` classes. +/// @brief Base class for all `PointLocator` classes. /// -/// `PointLocatorBase` uses the curiously recurring template pattern (CRTP). Subclasses -/// must provide their own type for the template parameter. Subclasses must implement -/// `Build` and `PrepareForExecution` methods. +/// `PointLocatorBase` subclasses must implement the pure virtual `Build()` method. +/// They also must provide a `PrepareForExecution()` method to satisfy the +/// `ExecutionObjectBase` superclass. /// -template -class VTKM_ALWAYS_EXPORT PointLocatorBase : public vtkm::cont::ExecutionObjectBase +/// If a derived class changes its state in a way that invalidates its internal search +/// structure, it should call the protected `SetModified()` method. This will alert the +/// base class to rebuild the structure on the next call to `Update()`. +class VTKM_CONT_EXPORT PointLocatorBase : public vtkm::cont::ExecutionObjectBase { public: + virtual ~PointLocatorBase() = default; + + /// @brief Specify the `CoordinateSystem` defining the location of the cells. + /// + /// This is often retrieved from the `vtkm::cont::DataSet::GetCoordinateSystem()` method, + /// but it can be any array of size 3 `Vec`s. vtkm::cont::CoordinateSystem GetCoordinates() const { return this->Coords; } + /// @copydoc GetCoordinates void SetCoordinates(const vtkm::cont::CoordinateSystem& coords) { this->Coords = coords; this->SetModified(); } - - void Update() + /// @copydoc GetCoordinates + VTKM_CONT void SetCoordinates(const vtkm::cont::UnknownArrayHandle& coords) { - if (this->Modified) - { - static_cast(const_cast(this))->Build(); - this->Modified = false; - } + this->SetCoordinates({ "coords", coords }); } + void Update() const; + protected: void SetModified() { this->Modified = true; } bool GetModified() const { return this->Modified; } + virtual void Build() = 0; + private: vtkm::cont::CoordinateSystem Coords; mutable bool Modified = true; }; -} // vtkm::cont::internal } // vtkm::cont } // vtkm diff --git a/vtkm/cont/PointLocatorSparseGrid.h b/vtkm/cont/PointLocatorSparseGrid.h index 2e72387ee9..00d626728f 100644 --- a/vtkm/cont/PointLocatorSparseGrid.h +++ b/vtkm/cont/PointLocatorSparseGrid.h @@ -10,7 +10,7 @@ #ifndef vtk_m_cont_PointLocatorSparseGrid_h #define vtk_m_cont_PointLocatorSparseGrid_h -#include +#include #include namespace vtkm @@ -32,11 +32,8 @@ namespace cont /// Parallel Techniques." In _Eurographics Symposium on Parallel Graphics and Visualization /// (EGPGV)_, June 2019. DOI 10.2312/pgv.20191112. /// -class VTKM_CONT_EXPORT PointLocatorSparseGrid - : public vtkm::cont::internal::PointLocatorBase +class VTKM_CONT_EXPORT PointLocatorSparseGrid : public vtkm::cont::PointLocatorBase { - using Superclass = vtkm::cont::internal::PointLocatorBase; - public: using RangeType = vtkm::Vec; @@ -82,8 +79,7 @@ private: (this->Range[2].Max < this->Range[2].Min); } - friend Superclass; - VTKM_CONT void Build(); + VTKM_CONT void Build() override; RangeType Range = { { 0.0, -1.0 } }; vtkm::Id3 Dims = { 32 }; diff --git a/vtkm/cont/internal/CMakeLists.txt b/vtkm/cont/internal/CMakeLists.txt index e85bbb6e39..a512057197 100644 --- a/vtkm/cont/internal/CMakeLists.txt +++ b/vtkm/cont/internal/CMakeLists.txt @@ -32,7 +32,6 @@ set(headers OptionParserArguments.h ParallelRadixSort.h ParallelRadixSortInterface.h - PointLocatorBase.h ReverseConnectivityBuilder.h RuntimeDeviceConfiguration.h RuntimeDeviceConfigurationOptions.h -- GitLab From 8bb8ec53eac53399f867c2684eaa37216972b44c Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Thu, 10 Oct 2024 08:09:54 -0400 Subject: [PATCH 29/73] VTKmWrappers: allow pool size to be configured externally --- CMake/VTKmWrappers.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMake/VTKmWrappers.cmake b/CMake/VTKmWrappers.cmake index c1833a3744..0feb646bb2 100644 --- a/CMake/VTKmWrappers.cmake +++ b/CMake/VTKmWrappers.cmake @@ -174,8 +174,10 @@ function(vtkm_setup_job_pool) # We try to allocate a pool size where we presume each compilation process # will require 3GB of memory. To allow for other NON VTK-m jobs we leave at # least 3GB of memory as 'slop'. - cmake_host_system_information(RESULT vtkm_mem_ QUERY TOTAL_PHYSICAL_MEMORY) - math(EXPR vtkm_pool_size "(${vtkm_mem_}/3072)-1") + if (NOT DEFINED vtkm_pool_size) + cmake_host_system_information(RESULT vtkm_mem_ QUERY TOTAL_PHYSICAL_MEMORY) + math(EXPR vtkm_pool_size "(${vtkm_mem_}/3072)-1") + endif () if (vtkm_pool_size LESS 1) set(vtkm_pool_size 1) -- GitLab From 48e385af319543800398656645327243a29babfb Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 15 Oct 2024 12:01:34 -0400 Subject: [PATCH 30/73] Fix compile error for contour tree print A print for one of the contour tree objects was referencing members of itself that don't seem to exist. This causes the Intel compiler to fail to compile it. I'm at a loss about how any other compiler does not error out, but at any rate this should be correct. --- .../worklet/contourtree_distributed/HierarchicalContourTree.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h index a996d42928..e40d5f4f15 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h @@ -663,7 +663,7 @@ std::string HierarchicalContourTree::PrintDotSuperStructure(const cha auto hyperarcsPortal = this->Hyperarcs.ReadPortal(); auto regularNodeGlobalIdsPortal = this->RegularNodeGlobalIds.ReadPortal(); auto whichIterationPortal = this->WhichIteration.ReadPortal(); - auto whichRoundPortal = this->whichRound.ReadPortal(); + auto whichRoundPortal = this->WhichRound.ReadPortal(); auto superarcsPortal = this->Superarcs.ReadPortal(); auto superparentsPortal = this->Superparents.ReadPortal(); for (vtkm::Id supernode = 0; supernode < this->Supernodes.GetNumberOfValues(); supernode++) @@ -708,7 +708,7 @@ std::string HierarchicalContourTree::PrintDotSuperStructure(const cha if (contourtree_augmented::NoSuchElement(superarcTo)) { // no superarc // if it occurred on the final round, it's the global root and is shown as the NULL node - if (whichRoundPortal.Get(superarcFrom) == this->NRounds) + if (whichRoundPortal.Get(superarcFrom) == this->NumRounds) { // root node outstream << "\tSN" << std::setw(1) << superarcFrom << " -> SA" << std::setw(1) << superarc << " [label=\"S" << std::setw(1) << superarc << "\",style=dotted]\n"; -- GitLab From c805a6039ea500cb96158cfc11271987c9f67aa4 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 15 Oct 2024 13:06:36 -0400 Subject: [PATCH 31/73] Remove unused method from MIR tables The implementation of this method was incorrect as it referenced a class member that does not exist. Many compilers allowed it in a templated class when the method was never used, but other compilers attempt to compile the inlined method regardless. Since the method clearly is not needed, the easy solution is to remove it. --- vtkm/filter/contour/worklet/mir/MIRTables.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/vtkm/filter/contour/worklet/mir/MIRTables.h b/vtkm/filter/contour/worklet/mir/MIRTables.h index 3dff3329eb..a6f4d4f1fd 100644 --- a/vtkm/filter/contour/worklet/mir/MIRTables.h +++ b/vtkm/filter/contour/worklet/mir/MIRTables.h @@ -11400,11 +11400,6 @@ public: return FacesLookup[shape]; } - VTKM_EXEC vtkm::UInt8 GetPoint(vtkm::Id pointIndex) const - { - return this->CellFacePortal.Get(pointIndex); - } - private: typename vtkm::cont::ArrayHandle::ReadPortalType MIRTablesDataPortal; typename vtkm::cont::ArrayHandle::ReadPortalType MIRTablesIndicesPortal; -- GitLab From 674d370b3d021d6352045f09ce69f84c41e863cd Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Fri, 25 Oct 2024 08:31:54 -0400 Subject: [PATCH 32/73] Fix clang compile issue with a missing tempolate arg list Apparently, starting with LLVM clang version 20, if you use the `template` keyword to highlight a sub-element, you have to provide a template argument list. This is true even for a method where the template arguments can be completely determined by the types of the arguments. Fix this problem by providing an empty template arg list (so the compiler knows what is templated but still figures out its own types). Fixes #830 --- docs/changelog/missing-template-arg-list.md | 10 ++++++++++ vtkm/internal/VariantImplDetail.h | 2 +- vtkm/internal/VariantImplDetail.h.in | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 docs/changelog/missing-template-arg-list.md diff --git a/docs/changelog/missing-template-arg-list.md b/docs/changelog/missing-template-arg-list.md new file mode 100644 index 0000000000..6f7da3d463 --- /dev/null +++ b/docs/changelog/missing-template-arg-list.md @@ -0,0 +1,10 @@ +## Fix clang compile issue with a missing tempolate arg list + +Apparently, starting with LLVM clang version 20, if you use the `template` +keyword to highlight a sub-element, you have to provide a template argument +list. This is true even for a method where the template arguments can be +completely determined by the types of the arguments. Fix this problem by +providing an empty template arg list (so the compiler knows what is +templated but still figures out its own types). + +Fixes #830 diff --git a/vtkm/internal/VariantImplDetail.h b/vtkm/internal/VariantImplDetail.h index 89f10818c5..ce0b13dd7b 100644 --- a/vtkm/internal/VariantImplDetail.h +++ b/vtkm/internal/VariantImplDetail.h @@ -731,7 +731,7 @@ struct VariantCases // just the one that it currently holds. return f(storage.V7, std::forward(args)...); default: - return VariantCases::template CastAndCall( + return VariantCases::template CastAndCall<>( index - 8, std::forward(f), storage.Remaining, std::forward(args)...); } } diff --git a/vtkm/internal/VariantImplDetail.h.in b/vtkm/internal/VariantImplDetail.h.in index 46bebecc7f..256d58625b 100644 --- a/vtkm/internal/VariantImplDetail.h.in +++ b/vtkm/internal/VariantImplDetail.h.in @@ -400,7 +400,7 @@ $for(param_index in range(max_expanded))\ return f(storage.V$(param_index), std::forward(args)...); $endfor\ default: - return VariantCases::template CastAndCall( + return VariantCases::template CastAndCall<>( index - $(max_expanded), std::forward(f), storage.Remaining, std::forward(args)...); } } -- GitLab From 4da7683241016a368c9232cefafcefced369a628 Mon Sep 17 00:00:00 2001 From: Dave Allured Date: Wed, 27 Nov 2024 14:49:52 -0500 Subject: [PATCH 33/73] README.md: spelling and grammar --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1057ae26e9..9b794aeac5 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ VTK-m has been tested on the following configurations:c ## Building ## -VTK-m supports all majors platforms (Windows, Linux, OSX), and uses CMake +VTK-m supports all major platforms (Windows, Linux, OSX), and uses CMake to generate all the build rules for the project. The VTK-m source code is available from the [VTK-m download page] or by directly cloning the [VTK-m git repository]. @@ -155,7 +155,7 @@ Users Guide]. The VTK-m source distribution includes a number of examples. The goal of the VTK-m examples is to illustrate specific VTK-m concepts in a consistent and -simple format. However, these examples only cover a small portion of the +simple format. However, these examples cover only a small portion of the capabilities of VTK-m. Below is a simple example of using VTK-m to create a simple data set and use VTK-m's rendering -- GitLab From eb81cf2a6e5a5114c743c8439ab7c6003e033439 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Mon, 2 Dec 2024 21:57:53 -0500 Subject: [PATCH 34/73] Circumvent shadow warnings with thrust swap We have run into issues with the `nvcc` compiler giving shadow warnings for the internals of thrust like this: ``` /usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h: In constructor 'thrust::detail::unary_negate::unary_negate(const Predicate&)': /usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h:45:46: warning: declaration of 'pred' shadows a member of 'thrust::detail::unary_negate' [-Wshadow] explicit unary_negate(const Predicate& pred) : pred(pred) {} ^ /usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h:42:11: note: shadowed declaration is here Predicate pred; ^ ``` These warnings seem to be caused by the inclusion of `thrust/swap.h`. To prevent this, this header file is no longer included from `vtkm/Swap.h`. --- docs/changelog/thrust-swap-warnings.md | 17 +++++++++++++++++ vtkm/Swap.h | 14 +++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 docs/changelog/thrust-swap-warnings.md diff --git a/docs/changelog/thrust-swap-warnings.md b/docs/changelog/thrust-swap-warnings.md new file mode 100644 index 0000000000..8d55a122c9 --- /dev/null +++ b/docs/changelog/thrust-swap-warnings.md @@ -0,0 +1,17 @@ +## Circumvent shadow warnings with thrust swap + +We have run into issues with the `nvcc` compiler giving shadow warnings for +the internals of thrust like this: + +``` +/usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h: In constructor 'thrust::detail::unary_negate::unary_negate(const Predicate&)': +/usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h:45:46: warning: declaration of 'pred' shadows a member of 'thrust::detail::unary_negate' [-Wshadow] + explicit unary_negate(const Predicate& pred) : pred(pred) {} + ^ +/usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h:42:11: note: shadowed declaration is here + Predicate pred; + ^ +``` + +These warnings seem to be caused by the inclusion of `thrust/swap.h`. To +prevent this, this header file is no longer included from `vtkm/Swap.h`. diff --git a/vtkm/Swap.h b/vtkm/Swap.h index 342c5a20c9..3459078213 100644 --- a/vtkm/Swap.h +++ b/vtkm/Swap.h @@ -13,31 +13,27 @@ #include -#ifdef VTKM_CUDA -#include -#else #include -#endif namespace vtkm { /// Performs a swap operation. Safe to call from cuda code. -#if defined(VTKM_CUDA) +#if defined(VTKM_CUDA) && defined(VTKM_CUDA_DEVICE_PASS) // CUDA 12 adds a `cub::Swap` function that creates ambiguity with `vtkm::Swap`. // This happens when a function from the `cub` namespace is called with an object of a class // defined in the `vtkm` namespace as an argument. If that function has an unqualified call to // `Swap`, it results in ADL being used, causing the templated functions `cub::Swap` and // `vtkm::Swap` to conflict. -#if defined(VTKM_CUDA_VERSION_MAJOR) && (VTKM_CUDA_VERSION_MAJOR >= 12) && \ - defined(VTKM_CUDA_DEVICE_PASS) +#if defined(VTKM_CUDA_VERSION_MAJOR) && (VTKM_CUDA_VERSION_MAJOR >= 12) using cub::Swap; #else template VTKM_EXEC_CONT inline void Swap(T& a, T& b) { - using thrust::swap; - swap(a, b); + T temp = a; + a = b; + b = temp; } #endif #elif defined(VTKM_HIP) -- GitLab From f4562afdac51cc47999a16f8bc285e3f2202af66 Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Thu, 5 Dec 2024 13:55:46 -0500 Subject: [PATCH 35/73] Fix the AdvectAlgorithmTerminator code. --- vtkm/filter/flow/internal/AdvectAlgorithm.h | 59 +++- .../flow/internal/AdvectAlgorithmTerminator.h | 310 +++++++++++++++--- .../flow/internal/AdvectAlgorithmThreaded.h | 17 +- vtkm/filter/flow/internal/DebugStream.h | 30 ++ vtkm/filter/flow/internal/ParticleExchanger.h | 66 +++- .../flow/testing/GenerateTestDataSets.h | 2 + vtkm/filter/flow/testing/TestingFlow.cxx | 9 +- .../flow/testing/UnitTestAdvectionMPI.cxx | 42 +++ 8 files changed, 475 insertions(+), 60 deletions(-) create mode 100644 vtkm/filter/flow/internal/DebugStream.h diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index bf23f2732e..e2559dc058 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -21,6 +21,8 @@ #include #endif +#include "DebugStream.h" + namespace vtkm { namespace filter @@ -64,6 +66,7 @@ public: , Rank(this->Comm.rank()) , Terminator(this->Comm) , Exchanger(this->Comm) + , DebugStream(this->Rank) { } @@ -116,10 +119,27 @@ public: this->SetSeedArray(particles, blockIDs); } + virtual bool HaveAnyWork() + { + auto numActive = this->Active.size(); + auto numInactive = this->Inactive.size(); + vtkm::Id num = this->Exchanger.GetNumberOfBufferedSends(); + + bool haveWork = numActive > 0 || numInactive > 0 || num > 0; + this->DebugStream << "HaveAnyWork: AIB= " << numActive << " " << numInactive << " " << num + << " val= " << haveWork << std::endl; + + return haveWork; + } + //Advect all the particles. virtual void Go() { - while (!this->Terminator.Done()) + bool anyWork = this->HaveAnyWork(); + this->DebugStream << "Go: anyWork= " << anyWork << std::endl; + this->Terminator.SetStatus(anyWork, this->DebugStream); + + while (true) { std::vector v; vtkm::Id blockId = -1; @@ -128,12 +148,19 @@ public: //make this a pointer to avoid the copy? auto& block = this->GetDataSet(blockId); DSIHelperInfo bb(v, this->BoundsMap, this->ParticleBlockIDsMap); + this->DebugStream << " Advect " << v[0] << std::endl; block.Advect(bb, this->StepSize); + this->DebugStream << " Advect DONE " << v[0] << std::endl; this->UpdateResult(bb); } + if (this->Terminator.GetDone(this->DebugStream)) + break; + //this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); this->ExchangeParticles(); - this->Terminator.Control(!this->Active.empty()); + + anyWork = this->HaveAnyWork(); + this->Terminator.SetStatus(anyWork, this->DebugStream); } } @@ -157,6 +184,7 @@ public: const std::vector>& blockIds) { VTKM_ASSERT(particles.size() == blockIds.size()); + //this->Terminator.AddWork(particles.size(), this->DebugStream); auto pit = particles.begin(); auto bit = blockIds.begin(); @@ -226,14 +254,32 @@ public: std::vector incoming; std::unordered_map> incomingBlockIDs; - this->Exchanger.Exchange( - outgoing, outgoingRanks, this->ParticleBlockIDsMap, incoming, incomingBlockIDs); + this->Exchanger.Exchange(outgoing, + outgoingRanks, + this->ParticleBlockIDsMap, + incoming, + incomingBlockIDs, + this->DebugStream); //Cleanup what was sent. for (const auto& p : outgoing) this->ParticleBlockIDsMap.erase(p.GetID()); + //if (!incoming.empty()) + // this->Terminator.AddWork(incoming.size(), this->DebugStream); + //if (!outgoing.empty()) + // this->Terminator.RemoveWork(outgoing.size(), this->DebugStream); + + vtkm::Id n = this->Exchanger.GetNumberOfBufferedSends(); + if (n > 0) + { + //this->DebugStream<<"Add BufferedSend work"<Terminator.AddWork(n, this->DebugStream); + } this->UpdateActive(incoming, incomingBlockIDs); + + //bool haveWork = this->HaveAnyWork(); + //this->Terminator.Control(haveWork, this->DebugStream); } void GetOutgoingParticles(std::vector& outgoing, @@ -303,8 +349,6 @@ public: if (!particles.empty()) { - this->Terminator.AddWork(); - for (auto pit = particles.begin(); pit != particles.end(); pit++) { vtkm::Id particleID = pit->GetID(); @@ -340,6 +384,8 @@ public: { for (const auto& id : stuff.TermID) this->ParticleBlockIDsMap.erase(id); + + //this->Terminator.RemoveWork(numTerm, this->DebugStream); } return numTerm; @@ -361,6 +407,7 @@ public: AdvectAlgorithmTerminator Terminator; ParticleExchanger Exchanger; + DebugStreamType DebugStream; }; } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h index 7b955a3268..c9d60a294f 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -11,6 +11,8 @@ #ifndef vtk_m_filter_flow_internal_AdvectAlgorithmTerminator_h #define vtk_m_filter_flow_internal_AdvectAlgorithmTerminator_h +#include "DebugStream.h" + namespace vtkm { namespace filter @@ -20,88 +22,310 @@ namespace flow namespace internal { +namespace //anonymous +{ +#ifdef VTKM_ENABLE_MPI +class ParallelAdvectAlgorithmTerminator +{ + static constexpr int UNSET = -1; + static constexpr int IDLE = 0; + static constexpr int ACTIVE = 1; + static constexpr int DONE = 2; + +public: + ParallelAdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) + : FirstCall(true) + , MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) + , NumRanks(comm.size()) + , Rank(comm.rank()) + , Token(UNSET) + { + this->FromRank = this->Rank - 1; + this->ToRank = this->Rank + 1; + if (this->Rank == 0) + this->FromRank = this->NumRanks - 1; + if (this->Rank == this->NumRanks - 1) + this->ToRank = 0; + } + +private: + int SendCnt = 0; + int RecvCnt = 0; + bool FirstCall; + MPI_Comm MPIComm; + MPI_Request RecvReq, SendReq; + int NumRanks; + int Rank, ToRank, FromRank; + int Token; + int SendToken, RecvToken; + int Tag = 314; + int RoundCnt = 0; + int SendBuffer[2], RecvBuffer[2]; +}; +#endif +} //namespace anonymous + class AdvectAlgorithmTerminator { + static constexpr int UNSET = -1; + static constexpr int IDLE = 0; + static constexpr int ACTIVE = 1; + static constexpr int DONE = 2; + public: #ifdef VTKM_ENABLE_MPI AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) - : MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) + : FirstCall(true) + , MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) + , NumRanks(comm.size()) + , Rank(comm.rank()) + , Token(UNSET) + { + this->FromRank = this->Rank - 1; + this->ToRank = this->Rank + 1; + if (this->Rank == 0) + this->FromRank = this->NumRanks - 1; + if (this->Rank == this->NumRanks - 1) + this->ToRank = 0; + } #else - AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) + AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) {} #endif + + void SetStatus(const bool& haveWork, DebugStreamType& debugStream) { + if (this->Token == DONE) + return; + + if (haveWork) + this->Token = ACTIVE; + else + this->Token = IDLE; + + debugStream << "SetStatus from Work: " << this->StatusToStr() + << " S,R counters= " << this->SendCnt << " " << this->RecvCnt << std::endl; } - void AddWork() + std::string StatusToStr() const { return this->StatusToStr(this->Token); } + + std::string StatusToStr(const int& status) const { -#ifdef VTKM_ENABLE_MPI - this->Dirty = 1; -#endif - } + std::string str; + if (status == UNSET) + str = "UNSET"; + else if (status == IDLE) + str = "IDLE"; + else if (status == ACTIVE) + str = "ACTIVE"; + else if (status == DONE) + str = "DONE"; + else + str = "ERROR " + std::to_string(status); - bool Done() const { return this->State == AdvectAlgorithmTerminatorState::DONE; } + return str; + } - void Control(bool haveLocalWork) + std::string Info(int token = -100) const { + int tmp = this->Token; + if (token != -100) + tmp = token; + std::string str = this->StatusToStr(tmp) + " S,R cnt= " + std::to_string(this->SendCnt) + " " + + std::to_string(this->RecvCnt); + str = str + " roundCnt= " + std::to_string(this->RoundCnt); + if (this->SendCnt > 1 || this->RecvCnt > 1) + str = str + " ********** ERROR"; + return str; + } #ifdef VTKM_ENABLE_MPI - if (this->State == STATE_0 && !haveLocalWork) - { - MPI_Ibarrier(this->MPIComm, &this->StateReq); - this->Dirty = 0; - this->State = STATE_1; - } - else if (this->State == STATE_1) + bool CheckSendComplete(DebugStreamType& debugStream) + { + if (this->SendCnt > 0) { MPI_Status status; int flag; - MPI_Test(&this->StateReq, &flag, &status); + int err = MPI_Test(&this->SendReq, &flag, &status); if (flag == 1) { - int localDirty = this->Dirty; - MPI_Iallreduce( - &localDirty, &this->AllDirty, 1, MPI_INT, MPI_LOR, this->MPIComm, &this->StateReq); - this->State = STATE_2; + this->SendCnt--; + debugStream << " Send completed. cnt=" << this->SendCnt << std::endl; + return true; } } - else if (this->State == STATE_2) + + return false; + } + + bool CheckRecvComplete(DebugStreamType& debugStream) + { + if (this->RecvCnt > 0) { MPI_Status status; int flag; - MPI_Test(&this->StateReq, &flag, &status); + int err = MPI_Test(&this->RecvReq, &flag, &status); if (flag == 1) { - if (this->AllDirty == 0) //done - this->State = DONE; - else - this->State = STATE_0; //reset. + this->RecvCnt--; + this->RecvToken = this->RecvBuffer[0]; + this->RoundCnt = this->RecvBuffer[1]; + debugStream << " Recv(" << this->FromRank << ") " << this->StatusToStr(this->RecvToken) + << " " << this->RoundCnt << " cnt= " << this->RecvCnt << std::endl; + return true; } } -#else - if (!haveLocalWork) - this->State = DONE; -#endif + return false; } -private: - enum AdvectAlgorithmTerminatorState + void PostSendToken(DebugStreamType& debugStream) + { + this->SendToken = this->Token; + if (this->SendToken == IDLE && (this->SendCnt > 0 || this->RecvCnt > 0)) + { + debugStream << " idle --> active" << std::endl; + this->SendToken = ACTIVE; + } + this->SendCnt++; + this->SendBuffer[0] = this->SendToken; + this->SendBuffer[1] = this->RoundCnt; + MPI_Isend( + &this->SendBuffer, 2, MPI_INT, this->ToRank, this->Tag, this->MPIComm, &this->SendReq); + if (this->Rank == 0 && this->FirstCall) + debugStream << " BEGIN "; + + debugStream << " Send(" << this->ToRank << ") " << this->StatusToStr(this->SendToken) << " " + << this->RoundCnt << " " << &this->SendReq << " cnt= " << this->SendCnt + << std::endl; + } + + void PostRecvToken(DebugStreamType& debugStream) + { + MPI_Irecv( + &this->RecvBuffer, 2, MPI_INT, this->FromRank, this->Tag, this->MPIComm, &this->RecvReq); + this->RecvCnt++; + debugStream << " Post Irecv(" << this->FromRank << ") " << &this->RecvReq + << " cnt= " << this->RecvCnt << std::endl; + } + + // Rank 0 is the head of the ring. + void RingHead(DebugStreamType& debugStream) { - STATE_0, - STATE_1, - STATE_2, - DONE - }; + //Begin the ring on the first call. + if (this->FirstCall) + { + this->PostSendToken(debugStream); + debugStream << "GetDone1: from/to " << this->FromRank << " " << this->ToRank << " " + << this->Info() << std::endl; + this->FirstCall = false; + return; + } + + //If send is done, then post a receive and return. + if (this->CheckSendComplete(debugStream)) + { + this->PostRecvToken(debugStream); + debugStream << "GetDone1: from/to " << this->FromRank << " " << this->ToRank << " " + << this->Info() << std::endl; + return; + } + + //Tail of ring responds back to head. + if (this->CheckRecvComplete(debugStream)) + { + //If the entire ring is IDLE and root is still IDLE, we are done. + if (this->RecvToken == IDLE && this->Token == IDLE) + { + debugStream << " everyone idle. DONE" << std::endl; + this->Token = DONE; + } + this->RoundCnt++; + this->PostSendToken(debugStream); + debugStream << "GetDone1: from/to " << this->FromRank << " " << this->ToRank << " " + << this->Info() << " NEW ROUND" << std::endl; + } + } - AdvectAlgorithmTerminatorState State = AdvectAlgorithmTerminatorState::STATE_0; + // Rank 1 to NumRanks-1 is the body of the ring. + void RingBody(DebugStreamType& debugStream) + { + //We always need 1 active recv. + if (this->RecvCnt == 0) + { + this->PostRecvToken(debugStream); + //debugStream<<"GetDone1: from/to "<FromRank<<" "<ToRank<<" "<Info()<CheckSendComplete(debugStream); + + // Check if token received from prev neighbor. + if (this->CheckRecvComplete(debugStream)) + { + //If previous token is ACTIVE, pass on ACTIVE; + //If previous token is DONE, pass on DONE. + //otherwise, previous is IDLE, so pass on my token. + if (this->RecvToken == ACTIVE) + this->Token = ACTIVE; + else if (this->RecvToken == DONE) + this->Token = DONE; + + //Pass token to forward neighbor. + this->PostSendToken(debugStream); + debugStream << "GetDone1: from/to " << this->FromRank << " " << this->ToRank << " " + << this->Info() << std::endl; + } + } +#endif + + bool GetDone(DebugStreamType& debugStream) + { + debugStream << std::endl + << "GetDone0: from/to " << this->FromRank << " " << this->ToRank << " " + << this->Info() << std::endl; + // Once we are done, we need to wait for the sends to complete so that the forward neighbor gets the message. + if (this->Token == DONE && this->SendCnt == 0) + { + //If we have an outstanding recv, cancel it. + if (this->RecvCnt > 0) + { + MPI_Cancel(&this->RecvReq); + MPI_Request_free(&this->RecvReq); + this->RecvCnt--; + } + VTKM_ASSERT(this->RecvCnt == 0); + debugStream << "GetDone1: from/to " << this->FromRank << " " << this->ToRank << " " + << this->Info() << std::endl; + return true; + } + + //Otherwise, pass tokens along the ring. + if (this->Rank == 0) + this->RingHead(debugStream); + else + this->RingBody(debugStream); + + return false; + } + +private: + //bool Done; #ifdef VTKM_ENABLE_MPI - std::atomic Dirty; - int AllDirty = 0; - MPI_Request StateReq; + int SendCnt = 0; + int RecvCnt = 0; + bool FirstCall; MPI_Comm MPIComm; + MPI_Request RecvReq, SendReq; + int NumRanks; + int Rank, ToRank, FromRank; + + int Token; + int SendToken, RecvToken; + int Tag = 314; + int RoundCnt = 0; + int SendBuffer[2], RecvBuffer[2]; #endif }; - } } } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h index e79302048e..45ab312957 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h @@ -47,6 +47,9 @@ public: void Go() override { + this->DebugStream << "Go: work= " << this->HaveAnyWork() << std::endl; + //this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); + std::vector workerThreads; workerThreads.emplace_back(std::thread(AdvectAlgorithmThreaded::Worker, this)); this->Manage(); @@ -62,7 +65,8 @@ protected: { std::lock_guard lock(this->Mutex); //We have work if there particles in any queues or a worker is busy. - return !this->Active.empty() || !this->Inactive.empty() || this->WorkerActivate; + return !this->Active.empty() || !this->Inactive.empty() || this->WorkerActivate || + this->Exchanger.GetNumberOfBufferedSends() > 0; } bool GetActiveParticles(std::vector& particles, vtkm::Id& blockId) override @@ -70,6 +74,8 @@ protected: std::lock_guard lock(this->Mutex); bool val = this->AdvectAlgorithm::GetActiveParticles(particles, blockId); this->WorkerActivate = val; + if (val) + this->DebugStream << " Advect " << particles[0] << std::endl; return val; } @@ -135,17 +141,22 @@ protected: void Manage() { - while (!this->Terminator.Done()) + //this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); + while (!this->Terminator.GetDone(this->DebugStream)) { std::unordered_map>> workerResults; this->GetWorkerResults(workerResults); for (auto& it : workerResults) for (auto& r : it.second) + { this->UpdateResult(r); + this->DebugStream << " Advect DONE " << std::endl; + } + //this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); this->ExchangeParticles(); - this->Terminator.Control(this->HaveAnyWork()); + //this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); } //Let the workers know that we are done. diff --git a/vtkm/filter/flow/internal/DebugStream.h b/vtkm/filter/flow/internal/DebugStream.h new file mode 100644 index 0000000000..a67679c56c --- /dev/null +++ b/vtkm/filter/flow/internal/DebugStream.h @@ -0,0 +1,30 @@ +#pragma once + + +class DebugStreamType +{ +public: + DebugStreamType(int rank) + : Stream("Debug." + std::to_string(rank) + ".txt") + { + } + + template + DebugStreamType& operator<<(const T& value) + { + std::lock_guard lock(this->Mutex); + this->Stream << value; + return *this; + } + + DebugStreamType& operator<<(std::ostream& (*manip)(std::ostream&)) + { + std::lock_guard lock(this->Mutex); + this->Stream << manip; + return *this; + } + +private: + std::ofstream Stream; + std::mutex Mutex; +}; diff --git a/vtkm/filter/flow/internal/ParticleExchanger.h b/vtkm/filter/flow/internal/ParticleExchanger.h index 3da4eb7374..cab23d9d60 100644 --- a/vtkm/filter/flow/internal/ParticleExchanger.h +++ b/vtkm/filter/flow/internal/ParticleExchanger.h @@ -35,14 +35,24 @@ public: { } #ifdef VTKM_ENABLE_MPI - ~ParticleExchanger() { this->CleanupSendBuffers(false); } + ~ParticleExchanger() {} //{ this->CleanupSendBuffers(false); } #endif + vtkm::Id GetNumberOfBufferedSends() const + { +#ifdef VTKM_ENABLE_MPI + return static_cast(this->SendBuffers.size()); +#else + return 0; +#endif + } + void Exchange(const std::vector& outData, const std::vector& outRanks, const std::unordered_map>& outBlockIDsMap, std::vector& inData, - std::unordered_map>& inDataBlockIDsMap) + std::unordered_map>& inDataBlockIDsMap, + DebugStreamType& debugStream) { VTKM_ASSERT(outData.size() == outRanks.size()); @@ -51,9 +61,16 @@ public: #ifdef VTKM_ENABLE_MPI else { + debugStream << " PE::sendbuffs0= " << this->GetNumberOfBufferedSends() << std::endl; + this->CleanupSendBuffers(true, debugStream); + debugStream << " PE::sendbuffs1= " << this->GetNumberOfBufferedSends() << std::endl; + if (!outData.empty()) + debugStream << " PE::Send " << outData[0] << std::endl; this->SendParticles(outData, outRanks, outBlockIDsMap); this->RecvParticles(inData, inDataBlockIDsMap); - this->CleanupSendBuffers(true); + if (!inData.empty()) + debugStream << " PE::Recv " << inData[0] << std::endl; + debugStream << " PE::sendbuffs2= " << this->GetNumberOfBufferedSends() << std::endl; } #endif } @@ -76,7 +93,7 @@ private: #ifdef VTKM_ENABLE_MPI using ParticleCommType = std::pair>; - void CleanupSendBuffers(bool checkRequests) + void CleanupSendBuffers(bool checkRequests, DebugStreamType& debugStream) { if (!checkRequests) { @@ -86,17 +103,52 @@ private: return; } + debugStream << " PE::CleanupSendBuffers sz= " << this->SendBuffers.size() << std::endl; + if (this->SendBuffers.empty()) + { + return; + } + std::vector requests; for (auto& req : this->SendBuffers) requests.emplace_back(req.first); + //MPI_Testsome will update the complete requests to MPI_REQUEST_NULL. + //Because we are using the MPI_Request as a key in SendBuffers, we need + //to make a copy. + auto requestsOrig = requests; + std::vector status(requests.size()); std::vector indices(requests.size()); int num = 0; int err = MPI_Testsome(requests.size(), requests.data(), &num, indices.data(), status.data()); + if (err != MPI_SUCCESS) throw vtkm::cont::ErrorFilterExecution( "Error with MPI_Testsome in ParticleExchanger::CleanupSendBuffers"); + + debugStream << " MPI_Testsome0: " << num + << " --> SendBuffers.size= " << this->SendBuffers.size() << std::endl; + if (num > 0) + { + for (int i = 0; i < num; i++) + { + std::size_t idx = static_cast(indices[i]); + const auto& req = requestsOrig[idx]; + //const auto& stat = status[idx]; + auto it = this->SendBuffers.find(req); + if (it == this->SendBuffers.end()) + throw vtkm::cont::ErrorFilterExecution( + "Missing request in ParticleExchanger::CleanupSendBuffers"); + + //Delete the buffer and remove from SendBuffers. + delete it->second; + this->SendBuffers.erase(it); + std::cout << this->Rank << " SendBuffer: Delete" << std::endl; + } + } + debugStream << " MPI_Testsome1: " << num + << " --> SendBuffers.size= " << this->SendBuffers.size() << std::endl; } void SendParticles(const std::vector& outData, @@ -137,7 +189,8 @@ private: bb->reset(); MPI_Request req; - int err = MPI_Isend(bb->buffer.data(), bb->size(), MPI_BYTE, dst, 0, this->MPIComm, &req); + int err = + MPI_Isend(bb->buffer.data(), bb->size(), MPI_BYTE, dst, this->Tag, this->MPIComm, &req); if (err != MPI_SUCCESS) throw vtkm::cont::ErrorFilterExecution("Error in MPI_Isend inside Messenger::SendData"); this->SendBuffers[req] = bb; @@ -155,7 +208,7 @@ private: while (true) { int flag = 0; - int err = MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, this->MPIComm, &flag, &status); + int err = MPI_Iprobe(MPI_ANY_SOURCE, this->Tag, this->MPIComm, &flag, &status); if (err != MPI_SUCCESS) throw vtkm::cont::ErrorFilterExecution( "Error in MPI_Probe in ParticleExchanger::RecvParticles"); @@ -210,6 +263,7 @@ private: vtkm::Id NumRanks; vtkm::Id Rank; std::unordered_map SendBuffers; + int Tag = 100; #else vtkm::Id NumRanks = 1; vtkm::Id Rank = 0; diff --git a/vtkm/filter/flow/testing/GenerateTestDataSets.h b/vtkm/filter/flow/testing/GenerateTestDataSets.h index 6ec53a9ec2..fd699643f7 100644 --- a/vtkm/filter/flow/testing/GenerateTestDataSets.h +++ b/vtkm/filter/flow/testing/GenerateTestDataSets.h @@ -206,6 +206,7 @@ inline std::vector CreateAllDataSets(const vtkm::Bounds& bo std::vector dataSets; dataSets.push_back(vtkm::worklet::testing::CreateUniformDataSet(bounds, dims, addGhost)); + /* dataSets.push_back(vtkm::worklet::testing::CreateRectilinearDataSet(bounds, dims, addGhost)); dataSets.push_back(vtkm::worklet::testing::CreateExplicitFromStructuredDataSet( bounds, dims, ExplicitDataSetOption::SINGLE, addGhost)); @@ -213,6 +214,7 @@ inline std::vector CreateAllDataSets(const vtkm::Bounds& bo bounds, dims, ExplicitDataSetOption::CURVILINEAR, addGhost)); dataSets.push_back(vtkm::worklet::testing::CreateExplicitFromStructuredDataSet( bounds, dims, ExplicitDataSetOption::EXPLICIT, addGhost)); + */ return dataSets; } diff --git a/vtkm/filter/flow/testing/TestingFlow.cxx b/vtkm/filter/flow/testing/TestingFlow.cxx index 4b316e3735..36ff44549f 100644 --- a/vtkm/filter/flow/testing/TestingFlow.cxx +++ b/vtkm/filter/flow/testing/TestingFlow.cxx @@ -251,7 +251,11 @@ void TestPartitionedDataSet(vtkm::Id nPerRank, if (comm.rank() == comm.size() - 1) { bool checkEnds = out.GetNumberOfPartitions() == static_cast(blockIds.size()); - VTKM_TEST_ASSERT(out.GetNumberOfPartitions() == 1, "Wrong number of partitions in output"); + auto nP = out.GetNumberOfPartitions(); + if (nP != 1) + std::cout << comm.rank() << " numPartitions= " << nP << std::endl; + VTKM_TEST_ASSERT(out.GetNumberOfPartitions() == 1, + "Wrong number of partitions in output 1"); ValidateOutput(out.GetPartition(0), numSeeds, xMaxRanges[xMaxRanges.size() - 1], @@ -260,7 +264,8 @@ void TestPartitionedDataSet(vtkm::Id nPerRank, duplicateBlocks); } else - VTKM_TEST_ASSERT(out.GetNumberOfPartitions() == 0, "Wrong number of partitions in output"); + VTKM_TEST_ASSERT(out.GetNumberOfPartitions() == 0, + "Wrong number of partitions in output 0"); } else if (fType == PATHLINE) { diff --git a/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx b/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx index 9a992ed8e5..6747669a40 100644 --- a/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx +++ b/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx @@ -14,25 +14,67 @@ #include +#include +static void InitDebug() +{ + int nRanks, rank; + MPI_Comm_size(MPI_COMM_WORLD, &nRanks); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier(MPI_COMM_WORLD); + std::cout << rank << " InitDebug()" << std::endl; + + for (int r = 0; r < nRanks; r++) + { + if (r == rank) + std::cout << "Rank: " << r << " pid= " << getpid() << std::endl; + MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier(MPI_COMM_WORLD); + } + + int doLoop = (rank == 0); + while (doLoop) + { + sleep(1); + //std::cout<<"Sleeping..."< 1) { -- GitLab From e8d06177069c531275a1d8cb0829eca40ff8e0f4 Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Thu, 5 Dec 2024 15:25:59 -0500 Subject: [PATCH 36/73] refactor the terminator code. --- .../flow/internal/AdvectAlgorithmTerminator.h | 334 +++++++++--------- 1 file changed, 166 insertions(+), 168 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h index c9d60a294f..280be3d03a 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -22,9 +22,9 @@ namespace flow namespace internal { +#ifdef VTKM_ENABLE_MPI namespace //anonymous { -#ifdef VTKM_ENABLE_MPI class ParallelAdvectAlgorithmTerminator { static constexpr int UNSET = -1; @@ -48,164 +48,50 @@ public: this->ToRank = 0; } -private: - int SendCnt = 0; - int RecvCnt = 0; - bool FirstCall; - MPI_Comm MPIComm; - MPI_Request RecvReq, SendReq; - int NumRanks; - int Rank, ToRank, FromRank; - int Token; - int SendToken, RecvToken; - int Tag = 314; - int RoundCnt = 0; - int SendBuffer[2], RecvBuffer[2]; -}; -#endif -} //namespace anonymous - -class AdvectAlgorithmTerminator -{ - static constexpr int UNSET = -1; - static constexpr int IDLE = 0; - static constexpr int ACTIVE = 1; - static constexpr int DONE = 2; - -public: -#ifdef VTKM_ENABLE_MPI - AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) - : FirstCall(true) - , MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) - , NumRanks(comm.size()) - , Rank(comm.rank()) - , Token(UNSET) - { - this->FromRank = this->Rank - 1; - this->ToRank = this->Rank + 1; - if (this->Rank == 0) - this->FromRank = this->NumRanks - 1; - if (this->Rank == this->NumRanks - 1) - this->ToRank = 0; - } -#else - AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) {} -#endif - void SetStatus(const bool& haveWork, DebugStreamType& debugStream) { - if (this->Token == DONE) - return; - - if (haveWork) - this->Token = ACTIVE; - else - this->Token = IDLE; - - debugStream << "SetStatus from Work: " << this->StatusToStr() - << " S,R counters= " << this->SendCnt << " " << this->RecvCnt << std::endl; - } - - std::string StatusToStr() const { return this->StatusToStr(this->Token); } - - std::string StatusToStr(const int& status) const - { - std::string str; - if (status == UNSET) - str = "UNSET"; - else if (status == IDLE) - str = "IDLE"; - else if (status == ACTIVE) - str = "ACTIVE"; - else if (status == DONE) - str = "DONE"; - else - str = "ERROR " + std::to_string(status); - - return str; - } - - std::string Info(int token = -100) const - { - int tmp = this->Token; - if (token != -100) - tmp = token; - std::string str = this->StatusToStr(tmp) + " S,R cnt= " + std::to_string(this->SendCnt) + " " + - std::to_string(this->RecvCnt); - str = str + " roundCnt= " + std::to_string(this->RoundCnt); - if (this->SendCnt > 1 || this->RecvCnt > 1) - str = str + " ********** ERROR"; - return str; - } -#ifdef VTKM_ENABLE_MPI - bool CheckSendComplete(DebugStreamType& debugStream) - { - if (this->SendCnt > 0) + if (this->Token != DONE) { - MPI_Status status; - int flag; - int err = MPI_Test(&this->SendReq, &flag, &status); - if (flag == 1) - { - this->SendCnt--; - debugStream << " Send completed. cnt=" << this->SendCnt << std::endl; - return true; - } + if (haveWork) + this->Token = ACTIVE; + else + this->Token = IDLE; } - - return false; + debugStream << "SetStatus from Work: " << this->StatusToStr() + << " S,R counters= " << this->SendCnt << " " << this->RecvCnt << std::endl; } - bool CheckRecvComplete(DebugStreamType& debugStream) + bool GetDone(DebugStreamType& debugStream) { - if (this->RecvCnt > 0) + debugStream << std::endl + << "GetDone0: from/to " << this->FromRank << " " << this->ToRank << " " + << this->Info() << std::endl; + // Once we are done, we need to wait for the sends to complete so that the forward neighbor gets the message. + if (this->Token == DONE && this->SendCnt == 0) { - MPI_Status status; - int flag; - int err = MPI_Test(&this->RecvReq, &flag, &status); - if (flag == 1) + //If we have an outstanding recv, cancel it. + if (this->RecvCnt > 0) { + MPI_Cancel(&this->RecvReq); + MPI_Request_free(&this->RecvReq); this->RecvCnt--; - this->RecvToken = this->RecvBuffer[0]; - this->RoundCnt = this->RecvBuffer[1]; - debugStream << " Recv(" << this->FromRank << ") " << this->StatusToStr(this->RecvToken) - << " " << this->RoundCnt << " cnt= " << this->RecvCnt << std::endl; - return true; } + VTKM_ASSERT(this->RecvCnt == 0); + debugStream << "GetDone1: from/to " << this->FromRank << " " << this->ToRank << " " + << this->Info() << std::endl; + return true; } - return false; - } - void PostSendToken(DebugStreamType& debugStream) - { - this->SendToken = this->Token; - if (this->SendToken == IDLE && (this->SendCnt > 0 || this->RecvCnt > 0)) - { - debugStream << " idle --> active" << std::endl; - this->SendToken = ACTIVE; - } - this->SendCnt++; - this->SendBuffer[0] = this->SendToken; - this->SendBuffer[1] = this->RoundCnt; - MPI_Isend( - &this->SendBuffer, 2, MPI_INT, this->ToRank, this->Tag, this->MPIComm, &this->SendReq); - if (this->Rank == 0 && this->FirstCall) - debugStream << " BEGIN "; - - debugStream << " Send(" << this->ToRank << ") " << this->StatusToStr(this->SendToken) << " " - << this->RoundCnt << " " << &this->SendReq << " cnt= " << this->SendCnt - << std::endl; - } + //Otherwise, pass tokens along the ring. + if (this->Rank == 0) + this->RingHead(debugStream); + else + this->RingBody(debugStream); - void PostRecvToken(DebugStreamType& debugStream) - { - MPI_Irecv( - &this->RecvBuffer, 2, MPI_INT, this->FromRank, this->Tag, this->MPIComm, &this->RecvReq); - this->RecvCnt++; - debugStream << " Post Irecv(" << this->FromRank << ") " << &this->RecvReq - << " cnt= " << this->RecvCnt << std::endl; + return false; } +private: // Rank 0 is the head of the ring. void RingHead(DebugStreamType& debugStream) { @@ -275,41 +161,76 @@ public: << this->Info() << std::endl; } } -#endif - bool GetDone(DebugStreamType& debugStream) + bool CheckSendComplete(DebugStreamType& debugStream) { - debugStream << std::endl - << "GetDone0: from/to " << this->FromRank << " " << this->ToRank << " " - << this->Info() << std::endl; - // Once we are done, we need to wait for the sends to complete so that the forward neighbor gets the message. - if (this->Token == DONE && this->SendCnt == 0) + if (this->SendCnt > 0) { - //If we have an outstanding recv, cancel it. - if (this->RecvCnt > 0) + MPI_Status status; + int flag; + int err = MPI_Test(&this->SendReq, &flag, &status); + if (flag == 1) { - MPI_Cancel(&this->RecvReq); - MPI_Request_free(&this->RecvReq); - this->RecvCnt--; + this->SendCnt--; + debugStream << " Send completed. cnt=" << this->SendCnt << std::endl; + return true; } - VTKM_ASSERT(this->RecvCnt == 0); - debugStream << "GetDone1: from/to " << this->FromRank << " " << this->ToRank << " " - << this->Info() << std::endl; - return true; } - //Otherwise, pass tokens along the ring. - if (this->Rank == 0) - this->RingHead(debugStream); - else - this->RingBody(debugStream); + return false; + } + bool CheckRecvComplete(DebugStreamType& debugStream) + { + if (this->RecvCnt > 0) + { + MPI_Status status; + int flag; + int err = MPI_Test(&this->RecvReq, &flag, &status); + if (flag == 1) + { + this->RecvCnt--; + this->RecvToken = this->RecvBuffer[0]; + this->RoundCnt = this->RecvBuffer[1]; + debugStream << " Recv(" << this->FromRank << ") " << this->StatusToStr(this->RecvToken) + << " " << this->RoundCnt << " cnt= " << this->RecvCnt << std::endl; + return true; + } + } return false; } -private: - //bool Done; -#ifdef VTKM_ENABLE_MPI + void PostSendToken(DebugStreamType& debugStream) + { + this->SendToken = this->Token; + if (this->SendToken == IDLE && (this->SendCnt > 0 || this->RecvCnt > 0)) + { + debugStream << " idle --> active" << std::endl; + this->SendToken = ACTIVE; + } + this->SendCnt++; + this->SendBuffer[0] = this->SendToken; + this->SendBuffer[1] = this->RoundCnt; + MPI_Isend( + &this->SendBuffer, 2, MPI_INT, this->ToRank, this->Tag, this->MPIComm, &this->SendReq); + if (this->Rank == 0 && this->FirstCall) + debugStream << " BEGIN "; + + debugStream << " Send(" << this->ToRank << ") " << this->StatusToStr(this->SendToken) << " " + << this->RoundCnt << " " << &this->SendReq << " cnt= " << this->SendCnt + << std::endl; + } + + void PostRecvToken(DebugStreamType& debugStream) + { + MPI_Irecv( + &this->RecvBuffer, 2, MPI_INT, this->FromRank, this->Tag, this->MPIComm, &this->RecvReq); + this->RecvCnt++; + debugStream << " Post Irecv(" << this->FromRank << ") " << &this->RecvReq + << " cnt= " << this->RecvCnt << std::endl; + } + + int SendCnt = 0; int RecvCnt = 0; bool FirstCall; @@ -317,12 +238,89 @@ private: MPI_Request RecvReq, SendReq; int NumRanks; int Rank, ToRank, FromRank; - int Token; int SendToken, RecvToken; int Tag = 314; int RoundCnt = 0; int SendBuffer[2], RecvBuffer[2]; + + + //stuff to toss. + std::string StatusToStr() const { return this->StatusToStr(this->Token); } + + std::string StatusToStr(const int& status) const + { + std::string str; + if (status == UNSET) + str = "UNSET"; + else if (status == IDLE) + str = "IDLE"; + else if (status == ACTIVE) + str = "ACTIVE"; + else if (status == DONE) + str = "DONE"; + else + str = "ERROR " + std::to_string(status); + + return str; + } + + std::string Info(int token = -100) const + { + int tmp = this->Token; + if (token != -100) + tmp = token; + std::string str = this->StatusToStr(tmp) + " S,R cnt= " + std::to_string(this->SendCnt) + " " + + std::to_string(this->RecvCnt); + str = str + " roundCnt= " + std::to_string(this->RoundCnt); + if (this->SendCnt > 1 || this->RecvCnt > 1) + str = str + " ********** ERROR"; + return str; + } +}; +} //namespace anonymous +#endif + +class AdvectAlgorithmTerminator +{ + static constexpr int UNSET = -1; + static constexpr int IDLE = 0; + static constexpr int ACTIVE = 1; + static constexpr int DONE = 2; + +public: +#ifdef VTKM_ENABLE_MPI + AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) + : HaveWork(false) + , ParallelTerminator(comm) +#else + AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) + : HaveWork(false) +#endif + { + } + + void SetStatus(const bool& haveWork, DebugStreamType& debugStream) + { + this->HaveWork = haveWork; +#ifdef VTKM_ENABLE_MPI + this->ParallelTerminator.SetStatus(this->HaveWork, debugStream); +#endif + } + + bool GetDone(DebugStreamType& debugStream) + { +#ifdef VTKM_ENABLE_MPI + return this->ParallelTerminator.GetDone(debugStream); +#else + return !this->HaveWork; +#endif + } + +private: + bool HaveWork; +#ifdef VTKM_ENABLE_MPI + ParallelAdvectAlgorithmTerminator ParallelTerminator; #endif }; -- GitLab From b12200032e3aa0df566366ea7f89755a7c4ca97d Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Thu, 5 Dec 2024 21:25:06 -0500 Subject: [PATCH 37/73] Fix timing-based bug in terminator. --- vtkm/filter/flow/internal/AdvectAlgorithm.h | 89 +++-- .../flow/internal/AdvectAlgorithmTerminator.h | 376 ++++++------------ .../flow/internal/AdvectAlgorithmThreaded.h | 54 ++- vtkm/filter/flow/internal/ParticleExchanger.h | 10 +- vtkm/filter/flow/testing/TestingFlow.cxx | 4 +- 5 files changed, 222 insertions(+), 311 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index e2559dc058..04565426bc 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -11,6 +11,7 @@ #ifndef vtk_m_filter_flow_internal_AdvectAlgorithm_h #define vtk_m_filter_flow_internal_AdvectAlgorithm_h + #include #include #include @@ -53,6 +54,9 @@ ParticleMessenger::Exchange() */ + +#define DEBUG_STREAM(x) this->DebugStream << x; + template class AdvectAlgorithm { @@ -68,6 +72,7 @@ public: , Exchanger(this->Comm) , DebugStream(this->Rank) { + this->DebugStream << "ctor\n"; } void Execute(const vtkm::cont::ArrayHandle& seeds, vtkm::FloatDefault stepSize) @@ -119,48 +124,46 @@ public: this->SetSeedArray(particles, blockIDs); } - virtual bool HaveAnyWork() + bool HaveWork() { - auto numActive = this->Active.size(); - auto numInactive = this->Inactive.size(); - vtkm::Id num = this->Exchanger.GetNumberOfBufferedSends(); - - bool haveWork = numActive > 0 || numInactive > 0 || num > 0; - this->DebugStream << "HaveAnyWork: AIB= " << numActive << " " << numInactive << " " << num - << " val= " << haveWork << std::endl; + int activeCnt = this->Active.size(); + int inactiveCnt = this->Inactive.size(); + int numBuffers = this->Exchanger.GetNumberOfBufferedSends(); + bool haveWork = (activeCnt > 0 || inactiveCnt > 0 || numBuffers > 0); return haveWork; } //Advect all the particles. virtual void Go() { - bool anyWork = this->HaveAnyWork(); - this->DebugStream << "Go: anyWork= " << anyWork << std::endl; - this->Terminator.SetStatus(anyWork, this->DebugStream); + this->NumParticlesWorkingOn = 0; + if (!this->Active.empty() || !this->Inactive.empty()) + { + this->NumParticlesWorkingOn = this->Inactive.size(); + for (const auto& it : this->Active) + this->NumParticlesWorkingOn += it.second.size(); + } - while (true) + while (!this->Terminator.Done()) { std::vector v; vtkm::Id blockId = -1; + + this->Terminator.Control(this->HaveWork(), this->DebugStream); if (this->GetActiveParticles(v, blockId)) { //make this a pointer to avoid the copy? auto& block = this->GetDataSet(blockId); DSIHelperInfo bb(v, this->BoundsMap, this->ParticleBlockIDsMap); - this->DebugStream << " Advect " << v[0] << std::endl; + this->DebugStream << " Advect: " << v[0] << "\n"; block.Advect(bb, this->StepSize); - this->DebugStream << " Advect DONE " << v[0] << std::endl; this->UpdateResult(bb); + this->DebugStream << " Advect DONE.\n"; } - if (this->Terminator.GetDone(this->DebugStream)) - break; - //this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); + //this->Terminator.Control(this->HaveWork(), this->DebugStream); this->ExchangeParticles(); - - anyWork = this->HaveAnyWork(); - this->Terminator.SetStatus(anyWork, this->DebugStream); } } @@ -184,7 +187,6 @@ public: const std::vector>& blockIds) { VTKM_ASSERT(particles.size() == blockIds.size()); - //this->Terminator.AddWork(particles.size(), this->DebugStream); auto pit = particles.begin(); auto bit = blockIds.begin(); @@ -244,7 +246,7 @@ public: return !particles.empty(); } - void ExchangeParticles() + bool ExchangeParticles() { std::vector outgoing; std::vector outgoingRanks; @@ -254,6 +256,10 @@ public: std::vector incoming; std::unordered_map> incomingBlockIDs; + if (!outgoing.empty()) + this->DebugStream << "Exchange: outgoing= " << std::to_string(outgoing.size()) << " " + << outgoing[0] << "\n"; + this->Exchanger.Exchange(outgoing, outgoingRanks, this->ParticleBlockIDsMap, @@ -261,25 +267,29 @@ public: incomingBlockIDs, this->DebugStream); + bool val = false; + if (!incoming.empty()) + { + this->Terminator.AddWork(incoming.size(), this->DebugStream); + this->NumParticlesWorkingOn += incoming.size(); + val = true; + } + if (!outgoing.empty()) + { + this->Terminator.RemoveWork(outgoing.size(), this->DebugStream); + this->NumParticlesWorkingOn -= outgoing.size(); + val = true; + } + //Cleanup what was sent. for (const auto& p : outgoing) this->ParticleBlockIDsMap.erase(p.GetID()); - //if (!incoming.empty()) - // this->Terminator.AddWork(incoming.size(), this->DebugStream); - //if (!outgoing.empty()) - // this->Terminator.RemoveWork(outgoing.size(), this->DebugStream); - - vtkm::Id n = this->Exchanger.GetNumberOfBufferedSends(); - if (n > 0) - { - //this->DebugStream<<"Add BufferedSend work"<Terminator.AddWork(n, this->DebugStream); - } + if (!incoming.empty()) + this->DebugStream << "Exchange: incoming= " << incoming.size() << " " << incoming[0] << "\n"; this->UpdateActive(incoming, incomingBlockIDs); - //bool haveWork = this->HaveAnyWork(); - //this->Terminator.Control(haveWork, this->DebugStream); + return val; } void GetOutgoingParticles(std::vector& outgoing, @@ -349,6 +359,8 @@ public: if (!particles.empty()) { + //this->Terminator.AddWork(this->DebugStream); + for (auto pit = particles.begin(); pit != particles.end(); pit++) { vtkm::Id particleID = pit->GetID(); @@ -382,10 +394,11 @@ public: //Update terminated particles. if (numTerm > 0) { + this->DebugStream << "Terminated: " << numTerm << "\n"; for (const auto& id : stuff.TermID) this->ParticleBlockIDsMap.erase(id); - - //this->Terminator.RemoveWork(numTerm, this->DebugStream); + this->Terminator.RemoveWork(numTerm, this->DebugStream); + this->NumParticlesWorkingOn -= numTerm; } return numTerm; @@ -407,6 +420,8 @@ public: AdvectAlgorithmTerminator Terminator; ParticleExchanger Exchanger; + vtkm::Id NumParticlesWorkingOn = 0; + DebugStreamType DebugStream; }; diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h index 280be3d03a..d8a0ce68ac 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -22,308 +22,182 @@ namespace flow namespace internal { -#ifdef VTKM_ENABLE_MPI -namespace //anonymous -{ -class ParallelAdvectAlgorithmTerminator +class AdvectAlgorithmTerminator { - static constexpr int UNSET = -1; - static constexpr int IDLE = 0; - static constexpr int ACTIVE = 1; - static constexpr int DONE = 2; + bool FirstCall; public: - ParallelAdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) - : FirstCall(true) +#ifdef VTKM_ENABLE_MPI + AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) + : AllDirty(1) + , Dirty(1) + , LocalWork(0) , MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) - , NumRanks(comm.size()) , Rank(comm.rank()) - , Token(UNSET) + , State(STATE_0) +#else + AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) +#endif { - this->FromRank = this->Rank - 1; - this->ToRank = this->Rank + 1; - if (this->Rank == 0) - this->FromRank = this->NumRanks - 1; - if (this->Rank == this->NumRanks - 1) - this->ToRank = 0; + this->FirstCall = true; } - void SetStatus(const bool& haveWork, DebugStreamType& debugStream) + std::string StateToStr() const { - if (this->Token != DONE) - { - if (haveWork) - this->Token = ACTIVE; - else - this->Token = IDLE; - } - debugStream << "SetStatus from Work: " << this->StatusToStr() - << " S,R counters= " << this->SendCnt << " " << this->RecvCnt << std::endl; + if (this->State == STATE_0) + return "STATE_0"; + else if (this->State == STATE_1) + return "STATE_1"; + else if (this->State == STATE_1B) + return "STATE_1B"; + else if (this->State == STATE_2) + return "STATE_2"; + else if (this->State == DONE) + return "STATE_DONE"; + else + return "******STATE_ERROR"; } - bool GetDone(DebugStreamType& debugStream) + void AddWork(int numWork, DebugStreamType& DebugStream) { - debugStream << std::endl - << "GetDone0: from/to " << this->FromRank << " " << this->ToRank << " " - << this->Info() << std::endl; - // Once we are done, we need to wait for the sends to complete so that the forward neighbor gets the message. - if (this->Token == DONE && this->SendCnt == 0) - { - //If we have an outstanding recv, cancel it. - if (this->RecvCnt > 0) - { - MPI_Cancel(&this->RecvReq); - MPI_Request_free(&this->RecvReq); - this->RecvCnt--; - } - VTKM_ASSERT(this->RecvCnt == 0); - debugStream << "GetDone1: from/to " << this->FromRank << " " << this->ToRank << " " - << this->Info() << std::endl; - return true; - } - - //Otherwise, pass tokens along the ring. - if (this->Rank == 0) - this->RingHead(debugStream); - else - this->RingBody(debugStream); - - return false; +#if 0 +#ifdef VTKM_ENABLE_MPI + this->LocalWork += numWork; + this->Dirty = 1; + //this->State = STATE_0; + DebugStream<StateToStr()<<": AddWork: localWork= "<LocalWork<<" dirty= "<Dirty<LocalWork > 0); + this->LocalWork -= numWork; + if (this->LocalWork == 0) + this->Dirty = 0; + VTKM_ASSERT(this->LocalWork >= 0); + + DebugStream<StateToStr()<<": RemoveWork: localWork= "<LocalWork<<" dirty= "<Dirty<State == AdvectAlgorithmTerminatorState::DONE; } + + void Control(bool haveLocalWork, DebugStreamType& DebugStream) { - //Begin the ring on the first call. +#ifdef VTKM_ENABLE_MPI + //DebugStream<StateToStr()<<": Control: localWork= "<FromRank << " " << this->ToRank << " " - << this->Info() << std::endl; + haveLocalWork = true; this->FirstCall = false; - return; - } - - //If send is done, then post a receive and return. - if (this->CheckSendComplete(debugStream)) - { - this->PostRecvToken(debugStream); - debugStream << "GetDone1: from/to " << this->FromRank << " " << this->ToRank << " " - << this->Info() << std::endl; - return; } - //Tail of ring responds back to head. - if (this->CheckRecvComplete(debugStream)) + if (this->State == STATE_2 && haveLocalWork) { - //If the entire ring is IDLE and root is still IDLE, we are done. - if (this->RecvToken == IDLE && this->Token == IDLE) - { - debugStream << " everyone idle. DONE" << std::endl; - this->Token = DONE; - } - this->RoundCnt++; - this->PostSendToken(debugStream); - debugStream << "GetDone1: from/to " << this->FromRank << " " << this->ToRank << " " - << this->Info() << " NEW ROUND" << std::endl; + DebugStream << " DEATH" << std::endl; } - } - - // Rank 1 to NumRanks-1 is the body of the ring. - void RingBody(DebugStreamType& debugStream) - { - //We always need 1 active recv. - if (this->RecvCnt == 0) + if (haveLocalWork) { - this->PostRecvToken(debugStream); - //debugStream<<"GetDone1: from/to "<FromRank<<" "<ToRank<<" "<Info()<Dirty = 1; + DebugStream << this->StateToStr() << ": Control: Have local work" << std::endl; } + else + DebugStream << this->StateToStr() << ": Control NO WORK Dirty= " << this->Dirty << " local " + << this->LocalDirty << std::endl; - //Check for completed send. - this->CheckSendComplete(debugStream); - - // Check if token received from prev neighbor. - if (this->CheckRecvComplete(debugStream)) + if (this->State == STATE_0 && !haveLocalWork) { - //If previous token is ACTIVE, pass on ACTIVE; - //If previous token is DONE, pass on DONE. - //otherwise, previous is IDLE, so pass on my token. - if (this->RecvToken == ACTIVE) - this->Token = ACTIVE; - else if (this->RecvToken == DONE) - this->Token = DONE; - - //Pass token to forward neighbor. - this->PostSendToken(debugStream); - debugStream << "GetDone1: from/to " << this->FromRank << " " << this->ToRank << " " - << this->Info() << std::endl; + DebugStream << this->StateToStr() << ": Control: --> STATE_1 (no local work), call barrier(" + << this->BarrierCnt << ") Dirty=0" << std::endl; + MPI_Ibarrier(this->MPIComm, &this->StateReq); + this->Dirty = 0; + this->State = STATE_1; } - } - - bool CheckSendComplete(DebugStreamType& debugStream) - { - if (this->SendCnt > 0) + else if (this->State == STATE_1) { MPI_Status status; int flag; - int err = MPI_Test(&this->SendReq, &flag, &status); + MPI_Test(&this->StateReq, &flag, &status); if (flag == 1) { - this->SendCnt--; - debugStream << " Send completed. cnt=" << this->SendCnt << std::endl; - return true; + DebugStream << this->StateToStr() << ": Control: HIT barrier(" << this->BarrierCnt + << ") Dirty= " << this->Dirty << std::endl; + this->State = STATE_1B; } } - - return false; - } - - bool CheckRecvComplete(DebugStreamType& debugStream) - { - if (this->RecvCnt > 0) + else if (this->State == STATE_1B) + { + DebugStream << this->StateToStr() << ": Control: Check for new work. dirty= " << this->Dirty + << std::endl; + this->LocalDirty = this->Dirty; + DebugStream << this->StateToStr() << ": Control: call ireduce(" << this->IReduceCnt + << ") : localDirty=" << this->LocalDirty << std::endl; + DebugStream << this->StateToStr() << ": Control: --> STATE_2" << std::endl; + MPI_Iallreduce( + &this->LocalDirty, &this->AllDirty, 1, MPI_INT, MPI_LOR, this->MPIComm, &this->StateReq); + this->State = STATE_2; + this->BarrierCnt++; + } + else if (this->State == STATE_2) { MPI_Status status; int flag; - int err = MPI_Test(&this->RecvReq, &flag, &status); + MPI_Test(&this->StateReq, &flag, &status); if (flag == 1) { - this->RecvCnt--; - this->RecvToken = this->RecvBuffer[0]; - this->RoundCnt = this->RecvBuffer[1]; - debugStream << " Recv(" << this->FromRank << ") " << this->StatusToStr(this->RecvToken) - << " " << this->RoundCnt << " cnt= " << this->RecvCnt << std::endl; - return true; + DebugStream << this->StateToStr() << ": Control: HIT ireduce(" << this->IReduceCnt + << ") AllDirty= " << this->AllDirty << std::endl; + if (this->AllDirty == 0) //done + { + DebugStream << this->StateToStr() << ": Control: --> DONE allDirty= " << this->AllDirty + << " Dirty= " << this->Dirty << std::endl; + this->State = DONE; + } + else + { + DebugStream << this->StateToStr() << ": Control: --> STATE_0 allDirty= " << this->AllDirty + << " (reset)" << std::endl; + this->State = STATE_0; //reset. + } + this->IReduceCnt++; } } - return false; - } - - void PostSendToken(DebugStreamType& debugStream) - { - this->SendToken = this->Token; - if (this->SendToken == IDLE && (this->SendCnt > 0 || this->RecvCnt > 0)) - { - debugStream << " idle --> active" << std::endl; - this->SendToken = ACTIVE; - } - this->SendCnt++; - this->SendBuffer[0] = this->SendToken; - this->SendBuffer[1] = this->RoundCnt; - MPI_Isend( - &this->SendBuffer, 2, MPI_INT, this->ToRank, this->Tag, this->MPIComm, &this->SendReq); - if (this->Rank == 0 && this->FirstCall) - debugStream << " BEGIN "; - - debugStream << " Send(" << this->ToRank << ") " << this->StatusToStr(this->SendToken) << " " - << this->RoundCnt << " " << &this->SendReq << " cnt= " << this->SendCnt - << std::endl; - } - - void PostRecvToken(DebugStreamType& debugStream) - { - MPI_Irecv( - &this->RecvBuffer, 2, MPI_INT, this->FromRank, this->Tag, this->MPIComm, &this->RecvReq); - this->RecvCnt++; - debugStream << " Post Irecv(" << this->FromRank << ") " << &this->RecvReq - << " cnt= " << this->RecvCnt << std::endl; - } - - - int SendCnt = 0; - int RecvCnt = 0; - bool FirstCall; - MPI_Comm MPIComm; - MPI_Request RecvReq, SendReq; - int NumRanks; - int Rank, ToRank, FromRank; - int Token; - int SendToken, RecvToken; - int Tag = 314; - int RoundCnt = 0; - int SendBuffer[2], RecvBuffer[2]; - - - //stuff to toss. - std::string StatusToStr() const { return this->StatusToStr(this->Token); } - - std::string StatusToStr(const int& status) const - { - std::string str; - if (status == UNSET) - str = "UNSET"; - else if (status == IDLE) - str = "IDLE"; - else if (status == ACTIVE) - str = "ACTIVE"; - else if (status == DONE) - str = "DONE"; - else - str = "ERROR " + std::to_string(status); - - return str; - } - - std::string Info(int token = -100) const - { - int tmp = this->Token; - if (token != -100) - tmp = token; - std::string str = this->StatusToStr(tmp) + " S,R cnt= " + std::to_string(this->SendCnt) + " " + - std::to_string(this->RecvCnt); - str = str + " roundCnt= " + std::to_string(this->RoundCnt); - if (this->SendCnt > 1 || this->RecvCnt > 1) - str = str + " ********** ERROR"; - return str; - } -}; -} //namespace anonymous -#endif - -class AdvectAlgorithmTerminator -{ - static constexpr int UNSET = -1; - static constexpr int IDLE = 0; - static constexpr int ACTIVE = 1; - static constexpr int DONE = 2; - -public: -#ifdef VTKM_ENABLE_MPI - AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) - : HaveWork(false) - , ParallelTerminator(comm) #else - AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) - : HaveWork(false) -#endif - { - } - - void SetStatus(const bool& haveWork, DebugStreamType& debugStream) - { - this->HaveWork = haveWork; -#ifdef VTKM_ENABLE_MPI - this->ParallelTerminator.SetStatus(this->HaveWork, debugStream); + if (!haveLocalWork) + this->State = DONE; #endif } - bool GetDone(DebugStreamType& debugStream) +private: + enum AdvectAlgorithmTerminatorState { -#ifdef VTKM_ENABLE_MPI - return this->ParallelTerminator.GetDone(debugStream); -#else - return !this->HaveWork; -#endif - } + STATE_0, + STATE_1, + STATE_1B, + STATE_2, + DONE + }; -private: - bool HaveWork; #ifdef VTKM_ENABLE_MPI - ParallelAdvectAlgorithmTerminator ParallelTerminator; + int AllDirty; + int BarrierCnt = 0; + int IReduceCnt = 0; + //Dirty: Has this rank seen any work since entering state? + std::atomic Dirty; + int LocalDirty; + std::atomic LocalWork; + MPI_Comm MPIComm; + vtkm::Id Rank; + AdvectAlgorithmTerminatorState State = AdvectAlgorithmTerminatorState::STATE_0; + MPI_Request StateReq; #endif }; + } } } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h index 45ab312957..326ef42cf9 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h @@ -11,6 +11,7 @@ #ifndef vtk_m_filter_flow_internal_AdvectAlgorithmThreaded_h #define vtk_m_filter_flow_internal_AdvectAlgorithmThreaded_h +#include "DebugStream.h" #include #include #include @@ -47,9 +48,17 @@ public: void Go() override { - this->DebugStream << "Go: work= " << this->HaveAnyWork() << std::endl; - //this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); - + this->DebugStream << "GO() #active= " << this->Active.size() << std::endl; + this->NumParticlesWorkingOn = 0; + if (!this->Active.empty() || !this->Inactive.empty()) + { + this->NumParticlesWorkingOn = this->Inactive.size(); + for (const auto& it : this->Active) + this->NumParticlesWorkingOn += it.second.size(); + this->Terminator.AddWork(this->NumParticlesWorkingOn, this->DebugStream); + } + this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); + //this->DebugStream<<"HaveLocalWork: "<HaveAnyWork()< workerThreads; workerThreads.emplace_back(std::thread(AdvectAlgorithmThreaded::Worker, this)); this->Manage(); @@ -64,9 +73,16 @@ protected: bool HaveAnyWork() { std::lock_guard lock(this->Mutex); + return this->NumParticlesWorkingOn > 0; + + /* //We have work if there particles in any queues or a worker is busy. - return !this->Active.empty() || !this->Inactive.empty() || this->WorkerActivate || - this->Exchanger.GetNumberOfBufferedSends() > 0; + //return !this->Active.empty() || !this->Inactive.empty() || this->WorkerActivate; + bool val = (!this->Active.empty() || !this->Inactive.empty() || this->WorkerActivate); +// this->DebugStream<<"HaveAnyWork= "<& particles, vtkm::Id& blockId) override @@ -74,8 +90,6 @@ protected: std::lock_guard lock(this->Mutex); bool val = this->AdvectAlgorithm::GetActiveParticles(particles, blockId); this->WorkerActivate = val; - if (val) - this->DebugStream << " Advect " << particles[0] << std::endl; return val; } @@ -110,6 +124,7 @@ protected: void WorkerWait() { + this->DebugStream << " WorkerWait()" << std::endl; std::unique_lock lock(this->Mutex); this->WorkerActivateCondition.wait(lock, [this] { return WorkerActivate || Done; }); } @@ -123,15 +138,19 @@ protected: void Work() { + this->DebugStream << " Work()" << std::endl; while (!this->CheckDone()) { std::vector v; vtkm::Id blockId = -1; if (this->GetActiveParticles(v, blockId)) { + //this->DebugStream<<" Work: numP= "<GetDataSet(blockId); DSIHelperInfo bb(v, this->BoundsMap, this->ParticleBlockIDsMap); + this->DebugStream << " Advect " << v.size() << " v[0]= " << v[0] << std::endl; block.Advect(bb, this->StepSize); + this->DebugStream << " Advect DONE: " << v[0] << std::endl; this->UpdateWorkerResult(blockId, bb); } else @@ -141,25 +160,28 @@ protected: void Manage() { + this->DebugStream << "Manage() numP= " << this->NumParticlesWorkingOn << std::endl; //this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); - while (!this->Terminator.GetDone(this->DebugStream)) + while (!this->Terminator.Done()) { + //this->DebugStream<<" 0_Manage() numP= "<NumParticlesWorkingOn<>> workerResults; this->GetWorkerResults(workerResults); + //bool localWork = !workerResults.empty(); + vtkm::Id numTerm = 0; for (auto& it : workerResults) for (auto& r : it.second) - { - this->UpdateResult(r); - this->DebugStream << " Advect DONE " << std::endl; - } - - //this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); - this->ExchangeParticles(); - //this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); + numTerm += this->UpdateResult(r); + + bool val = this->ExchangeParticles(); + if (val || numTerm > 0) + this->DebugStream << " 1_Manage() numP= " << this->NumParticlesWorkingOn << std::endl; + this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); } //Let the workers know that we are done. + this->DebugStream << "Manage() DONE numP= " << this->NumParticlesWorkingOn << std::endl; this->SetDone(); } diff --git a/vtkm/filter/flow/internal/ParticleExchanger.h b/vtkm/filter/flow/internal/ParticleExchanger.h index cab23d9d60..485f1e710c 100644 --- a/vtkm/filter/flow/internal/ParticleExchanger.h +++ b/vtkm/filter/flow/internal/ParticleExchanger.h @@ -61,16 +61,16 @@ public: #ifdef VTKM_ENABLE_MPI else { - debugStream << " PE::sendbuffs0= " << this->GetNumberOfBufferedSends() << std::endl; + //debugStream<<" PE::sendbuffs0= "<GetNumberOfBufferedSends()<CleanupSendBuffers(true, debugStream); - debugStream << " PE::sendbuffs1= " << this->GetNumberOfBufferedSends() << std::endl; + //debugStream<<" PE::sendbuffs1= "<GetNumberOfBufferedSends()<SendParticles(outData, outRanks, outBlockIDsMap); this->RecvParticles(inData, inDataBlockIDsMap); if (!inData.empty()) debugStream << " PE::Recv " << inData[0] << std::endl; - debugStream << " PE::sendbuffs2= " << this->GetNumberOfBufferedSends() << std::endl; + //debugStream<<" PE::sendbuffs2= "<GetNumberOfBufferedSends()<Rank<<" SendBuffer: Delete"< seedArray; - seedArray = vtkm::cont::make_ArrayHandle({ vtkm::Particle(vtkm::Vec3f(.2f, 1.0f, .2f), 0), - vtkm::Particle(vtkm::Vec3f(.2f, 2.0f, .2f), 1) }); + seedArray = vtkm::cont::make_ArrayHandle({ vtkm::Particle(vtkm::Vec3f(.2f, 1.0f, .2f), 0) }); + //vtkm::Particle(vtkm::Vec3f(.2f, 2.0f, .2f), 1) }); vtkm::Id numSeeds = seedArray.GetNumberOfValues(); if (fType == STREAMLINE) -- GitLab From 97a4ecd797954d7196a112d308e0d5eb2eb71f3c Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Fri, 6 Dec 2024 12:23:34 -0500 Subject: [PATCH 38/73] Cleanup. --- vtkm/filter/flow/internal/AdvectAlgorithm.h | 38 +------------------ .../flow/internal/AdvectAlgorithmTerminator.h | 26 ------------- .../flow/internal/AdvectAlgorithmThreaded.h | 31 ++++----------- vtkm/filter/flow/testing/TestingFlow.cxx | 4 +- .../flow/testing/UnitTestAdvectionMPI.cxx | 2 - 5 files changed, 12 insertions(+), 89 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index 04565426bc..43164fae94 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -124,7 +124,7 @@ public: this->SetSeedArray(particles, blockIDs); } - bool HaveWork() + virtual bool HaveWork() { int activeCnt = this->Active.size(); int inactiveCnt = this->Inactive.size(); @@ -137,14 +137,6 @@ public: //Advect all the particles. virtual void Go() { - this->NumParticlesWorkingOn = 0; - if (!this->Active.empty() || !this->Inactive.empty()) - { - this->NumParticlesWorkingOn = this->Inactive.size(); - for (const auto& it : this->Active) - this->NumParticlesWorkingOn += it.second.size(); - } - while (!this->Terminator.Done()) { std::vector v; @@ -162,7 +154,6 @@ public: this->DebugStream << " Advect DONE.\n"; } - //this->Terminator.Control(this->HaveWork(), this->DebugStream); this->ExchangeParticles(); } } @@ -246,7 +237,7 @@ public: return !particles.empty(); } - bool ExchangeParticles() + void ExchangeParticles() { std::vector outgoing; std::vector outgoingRanks; @@ -256,10 +247,6 @@ public: std::vector incoming; std::unordered_map> incomingBlockIDs; - if (!outgoing.empty()) - this->DebugStream << "Exchange: outgoing= " << std::to_string(outgoing.size()) << " " - << outgoing[0] << "\n"; - this->Exchanger.Exchange(outgoing, outgoingRanks, this->ParticleBlockIDsMap, @@ -267,29 +254,11 @@ public: incomingBlockIDs, this->DebugStream); - bool val = false; - if (!incoming.empty()) - { - this->Terminator.AddWork(incoming.size(), this->DebugStream); - this->NumParticlesWorkingOn += incoming.size(); - val = true; - } - if (!outgoing.empty()) - { - this->Terminator.RemoveWork(outgoing.size(), this->DebugStream); - this->NumParticlesWorkingOn -= outgoing.size(); - val = true; - } - //Cleanup what was sent. for (const auto& p : outgoing) this->ParticleBlockIDsMap.erase(p.GetID()); - if (!incoming.empty()) - this->DebugStream << "Exchange: incoming= " << incoming.size() << " " << incoming[0] << "\n"; this->UpdateActive(incoming, incomingBlockIDs); - - return val; } void GetOutgoingParticles(std::vector& outgoing, @@ -397,8 +366,6 @@ public: this->DebugStream << "Terminated: " << numTerm << "\n"; for (const auto& id : stuff.TermID) this->ParticleBlockIDsMap.erase(id); - this->Terminator.RemoveWork(numTerm, this->DebugStream); - this->NumParticlesWorkingOn -= numTerm; } return numTerm; @@ -420,7 +387,6 @@ public: AdvectAlgorithmTerminator Terminator; ParticleExchanger Exchanger; - vtkm::Id NumParticlesWorkingOn = 0; DebugStreamType DebugStream; }; diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h index d8a0ce68ac..00cd33e7b9 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -58,31 +58,6 @@ public: return "******STATE_ERROR"; } - void AddWork(int numWork, DebugStreamType& DebugStream) - { -#if 0 -#ifdef VTKM_ENABLE_MPI - this->LocalWork += numWork; - this->Dirty = 1; - //this->State = STATE_0; - DebugStream<StateToStr()<<": AddWork: localWork= "<LocalWork<<" dirty= "<Dirty<LocalWork > 0); - this->LocalWork -= numWork; - if (this->LocalWork == 0) - this->Dirty = 0; - VTKM_ASSERT(this->LocalWork >= 0); - - DebugStream<StateToStr()<<": RemoveWork: localWork= "<LocalWork<<" dirty= "<Dirty<State == AdvectAlgorithmTerminatorState::DONE; } void Control(bool haveLocalWork, DebugStreamType& DebugStream) @@ -197,7 +172,6 @@ private: #endif }; - } } } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h index 326ef42cf9..b8713c1464 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h @@ -39,6 +39,7 @@ public: : AdvectAlgorithm(bm, blocks) , Done(false) { + this->DebugStream << "ctor. meow" << std::endl; //For threaded algorithm, the particles go out of scope in the Work method. //When this happens, they are destructed by the time the Manage thread gets them. //Set the copy flag so the std::vector is copied into the ArrayHandle @@ -48,17 +49,9 @@ public: void Go() override { - this->DebugStream << "GO() #active= " << this->Active.size() << std::endl; - this->NumParticlesWorkingOn = 0; - if (!this->Active.empty() || !this->Inactive.empty()) - { - this->NumParticlesWorkingOn = this->Inactive.size(); - for (const auto& it : this->Active) - this->NumParticlesWorkingOn += it.second.size(); - this->Terminator.AddWork(this->NumParticlesWorkingOn, this->DebugStream); - } - this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); - //this->DebugStream<<"HaveLocalWork: "<HaveAnyWork()<DebugStream << "Go: localwork= " << this->HaveWork() << std::endl; + this->Terminator.Control(this->HaveWork(), this->DebugStream); + this->DebugStream << "HaveLocalWork: " << this->HaveWork() << std::endl; std::vector workerThreads; workerThreads.emplace_back(std::thread(AdvectAlgorithmThreaded::Worker, this)); this->Manage(); @@ -70,10 +63,10 @@ public: } protected: - bool HaveAnyWork() + bool HaveWork() override { std::lock_guard lock(this->Mutex); - return this->NumParticlesWorkingOn > 0; + return this->AdvectAlgorithm::HaveWork() || this->WorkerActivate; /* //We have work if there particles in any queues or a worker is busy. @@ -160,11 +153,8 @@ protected: void Manage() { - this->DebugStream << "Manage() numP= " << this->NumParticlesWorkingOn << std::endl; - //this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); while (!this->Terminator.Done()) { - //this->DebugStream<<" 0_Manage() numP= "<NumParticlesWorkingOn<>> workerResults; this->GetWorkerResults(workerResults); @@ -174,14 +164,9 @@ protected: for (auto& r : it.second) numTerm += this->UpdateResult(r); - bool val = this->ExchangeParticles(); - if (val || numTerm > 0) - this->DebugStream << " 1_Manage() numP= " << this->NumParticlesWorkingOn << std::endl; - this->Terminator.Control(this->HaveAnyWork(), this->DebugStream); + this->ExchangeParticles(); + this->Terminator.Control(this->HaveWork(), this->DebugStream); } - - //Let the workers know that we are done. - this->DebugStream << "Manage() DONE numP= " << this->NumParticlesWorkingOn << std::endl; this->SetDone(); } diff --git a/vtkm/filter/flow/testing/TestingFlow.cxx b/vtkm/filter/flow/testing/TestingFlow.cxx index b1d1c5ba56..36ff44549f 100644 --- a/vtkm/filter/flow/testing/TestingFlow.cxx +++ b/vtkm/filter/flow/testing/TestingFlow.cxx @@ -210,8 +210,8 @@ void TestPartitionedDataSet(vtkm::Id nPerRank, AddVectorFields(pds, fieldName, vecX); vtkm::cont::ArrayHandle seedArray; - seedArray = vtkm::cont::make_ArrayHandle({ vtkm::Particle(vtkm::Vec3f(.2f, 1.0f, .2f), 0) }); - //vtkm::Particle(vtkm::Vec3f(.2f, 2.0f, .2f), 1) }); + seedArray = vtkm::cont::make_ArrayHandle({ vtkm::Particle(vtkm::Vec3f(.2f, 1.0f, .2f), 0), + vtkm::Particle(vtkm::Vec3f(.2f, 2.0f, .2f), 1) }); vtkm::Id numSeeds = seedArray.GetNumberOfValues(); if (fType == STREAMLINE) diff --git a/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx b/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx index 6747669a40..8544a22a94 100644 --- a/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx +++ b/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx @@ -68,8 +68,6 @@ void DoTest() continue; for (bool useThreaded : { true, false }) { - if (useThreaded) - continue; for (bool useBlockIds : { true, false }) { if (useBlockIds) -- GitLab From 3e572a71cd1a44de3ec21cdb052d375e86e4ed73 Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Fri, 6 Dec 2024 12:31:32 -0500 Subject: [PATCH 39/73] Remove DebugStream code. --- vtkm/filter/flow/internal/AdvectAlgorithm.h | 23 ++------ .../flow/internal/AdvectAlgorithmTerminator.h | 53 +------------------ .../flow/internal/AdvectAlgorithmThreaded.h | 22 +------- vtkm/filter/flow/internal/DebugStream.h | 30 ----------- vtkm/filter/flow/internal/ParticleExchanger.h | 21 ++------ 5 files changed, 9 insertions(+), 140 deletions(-) delete mode 100644 vtkm/filter/flow/internal/DebugStream.h diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index 43164fae94..b51206dd4e 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -22,8 +22,6 @@ #include #endif -#include "DebugStream.h" - namespace vtkm { namespace filter @@ -55,8 +53,6 @@ ParticleMessenger::Exchange() */ -#define DEBUG_STREAM(x) this->DebugStream << x; - template class AdvectAlgorithm { @@ -70,9 +66,7 @@ public: , Rank(this->Comm.rank()) , Terminator(this->Comm) , Exchanger(this->Comm) - , DebugStream(this->Rank) { - this->DebugStream << "ctor\n"; } void Execute(const vtkm::cont::ArrayHandle& seeds, vtkm::FloatDefault stepSize) @@ -142,16 +136,14 @@ public: std::vector v; vtkm::Id blockId = -1; - this->Terminator.Control(this->HaveWork(), this->DebugStream); + this->Terminator.Control(this->HaveWork()); if (this->GetActiveParticles(v, blockId)) { //make this a pointer to avoid the copy? auto& block = this->GetDataSet(blockId); DSIHelperInfo bb(v, this->BoundsMap, this->ParticleBlockIDsMap); - this->DebugStream << " Advect: " << v[0] << "\n"; block.Advect(bb, this->StepSize); this->UpdateResult(bb); - this->DebugStream << " Advect DONE.\n"; } this->ExchangeParticles(); @@ -247,12 +239,8 @@ public: std::vector incoming; std::unordered_map> incomingBlockIDs; - this->Exchanger.Exchange(outgoing, - outgoingRanks, - this->ParticleBlockIDsMap, - incoming, - incomingBlockIDs, - this->DebugStream); + this->Exchanger.Exchange( + outgoing, outgoingRanks, this->ParticleBlockIDsMap, incoming, incomingBlockIDs); //Cleanup what was sent. for (const auto& p : outgoing) @@ -328,8 +316,6 @@ public: if (!particles.empty()) { - //this->Terminator.AddWork(this->DebugStream); - for (auto pit = particles.begin(); pit != particles.end(); pit++) { vtkm::Id particleID = pit->GetID(); @@ -363,7 +349,6 @@ public: //Update terminated particles. if (numTerm > 0) { - this->DebugStream << "Terminated: " << numTerm << "\n"; for (const auto& id : stuff.TermID) this->ParticleBlockIDsMap.erase(id); } @@ -387,8 +372,6 @@ public: AdvectAlgorithmTerminator Terminator; ParticleExchanger Exchanger; - - DebugStreamType DebugStream; }; } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h index 00cd33e7b9..b22ffd3e2a 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -11,8 +11,6 @@ #ifndef vtk_m_filter_flow_internal_AdvectAlgorithmTerminator_h #define vtk_m_filter_flow_internal_AdvectAlgorithmTerminator_h -#include "DebugStream.h" - namespace vtkm { namespace filter @@ -42,52 +40,22 @@ public: this->FirstCall = true; } - std::string StateToStr() const - { - if (this->State == STATE_0) - return "STATE_0"; - else if (this->State == STATE_1) - return "STATE_1"; - else if (this->State == STATE_1B) - return "STATE_1B"; - else if (this->State == STATE_2) - return "STATE_2"; - else if (this->State == DONE) - return "STATE_DONE"; - else - return "******STATE_ERROR"; - } - bool Done() const { return this->State == AdvectAlgorithmTerminatorState::DONE; } - void Control(bool haveLocalWork, DebugStreamType& DebugStream) + void Control(bool haveLocalWork) { #ifdef VTKM_ENABLE_MPI - //DebugStream<StateToStr()<<": Control: localWork= "<Dirty = 1; - DebugStream << this->StateToStr() << ": Control: Have local work" << std::endl; - } - else - DebugStream << this->StateToStr() << ": Control NO WORK Dirty= " << this->Dirty << " local " - << this->LocalDirty << std::endl; if (this->State == STATE_0 && !haveLocalWork) { - DebugStream << this->StateToStr() << ": Control: --> STATE_1 (no local work), call barrier(" - << this->BarrierCnt << ") Dirty=0" << std::endl; MPI_Ibarrier(this->MPIComm, &this->StateReq); this->Dirty = 0; this->State = STATE_1; @@ -98,20 +66,11 @@ public: int flag; MPI_Test(&this->StateReq, &flag, &status); if (flag == 1) - { - DebugStream << this->StateToStr() << ": Control: HIT barrier(" << this->BarrierCnt - << ") Dirty= " << this->Dirty << std::endl; this->State = STATE_1B; - } } else if (this->State == STATE_1B) { - DebugStream << this->StateToStr() << ": Control: Check for new work. dirty= " << this->Dirty - << std::endl; this->LocalDirty = this->Dirty; - DebugStream << this->StateToStr() << ": Control: call ireduce(" << this->IReduceCnt - << ") : localDirty=" << this->LocalDirty << std::endl; - DebugStream << this->StateToStr() << ": Control: --> STATE_2" << std::endl; MPI_Iallreduce( &this->LocalDirty, &this->AllDirty, 1, MPI_INT, MPI_LOR, this->MPIComm, &this->StateReq); this->State = STATE_2; @@ -124,20 +83,10 @@ public: MPI_Test(&this->StateReq, &flag, &status); if (flag == 1) { - DebugStream << this->StateToStr() << ": Control: HIT ireduce(" << this->IReduceCnt - << ") AllDirty= " << this->AllDirty << std::endl; if (this->AllDirty == 0) //done - { - DebugStream << this->StateToStr() << ": Control: --> DONE allDirty= " << this->AllDirty - << " Dirty= " << this->Dirty << std::endl; this->State = DONE; - } else - { - DebugStream << this->StateToStr() << ": Control: --> STATE_0 allDirty= " << this->AllDirty - << " (reset)" << std::endl; this->State = STATE_0; //reset. - } this->IReduceCnt++; } } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h index b8713c1464..9c1c6689e6 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h @@ -11,7 +11,6 @@ #ifndef vtk_m_filter_flow_internal_AdvectAlgorithmThreaded_h #define vtk_m_filter_flow_internal_AdvectAlgorithmThreaded_h -#include "DebugStream.h" #include #include #include @@ -39,7 +38,6 @@ public: : AdvectAlgorithm(bm, blocks) , Done(false) { - this->DebugStream << "ctor. meow" << std::endl; //For threaded algorithm, the particles go out of scope in the Work method. //When this happens, they are destructed by the time the Manage thread gets them. //Set the copy flag so the std::vector is copied into the ArrayHandle @@ -49,9 +47,7 @@ public: void Go() override { - this->DebugStream << "Go: localwork= " << this->HaveWork() << std::endl; - this->Terminator.Control(this->HaveWork(), this->DebugStream); - this->DebugStream << "HaveLocalWork: " << this->HaveWork() << std::endl; + this->Terminator.Control(this->HaveWork()); std::vector workerThreads; workerThreads.emplace_back(std::thread(AdvectAlgorithmThreaded::Worker, this)); this->Manage(); @@ -67,15 +63,6 @@ protected: { std::lock_guard lock(this->Mutex); return this->AdvectAlgorithm::HaveWork() || this->WorkerActivate; - - /* - //We have work if there particles in any queues or a worker is busy. - //return !this->Active.empty() || !this->Inactive.empty() || this->WorkerActivate; - bool val = (!this->Active.empty() || !this->Inactive.empty() || this->WorkerActivate); -// this->DebugStream<<"HaveAnyWork= "<& particles, vtkm::Id& blockId) override @@ -117,7 +104,6 @@ protected: void WorkerWait() { - this->DebugStream << " WorkerWait()" << std::endl; std::unique_lock lock(this->Mutex); this->WorkerActivateCondition.wait(lock, [this] { return WorkerActivate || Done; }); } @@ -131,19 +117,15 @@ protected: void Work() { - this->DebugStream << " Work()" << std::endl; while (!this->CheckDone()) { std::vector v; vtkm::Id blockId = -1; if (this->GetActiveParticles(v, blockId)) { - //this->DebugStream<<" Work: numP= "<GetDataSet(blockId); DSIHelperInfo bb(v, this->BoundsMap, this->ParticleBlockIDsMap); - this->DebugStream << " Advect " << v.size() << " v[0]= " << v[0] << std::endl; block.Advect(bb, this->StepSize); - this->DebugStream << " Advect DONE: " << v[0] << std::endl; this->UpdateWorkerResult(blockId, bb); } else @@ -165,7 +147,7 @@ protected: numTerm += this->UpdateResult(r); this->ExchangeParticles(); - this->Terminator.Control(this->HaveWork(), this->DebugStream); + this->Terminator.Control(this->HaveWork()); } this->SetDone(); } diff --git a/vtkm/filter/flow/internal/DebugStream.h b/vtkm/filter/flow/internal/DebugStream.h deleted file mode 100644 index a67679c56c..0000000000 --- a/vtkm/filter/flow/internal/DebugStream.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - - -class DebugStreamType -{ -public: - DebugStreamType(int rank) - : Stream("Debug." + std::to_string(rank) + ".txt") - { - } - - template - DebugStreamType& operator<<(const T& value) - { - std::lock_guard lock(this->Mutex); - this->Stream << value; - return *this; - } - - DebugStreamType& operator<<(std::ostream& (*manip)(std::ostream&)) - { - std::lock_guard lock(this->Mutex); - this->Stream << manip; - return *this; - } - -private: - std::ofstream Stream; - std::mutex Mutex; -}; diff --git a/vtkm/filter/flow/internal/ParticleExchanger.h b/vtkm/filter/flow/internal/ParticleExchanger.h index 485f1e710c..3dc8749bab 100644 --- a/vtkm/filter/flow/internal/ParticleExchanger.h +++ b/vtkm/filter/flow/internal/ParticleExchanger.h @@ -51,8 +51,7 @@ public: const std::vector& outRanks, const std::unordered_map>& outBlockIDsMap, std::vector& inData, - std::unordered_map>& inDataBlockIDsMap, - DebugStreamType& debugStream) + std::unordered_map>& inDataBlockIDsMap) { VTKM_ASSERT(outData.size() == outRanks.size()); @@ -61,16 +60,9 @@ public: #ifdef VTKM_ENABLE_MPI else { - //debugStream<<" PE::sendbuffs0= "<GetNumberOfBufferedSends()<CleanupSendBuffers(true, debugStream); - //debugStream<<" PE::sendbuffs1= "<GetNumberOfBufferedSends()<CleanupSendBuffers(true); this->SendParticles(outData, outRanks, outBlockIDsMap); this->RecvParticles(inData, inDataBlockIDsMap); - if (!inData.empty()) - debugStream << " PE::Recv " << inData[0] << std::endl; - //debugStream<<" PE::sendbuffs2= "<GetNumberOfBufferedSends()<>; - void CleanupSendBuffers(bool checkRequests, DebugStreamType& debugStream) + void CleanupSendBuffers(bool checkRequests) { if (!checkRequests) { @@ -103,11 +95,8 @@ private: return; } - //debugStream<<" PE::CleanupSendBuffers sz= "<SendBuffers.size()<SendBuffers.empty()) - { return; - } std::vector requests; for (auto& req : this->SendBuffers) @@ -127,8 +116,6 @@ private: throw vtkm::cont::ErrorFilterExecution( "Error with MPI_Testsome in ParticleExchanger::CleanupSendBuffers"); - debugStream << " MPI_Testsome0: " << num - << " --> SendBuffers.size= " << this->SendBuffers.size() << std::endl; if (num > 0) { for (int i = 0; i < num; i++) @@ -147,8 +134,6 @@ private: //std::cout<Rank<<" SendBuffer: Delete"< SendBuffers.size= " << this->SendBuffers.size() << std::endl; } void SendParticles(const std::vector& outData, -- GitLab From 025378917eaa8c62d2122289c4bb2ab82f3548cb Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 22 Nov 2024 12:20:59 -0500 Subject: [PATCH 40/73] CellFace: Add CellFaceMinPointId --- vtkm/exec/CellFace.h | 47 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/vtkm/exec/CellFace.h b/vtkm/exec/CellFace.h index 1f14d77b46..26dfa61c25 100644 --- a/vtkm/exec/CellFace.h +++ b/vtkm/exec/CellFace.h @@ -278,7 +278,7 @@ static inline VTKM_EXEC vtkm::ErrorCode CellFaceCanonicalId( vtkm::IdComponent numPointsInFace; result = { -1 }; VTKM_RETURN_ON_ERROR(vtkm::exec::CellFaceNumberOfPoints(faceIndex, shape, numPointsInFace)); - if (numPointsInFace == 0) + if (numPointsInFace < 1) { // An invalid face. We should already have gotten an error from // CellFaceNumberOfPoints. @@ -343,6 +343,51 @@ static inline VTKM_EXEC vtkm::ErrorCode CellFaceCanonicalId( return vtkm::ErrorCode::Success; } +/// \brief Returns the min point id of a cell face +/// Given information about a cell face and the global point indices for that cell, returns a +/// vtkm::Id that contains the minimum point id for that face. +template +static inline VTKM_EXEC vtkm::ErrorCode CellFaceMinPointId( + vtkm::IdComponent faceIndex, + CellShapeTag shape, + const GlobalPointIndicesVecType& globalPointIndicesVec, + vtkm::Id& minFacePointId) +{ + vtkm::IdComponent numPointsInFace; + minFacePointId = { -1 }; + VTKM_RETURN_ON_ERROR(vtkm::exec::CellFaceNumberOfPoints(faceIndex, shape, numPointsInFace)); + if (numPointsInFace < 1) + { + // An invalid face. We should already have gotten an error from + // CellFaceNumberOfPoints. + return vtkm::ErrorCode::InvalidFaceId; + } + + detail::CellFaceTables table; + minFacePointId = globalPointIndicesVec[table.PointsInFace(shape.Id, faceIndex, 0)]; + vtkm::Id nextPoint = globalPointIndicesVec[table.PointsInFace(shape.Id, faceIndex, 1)]; + if (nextPoint < minFacePointId) + { + minFacePointId = nextPoint; + } + nextPoint = globalPointIndicesVec[table.PointsInFace(shape.Id, faceIndex, 2)]; + if (nextPoint < minFacePointId) + { + minFacePointId = nextPoint; + } + + // Check the rest of the points to see if they are in the lowest 3 + for (vtkm::IdComponent pointIndex = 3; pointIndex < numPointsInFace; pointIndex++) + { + nextPoint = globalPointIndicesVec[table.PointsInFace(shape.Id, faceIndex, pointIndex)]; + if (nextPoint < minFacePointId) + { + minFacePointId = nextPoint; + } + } + + return vtkm::ErrorCode::Success; +} } } // namespace vtkm::exec -- GitLab From 37bab7d146afbcaa51525a7ac658f91ff0944e05 Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Mon, 9 Dec 2024 07:21:24 -0500 Subject: [PATCH 41/73] Fix deprecated decl. Remove sync comm tests. --- vtkm/filter/flow/FilterParticleAdvection.h | 10 ++++--- vtkm/filter/flow/internal/AdvectAlgorithm.h | 8 ++--- .../flow/testing/UnitTestStreamlineAMRMPI.cxx | 30 +++---------------- 3 files changed, 12 insertions(+), 36 deletions(-) diff --git a/vtkm/filter/flow/FilterParticleAdvection.h b/vtkm/filter/flow/FilterParticleAdvection.h index 3ec9085614..5cb8660234 100644 --- a/vtkm/filter/flow/FilterParticleAdvection.h +++ b/vtkm/filter/flow/FilterParticleAdvection.h @@ -91,17 +91,19 @@ public: VTKM_CONT void SetUseThreadedAlgorithm(bool val) { this->UseThreadedAlgorithm = val; } - VTKM_DEPRECATED(2.2, "All communication is asynchronous no."); + VTKM_DEPRECATED(2.2, "All communication is asynchronous now.") VTKM_CONT void SetUseAsynchronousCommunication() {} - VTKM_DEPRECATED(2.2, "All communication is asynchronous no."); + + VTKM_DEPRECATED(2.2, "All communication is asynchronous now.") VTKM_CONT bool GetUseAsynchronousCommunication() { return true; } - VTKM_DEPRECATED(2.2, "All communication is asynchronous no."); + VTKM_DEPRECATED(2.2, "All communication is asynchronous now.") VTKM_CONT void SetUseSynchronousCommunication() {} - VTKM_DEPRECATED(2.2, "All communication is asynchronous no."); + + VTKM_DEPRECATED(2.2, "All communication is asynchronous now.") VTKM_CONT bool GetUseSynchronousCommunication() { return false; } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index b51206dd4e..75e66321ed 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -120,12 +120,8 @@ public: virtual bool HaveWork() { - int activeCnt = this->Active.size(); - int inactiveCnt = this->Inactive.size(); - int numBuffers = this->Exchanger.GetNumberOfBufferedSends(); - - bool haveWork = (activeCnt > 0 || inactiveCnt > 0 || numBuffers > 0); - return haveWork; + return !this->Active.empty() || !this->Inactive.empty() || + this->Exchanger.GetNumberOfBufferedSends() > 0; } //Advect all the particles. diff --git a/vtkm/filter/flow/testing/UnitTestStreamlineAMRMPI.cxx b/vtkm/filter/flow/testing/UnitTestStreamlineAMRMPI.cxx index 0aa7b613a8..c01c253271 100644 --- a/vtkm/filter/flow/testing/UnitTestStreamlineAMRMPI.cxx +++ b/vtkm/filter/flow/testing/UnitTestStreamlineAMRMPI.cxx @@ -52,7 +52,6 @@ void SetFilter(FilterType& filter, const std::string& fieldName, vtkm::cont::ArrayHandle seedArray, bool useThreaded, - bool useAsyncComm, bool useBlockIds, const std::vector& blockIds) { @@ -61,16 +60,12 @@ void SetFilter(FilterType& filter, filter.SetSeeds(seedArray); filter.SetActiveField(fieldName); filter.SetUseThreadedAlgorithm(useThreaded); - if (useAsyncComm) - filter.SetUseAsynchronousCommunication(); - else - filter.SetUseSynchronousCommunication(); if (useBlockIds) filter.SetBlockIDs(blockIds); } -void TestAMRStreamline(FilterType fType, bool useThreaded, bool useAsyncComm) +void TestAMRStreamline(FilterType fType, bool useThreaded) { auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); if (comm.rank() == 0) @@ -89,11 +84,6 @@ void TestAMRStreamline(FilterType fType, bool useThreaded, bool useAsyncComm) } if (useThreaded) std::cout << " - using threaded"; - if (useAsyncComm) - std::cout << " - usingAsyncComm"; - else - std::cout << " - usingSyncComm"; - std::cout << " - on an AMR data set" << std::endl; } @@ -170,22 +160,13 @@ void TestAMRStreamline(FilterType fType, bool useThreaded, bool useAsyncComm) if (fType == STREAMLINE) { vtkm::filter::flow::Streamline streamline; - SetFilter(streamline, - stepSize, - numSteps, - fieldName, - seedArray, - useThreaded, - useAsyncComm, - false, - {}); + SetFilter(streamline, stepSize, numSteps, fieldName, seedArray, useThreaded, false, {}); out = streamline.Execute(pds); } else if (fType == PATHLINE) { vtkm::filter::flow::Pathline pathline; - SetFilter( - pathline, stepSize, numSteps, fieldName, seedArray, useThreaded, useAsyncComm, false, {}); + SetFilter(pathline, stepSize, numSteps, fieldName, seedArray, useThreaded, false, {}); //Create timestep 2 auto pds2 = vtkm::cont::PartitionedDataSet(pds); pathline.SetPreviousTime(0); @@ -314,10 +295,7 @@ void DoTest() { for (auto useThreaded : { true, false }) { - for (auto useAsyncComm : { true, false }) - { - TestAMRStreamline(fType, useThreaded, useAsyncComm); - } + TestAMRStreamline(fType, useThreaded); } } } -- GitLab From 6b0dcbc07ca9a5c85b1fae6b423bae6a081e50e6 Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Mon, 9 Dec 2024 07:28:34 -0500 Subject: [PATCH 42/73] Code cleanup. --- vtkm/filter/flow/internal/AdvectAlgorithm.h | 25 ++------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index 75e66321ed..c8110bc994 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -31,28 +31,6 @@ namespace flow namespace internal { -/* -ParticleMessenger::Exchange() - - SendParticles(outData--> map[dstRank]=vector of pairs); - -- SendParticles(map...) - --- for each m : map SendParticles(m); - ---- SendParticles(dst, container) - ----- serialize, SendData(dst, buff); - ------ SendDataAsync(dst,buff) - ------- header??, req=mpi_isend(), store req. - - - RecvAny(data, block); - -- RecvData(tags, buffers, block) - --- RecvDataAsyncProbe(tag, buffers, block) - ---- while (true) - ----- if block: MPI_Probe() msgReceived=true - ----- else : MPI_Iprobe msgReceived = check - ----- if msgRecvd: MPI_Get_count(), MPI_Recv(), buffers, blockAndWait=false - - -*/ - - template class AdvectAlgorithm { @@ -265,6 +243,7 @@ public: auto ranks = this->BoundsMap.FindRank(bid[0]); VTKM_ASSERT(!ranks.empty()); + //Only 1 rank has the block. if (ranks.size() == 1) { if (ranks[0] == this->Rank) @@ -280,7 +259,7 @@ public: } else { - //Decide where it should go... + //Multiple ranks have the block, decide where it should go... //Random selection: vtkm::Id outRank = std::rand() % ranks.size(); -- GitLab From 4ab4d82efe46bcf7e3afb4ba5ac16edbcbca3094 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Mon, 9 Dec 2024 14:13:50 -0500 Subject: [PATCH 43/73] Add documentation for ArrayCopyShallowIfPossible function --- .../examples/GuideExampleUnknownArrayHandle.cxx | 16 ++++++++++++++++ docs/users-guide/unknown-array-handle.rst | 17 +++++++++++++---- vtkm/cont/ArrayCopy.h | 2 +- vtkm/cont/UnknownArrayHandle.h | 12 ++++++------ 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/docs/users-guide/examples/GuideExampleUnknownArrayHandle.cxx b/docs/users-guide/examples/GuideExampleUnknownArrayHandle.cxx index 1d138d616e..5540a79ae3 100644 --- a/docs/users-guide/examples/GuideExampleUnknownArrayHandle.cxx +++ b/docs/users-guide/examples/GuideExampleUnknownArrayHandle.cxx @@ -9,6 +9,7 @@ //============================================================================ #include +#include #include #include #include @@ -180,6 +181,20 @@ VTKM_CONT vtkm::cont::ArrayHandle CopyToDefaultArray( //// END-EXAMPLE UnknownArrayHandleDeepCopy //// +//// +//// BEGIN-EXAMPLE ArrayCopyShallow +//// +VTKM_CONT vtkm::cont::ArrayHandle GetAsFloatArray( + const vtkm::cont::UnknownArrayHandle& unknownArray) +{ + vtkm::cont::ArrayHandle output; + vtkm::cont::ArrayCopyShallowIfPossible(unknownArray, output); + return output; +} +//// +//// END-EXAMPLE ArrayCopyShallow +//// + //// //// BEGIN-EXAMPLE UnknownArrayHandleShallowCopy //// @@ -231,6 +246,7 @@ void CastUnknownArrayHandle() GetMiddleValue(unknownArray); CopyToDefaultArray(unknownArray); + GetAsFloatArray(unknownArray); GetAsDefaultArray(unknownArray); } diff --git a/docs/users-guide/unknown-array-handle.rst b/docs/users-guide/unknown-array-handle.rst index d8d35c6de8..ca6506ee9e 100644 --- a/docs/users-guide/unknown-array-handle.rst +++ b/docs/users-guide/unknown-array-handle.rst @@ -185,9 +185,21 @@ This can be done with the :func:`vtkm::cont::UnknownArrayHandle::DeepCopyFrom` m :file: GuideExampleUnknownArrayHandle.cxx :caption: Deep copy arrays of unknown types. +.. didyouknow:: + The :class:`vtkm::cont::UnknownArrayHandle` copy methods behave similarly to the :func:`vtkm::cont::ArrayCopy` functions. + It is often the case that you have good reason to believe that an array is of an expected type, but you have no way to be sure. To simplify code, the most rational thing to do is to get the array as the expected type if that is indeed what it is, or to copy it to an array of that type otherwise. -The :func:`vtkm::cont::UnknownArrayHandle::CopyShallowIfPossible` does just that. +The :func:`vtkm::cont::ArrayCopyShallowIfPossible` does just that. + +.. doxygenfunction:: vtkm::cont::ArrayCopyShallowIfPossible + +.. load-example:: ArrayCopyShallow + :file: GuideExampleUnknownArrayHandle.cxx + :caption: Using :func:`vtkm::cont::ArrayCopyShallowIfPossible` to get an unknown array as a particular type. + +:class:`vtkm::cont::UnknownArrayHandle` also has a method to do a similar shallow copy into it. +This method works by setting an array of a particular type into the :class:`vtkm::cont::UnknownArrayHandle`. .. doxygenfunction:: vtkm::cont::UnknownArrayHandle::CopyShallowIfPossible(const vtkm::cont::UnknownArrayHandle&) .. doxygenfunction:: vtkm::cont::UnknownArrayHandle::CopyShallowIfPossible(const vtkm::cont::UnknownArrayHandle&) const @@ -196,9 +208,6 @@ The :func:`vtkm::cont::UnknownArrayHandle::CopyShallowIfPossible` does just that :file: GuideExampleUnknownArrayHandle.cxx :caption: Using :func:`vtkm::cont::UnknownArrayHandle::CopyShallowIfPossible` to get an unknown array as a particular type. -.. didyouknow:: - The :class:`vtkm::cont::UnknownArrayHandle` copy methods behave similarly to the :func:`vtkm::cont::ArrayCopy` functions. - ---------------------------------------- Casting to a List of Potential Types diff --git a/vtkm/cont/ArrayCopy.h b/vtkm/cont/ArrayCopy.h index 174efc9fcf..55504fb3a2 100644 --- a/vtkm/cont/ArrayCopy.h +++ b/vtkm/cont/ArrayCopy.h @@ -151,7 +151,7 @@ void ArrayCopy(const vtkm::cont::UnknownArrayHandle&, const vtkm::cont::ArrayHan /// assumption about it being a particular type). You really just want a shallow /// copy (a reference in a concrete `ArrayHandle`) if that is possible. /// -/// `ArrayCopyShallowIfPossible` pulls an array of a specific type from an +/// `ArrayCopyShallowIfPossible()` pulls an array of a specific type from an /// `UnknownArrayHandle`. If the type is compatible, it will perform a shallow copy. /// If it is not possible, a deep copy is performed to get it to the correct type. /// diff --git a/vtkm/cont/UnknownArrayHandle.h b/vtkm/cont/UnknownArrayHandle.h index f36b024230..717142b64b 100644 --- a/vtkm/cont/UnknownArrayHandle.h +++ b/vtkm/cont/UnknownArrayHandle.h @@ -734,9 +734,9 @@ public: /// the array is deep copied. /// /// This method is roughly equivalent to the `vtkm::cont::ArrayCopyShallowIfPossible()` function - /// (defined in `vtkm/cont/ArrayCopy.h`). However, this method can be used without - /// having to use a device compiler (whereas `vtkm::cont::ArrayCopyShallowIfPossible()` does require - /// a device device compiler). + /// (defined in `vtkm/cont/ArrayCopy.h`). This form allows you to copy into a type defined + /// elsewhere (and hidden in the `UnknownArrayHandle`) whereas `ArrayCopyShallowIfPossible()` + /// must be copied into an `ArrayHandle` of a known type. /// void CopyShallowIfPossible(const vtkm::cont::UnknownArrayHandle& source); @@ -750,9 +750,9 @@ public: /// of `CopyShallowIfPossible()` throws an exception. /// /// This method is roughly equivalent to the `vtkm::cont::ArrayCopyShallowIfPossible()` function - /// (defined in `vtkm/cont/ArrayCopy.h`). However, this method can be used without - /// having to use a device compiler (whereas `vtkm::cont::ArrayCopyShallowIfPossible()` does require - /// a device device compiler). + /// (defined in `vtkm/cont/ArrayCopy.h`). This form allows you to copy into a type defined + /// elsewhere (and hidden in the `UnknownArrayHandle`) whereas `ArrayCopyShallowIfPossible()` + /// must be copied into an `ArrayHandle` of a known type. /// void CopyShallowIfPossible(const vtkm::cont::UnknownArrayHandle& source) const; -- GitLab From 40a2bb719f2a1f31dd54f4f838a5be67521a52f5 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Mon, 11 Nov 2024 10:32:25 -0500 Subject: [PATCH 44/73] Add constexpr to Vec methods The `constexpr` keyword is helpful to add to functions and macros where possible. Better than `inline`, it tells the compiler that it can perform optimizations based on analysis of expressions and literals given in the code. In particular, this should help code that loops over components have proper optimizations like loop unrolling when using `Vec` types that have the number of components fixed. --- docs/changelog/vec-constexpr.md | 8 ++++ vtkm/Types.h | 70 +++++++++++++++++---------------- 2 files changed, 45 insertions(+), 33 deletions(-) create mode 100644 docs/changelog/vec-constexpr.md diff --git a/docs/changelog/vec-constexpr.md b/docs/changelog/vec-constexpr.md new file mode 100644 index 0000000000..b51f389643 --- /dev/null +++ b/docs/changelog/vec-constexpr.md @@ -0,0 +1,8 @@ +## Add constexpr to Vec methods + +The `constexpr` keyword is helpful to add to functions and macros where +possible. Better than `inline`, it tells the compiler that it can perform +optimizations based on analysis of expressions and literals given in the +code. In particular, this should help code that loops over components have +proper optimizations like loop unrolling when using `Vec` types that have +the number of components fixed. diff --git a/vtkm/Types.h b/vtkm/Types.h index 8ec4944c78..447f9e8987 100644 --- a/vtkm/Types.h +++ b/vtkm/Types.h @@ -393,26 +393,29 @@ public: using ComponentType = T; protected: - VecBaseCommon() = default; + constexpr VecBaseCommon() = default; VTKM_EXEC_CONT - const DerivedClass& Derived() const { return *static_cast(this); } + constexpr const DerivedClass& Derived() const { return *static_cast(this); } VTKM_EXEC_CONT - DerivedClass& Derived() { return *static_cast(this); } + constexpr DerivedClass& Derived() { return *static_cast(this); } private: // Only for internal use VTKM_EXEC_CONT - inline vtkm::IdComponent NumComponents() const { return this->Derived().GetNumberOfComponents(); } + constexpr vtkm::IdComponent NumComponents() const + { + return this->Derived().GetNumberOfComponents(); + } // Only for internal use VTKM_EXEC_CONT - inline const T& Component(vtkm::IdComponent index) const { return this->Derived()[index]; } + constexpr const T& Component(vtkm::IdComponent index) const { return this->Derived()[index]; } // Only for internal use VTKM_EXEC_CONT - inline T& Component(vtkm::IdComponent index) { return this->Derived()[index]; } + constexpr T& Component(vtkm::IdComponent index) { return this->Derived()[index]; } public: template @@ -581,10 +584,10 @@ public: #endif // not using cuda < 8 VTKM_EXEC_CONT - ComponentType* GetPointer() { return &this->Component(0); } + constexpr ComponentType* GetPointer() { return &this->Component(0); } VTKM_EXEC_CONT - const ComponentType* GetPointer() const { return &this->Component(0); } + constexpr const ComponentType* GetPointer() const { return &this->Component(0); } }; @@ -682,7 +685,7 @@ public: return this->Components[idx]; } - inline VTKM_EXEC_CONT ComponentType& operator[](vtkm::IdComponent idx) + inline VTKM_EXEC_CONT constexpr ComponentType& operator[](vtkm::IdComponent idx) { VTKM_ASSERT(idx >= 0); VTKM_ASSERT(idx < NUM_COMPONENTS); @@ -779,7 +782,7 @@ class VTKM_ALWAYS_EXPORT VecCBase : public vtkm::detail::VecBaseCommon constexpr Vec(T value, Ts&&... values) @@ -838,7 +841,7 @@ public: using ComponentType = T; static constexpr vtkm::IdComponent NUM_COMPONENTS = 0; - Vec() = default; + constexpr Vec() = default; VTKM_EXEC_CONT explicit Vec(const ComponentType&) {} template @@ -899,7 +902,7 @@ class VTKM_ALWAYS_EXPORT Vec : public detail::VecBase> using Superclass = detail::VecBase>; public: - Vec() = default; + constexpr Vec() = default; VTKM_EXEC_CONT Vec(const T& value) : Superclass(value) { @@ -1015,7 +1018,7 @@ class VTKM_ALWAYS_EXPORT Vec : public detail::VecBase> using Superclass = detail::VecBase>; public: - Vec() = default; + constexpr Vec() = default; VTKM_EXEC_CONT Vec(const T& value) : Superclass(value) { @@ -1133,7 +1136,7 @@ class VTKM_ALWAYS_EXPORT Vec : public detail::VecBase> using Superclass = detail::VecBase>; public: - Vec() = default; + constexpr Vec() = default; VTKM_EXEC_CONT Vec(const T& value) : Superclass(value) { @@ -1286,55 +1289,55 @@ public: #endif VTKM_EXEC_CONT - VecC() + constexpr VecC() : Components(nullptr) , NumberOfComponents(0) { } VTKM_EXEC_CONT - VecC(T* array, vtkm::IdComponent size) + constexpr VecC(T* array, vtkm::IdComponent size) : Components(array) , NumberOfComponents(size) { } template - VTKM_EXEC_CONT VecC(vtkm::Vec& src) + constexpr VTKM_EXEC_CONT VecC(vtkm::Vec& src) : Components(src.GetPointer()) , NumberOfComponents(Size) { } VTKM_EXEC_CONT - explicit VecC(T& src) + explicit constexpr VecC(T& src) : Components(&src) , NumberOfComponents(1) { } VTKM_EXEC_CONT - VecC(const VecC& src) + constexpr VecC(const VecC& src) : Components(src.Components) , NumberOfComponents(src.NumberOfComponents) { } - inline VTKM_EXEC_CONT const T& operator[](vtkm::IdComponent index) const + inline VTKM_EXEC_CONT constexpr const T& operator[](vtkm::IdComponent index) const { VTKM_ASSERT(index >= 0); VTKM_ASSERT(index < this->NumberOfComponents); return this->Components[index]; } - inline VTKM_EXEC_CONT T& operator[](vtkm::IdComponent index) + inline VTKM_EXEC_CONT constexpr T& operator[](vtkm::IdComponent index) { VTKM_ASSERT(index >= 0); VTKM_ASSERT(index < this->NumberOfComponents); return this->Components[index]; } - inline VTKM_EXEC_CONT vtkm::IdComponent GetNumberOfComponents() const + inline VTKM_EXEC_CONT constexpr vtkm::IdComponent GetNumberOfComponents() const { return this->NumberOfComponents; } @@ -1380,55 +1383,55 @@ public: #endif VTKM_EXEC_CONT - VecCConst() + constexpr VecCConst() : Components(nullptr) , NumberOfComponents(0) { } VTKM_EXEC_CONT - VecCConst(const T* array, vtkm::IdComponent size) + constexpr VecCConst(const T* array, vtkm::IdComponent size) : Components(array) , NumberOfComponents(size) { } template - VTKM_EXEC_CONT VecCConst(const vtkm::Vec& src) + VTKM_EXEC_CONT constexpr VecCConst(const vtkm::Vec& src) : Components(src.GetPointer()) , NumberOfComponents(Size) { } VTKM_EXEC_CONT - explicit VecCConst(const T& src) + explicit constexpr VecCConst(const T& src) : Components(&src) , NumberOfComponents(1) { } VTKM_EXEC_CONT - VecCConst(const VecCConst& src) + constexpr VecCConst(const VecCConst& src) : Components(src.Components) , NumberOfComponents(src.NumberOfComponents) { } VTKM_EXEC_CONT - VecCConst(const VecC& src) + constexpr VecCConst(const VecC& src) : Components(src.Components) , NumberOfComponents(src.NumberOfComponents) { } - inline VTKM_EXEC_CONT const T& operator[](vtkm::IdComponent index) const + inline VTKM_EXEC_CONT constexpr const T& operator[](vtkm::IdComponent index) const { VTKM_ASSERT(index >= 0); VTKM_ASSERT(index < this->NumberOfComponents); return this->Components[index]; } - inline VTKM_EXEC_CONT vtkm::IdComponent GetNumberOfComponents() const + inline VTKM_EXEC_CONT constexpr vtkm::IdComponent GetNumberOfComponents() const { return this->NumberOfComponents; } @@ -1449,7 +1452,7 @@ private: /// Creates a \c VecC from an input array. /// template -static inline VTKM_EXEC_CONT vtkm::VecC make_VecC(T* array, vtkm::IdComponent size) +static inline VTKM_EXEC_CONT constexpr vtkm::VecC make_VecC(T* array, vtkm::IdComponent size) { return vtkm::VecC(array, size); } @@ -1457,7 +1460,8 @@ static inline VTKM_EXEC_CONT vtkm::VecC make_VecC(T* array, vtkm::IdComponent /// Creates a \c VecCConst from a constant input array. /// template -static inline VTKM_EXEC_CONT vtkm::VecCConst make_VecC(const T* array, vtkm::IdComponent size) +static inline VTKM_EXEC_CONT constexpr vtkm::VecCConst make_VecC(const T* array, + vtkm::IdComponent size) { return vtkm::VecCConst(array, size); } -- GitLab From 777244cb60f4d14b44b696d45bde6dfdca03e206 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 22 Nov 2024 12:37:28 -0500 Subject: [PATCH 45/73] ExternalFaces: move external faces in the beginning ExternalFaces: Improve performance and memory consumption `ExternalFaces` now uses a new algorithm that has the following combination of novel characteristics: 1. employs minimum point id as its face hash function instead of FNV1A of canonicalFaceID, that yields cache-friendly memory accesses which leads to enhanced performance. 2. employs an atomic hash counting approach, instead of hash sorting, to perform a reduce-by-key operation on the faces' hashes. 3. enhances performance by ensuring that the computation of face properties occurs only once and by avoiding the processing of a known internal face more than once. 4. embraces frugality in memory consumption, enabling the processing of larger datasets, especially on GPUs. When evaluated on Frontier super-computer on 4 large datasets: The new `ExternalFaces` algorithm: 1. has 4.73x to 5.99x less memory footprint 2. is 4.96x to 7.37x faster on the CPU 3. is 1.54x faster on the GPU, and the original algorithm could not execute on large datasets due to its high memory footprint --- .../entity_extraction/worklet/ExternalFaces.h | 688 +++++++++++------- 1 file changed, 417 insertions(+), 271 deletions(-) diff --git a/vtkm/filter/entity_extraction/worklet/ExternalFaces.h b/vtkm/filter/entity_extraction/worklet/ExternalFaces.h index f89b2e2ea2..8ccb9a9a85 100644 --- a/vtkm/filter/entity_extraction/worklet/ExternalFaces.h +++ b/vtkm/filter/entity_extraction/worklet/ExternalFaces.h @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -22,25 +23,18 @@ #include #include #include -#include #include #include -#include -#include #include #include #include #include -#include #include -#include #include -#include -#include #include +#include #include -#include namespace vtkm { @@ -136,13 +130,6 @@ struct ExternalFaces using ScatterType = vtkm::worklet::ScatterCounting; - template - VTKM_CONT static ScatterType MakeScatter(const CountArrayType& countArray) - { - VTKM_IS_ARRAY_HANDLE(CountArrayType); - return ScatterType(countArray); - } - VTKM_CONT BuildConnectivityStructured(const vtkm::Vec3f_64& min_point, const vtkm::Vec3f_64& max_point) : MinPoint(min_point) @@ -321,7 +308,7 @@ struct ExternalFaces vtkm::Vec3f_64 MaxPoint; }; - //Worklet that returns the number of faces for each cell/shape + // Worklet that returns the number of faces for each cell/shape class NumFacesPerCell : public vtkm::worklet::WorkletVisitCellsWithPoints { public: @@ -330,252 +317,356 @@ struct ExternalFaces using InputDomain = _1; template - VTKM_EXEC void operator()(CellShapeTag shape, vtkm::IdComponent& numFaces) const + VTKM_EXEC void operator()(CellShapeTag shape, vtkm::IdComponent& numFacesInCell) const { - vtkm::exec::CellFaceNumberOfFaces(shape, numFaces); + vtkm::exec::CellFaceNumberOfFaces(shape, numFacesInCell); } }; - //Worklet that identifies a cell face by a hash value. Not necessarily completely unique. + // Worklet that identifies a cell face by a hash value. Not necessarily completely unique. class FaceHash : public vtkm::worklet::WorkletVisitCellsWithPoints { public: - using ControlSignature = void(CellSetIn cellset, - FieldOut faceHashes, - FieldOut originCells, - FieldOut originFaces); - using ExecutionSignature = void(_2, _3, _4, CellShape, PointIndices, InputIndex, VisitIndex); + using ControlSignature = void(CellSetIn cellset, FieldOutCell cellFaceHashes); + using ExecutionSignature = void(CellShape, PointIndices, _2); using InputDomain = _1; - using ScatterType = vtkm::worklet::ScatterCounting; - - template - VTKM_EXEC void operator()(vtkm::HashType& faceHash, - vtkm::Id& cellIndex, - vtkm::IdComponent& faceIndex, - CellShapeTag shape, + template + VTKM_EXEC void operator()(const CellShapeTag shape, const CellNodeVecType& cellNodeIds, - vtkm::Id inputIndex, - vtkm::IdComponent visitIndex) const + CellFaceHashes& cellFaceHashes) const { - vtkm::Id3 faceId; - vtkm::exec::CellFaceCanonicalId(visitIndex, shape, cellNodeIds, faceId); - faceHash = vtkm::Hash(faceId); - - cellIndex = inputIndex; - faceIndex = visitIndex; + const vtkm::IdComponent numFaces = cellFaceHashes.GetNumberOfComponents(); + for (vtkm::IdComponent faceIndex = 0; faceIndex < numFaces; ++faceIndex) + { + vtkm::Id minFacePointId; + vtkm::exec::CellFaceMinPointId(faceIndex, shape, cellNodeIds, minFacePointId); + cellFaceHashes[faceIndex] = static_cast(minFacePointId); + } } }; - // Worklet that identifies the number of cells written out per face. - // Because there can be collisions in the face ids, this instance might - // represent multiple faces, which have to be checked. The resulting - // number is the total number of external faces. - class FaceCounts : public vtkm::worklet::WorkletReduceByKey + // Worklet that identifies the number of faces per hash. + class NumFacesPerHash : public vtkm::worklet::WorkletMapField { public: - using ControlSignature = void(KeysIn keys, - WholeCellSetIn<> inputCells, - ValuesIn originCells, - ValuesIn originFaces, - ReducedValuesOut numOutputCells); - using ExecutionSignature = _5(_2, _3, _4); + using ControlSignature = void(FieldIn faceHashes, AtomicArrayInOut numFacesPerHash); + using ExecutionSignature = void(_1, _2); using InputDomain = _1; - template - VTKM_EXEC vtkm::IdComponent operator()(const CellSetType& cellSet, - const OriginCellsType& originCells, - const OriginFacesType& originFaces) const + template + VTKM_EXEC void operator()(const vtkm::HashType& faceHash, + NumFacesPerHashArray& numFacesPerHash) const + { + // MemoryOrder::Relaxed is safe here, since we're not using the atomics for synchronization. + numFacesPerHash.Add(faceHash, 1, vtkm::MemoryOrder::Relaxed); + } + }; + + /// Class to pack and unpack cell and face indices to/from a single integer. + class CellFaceIdPacker + { + public: + using CellAndFaceIdType = vtkm::UInt64; + using CellIdType = vtkm::Id; + using FaceIdType = vtkm::Int8; + + static constexpr CellAndFaceIdType GetNumFaceIdBits() + { + static_assert(vtkm::exec::detail::CellFaceTables::MAX_NUM_FACES == 6, + "MAX_NUM_FACES must be 6, otherwise, update GetNumFaceIdBits"); + return 3; + } + static constexpr CellAndFaceIdType GetFaceMask() { return (1ULL << GetNumFaceIdBits()) - 1; } + + /// Pack function for both cellIndex and faceIndex + VTKM_EXEC inline static constexpr CellAndFaceIdType Pack(const CellIdType& cellIndex, + const FaceIdType& faceIndex) + { + // Pack the cellIndex in the higher bits, leaving FACE_INDEX_BITS bits for faceIndex + return static_cast(cellIndex << GetNumFaceIdBits()) | + static_cast(faceIndex); + } + + /// Unpacking function for both cellIndex and faceIndex + /// This is templated because we don't want to create a copy of the packedCellAndFaceId value. + template + VTKM_EXEC inline static constexpr void Unpack(const TCellAndFaceIdType& packedCellAndFaceId, + CellIdType& cellIndex, + FaceIdType& faceIndex) { - vtkm::IdComponent numCellsOnHash = originCells.GetNumberOfComponents(); - VTKM_ASSERT(originFaces.GetNumberOfComponents() == numCellsOnHash); + // Extract faceIndex from the lower GetNumFaceIdBits bits + faceIndex = static_cast(packedCellAndFaceId & GetFaceMask()); + // Extract cellIndex by shifting back + cellIndex = static_cast(packedCellAndFaceId >> GetNumFaceIdBits()); + } + }; - // Start by assuming all faces are unique, then remove one for each - // face we find a duplicate for. - vtkm::IdComponent numExternalFaces = numCellsOnHash; + // Worklet that writes out the cell and face ids of each face per hash. + class BuildFacesPerHash : public vtkm::worklet::WorkletMapField + { + public: + using ControlSignature = void(FieldIn cellFaceHashes, + AtomicArrayInOut numFacesPerHash, + WholeArrayOut cellAndFaceIdOfFacesPerHash); + using ExecutionSignature = void(InputIndex, _1, _2, _3); + using InputDomain = _1; - for (vtkm::IdComponent myIndex = 0; - myIndex < numCellsOnHash - 1; // Don't need to check last face - myIndex++) + template + VTKM_EXEC void operator()(vtkm::Id inputIndex, + const CellFaceHashes& cellFaceHashes, + NumFacesPerHashArray& numFacesPerHash, + CellAndFaceIdOfFacePerHashArray& cellAndFaceIdOfFacesPerHash) const + { + const vtkm::IdComponent numFaces = cellFaceHashes.GetNumberOfComponents(); + for (vtkm::IdComponent faceIndex = 0; faceIndex < numFaces; ++faceIndex) { - vtkm::Id3 myFace; - vtkm::exec::CellFaceCanonicalId(originFaces[myIndex], - cellSet.GetCellShape(originCells[myIndex]), - cellSet.GetIndices(originCells[myIndex]), - myFace); - for (vtkm::IdComponent otherIndex = myIndex + 1; otherIndex < numCellsOnHash; otherIndex++) - { - vtkm::Id3 otherFace; - vtkm::exec::CellFaceCanonicalId(originFaces[otherIndex], - cellSet.GetCellShape(originCells[otherIndex]), - cellSet.GetIndices(originCells[otherIndex]), - otherFace); - if (myFace == otherFace) - { - // Faces are the same. Must be internal. Remove 2, one for each face. We don't have to - // worry about otherFace matching anything else because a proper topology will have at - // most 2 cells sharing a face, so there should be no more matches. - numExternalFaces -= 2; - break; - } - } + const auto& faceHash = cellFaceHashes[faceIndex]; + // MemoryOrder::Relaxed is safe here, since we're not using the atomics for synchronization. + const vtkm::IdComponent hashFaceIndex = + numFacesPerHash.Add(faceHash, -1, vtkm::MemoryOrder::Relaxed) - 1; + cellAndFaceIdOfFacesPerHash.Get(faceHash)[hashFaceIndex] = + CellFaceIdPacker::Pack(inputIndex, static_cast(faceIndex)); } - - return numExternalFaces; } }; -private: - // Resolves duplicate hashes by finding a specified unique face for a given hash. - // Given a cell set (from a WholeCellSetIn) and the cell/face id pairs for each face - // associated with a given hash, returns the index of the cell/face provided of the - // visitIndex-th unique face. Basically, this method searches through all the cell/face - // pairs looking for unique sets and returns the one associated with visitIndex. - template - VTKM_EXEC static vtkm::IdComponent FindUniqueFace(const CellSetType& cellSet, - const OriginCellsType& originCells, - const OriginFacesType& originFaces, - vtkm::IdComponent visitIndex) + // Worklet that identifies the number of external faces per Hash. + // Because there can be collisions in the hash, this instance hash might + // represent multiple faces, which have to be checked. The resulting + // number is the total number of external faces. It also reorders the + // faces so that the external faces are first, followed by the internal faces. + class FaceCounts : public vtkm::worklet::WorkletMapField { - vtkm::IdComponent numCellsOnHash = originCells.GetNumberOfComponents(); - VTKM_ASSERT(originFaces.GetNumberOfComponents() == numCellsOnHash); + public: + using ControlSignature = void(FieldInOut cellAndFaceIdOfFacesInHash, + WholeCellSetIn<> inputCells, + FieldOut externalFacesInHash); + using ExecutionSignature = _3(_1, _2); + using InputDomain = _1; - // Find the visitIndex-th unique face. - vtkm::IdComponent numFound = 0; - vtkm::IdComponent myIndex = 0; - while (true) + template + VTKM_EXEC vtkm::IdComponent operator()(CellAndFaceIdOfFacesInHash& cellAndFaceIdOfFacesInHash, + const CellSetType& cellSet) const { - VTKM_ASSERT(myIndex < numCellsOnHash); - vtkm::Id3 myFace; - vtkm::exec::CellFaceCanonicalId(originFaces[myIndex], - cellSet.GetCellShape(originCells[myIndex]), - cellSet.GetIndices(originCells[myIndex]), - myFace); - bool foundPair = false; - for (vtkm::IdComponent otherIndex = 0; otherIndex < numCellsOnHash; otherIndex++) + const vtkm::IdComponent numFacesInHash = cellAndFaceIdOfFacesInHash.GetNumberOfComponents(); + + static constexpr vtkm::IdComponent FACE_CANONICAL_IDS_CACHE_SIZE = 100; + if (numFacesInHash <= 1) { - if (otherIndex == myIndex) + // Either one or zero faces. If there is one, it's external, In either case, do nothing. + return numFacesInHash; + } + else if (numFacesInHash <= FACE_CANONICAL_IDS_CACHE_SIZE) // Fast path with caching + { + CellFaceIdPacker::CellIdType myCellId; + CellFaceIdPacker::FaceIdType myFaceId; + vtkm::Vec faceCanonicalIds; + for (vtkm::IdComponent faceIndex = 0; faceIndex < numFacesInHash; ++faceIndex) { - continue; + CellFaceIdPacker::Unpack(cellAndFaceIdOfFacesInHash[faceIndex], myCellId, myFaceId); + vtkm::exec::CellFaceCanonicalId(myFaceId, + cellSet.GetCellShape(myCellId), + cellSet.GetIndices(myCellId), + faceCanonicalIds[faceIndex]); } - vtkm::Id3 otherFace; - vtkm::exec::CellFaceCanonicalId(originFaces[otherIndex], - cellSet.GetCellShape(originCells[otherIndex]), - cellSet.GetIndices(originCells[otherIndex]), - otherFace); - if (myFace == otherFace) + // Start by assuming all faces are duplicate, then remove two for each duplicate pair found. + vtkm::IdComponent numExternalFaces = 0; + // Iterate over the faces in the hash in reverse order (to minimize the swaps being + // performed) and find duplicates faces. Put duplicates at the end and unique faces + // at the beginning. Narrow this range until all unique/duplicate are found. + for (vtkm::IdComponent myIndex = numFacesInHash - 1; myIndex >= numExternalFaces;) { - // Faces are the same. Must be internal. - foundPair = true; - break; + bool isInternal = false; + const vtkm::Id3& myFace = faceCanonicalIds[myIndex]; + vtkm::IdComponent otherIndex; + for (otherIndex = myIndex - 1; otherIndex >= numExternalFaces; --otherIndex) + { + const vtkm::Id3& otherFace = faceCanonicalIds[otherIndex]; + // The first id of the canonical face id is the minimum point id of the face. Since that + // is the hash function, we already know that all faces have the same minimum point id. + if (/*myFace[0] == otherFace[0] && */ myFace[1] == otherFace[1] && + myFace[2] == otherFace[2]) + { + // Faces are the same. Must be internal. We don't have to worry about otherFace + // matching anything else because a proper topology will have at most 2 cells sharing + // a face, so there should be no more matches. + isInternal = true; + break; + } + } + if (isInternal) // If two faces are internal, + { // swap them to the end of the list to avoid revisiting them. + --myIndex; // decrement for the first duplicate face, which is at the end + if (myIndex != otherIndex) + { + FaceCounts::SwapFace( + cellAndFaceIdOfFacesInHash[otherIndex], cellAndFaceIdOfFacesInHash[myIndex]); + vtkm::Swap(faceCanonicalIds[otherIndex], faceCanonicalIds[myIndex]); + } + --myIndex; // decrement for the second duplicate face + } + else // If the face is external + { // swap it to the front of the list, to avoid revisiting it. + if (myIndex != numExternalFaces) + { + FaceCounts::SwapFace( + cellAndFaceIdOfFacesInHash[myIndex], cellAndFaceIdOfFacesInHash[numExternalFaces]); + vtkm::Swap(faceCanonicalIds[myIndex], faceCanonicalIds[numExternalFaces]); + } + ++numExternalFaces; // increment for the new external face + // myIndex remains the same, since we have a new face to check at the same myIndex. + // However, numExternalFaces has incremented, so the loop could still terminate. + } } + return numExternalFaces; } - - if (!foundPair) + else // Slow path without caching { - if (numFound == visitIndex) - { - break; - } - else + CellFaceIdPacker::CellIdType myCellId, otherCellId; + CellFaceIdPacker::FaceIdType myFaceId, otherFaceId; + vtkm::Id3 myFace, otherFace; + // Start by assuming all faces are duplicate, then remove two for each duplicate pair found. + vtkm::IdComponent numExternalFaces = 0; + // Iterate over the faces in the hash in reverse order (to minimize the swaps being + // performed) and find duplicates faces. Put duplicates at the end and unique faces + // at the beginning. Narrow this range until all unique/duplicate are found. + for (vtkm::IdComponent myIndex = numFacesInHash - 1; myIndex >= numExternalFaces;) { - numFound++; + bool isInternal = false; + CellFaceIdPacker::Unpack(cellAndFaceIdOfFacesInHash[myIndex], myCellId, myFaceId); + vtkm::exec::CellFaceCanonicalId( + myFaceId, cellSet.GetCellShape(myCellId), cellSet.GetIndices(myCellId), myFace); + vtkm::IdComponent otherIndex; + for (otherIndex = myIndex - 1; otherIndex >= numExternalFaces; --otherIndex) + { + CellFaceIdPacker::Unpack( + cellAndFaceIdOfFacesInHash[otherIndex], otherCellId, otherFaceId); + vtkm::exec::CellFaceCanonicalId(otherFaceId, + cellSet.GetCellShape(otherCellId), + cellSet.GetIndices(otherCellId), + otherFace); + // The first id of the canonical face id is the minimum point id of the face. Since that + // is the hash function, we already know that all faces have the same minimum point id. + if (/*myFace[0] == otherFace[0] && */ myFace[1] == otherFace[1] && + myFace[2] == otherFace[2]) + { + // Faces are the same. Must be internal. We don't have to worry about otherFace + // matching anything else because a proper topology will have at most 2 cells sharing + // a face, so there should be no more matches. + isInternal = true; + break; + } + } + if (isInternal) // If two faces are internal, + { // swap them to the end of the list to avoid revisiting them. + --myIndex; // decrement for the first duplicate face, which is at the end + if (myIndex != otherIndex) + { + FaceCounts::SwapFace( + cellAndFaceIdOfFacesInHash[otherIndex], cellAndFaceIdOfFacesInHash[myIndex]); + } + --myIndex; // decrement for the second duplicate face + } + else // If the face is external + { // swap it to the front of the list, to avoid revisiting it. + if (myIndex != numExternalFaces) + { + FaceCounts::SwapFace( + cellAndFaceIdOfFacesInHash[myIndex], cellAndFaceIdOfFacesInHash[numExternalFaces]); + } + ++numExternalFaces; // increment for the new external face + // myIndex remains the same, since we have a new face to check at the same myIndex. + // However, numExternalFaces has incremented, so the loop could still terminate. + } } + return numExternalFaces; } - - myIndex++; } - return myIndex; - } + private: + template + VTKM_EXEC inline static void SwapFace(FaceRefT&& cellAndFace1, FaceRefT&& cellAndFace2) + { + const FaceT tmpCellAndFace = cellAndFace1; + cellAndFace1 = cellAndFace2; + cellAndFace2 = tmpCellAndFace; + } + }; public: // Worklet that returns the number of points for each outputted face. // Have to manage the case where multiple faces have the same hash. - class NumPointsPerFace : public vtkm::worklet::WorkletReduceByKey + class NumPointsPerFace : public vtkm::worklet::WorkletMapField { public: - using ControlSignature = void(KeysIn keys, + using ControlSignature = void(FieldIn cellAndFaceIdOfFacesInHash, WholeCellSetIn<> inputCells, - ValuesIn originCells, - ValuesIn originFaces, - ReducedValuesOut numPointsInFace); - using ExecutionSignature = void(_2, _3, _4, VisitIndex, _5); + FieldOut numPointsInExternalFace); + using ExecutionSignature = void(_1, _2, VisitIndex, _3); using InputDomain = _1; using ScatterType = vtkm::worklet::ScatterCounting; - template - VTKM_CONT static ScatterType MakeScatter(const CountArrayType& countArray) - { - VTKM_IS_ARRAY_HANDLE(CountArrayType); - return ScatterType(countArray); - } - - template - VTKM_EXEC void operator()(const CellSetType& cellSet, - const OriginCellsType& originCells, - const OriginFacesType& originFaces, + template + VTKM_EXEC void operator()(const CellAndFaceIdOfFacesInHash& cellAndFaceIdOfFacesInHash, + const CellSetType& cellSet, vtkm::IdComponent visitIndex, - vtkm::IdComponent& numFacePoints) const + vtkm::IdComponent& numPointsInExternalFace) const { - vtkm::IdComponent myIndex = - ExternalFaces::FindUniqueFace(cellSet, originCells, originFaces, visitIndex); + // external faces are first, so we can use the visit index directly + CellFaceIdPacker::CellIdType myCellId; + CellFaceIdPacker::FaceIdType myFaceId; + CellFaceIdPacker::Unpack(cellAndFaceIdOfFacesInHash[visitIndex], myCellId, myFaceId); vtkm::exec::CellFaceNumberOfPoints( - originFaces[myIndex], cellSet.GetCellShape(originCells[myIndex]), numFacePoints); + myFaceId, cellSet.GetCellShape(myCellId), numPointsInExternalFace); } }; // Worklet that returns the shape and connectivity for each external face - class BuildConnectivity : public vtkm::worklet::WorkletReduceByKey + class BuildConnectivity : public vtkm::worklet::WorkletMapField { public: - using ControlSignature = void(KeysIn keys, + using ControlSignature = void(FieldIn cellAndFaceIdOfFacesInHash, WholeCellSetIn<> inputCells, - ValuesIn originCells, - ValuesIn originFaces, - ReducedValuesOut shapesOut, - ReducedValuesOut connectivityOut, - ReducedValuesOut cellIdMapOut); - using ExecutionSignature = void(_2, _3, _4, VisitIndex, _5, _6, _7); + FieldOut shapesOut, + FieldOut connectivityOut, + FieldOut cellIdMapOut); + using ExecutionSignature = void(_1, _2, VisitIndex, _3, _4, _5); using InputDomain = _1; using ScatterType = vtkm::worklet::ScatterCounting; - template - VTKM_EXEC void operator()(const CellSetType& cellSet, - const OriginCellsType& originCells, - const OriginFacesType& originFaces, + template + VTKM_EXEC void operator()(const CellAndFaceIdOfFacesInHash& cellAndFaceIdOfFacesInHash, + const CellSetType& cellSet, vtkm::IdComponent visitIndex, vtkm::UInt8& shapeOut, ConnectivityType& connectivityOut, vtkm::Id& cellIdMapOut) const { - const vtkm::IdComponent myIndex = - ExternalFaces::FindUniqueFace(cellSet, originCells, originFaces, visitIndex); - const vtkm::IdComponent myFace = originFaces[myIndex]; + // external faces are first, so we can use the visit index directly + CellFaceIdPacker::CellIdType myCellId; + CellFaceIdPacker::FaceIdType myFaceId; + CellFaceIdPacker::Unpack(cellAndFaceIdOfFacesInHash[visitIndex], myCellId, myFaceId); - - typename CellSetType::CellShapeTag shapeIn = cellSet.GetCellShape(originCells[myIndex]); - vtkm::exec::CellFaceShape(myFace, shapeIn, shapeOut); - cellIdMapOut = originCells[myIndex]; + const typename CellSetType::CellShapeTag shapeIn = cellSet.GetCellShape(myCellId); + vtkm::exec::CellFaceShape(myFaceId, shapeIn, shapeOut); + cellIdMapOut = myCellId; vtkm::IdComponent numFacePoints; - vtkm::exec::CellFaceNumberOfPoints(myFace, shapeIn, numFacePoints); - + vtkm::exec::CellFaceNumberOfPoints(myFaceId, shapeIn, numFacePoints); VTKM_ASSERT(numFacePoints == connectivityOut.GetNumberOfComponents()); - typename CellSetType::IndicesType inCellIndices = cellSet.GetIndices(originCells[myIndex]); - - for (vtkm::IdComponent facePointIndex = 0; facePointIndex < numFacePoints; facePointIndex++) + const typename CellSetType::IndicesType inCellIndices = cellSet.GetIndices(myCellId); + for (vtkm::IdComponent facePointIndex = 0; facePointIndex < numFacePoints; ++facePointIndex) { vtkm::IdComponent localFaceIndex; - vtkm::ErrorCode status = - vtkm::exec::CellFaceLocalIndex(facePointIndex, myFace, shapeIn, localFaceIndex); + const vtkm::ErrorCode status = + vtkm::exec::CellFaceLocalIndex(facePointIndex, myFaceId, shapeIn, localFaceIndex); if (status == vtkm::ErrorCode::Success) { connectivityOut[facePointIndex] = inCellIndices[localFaceIndex]; @@ -690,6 +781,9 @@ public: const vtkm::cont::CoordinateSystem& coord, vtkm::cont::CellSetExplicit& outCellSet) { + // create an invoker + vtkm::cont::Invoker invoke; + vtkm::Vec3f_64 MinPoint; vtkm::Vec3f_64 MaxPoint; @@ -723,17 +817,17 @@ public: MaxPoint = MinPoint + spacing * unitLength; } - // Create a worklet to count the number of external faces on each cell + // Count the number of external faces per cell vtkm::cont::ArrayHandle numExternalFaces; - vtkm::worklet::DispatcherMapTopology - numExternalFacesDispatcher((NumExternalFacesPerStructuredCell(MinPoint, MaxPoint))); - - numExternalFacesDispatcher.Invoke(inCellSet, numExternalFaces, coordData); + invoke(NumExternalFacesPerStructuredCell(MinPoint, MaxPoint), + inCellSet, + numExternalFaces, + coordData); vtkm::Id numberOfExternalFaces = vtkm::cont::Algorithm::Reduce(numExternalFaces, 0, vtkm::Sum()); - auto scatterCellToExternalFace = BuildConnectivityStructured::MakeScatter(numExternalFaces); + vtkm::worklet::ScatterCounting scatterCellToExternalFace(numExternalFaces); // Maps output cells to input cells. Store this for cell field mapping. this->CellIdMap = scatterCellToExternalFace.GetOutputToInputMap(); @@ -748,17 +842,15 @@ public: // information to. faceConnectivity.Allocate(connectivitySize); - vtkm::worklet::DispatcherMapTopology - buildConnectivityStructuredDispatcher(BuildConnectivityStructured(MinPoint, MaxPoint), - scatterCellToExternalFace); - - buildConnectivityStructuredDispatcher.Invoke( - inCellSet, - inCellSet, - faceShapes, - facePointCount, - vtkm::cont::make_ArrayHandleGroupVec<4>(faceConnectivity), - coordData); + // Build connectivity for external faces + invoke(BuildConnectivityStructured(MinPoint, MaxPoint), + scatterCellToExternalFace, + inCellSet, + inCellSet, + faceShapes, + facePointCount, + vtkm::cont::make_ArrayHandleGroupVec<4>(faceConnectivity), + coordData); auto offsets = vtkm::cont::ConvertNumComponentsToOffsets(facePointCount); @@ -780,14 +872,22 @@ public: using OffsetsArrayType = vtkm::cont::ArrayHandle; using ConnectivityArrayType = vtkm::cont::ArrayHandle; - //Create a worklet to map the number of faces to each cell - vtkm::cont::ArrayHandle facesPerCell; - vtkm::worklet::DispatcherMapTopology numFacesDispatcher; + // create an invoker + vtkm::cont::Invoker invoke; + + // Create an array to store the number of faces per cell + vtkm::cont::ArrayHandle numFacesPerCell; - numFacesDispatcher.Invoke(inCellSet, facesPerCell); + // Compute the number of faces per cell + invoke(NumFacesPerCell(), inCellSet, numFacesPerCell); - vtkm::worklet::ScatterCounting scatterCellToFace(facesPerCell); - facesPerCell.ReleaseResources(); + // Compute the offsets into a packed array holding face information for each cell. + vtkm::Id totalNumberOfFaces; + vtkm::cont::ArrayHandle facesPerCellOffsets; + vtkm::cont::ConvertNumComponentsToOffsets( + numFacesPerCell, facesPerCellOffsets, totalNumberOfFaces); + // Release the resources of numFacesPerCell that is not needed anymore + numFacesPerCell.ReleaseResources(); PointCountArrayType polyDataPointCount; ShapeArrayType polyDataShapes; @@ -798,9 +898,8 @@ public: if (this->PassPolyData) { vtkm::cont::ArrayHandle isPolyDataCell; - vtkm::worklet::DispatcherMapTopology isPolyDataCellDispatcher; - isPolyDataCellDispatcher.Invoke(inCellSet, isPolyDataCell); + invoke(IsPolyDataCell(), inCellSet, isPolyDataCell); vtkm::worklet::ScatterCounting scatterPolyDataCells(isPolyDataCell); @@ -808,28 +907,23 @@ public: if (scatterPolyDataCells.GetOutputRange(inCellSet.GetNumberOfCells()) != 0) { - vtkm::worklet::DispatcherMapTopology - countPolyDataCellPointsDispatcher(scatterPolyDataCells); - - countPolyDataCellPointsDispatcher.Invoke(inCellSet, polyDataPointCount); + invoke(CountPolyDataCellPoints(), scatterPolyDataCells, inCellSet, polyDataPointCount); vtkm::cont::ConvertNumComponentsToOffsets( polyDataPointCount, polyDataOffsets, polyDataConnectivitySize); - vtkm::worklet::DispatcherMapTopology passPolyDataCellsDispatcher( - scatterPolyDataCells); - polyDataConnectivity.Allocate(polyDataConnectivitySize); - passPolyDataCellsDispatcher.Invoke( - inCellSet, - polyDataShapes, - vtkm::cont::make_ArrayHandleGroupVecVariable(polyDataConnectivity, polyDataOffsets), - polyDataCellIdMap); + invoke(PassPolyDataCells(), + scatterPolyDataCells, + inCellSet, + polyDataShapes, + vtkm::cont::make_ArrayHandleGroupVecVariable(polyDataConnectivity, polyDataOffsets), + polyDataCellIdMap); } } - if (scatterCellToFace.GetOutputRange(inCellSet.GetNumberOfCells()) == 0) + if (totalNumberOfFaces == 0) { if (!polyDataConnectivitySize) { @@ -848,86 +942,138 @@ public: } } + // Create an array to store the hash values of the faces vtkm::cont::ArrayHandle faceHashes; - vtkm::cont::ArrayHandle originCells; - vtkm::cont::ArrayHandle originFaces; - vtkm::worklet::DispatcherMapTopology faceHashDispatcher(scatterCellToFace); - - faceHashDispatcher.Invoke(inCellSet, faceHashes, originCells, originFaces); - - vtkm::worklet::Keys faceKeys(faceHashes); - - vtkm::cont::ArrayHandle faceOutputCount; - vtkm::worklet::DispatcherReduceByKey faceCountDispatcher; - - faceCountDispatcher.Invoke(faceKeys, inCellSet, originCells, originFaces, faceOutputCount); - - auto scatterCullInternalFaces = NumPointsPerFace::MakeScatter(faceOutputCount); - - PointCountArrayType facePointCount; - vtkm::worklet::DispatcherReduceByKey pointsPerFaceDispatcher( - scatterCullInternalFaces); - - pointsPerFaceDispatcher.Invoke(faceKeys, inCellSet, originCells, originFaces, facePointCount); - - ShapeArrayType faceShapes; - - OffsetsArrayType faceOffsets; + faceHashes.Allocate(totalNumberOfFaces); + + // Create a group vec array to access the faces of each cell conveniently + auto faceHashesGroupVec = + vtkm::cont::make_ArrayHandleGroupVecVariable(faceHashes, facesPerCellOffsets); + + // Compute the hash values of the faces + invoke(FaceHash(), inCellSet, faceHashesGroupVec); + + // Create an array to store the number of faces per hash + const vtkm::Id numberOfHashes = inCellSet.GetNumberOfPoints(); + vtkm::cont::ArrayHandle numFacesPerHash; + numFacesPerHash.AllocateAndFill(numberOfHashes, 0); + + // Count the number of faces per hash + invoke(NumFacesPerHash(), faceHashes, numFacesPerHash); + + // Compute the offsets for a packed array holding face information for each hash. + vtkm::cont::ArrayHandle facesPerHashOffsets; + vtkm::cont::ConvertNumComponentsToOffsets(numFacesPerHash, facesPerHashOffsets); + + // Create an array to store the cell and face ids of each face per hash + vtkm::cont::ArrayHandle cellAndFaceIdOfFacesPerHash; + cellAndFaceIdOfFacesPerHash.Allocate(totalNumberOfFaces); + + // Create a group vec array to access/write the cell and face ids of each face per hash + auto cellAndFaceIdOfFacesPerHashGroupVec = vtkm::cont::make_ArrayHandleGroupVecVariable( + cellAndFaceIdOfFacesPerHash, facesPerHashOffsets); + + // Build the cell and face ids of all faces per hash + invoke(BuildFacesPerHash(), + faceHashesGroupVec, + numFacesPerHash, + cellAndFaceIdOfFacesPerHashGroupVec); + // Release the resources of the arrays that are not needed anymore + facesPerCellOffsets.ReleaseResources(); + faceHashes.ReleaseResources(); + numFacesPerHash.ReleaseResources(); + + // Create an array to count the number of external faces per hash + vtkm::cont::ArrayHandle numExternalFacesPerHash; + numExternalFacesPerHash.Allocate(numberOfHashes); + + // Compute the number of external faces per hash + invoke(FaceCounts(), cellAndFaceIdOfFacesPerHashGroupVec, inCellSet, numExternalFacesPerHash); + + // Create a scatter counting object to only access the hashes with external faces + vtkm::worklet::ScatterCounting scatterCullInternalFaces(numExternalFacesPerHash); + const vtkm::Id numberOfExternalFaces = scatterCullInternalFaces.GetOutputRange(numberOfHashes); + // Release the resources of externalFacesPerHash that is not needed anymore + numExternalFacesPerHash.ReleaseResources(); + + // Create an array to store the number of points of the external faces + PointCountArrayType numPointsPerExternalFace; + numPointsPerExternalFace.Allocate(numberOfExternalFaces); + + // Compute the number of points of the external faces + invoke(NumPointsPerFace(), + scatterCullInternalFaces, + cellAndFaceIdOfFacesPerHashGroupVec, + inCellSet, + numPointsPerExternalFace); + + // Compute the offsets for a packed array holding the point connections for each external face. + OffsetsArrayType pointsPerExternalFaceOffsets; vtkm::Id connectivitySize; - vtkm::cont::ConvertNumComponentsToOffsets(facePointCount, faceOffsets, connectivitySize); + vtkm::cont::ConvertNumComponentsToOffsets( + numPointsPerExternalFace, pointsPerExternalFaceOffsets, connectivitySize); - ConnectivityArrayType faceConnectivity; - // Must pre allocate because worklet invocation will not have enough - // information to. - faceConnectivity.Allocate(connectivitySize); + // Create an array to connectivity of the external faces + ConnectivityArrayType externalFacesConnectivity; + externalFacesConnectivity.Allocate(connectivitySize); - vtkm::worklet::DispatcherReduceByKey buildConnectivityDispatcher( - scatterCullInternalFaces); + // Create a group vec array to access the connectivity of each external face + auto externalFacesConnectivityGroupVec = vtkm::cont::make_ArrayHandleGroupVecVariable( + externalFacesConnectivity, pointsPerExternalFaceOffsets); - vtkm::cont::ArrayHandle faceToCellIdMap; + // Create an array to store the shape of the external faces + ShapeArrayType externalFacesShapes; + externalFacesShapes.Allocate(numberOfExternalFaces); - // Create a view that doesn't have the last offset: - auto faceOffsetsTrim = - vtkm::cont::make_ArrayHandleView(faceOffsets, 0, faceOffsets.GetNumberOfValues() - 1); + // Create an array to store the cell id of the external faces + vtkm::cont::ArrayHandle faceToCellIdMap; + faceToCellIdMap.Allocate(numberOfExternalFaces); - buildConnectivityDispatcher.Invoke( - faceKeys, - inCellSet, - originCells, - originFaces, - faceShapes, - vtkm::cont::make_ArrayHandleGroupVecVariable(faceConnectivity, faceOffsets), - faceToCellIdMap); + // Build the connectivity of the external faces + invoke(BuildConnectivity(), + scatterCullInternalFaces, + cellAndFaceIdOfFacesPerHashGroupVec, + inCellSet, + externalFacesShapes, + externalFacesConnectivityGroupVec, + faceToCellIdMap); if (!polyDataConnectivitySize) { - outCellSet.Fill(inCellSet.GetNumberOfPoints(), faceShapes, faceConnectivity, faceOffsets); + outCellSet.Fill(inCellSet.GetNumberOfPoints(), + externalFacesShapes, + externalFacesConnectivity, + pointsPerExternalFaceOffsets); this->CellIdMap = faceToCellIdMap; } else { + // Create a view that doesn't have the last offset: + auto pointsPerExternalFaceOffsetsTrim = vtkm::cont::make_ArrayHandleView( + pointsPerExternalFaceOffsets, 0, pointsPerExternalFaceOffsets.GetNumberOfValues() - 1); + // Join poly data to face data output vtkm::cont::ArrayHandleConcatenate faceShapesArray( - faceShapes, polyDataShapes); + externalFacesShapes, polyDataShapes); ShapeArrayType joinedShapesArray; vtkm::cont::ArrayCopy(faceShapesArray, joinedShapesArray); vtkm::cont::ArrayHandleConcatenate pointCountArray( - facePointCount, polyDataPointCount); + numPointsPerExternalFace, polyDataPointCount); PointCountArrayType joinedPointCountArray; vtkm::cont::ArrayCopy(pointCountArray, joinedPointCountArray); vtkm::cont::ArrayHandleConcatenate - connectivityArray(faceConnectivity, polyDataConnectivity); + connectivityArray(externalFacesConnectivity, polyDataConnectivity); ConnectivityArrayType joinedConnectivity; vtkm::cont::ArrayCopy(connectivityArray, joinedConnectivity); // Adjust poly data offsets array with face connectivity size before join auto adjustedPolyDataOffsets = vtkm::cont::make_ArrayHandleTransform( - polyDataOffsets, BiasFunctor(faceConnectivity.GetNumberOfValues())); + polyDataOffsets, BiasFunctor(externalFacesConnectivity.GetNumberOfValues())); - auto offsetsArray = - vtkm::cont::make_ArrayHandleConcatenate(faceOffsetsTrim, adjustedPolyDataOffsets); + auto offsetsArray = vtkm::cont::make_ArrayHandleConcatenate(pointsPerExternalFaceOffsetsTrim, + adjustedPolyDataOffsets); OffsetsArrayType joinedOffsets; // Need to compile a special device copy because the precompiled ArrayCopy does not // know how to copy the ArrayHandleTransform. -- GitLab From dd664477ca16d13d37abe1fe2f89a47de1902258 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 22 Nov 2024 14:03:07 -0500 Subject: [PATCH 46/73] Add changelog --- ...prove-performance-and-memory-consuption.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 docs/changelog/external-faces-improve-performance-and-memory-consuption.md diff --git a/docs/changelog/external-faces-improve-performance-and-memory-consuption.md b/docs/changelog/external-faces-improve-performance-and-memory-consuption.md new file mode 100644 index 0000000000..164ea375ec --- /dev/null +++ b/docs/changelog/external-faces-improve-performance-and-memory-consuption.md @@ -0,0 +1,20 @@ +## ExternalFaces: Improve performance and memory consumption + +`ExternalFaces` now uses a new algorithm that has the following combination of novel characteristics: + +1. employs minimum point id as its face hash function instead of FNV1A of canonicalFaceID, that yields cache-friendly + memory accesses which leads to enhanced performance. +2. employs an atomic hash counting approach, instead of hash sorting, to perform a reduce-by-key operation on the faces' + hashes. +3. enhances performance by ensuring that the computation of face properties occurs only once and by avoiding the + processing of a known internal face more than once. +4. embraces frugality in memory consumption, enabling the processing of larger datasets, especially on GPUs. + +When evaluated on Frontier super-computer on 4 large datasets: + +The new `ExternalFaces` algorithm: + +1. has 4.73x to 5.99x less memory footprint +2. is 4.96x to 7.37x faster on the CPU +3. is 1.54x faster on the GPU, and the original algorithm could not execute on large datasets due to its high memory + footprint -- GitLab From 2aaa3203ea109e923400b8258641a1b50f4bb3a4 Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Wed, 11 Dec 2024 07:42:01 -0500 Subject: [PATCH 47/73] code cleanup. --- vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h | 1 - 1 file changed, 1 deletion(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h index 9c1c6689e6..ed1d5fa3de 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h @@ -140,7 +140,6 @@ protected: std::unordered_map>> workerResults; this->GetWorkerResults(workerResults); - //bool localWork = !workerResults.empty(); vtkm::Id numTerm = 0; for (auto& it : workerResults) for (auto& r : it.second) -- GitLab From abcff604922fe6f96d2f2ff28c2dfc1584da2e5e Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Wed, 11 Dec 2024 16:09:23 -0500 Subject: [PATCH 48/73] Use pyexpander to generate marching cubes tables This is a first step in implementing #829 where we need to add new cases to the tables. It is good to have an in-house script to generate these tables in any case. --- .../contour/worklet/contour/CMakeLists.txt | 4 +- .../worklet/contour/MarchingCellTables.h | 1148 ++++++++--------- .../worklet/contour/MarchingCellTables.h.in | 588 +++++++++ .../contour/worklet/contour/MarchingCells.h | 71 +- 4 files changed, 1180 insertions(+), 631 deletions(-) create mode 100644 vtkm/filter/contour/worklet/contour/MarchingCellTables.h.in diff --git a/vtkm/filter/contour/worklet/contour/CMakeLists.txt b/vtkm/filter/contour/worklet/contour/CMakeLists.txt index afae869d04..b0faf708af 100644 --- a/vtkm/filter/contour/worklet/contour/CMakeLists.txt +++ b/vtkm/filter/contour/worklet/contour/CMakeLists.txt @@ -21,9 +21,11 @@ set(headers FlyingEdgesPass4XWithNormals.h FlyingEdgesPass4Y.h FlyingEdgesTables.h - MarchingCellTables.h MarchingCells.h + MarchingCellTables.h ) #----------------------------------------------------------------------------- vtkm_declare_headers(${headers}) + +vtkm_pyexpander_generated_file(MarchingCellTables.h) diff --git a/vtkm/filter/contour/worklet/contour/MarchingCellTables.h b/vtkm/filter/contour/worklet/contour/MarchingCellTables.h index f6929c162f..56dc9e8d9d 100644 --- a/vtkm/filter/contour/worklet/contour/MarchingCellTables.h +++ b/vtkm/filter/contour/worklet/contour/MarchingCellTables.h @@ -1,4 +1,5 @@ -//============================================================================ +//============================================================================= +// // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. @@ -6,11 +7,16 @@ // 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. -//============================================================================ +// +//============================================================================= + +// **** DO NOT EDIT THIS FILE!!! **** +// This file is automatically generated by VariantDetail.h.in #ifndef vtk_m_MarchingCellTables_h #define vtk_m_MarchingCellTables_h #include +#include #include #include @@ -23,608 +29,566 @@ namespace worklet namespace marching_cells { -// clang-format off -VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent NumVerticesPerCellTable[] = { - 0, // CELL_SHAPE_EMPTY = 0, - 0, // CELL_SHAPE_VERTEX = 1, - 0, // CELL_SHAPE_POLY_VERTEX = 2, - 0, // CELL_SHAPE_LINE = 3, - 0, // CELL_SHAPE_POLY_LINE = 4, - 3, // CELL_SHAPE_TRIANGLE = 5, - 0, // CELL_SHAPE_TRIANGLE_STRIP = 6, - 0, // CELL_SHAPE_POLYGON = 7, - 0, // CELL_SHAPE_PIXEL = 8, - 4, // CELL_SHAPE_QUAD = 9, - 4, // CELL_SHAPE_TETRA = 10, - 0, // CELL_SHAPE_VOXEL = 11, - 8, // CELL_SHAPE_HEXAHEDRON = 12, - 6, // CELL_SHAPE_WEDGE = 13, - 5 // CELL_SHAPE_PYRAMID = 14, -}; - -VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent NumTrianglesTableOffset[] = { - 0, // CELL_SHAPE_EMPTY = 0, - 0, // CELL_SHAPE_VERTEX = 1, - 0, // CELL_SHAPE_POLY_VERTEX = 2, - 0, // CELL_SHAPE_LINE = 3, - 0, // CELL_SHAPE_POLY_LINE = 4, - 0, // CELL_SHAPE_TRIANGLE = 5, - 0, // CELL_SHAPE_TRIANGLE_STRIP = 6, - 0, // CELL_SHAPE_POLYGON = 7, - 0, // CELL_SHAPE_PIXEL = 8, - 0, // CELL_SHAPE_QUAD = 9, - 0, // CELL_SHAPE_TETRA = 10, - 0, // CELL_SHAPE_VOXEL = 11, - 16, // CELL_SHAPE_HEXAHEDRON = 12, - 16+256, // CELL_SHAPE_WEDGE = 13, - 16+256+64 // CELL_SHAPE_PYRAMID = 14, -}; - -VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent NumTrianglesTable[] = { - // CELL_SHAPE_TETRA, case 0 - 15 - 0, 1, 1, 2, 1, 2, 2, 1, 1, 2, 2, 1, 2, 1, 1, 0, - // CELL_SHAPE_HEXAHEDRON, case 0 - 255 - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 2, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, - 2, 3, 3, 2, 3, 4, 4, 3, 3, 4, 4, 3, 4, 5, 5, 2, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4, - 2, 3, 3, 4, 3, 4, 2, 3, 3, 4, 4, 5, 4, 5, 3, 2, - 3, 4, 4, 3, 4, 5, 3, 2, 4, 5, 5, 4, 5, 2, 4, 1, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 2, 4, 3, 4, 3, 5, 2, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4, - 3, 4, 4, 3, 4, 5, 5, 4, 4, 3, 5, 2, 5, 4, 2, 1, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 2, 3, 3, 2, - 3, 4, 4, 5, 4, 5, 5, 2, 4, 3, 5, 4, 3, 2, 4, 1, - 3, 4, 4, 5, 4, 5, 3, 4, 4, 5, 5, 2, 3, 4, 2, 1, - 2, 3, 3, 2, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 1, 0, - // CELL_SHAPE_WEDGE, case 0 - 63 - 0, 1, 1, 2, 1, 2, 2, 1, 1, 2, 2, 3, 2, 3, 3, 2, - 1, 2, 2, 3, 2, 3, 3, 2, 2, 3, 3, 2, 3, 4, 4, 1, - 1, 2, 2, 3, 2, 3, 3, 2, 2, 3, 3, 4, 3, 2, 4, 1, - 2, 3, 3, 4, 3, 4, 2, 1, 1, 2, 2, 1, 2, 1, 1, 0, - // CELL_SHAPE_PYRAMID, case 0 - 31 - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 2, - 2, 3, 3, 2, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 1, 0 -}; - -VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent EdgeTable[] = { - // CELL_SHAPE_TETRA, 6 edge * 2 vertices/edge = 12 entries - 0, 1, // edge 0 : vertex 0 -> vertex 1 - 1, 2, // edge 1 : vertex 1 -> vertex 2 - 0, 2, // edge 2 : vertex 0 -> vertex 2 - 0, 3, // edge 3 : vertex 0 -> vertex 3 - 1, 3, // edge 4 : vertex 1 -> vertex 3 - 2, 3, // edge 5 : vertex 2 -> vertex 3 - // CELL_SHAPE_HEXAHEDRON, 12 edges * 2 vertices/edge = 24 entries - 0, 1, // bottom layer - 1, 2, - 3, 2, - 0, 3, - 4, 5, // top layer - 5, 6, - 7, 6, - 4, 7, - 0, 4, // side - 1, 5, - 2, 6, - 3, 7, - // CELL_SHAPE_WEDGE, 9 edges * 2 vertices/edge = 18 entries - 0, 1, - 1, 2, - 2, 0, - 3, 4, - 4, 5, - 5, 3, - 0, 3, - 1, 4, - 2, 5, - // CELL_SHAPE_PYRAMID, 8 edges * 2 vertices/ede = 16 entries - 0, 1, - 1, 2, - 2, 3, - 3, 0, - 0, 4, - 1, 4, - 2, 4, - 3, 4 -}; - -VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent EdgeTableOffset[] = { - 0, // CELL_SHAPE_EMPTY = 0, - 0, // CELL_SHAPE_VERTEX = 1, - 0, // CELL_SHAPE_POLY_VERTEX = 2, - 0, // CELL_SHAPE_LINE = 3, - 0, // CELL_SHAPE_POLY_LINE = 4, - 0, // CELL_SHAPE_TRIANGLE = 5, - 0, // CELL_SHAPE_TRIANGLE_STRIP = 6, - 0, // CELL_SHAPE_POLYGON = 7, - 0, // CELL_SHAPE_PIXEL = 8, - 0, // CELL_SHAPE_QUAD = 9, - 0, // CELL_SHAPE_TETRA = 10, - 0, // CELL_SHAPE_VOXEL = 11, - 12, // CELL_SHAPE_HEXAHEDRON = 12, - 12+24, // CELL_SHAPE_WEDGE = 13, - 12+24+18 // CELL_SHAPE_PYRAMID = 14, -}; +// Make sure the table indices are the same as VTK-m's shape IDs. +VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_EMPTY == 0); +VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_VERTEX == 1); +VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_LINE == 3); +VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_POLY_LINE == 4); +VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_TRIANGLE == 5); +VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_POLYGON == 7); +VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_QUAD == 9); +VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_TETRA == 10); +VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_HEXAHEDRON == 12); +VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_WEDGE == 13); +VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_PYRAMID == 14); -VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent TriangleTable[] = { -#define X -1 - // CELL_SHAPE_TETRA, 16 cases, 7 edges/cases, 112 entries total - // FIXME, this is different winding rule than VTK - X, X, X, X, X, X, X, - 0, 3, 2, X, X, X, X, - 0, 1, 4, X, X, X, X, - 1, 4, 2, 2, 4, 3, X, - 1, 2, 5, X, X, X, X, - 0, 3, 5, 0, 5, 1, X, - 0, 2, 5, 0, 5, 4, X, - 5, 4, 3, X, X, X, X, - 3, 4, 5, X, X, X, X, - 4, 5, 0, 5, 2, 0, X, - 1, 5, 0, 5, 3, 0, X, - 5, 2, 1, X, X, X, X, - 3, 4, 2, 2, 4, 1, X, - 4, 1, 0, X, X, X, X, - 2, 3, 0, X, X, X, X, - X, X, X, X, X, X, X, - - // CELL_SHAPE_HEXAHEDRON, 256 cases, 16 edges/cases, 4096 entries total - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - 0, 8, 3, X, X, X, X, X, X, X, X, X, X, X, X, X, - 0, 1, 9, X, X, X, X, X, X, X, X, X, X, X, X, X, - 1, 8, 3, 9, 8, 1, X, X, X, X, X, X, X, X, X, X, - 1, 2, 10, X, X, X, X, X, X, X, X, X, X, X, X, X, - 0, 8, 3, 1, 2, 10, X, X, X, X, X, X, X, X, X, X, - 9, 2, 10, 0, 2, 9, X, X, X, X, X, X, X, X, X, X, - 2, 8, 3, 2, 10, 8, 10, 9, 8, X, X, X, X, X, X, X, - 3, 11, 2, X, X, X, X, X, X, X, X, X, X, X, X, X, - 0, 11, 2, 8, 11, 0, X, X, X, X, X, X, X, X, X, X, - 1, 9, 0, 2, 3, 11, X, X, X, X, X, X, X, X, X, X, - 1, 11, 2, 1, 9, 11, 9, 8, 11, X, X, X, X, X, X, X, - 3, 10, 1, 11, 10, 3, X, X, X, X, X, X, X, X, X, X, - 0, 10, 1, 0, 8, 10, 8, 11, 10, X, X, X, X, X, X, X, - 3, 9, 0, 3, 11, 9, 11, 10, 9, X, X, X, X, X, X, X, - 9, 8, 10, 10, 8, 11, X, X, X, X, X, X, X, X, X, X, - 4, 7, 8, X, X, X, X, X, X, X, X, X, X, X, X, X, - 4, 3, 0, 7, 3, 4, X, X, X, X, X, X, X, X, X, X, - 0, 1, 9, 8, 4, 7, X, X, X, X, X, X, X, X, X, X, - 4, 1, 9, 4, 7, 1, 7, 3, 1, X, X, X, X, X, X, X, - 1, 2, 10, 8, 4, 7, X, X, X, X, X, X, X, X, X, X, - 3, 4, 7, 3, 0, 4, 1, 2, 10, X, X, X, X, X, X, X, - 9, 2, 10, 9, 0, 2, 8, 4, 7, X, X, X, X, X, X, X, - 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, X, X, X, X, - 8, 4, 7, 3, 11, 2, X, X, X, X, X, X, X, X, X, X, 11, 4, 7, 11, 2, 4, 2, 0, - 4, X, X, X, X, X, X, X, 9, 0, 1, 8, 4, 7, 2, 3, 11, X, X, X, X, X, X, X, - 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, X, X, X, X, 3, 10, 1, 3, 11, 10, 7, 8, - 4, X, X, X, X, X, X, X, 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, X, X, X, X, - 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, X, X, X, X, 4, 7, 11, 4, 11, 9, 9, 11, - 10, X, X, X, X, X, X, X, 9, 5, 4, X, X, X, X, X, X, X, X, X, X, X, X, X, - 9, 5, 4, 0, 8, 3, X, X, X, X, X, X, X, X, X, X, 0, 5, 4, 1, 5, 0, X, X, - X, X, X, X, X, X, X, X, 8, 5, 4, 8, 3, 5, 3, 1, 5, X, X, X, X, X, X, X, - 1, 2, 10, 9, 5, 4, X, X, X, X, X, X, X, X, X, X, 3, 0, 8, 1, 2, 10, 4, 9, - 5, X, X, X, X, X, X, X, 5, 2, 10, 5, 4, 2, 4, 0, 2, X, X, X, X, X, X, X, - 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, X, X, X, X, 9, 5, 4, 2, 3, 11, X, X, - X, X, X, X, X, X, X, X, 0, 11, 2, 0, 8, 11, 4, 9, 5, X, X, X, X, X, X, X, - 0, 5, 4, 0, 1, 5, 2, 3, 11, X, X, X, X, X, X, X, 2, 1, 5, 2, 5, 8, 2, 8, - 11, 4, 8, 5, X, X, X, X, 10, 3, 11, 10, 1, 3, 9, 5, 4, X, X, X, X, X, X, X, - 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, X, X, X, X, 5, 4, 0, 5, 0, 11, 5, 11, - 10, 11, 0, 3, X, X, X, X, 5, 4, 8, 5, 8, 10, 10, 8, 11, X, X, X, X, X, X, X, - 9, 7, 8, 5, 7, 9, X, X, X, X, X, X, X, X, X, X, 9, 3, 0, 9, 5, 3, 5, 7, - 3, X, X, X, X, X, X, X, 0, 7, 8, 0, 1, 7, 1, 5, 7, X, X, X, X, X, X, X, - 1, 5, 3, 3, 5, 7, X, X, X, X, X, X, X, X, X, X, 9, 7, 8, 9, 5, 7, 10, 1, - 2, X, X, X, X, X, X, X, 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, X, X, X, X, - 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, X, X, X, X, 2, 10, 5, 2, 5, 3, 3, 5, - 7, X, X, X, X, X, X, X, 7, 9, 5, 7, 8, 9, 3, 11, 2, X, X, X, X, X, X, X, - 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, X, X, X, X, 2, 3, 11, 0, 1, 8, 1, 7, - 8, 1, 5, 7, X, X, X, X, 11, 2, 1, 11, 1, 7, 7, 1, 5, X, X, X, X, X, X, X, - 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, X, X, X, X, 5, 7, 0, 5, 0, 9, 7, 11, - 0, 1, 0, 10, 11, 10, 0, X, 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, X, - 11, 10, 5, 7, 11, 5, X, X, X, X, X, X, X, X, X, X, 10, 6, 5, X, X, X, X, X, - X, X, X, X, X, X, X, X, 0, 8, 3, 5, 10, 6, X, X, X, X, X, X, X, X, X, X, - 9, 0, 1, 5, 10, 6, X, X, X, X, X, X, X, X, X, X, 1, 8, 3, 1, 9, 8, 5, 10, - 6, X, X, X, X, X, X, X, 1, 6, 5, 2, 6, 1, X, X, X, X, X, X, X, X, X, X, - 1, 6, 5, 1, 2, 6, 3, 0, 8, X, X, X, X, X, X, X, 9, 6, 5, 9, 0, 6, 0, 2, - 6, X, X, X, X, X, X, X, 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, X, X, X, X, - 2, 3, 11, 10, 6, 5, X, X, X, X, X, X, X, X, X, X, 11, 0, 8, 11, 2, 0, 10, 6, - 5, X, X, X, X, X, X, X, 0, 1, 9, 2, 3, 11, 5, 10, 6, X, X, X, X, X, X, X, - 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, X, X, X, X, 6, 3, 11, 6, 5, 3, 5, 1, - 3, X, X, X, X, X, X, X, 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, X, X, X, X, - 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, X, X, X, X, 6, 5, 9, 6, 9, 11, 11, 9, - 8, X, X, X, X, X, X, X, 5, 10, 6, 4, 7, 8, X, X, X, X, X, X, X, X, X, X, - 4, 3, 0, 4, 7, 3, 6, 5, 10, X, X, X, X, X, X, X, 1, 9, 0, 5, 10, 6, 8, 4, - 7, X, X, X, X, X, X, X, 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, X, X, X, X, - 6, 1, 2, 6, 5, 1, 4, 7, 8, X, X, X, X, X, X, X, 1, 2, 5, 5, 2, 6, 3, 0, - 4, 3, 4, 7, X, X, X, X, 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, X, X, X, X, - 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, X, 3, 11, 2, 7, 8, 4, 10, 6, - 5, X, X, X, X, X, X, X, 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, X, X, X, X, - 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, X, X, X, X, 9, 2, 1, 9, 11, 2, 9, 4, - 11, 7, 11, 4, 5, 10, 6, X, 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, X, X, X, X, - 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, X, 0, 5, 9, 0, 6, 5, 0, 3, - 6, 11, 6, 3, 8, 4, 7, X, 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, X, X, X, X, - 10, 4, 9, 6, 4, 10, X, X, X, X, X, X, X, X, X, X, 4, 10, 6, 4, 9, 10, 0, 8, - 3, X, X, X, X, X, X, X, 10, 0, 1, 10, 6, 0, 6, 4, 0, X, X, X, X, X, X, X, - 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, X, X, X, X, 1, 4, 9, 1, 2, 4, 2, 6, - 4, X, X, X, X, X, X, X, 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, X, X, X, X, - 0, 2, 4, 4, 2, 6, X, X, X, X, X, X, X, X, X, X, 8, 3, 2, 8, 2, 4, 4, 2, - 6, X, X, X, X, X, X, X, 10, 4, 9, 10, 6, 4, 11, 2, 3, X, X, X, X, X, X, X, - 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, X, X, X, X, 3, 11, 2, 0, 1, 6, 0, 6, - 4, 6, 1, 10, X, X, X, X, 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, X, - 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, X, X, X, X, 8, 11, 1, 8, 1, 0, 11, 6, - 1, 9, 1, 4, 6, 4, 1, X, 3, 11, 6, 3, 6, 0, 0, 6, 4, X, X, X, X, X, X, X, - 6, 4, 8, 11, 6, 8, X, X, X, X, X, X, X, X, X, X, 7, 10, 6, 7, 8, 10, 8, 9, - 10, X, X, X, X, X, X, X, 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, X, X, X, X, - 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, X, X, X, X, 10, 6, 7, 10, 7, 1, 1, 7, - 3, X, X, X, X, X, X, X, 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, X, X, X, X, - 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, X, 7, 8, 0, 7, 0, 6, 6, 0, - 2, X, X, X, X, X, X, X, 7, 3, 2, 6, 7, 2, X, X, X, X, X, X, X, X, X, X, - 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, X, X, X, X, 2, 0, 7, 2, 7, 11, 0, 9, - 7, 6, 7, 10, 9, 10, 7, X, 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, X, - 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, X, X, X, X, 8, 9, 6, 8, 6, 7, 9, 1, - 6, 11, 6, 3, 1, 3, 6, X, 0, 9, 1, 11, 6, 7, X, X, X, X, X, X, X, X, X, X, - 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, X, X, X, X, 7, 11, 6, X, X, X, X, X, - X, X, X, X, X, X, X, X, 7, 6, 11, X, X, X, X, X, X, X, X, X, X, X, X, X, - 3, 0, 8, 11, 7, 6, X, X, X, X, X, X, X, X, X, X, 0, 1, 9, 11, 7, 6, X, X, - X, X, X, X, X, X, X, X, 8, 1, 9, 8, 3, 1, 11, 7, 6, X, X, X, X, X, X, X, - 10, 1, 2, 6, 11, 7, X, X, X, X, X, X, X, X, X, X, 1, 2, 10, 3, 0, 8, 6, 11, - 7, X, X, X, X, X, X, X, 2, 9, 0, 2, 10, 9, 6, 11, 7, X, X, X, X, X, X, X, - 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, X, X, X, X, 7, 2, 3, 6, 2, 7, X, X, - X, X, X, X, X, X, X, X, 7, 0, 8, 7, 6, 0, 6, 2, 0, X, X, X, X, X, X, X, - 2, 7, 6, 2, 3, 7, 0, 1, 9, X, X, X, X, X, X, X, 1, 6, 2, 1, 8, 6, 1, 9, - 8, 8, 7, 6, X, X, X, X, 10, 7, 6, 10, 1, 7, 1, 3, 7, X, X, X, X, X, X, X, - 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, X, X, X, X, 0, 3, 7, 0, 7, 10, 0, 10, - 9, 6, 10, 7, X, X, X, X, 7, 6, 10, 7, 10, 8, 8, 10, 9, X, X, X, X, X, X, X, - 6, 8, 4, 11, 8, 6, X, X, X, X, X, X, X, X, X, X, 3, 6, 11, 3, 0, 6, 0, 4, - 6, X, X, X, X, X, X, X, 8, 6, 11, 8, 4, 6, 9, 0, 1, X, X, X, X, X, X, X, - 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, X, X, X, X, 6, 8, 4, 6, 11, 8, 2, 10, - 1, X, X, X, X, X, X, X, 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, X, X, X, X, - 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, X, X, X, X, 10, 9, 3, 10, 3, 2, 9, 4, - 3, 11, 3, 6, 4, 6, 3, X, 8, 2, 3, 8, 4, 2, 4, 6, 2, X, X, X, X, X, X, X, - 0, 4, 2, 4, 6, 2, X, X, X, X, X, X, X, X, X, X, 1, 9, 0, 2, 3, 4, 2, 4, - 6, 4, 3, 8, X, X, X, X, 1, 9, 4, 1, 4, 2, 2, 4, 6, X, X, X, X, X, X, X, - 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, X, X, X, X, 10, 1, 0, 10, 0, 6, 6, 0, - 4, X, X, X, X, X, X, X, 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, X, - 10, 9, 4, 6, 10, 4, X, X, X, X, X, X, X, X, X, X, 4, 9, 5, 7, 6, 11, X, X, - X, X, X, X, X, X, X, X, 0, 8, 3, 4, 9, 5, 11, 7, 6, X, X, X, X, X, X, X, - 5, 0, 1, 5, 4, 0, 7, 6, 11, X, X, X, X, X, X, X, 11, 7, 6, 8, 3, 4, 3, 5, - 4, 3, 1, 5, X, X, X, X, 9, 5, 4, 10, 1, 2, 7, 6, 11, X, X, X, X, X, X, X, - 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, X, X, X, X, 7, 6, 11, 5, 4, 10, 4, 2, - 10, 4, 0, 2, X, X, X, X, 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, X, - 7, 2, 3, 7, 6, 2, 5, 4, 9, X, X, X, X, X, X, X, 9, 5, 4, 0, 8, 6, 0, 6, - 2, 6, 8, 7, X, X, X, X, 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, X, X, X, X, - 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, X, 9, 5, 4, 10, 1, 6, 1, 7, - 6, 1, 3, 7, X, X, X, X, 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, X, - 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, X, 7, 6, 10, 7, 10, 8, 5, 4, - 10, 4, 8, 10, X, X, X, X, 6, 9, 5, 6, 11, 9, 11, 8, 9, X, X, X, X, X, X, X, - 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, X, X, X, X, 0, 11, 8, 0, 5, 11, 0, 1, - 5, 5, 6, 11, X, X, X, X, 6, 11, 3, 6, 3, 5, 5, 3, 1, X, X, X, X, X, X, X, - 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, X, X, X, X, 0, 11, 3, 0, 6, 11, 0, 9, - 6, 5, 6, 9, 1, 2, 10, X, 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, X, - 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, X, X, X, X, 5, 8, 9, 5, 2, 8, 5, 6, - 2, 3, 8, 2, X, X, X, X, 9, 5, 6, 9, 6, 0, 0, 6, 2, X, X, X, X, X, X, X, - 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, X, 1, 5, 6, 2, 1, 6, X, X, - X, X, X, X, X, X, X, X, 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, X, - 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, X, X, X, X, 0, 3, 8, 5, 6, 10, X, X, - X, X, X, X, X, X, X, X, 10, 5, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, - 11, 5, 10, 7, 5, 11, X, X, X, X, X, X, X, X, X, X, 11, 5, 10, 11, 7, 5, 8, 3, - 0, X, X, X, X, X, X, X, 5, 11, 7, 5, 10, 11, 1, 9, 0, X, X, X, X, X, X, X, - 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, X, X, X, X, 11, 1, 2, 11, 7, 1, 7, 5, - 1, X, X, X, X, X, X, X, 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, X, X, X, X, - 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, X, X, X, X, 7, 5, 2, 7, 2, 11, 5, 9, - 2, 3, 2, 8, 9, 8, 2, X, 2, 5, 10, 2, 3, 5, 3, 7, 5, X, X, X, X, X, X, X, - 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, X, X, X, X, 9, 0, 1, 5, 10, 3, 5, 3, - 7, 3, 10, 2, X, X, X, X, 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, X, - 1, 3, 5, 3, 7, 5, X, X, X, X, X, X, X, X, X, X, 0, 8, 7, 0, 7, 1, 1, 7, - 5, X, X, X, X, X, X, X, 9, 0, 3, 9, 3, 5, 5, 3, 7, X, X, X, X, X, X, X, - 9, 8, 7, 5, 9, 7, X, X, X, X, X, X, X, X, X, X, 5, 8, 4, 5, 10, 8, 10, 11, - 8, X, X, X, X, X, X, X, 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, X, X, X, X, - 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, X, X, X, X, 10, 11, 4, 10, 4, 5, 11, 3, - 4, 9, 4, 1, 3, 1, 4, X, 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, X, X, X, X, - 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, X, 0, 2, 5, 0, 5, 9, 2, 11, - 5, 4, 5, 8, 11, 8, 5, X, 9, 4, 5, 2, 11, 3, X, X, X, X, X, X, X, X, X, X, - 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, X, X, X, X, 5, 10, 2, 5, 2, 4, 4, 2, - 0, X, X, X, X, X, X, X, 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, X, - 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, X, X, X, X, 8, 4, 5, 8, 5, 3, 3, 5, - 1, X, X, X, X, X, X, X, 0, 4, 5, 1, 0, 5, X, X, X, X, X, X, X, X, X, X, - 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, X, X, X, X, 9, 4, 5, X, X, X, X, X, - X, X, X, X, X, X, X, X, 4, 11, 7, 4, 9, 11, 9, 10, 11, X, X, X, X, X, X, X, - 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, X, X, X, X, 1, 10, 11, 1, 11, 4, 1, 4, - 0, 7, 4, 11, X, X, X, X, 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, X, - 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, X, X, X, X, 9, 7, 4, 9, 11, 7, 9, 1, - 11, 2, 11, 1, 0, 8, 3, X, 11, 7, 4, 11, 4, 2, 2, 4, 0, X, X, X, X, X, X, X, - 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, X, X, X, X, 2, 9, 10, 2, 7, 9, 2, 3, - 7, 7, 4, 9, X, X, X, X, 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, X, - 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, X, 1, 10, 2, 8, 7, 4, X, X, - X, X, X, X, X, X, X, X, 4, 9, 1, 4, 1, 7, 7, 1, 3, X, X, X, X, X, X, X, - 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, X, X, X, X, 4, 0, 3, 7, 4, 3, X, X, - X, X, X, X, X, X, X, X, 4, 8, 7, X, X, X, X, X, X, X, X, X, X, X, X, X, - 9, 10, 8, 10, 11, 8, X, X, X, X, X, X, X, X, X, X, 3, 0, 9, 3, 9, 11, 11, 9, - 10, X, X, X, X, X, X, X, 0, 1, 10, 0, 10, 8, 8, 10, 11, X, X, X, X, X, X, X, - 3, 1, 10, 11, 3, 10, X, X, X, X, X, X, X, X, X, X, 1, 2, 11, 1, 11, 9, 9, 11, - 8, X, X, X, X, X, X, X, 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, X, X, X, X, - 0, 2, 11, 8, 0, 11, X, X, X, X, X, X, X, X, X, X, 3, 2, 11, X, X, X, X, X, - X, X, X, X, X, X, X, X, 2, 3, 8, 2, 8, 10, 10, 8, 9, X, X, X, X, X, X, X, - 9, 10, 2, 0, 9, 2, X, X, X, X, X, X, X, X, X, X, 2, 3, 8, 2, 8, 10, 0, 1, - 8, 1, 10, 8, X, X, X, X, 1, 10, 2, X, X, X, X, X, X, X, X, X, X, X, X, X, - 1, 3, 8, 9, 1, 8, X, X, X, X, X, X, X, X, X, X, 0, 9, 1, X, X, X, X, X, - X, X, X, X, X, X, X, X, 0, 3, 8, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - - // CELL_SHAPE_WEDGE = 13, 64 cases, 13 edges/case, 832 total entries - X, X, X, X, X, X, X, X, X, X, X, X, X, //0 - 0, 6, 2, X, X, X, X, X, X, X, X, X, X, //1 - 0, 1, 7, X, X, X, X, X, X, X, X, X, X, //2 - 6, 1, 7, 6, 2, 1, X, X, X, X, X, X, X, //3 - 1, 2, 8, X, X, X, X, X, X, X, X, X, X, //4 - 6, 1, 0, 6, 8, 1, X, X, X, X, X, X, X, //5 - 0, 2, 8, 7, 0, 8, X, X, X, X, X, X, X, //6 - 7, 6, 8, X, X, X, X, X, X, X, X, X, X, //7 - 3, 5, 6, X, X, X, X, X, X, X, X, X, X, //8 - 3, 5, 0, 5, 2, 0, X, X, X, X, X, X, X, //9 - 0, 1, 7, 6, 3, 5, X, X, X, X, X, X, X, //10 - 1, 7, 3, 1, 3, 5, 1, 5, 2, X, X, X, X, //11 - 2, 8, 1, 6, 3, 5, X, X, X, X, X, X, X, //12 - 0, 3, 1, 1, 3, 5, 1, 5, 8, X, X, X, X, //13 - 6, 3, 5, 0, 8, 7, 0, 2, 8, X, X, X, X, //14 - 7, 3, 5, 7, 5, 8, X, X, X, X, X, X, X, //15 - 7, 4, 3, X, X, X, X, X, X, X, X, X, X, //16 - 7, 4, 3, 0, 6, 2, X, X, X, X, X, X, X, //17 - 0, 1, 3, 1, 4, 3, X, X, X, X, X, X, X, //18 - 1, 4, 3, 1, 3, 6, 1, 6, 2, X, X, X, X, //19 - 7, 4, 3, 2, 8, 1, X, X, X, X, X, X, X, //20 - 7, 4, 3, 6, 1, 0, 6, 8, 1, X, X, X, X, //21 - 0, 4, 3, 0, 8, 4, 0, 2, 8, X, X, X, X, //22 - 6, 8, 3, 3, 8, 4, X, X, X, X, X, X, X, //23 - 6, 7, 4, 6, 4, 5, X, X, X, X, X, X, X, //24 - 0, 7, 5, 7, 4, 5, 2, 0, 5, X, X, X, X, //25 - 1, 6, 0, 1, 5, 6, 1, 4, 5, X, X, X, X, //26 - 2, 1, 5, 5, 1, 4, X, X, X, X, X, X, X, //27 - 2, 8, 1, 6, 7, 5, 7, 4, 5, X, X, X, X, //28 - 0, 7, 5, 7, 4, 5, 0, 5, 1, 1, 5, 8, X, //29 - 0, 2, 8, 0, 8, 4, 0, 4, 5, 0, 5, 6, X, //30 - 8, 4, 5, X, X, X, X, X, X, X, X, X, X, //31 - 4, 8, 5, X, X, X, X, X, X, X, X, X, X, //32 - 4, 8, 5, 0, 6, 2, X, X, X, X, X, X, X, //33 - 4, 8, 5, 0, 1, 7, X, X, X, X, X, X, X, //34 - 4, 8, 5, 6, 1, 7, 6, 2, 1, X, X, X, X, //35 - 1, 5, 4, 2, 5, 1, X, X, X, X, X, X, X, //36 - 1, 5, 4, 1, 6, 5, 1, 0, 6, X, X, X, X, //37 - 5, 4, 7, 5, 7, 0, 5, 0, 2, X, X, X, X, //38 - 6, 4, 7, 6, 5, 4, X, X, X, X, X, X, X, //39 - 6, 3, 8, 3, 4, 8, X, X, X, X, X, X, X, //40 - 0, 3, 4, 0, 4, 8, 0, 8, 2, X, X, X, X, //41 - 7, 0, 1, 6, 3, 4, 6, 4, 8, X, X, X, X, //42 - 1, 7, 3, 1, 3, 2, 2, 3, 8, 8, 3, 4, X, //43 - 2, 6, 1, 6, 3, 1, 3, 4, 1, X, X, X, X, //44 - 0, 3, 1, 1, 3, 4, X, X, X, X, X, X, X, //45 - 7, 0, 4, 4, 0, 2, 4, 2, 3, 3, 2, 6, X, //46 - 7, 3, 4, X, X, X, X, X, X, X, X, X, X, //47 - 7, 8, 5, 7, 5, 3, X, X, X, X, X, X, X, //48 - 0, 6, 2, 7, 8, 5, 7, 5, 3, X, X, X, X, //49 - 0, 1, 3, 1, 5, 3, 1, 8, 5, X, X, X, X, //50 - 2, 1, 6, 6, 1, 3, 5, 1, 8, 3, 1, 5, X, //51 - 1, 3, 7, 1, 5, 3, 1, 2, 5, X, X, X, X, //52 - 1, 0, 6, 1, 6, 5, 1, 5, 7, 7, 5, 3, X, //53 - 0, 2, 5, 0, 5, 3, X, X, X, X, X, X, X, //54 - 3, 6, 5, X, X, X, X, X, X, X, X, X, X, //55 - 7, 8, 6, X, X, X, X, X, X, X, X, X, X, //56 - 0, 7, 8, 0, 8, 2, X, X, X, X, X, X, X, //57 - 0, 1, 6, 1, 8, 6, X, X, X, X, X, X, X, //58 - 2, 1, 8, X, X, X, X, X, X, X, X, X, X, //59 - 6, 7, 1, 6, 1, 2, X, X, X, X, X, X, X, //60 - 0, 7, 1, X, X, X, X, X, X, X, X, X, X, //61 - 0, 2, 6, X, X, X, X, X, X, X, X, X, X, //62 - X, X, X, X, X, X, X, X, X, X, X, X, X, //63 - - // CELL_SHAPE_PYRAMID = 14, 32 cases, 13 edges/case, 416 total entries - X, X, X, X, X, X, X, X, X, X, X, X, X, //0 - 3, 4, 0, X, X, X, X, X, X, X, X, X, X, //1 - 5, 1, 0, X, X, X, X, X, X, X, X, X, X, //2 - 5, 1, 4, 1, 3, 4, X, X, X, X, X, X, X, //3 - 6, 2, 1, X, X, X, X, X, X, X, X, X, X, //4 - 3, 4, 0, 6, 2, 1, X, X, X, X, X, X, X, //5 - 5, 2, 0, 6, 2, 5, X, X, X, X, X, X, X, //6 - 2, 3, 4, 2, 4, 6, 4, 5, 6, X, X, X, X, //7 - 2, 7, 3, X, X, X, X, X, X, X, X, X, X, //8 - 2, 7, 4, 4, 0, 2, X, X, X, X, X, X, X, //9 - 5, 1, 0, 2, 7, 3, X, X, X, X, X, X, X, //10 - 5, 7, 4, 1, 7, 5, 2, 7, 1, X, X, X, X, //11 - 6, 3, 1, 7, 3, 6, X, X, X, X, X, X, X, //12 - 4, 6, 7, 0, 6, 4, 1, 6, 0, X, X, X, X, //13 - 7, 5, 6, 3, 5, 7, 0, 5, 3, X, X, X, X, //14 - 7, 4, 5, 7, 5, 6, X, X, X, X, X, X, X, //15 - 7, 5, 4, 7, 6, 5, X, X, X, X, X, X, X, //16 - 5, 0, 3, 6, 5, 3, 7, 6, 3, X, X, X, X, //17 - 1, 0, 4, 7, 1, 4, 6, 1, 7, X, X, X, X, //18 - 6, 1, 3, 7, 6, 3, X, X, X, X, X, X, X, //19 - 7, 5, 4, 7, 1, 5, 7, 2, 1, X, X, X, X, //20 - 3, 7, 0, 7, 5, 0, 7, 2, 5, 2, 1, 5, X, //21 - 4, 2, 0, 7, 2, 4, X, X, X, X, X, X, X, //22 - 7, 2, 3, X, X, X, X, X, X, X, X, X, X, //23 - 2, 4, 3, 5, 4, 2, 6, 5, 2, X, X, X, X, //24 - 2, 5, 0, 2, 6, 5, X, X, X, X, X, X, X, //25 - 6, 1, 0, 4, 6, 0, 3, 6, 4, 3, 2, 6, X, //26 - 2, 6, 1, X, X, X, X, X, X, X, X, X, X, //27 - 1, 4, 3, 1, 5, 4, X, X, X, X, X, X, X, //28 - 1, 5, 0, X, X, X, X, X, X, X, X, X, X, //29 - 4, 3, 0, X, X, X, X, X, X, X, X, X, X, //30 - X, X, X, X, X, X, X, X, X, X, X, X, X //31 - -#undef X -}; - -VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent TriangleTableOffset[] = { - 0, // CELL_SHAPE_EMPTY = 0, - 0, // CELL_SHAPE_VERTEX = 1, - 0, // CELL_SHAPE_POLY_VERTEX = 2, - 0, // CELL_SHAPE_LINE = 3, - 0, // CELL_SHAPE_POLY_LINE = 4, - 0, // CELL_SHAPE_TRIANGLE = 5, - 0, // CELL_SHAPE_TRIANGLE_STRIP = 6, - 0, // CELL_SHAPE_POLYGON = 7, - 0, // CELL_SHAPE_PIXEL = 8, - 0, // CELL_SHAPE_QUAD = 9, - 0, // CELL_SHAPE_TETRA = 10, - 0, // CELL_SHAPE_VOXEL = 11, - 112, // CELL_SHAPE_HEXAHEDRON = 12, - 112+4096, // CELL_SHAPE_WEDGE = 13, - 112+4096+832, // CELL_SHAPE_PYRAMID = 14, -}; - -// clang-format on -class CellClassifyTable : public vtkm::cont::ExecutionObjectBase +VTKM_EXEC inline vtkm::IdComponent GetNumTrianglesOffset(vtkm::UInt8 cellType) { -public: - template - class ExecObject - { - public: - VTKM_EXEC - vtkm::IdComponent GetNumVerticesPerCell(vtkm::Id shape) const - { - return this->NumVerticesPerCellPortal.Get(shape); - } - - VTKM_EXEC - vtkm::IdComponent GetNumTriangles(vtkm::Id shape, vtkm::IdComponent caseNumber) const - { - vtkm::IdComponent offset = this->NumTrianglesTableOffsetPortal.Get(shape); - return this->NumTrianglesTablePortal.Get(offset + caseNumber); - } - - private: - typename vtkm::cont::ArrayHandle::ReadPortalType NumVerticesPerCellPortal; - typename vtkm::cont::ArrayHandle::ReadPortalType NumTrianglesTablePortal; - typename vtkm::cont::ArrayHandle::ReadPortalType - NumTrianglesTableOffsetPortal; - - friend class CellClassifyTable; + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent offsets[] = { + 0, // CELL_SHAPE_EMPTY + 0, // CELL_SHAPE_VERTEX + 0, // CELL_SHAPE_POLY_VERTEX + 0, // CELL_SHAPE_LINE + 0, // CELL_SHAPE_POLY_LINE + 0, // CELL_SHAPE_TRIANGLE + 0, // CELL_SHAPE_TRIANGLE_STRIP + 0, // CELL_SHAPE_POLYGON + 0, // CELL_SHAPE_PIXEL + 0, // CELL_SHAPE_QUAD + 0, // CELL_SHAPE_TETRA + 16, // CELL_SHAPE_VOXEL + 16, // CELL_SHAPE_HEXAHEDRON + 272, // CELL_SHAPE_WEDGE + 336, // CELL_SHAPE_PYRAMID + 0 // dummy }; + // clang-format on - CellClassifyTable() - : NumVerticesPerCellArray(vtkm::cont::make_ArrayHandle(NumVerticesPerCellTable, - vtkm::NUMBER_OF_CELL_SHAPES, - vtkm::CopyFlag::Off)) - , NumTrianglesTableOffsetArray(vtkm::cont::make_ArrayHandle(NumTrianglesTableOffset, - vtkm::NUMBER_OF_CELL_SHAPES, - vtkm::CopyFlag::Off)) - , NumTrianglesTableArray( - vtkm::cont::make_ArrayHandle(NumTrianglesTable, - sizeof(NumTrianglesTable) / sizeof(NumTrianglesTable[0]), - vtkm::CopyFlag::Off)) - { - } - - template - ExecObject PrepareForExecution(DeviceAdapter, vtkm::cont::Token& token) - { - ExecObject execObject; - execObject.NumVerticesPerCellPortal = - this->NumVerticesPerCellArray.PrepareForInput(DeviceAdapter(), token); - execObject.NumTrianglesTableOffsetPortal = - this->NumTrianglesTableOffsetArray.PrepareForInput(DeviceAdapter(), token); - execObject.NumTrianglesTablePortal = - this->NumTrianglesTableArray.PrepareForInput(DeviceAdapter(), token); - return execObject; - } - -private: - vtkm::cont::ArrayHandle NumVerticesPerCellArray; - vtkm::cont::ArrayHandle NumTrianglesTableOffsetArray; - vtkm::cont::ArrayHandle NumTrianglesTableArray; -}; + return offsets[cellType]; +} -class TriangleGenerationTable : public vtkm::cont::ExecutionObjectBase +VTKM_EXEC inline vtkm::UInt8 GetNumTriangles(vtkm::UInt8 cellType, vtkm::UInt8 caseNumber) { -public: - template - class ExecObject - { - public: - VTKM_EXEC - vtkm::Pair GetEdgeVertices( - vtkm::Id shape, - vtkm::IdComponent caseNumber, - vtkm::IdComponent triangleNumber, - vtkm::IdComponent vertexNumber) const - { - VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent NumEntriesPerCase[] = { - 0, // CELL_SHAPE_EMPTY = 0, - 0, // CELL_SHAPE_VERTEX = 1, - 0, // CELL_SHAPE_POLY_VERTEX = 2, - 0, // CELL_SHAPE_LINE = 3, - 0, // CELL_SHAPE_POLY_LINE = 4, - 0, // CELL_SHAPE_TRIANGLE = 5, - 0, // CELL_SHAPE_TRIANGLE_STRIP = 6, - 0, // CELL_SHAPE_POLYGON = 7, - 0, // CELL_SHAPE_PIXEL = 8, - 0, // CELL_SHAPE_QUAD = 9, - 7, // CELL_SHAPE_TETRA = 10, - 0, // CELL_SHAPE_VOXEL = 11, - 16, // CELL_SHAPE_HEXAHEDRON = 12, - 13, // CELL_SHAPE_WEDGE = 13, - 13, // CELL_SHAPE_PYRAMID = 14, - }; - - vtkm::IdComponent triOffset = TriangleTableOffsetPortal.Get(shape) + - NumEntriesPerCase[shape] * caseNumber + triangleNumber * 3; - vtkm::IdComponent edgeIndex = TriangleTablePortal.Get(triOffset + vertexNumber); - vtkm::IdComponent edgeOffset = EdgeTableOffsetPortal.Get(shape); + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 numTriangles[] = { + // CELL_SHAPE_EMPTY + // CELL_SHAPE_VERTEX + // CELL_SHAPE_POLY_VERTEX + // CELL_SHAPE_LINE + // CELL_SHAPE_POLY_LINE + // CELL_SHAPE_TRIANGLE + // CELL_SHAPE_TRIANGLE_STRIP + // CELL_SHAPE_POLYGON + // CELL_SHAPE_PIXEL + // CELL_SHAPE_QUAD + // CELL_SHAPE_TETRA + 0, 1, 1, 2, 1, 2, 2, 1, 1, 2, 2, 1, 2, 1, 1, 0, // cases 0 - 15 + // CELL_SHAPE_VOXEL + // CELL_SHAPE_HEXAHEDRON + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 2, // cases 0 - 15 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 16 - 31 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 32 - 47 + 2, 3, 3, 2, 3, 4, 4, 3, 3, 4, 4, 3, 4, 5, 5, 2, // cases 48 - 63 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 64 - 79 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4, // cases 80 - 95 + 2, 3, 3, 4, 3, 4, 2, 3, 3, 4, 4, 5, 4, 5, 3, 2, // cases 96 - 111 + 3, 4, 4, 3, 4, 5, 3, 2, 4, 5, 5, 4, 5, 2, 4, 1, // cases 112 - 127 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 128 - 143 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 2, 4, 3, 4, 3, 5, 2, // cases 144 - 159 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4, // cases 160 - 175 + 3, 4, 4, 3, 4, 5, 5, 4, 4, 3, 5, 2, 5, 4, 2, 1, // cases 176 - 191 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 2, 3, 3, 2, // cases 192 - 207 + 3, 4, 4, 5, 4, 5, 5, 2, 4, 3, 5, 4, 3, 2, 4, 1, // cases 208 - 223 + 3, 4, 4, 5, 4, 5, 3, 4, 4, 5, 5, 2, 3, 4, 2, 1, // cases 224 - 239 + 2, 3, 3, 2, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 1, 0, // cases 240 - 255 + // CELL_SHAPE_WEDGE + 0, 1, 1, 2, 1, 2, 2, 1, 1, 2, 2, 3, 2, 3, 3, 2, // cases 0 - 15 + 1, 2, 2, 3, 2, 3, 3, 2, 2, 3, 3, 2, 3, 4, 4, 1, // cases 16 - 31 + 1, 2, 2, 3, 2, 3, 3, 2, 2, 3, 3, 4, 3, 2, 4, 1, // cases 32 - 47 + 2, 3, 3, 4, 3, 4, 2, 1, 1, 2, 2, 1, 2, 1, 1, 0, // cases 48 - 63 + // CELL_SHAPE_PYRAMID + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 2, // cases 0 - 15 + 2, 3, 3, 2, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 1, 0, // cases 16 - 31 + 0 // dummy + }; + // clang-format on - return { EdgeTablePortal.Get(edgeOffset + edgeIndex * 2 + 0), - EdgeTablePortal.Get(edgeOffset + edgeIndex * 2 + 1) }; - } + vtkm::IdComponent offset = GetNumTrianglesOffset(cellType); + return numTriangles[offset + caseNumber]; +} - private: - typename vtkm::cont::ArrayHandle::ReadPortalType EdgeTablePortal; - typename vtkm::cont::ArrayHandle::ReadPortalType EdgeTableOffsetPortal; - typename vtkm::cont::ArrayHandle::ReadPortalType TriangleTablePortal; - typename vtkm::cont::ArrayHandle::ReadPortalType TriangleTableOffsetPortal; - friend class TriangleGenerationTable; +VTKM_EXEC inline vtkm::IdComponent GetTriTableOffset(vtkm::UInt8 cellType, vtkm::UInt8 caseNumber) +{ + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent triTableOffset[] = { + // CELL_SHAPE_EMPTY + // CELL_SHAPE_VERTEX + // CELL_SHAPE_POLY_VERTEX + // CELL_SHAPE_LINE + // CELL_SHAPE_POLY_LINE + // CELL_SHAPE_TRIANGLE + // CELL_SHAPE_TRIANGLE_STRIP + // CELL_SHAPE_POLYGON + // CELL_SHAPE_PIXEL + // CELL_SHAPE_QUAD + // CELL_SHAPE_TETRA + 0, 0, 1, 2, 4, 5, 7, 9, // cases 0 - 7 + 10, 11, 13, 15, 16, 18, 19, 20, // cases 8 - 15 + // CELL_SHAPE_VOXEL + // CELL_SHAPE_HEXAHEDRON + 20, 20, 21, 22, 24, 25, 27, 29, // cases 0 - 7 + 32, 33, 35, 37, 40, 42, 45, 48, // cases 8 - 15 + 50, 51, 53, 55, 58, 60, 63, 66, // cases 16 - 23 + 70, 72, 75, 78, 82, 85, 89, 93, // cases 24 - 31 + 96, 97, 99, 101, 104, 106, 109, 112, // cases 32 - 39 + 116, 118, 121, 124, 128, 131, 135, 139, // cases 40 - 47 + 142, 144, 147, 150, 152, 155, 159, 163, // cases 48 - 55 + 166, 169, 173, 177, 180, 184, 189, 194, // cases 56 - 63 + 196, 197, 199, 201, 204, 206, 209, 212, // cases 64 - 71 + 216, 218, 221, 224, 228, 231, 235, 239, // cases 72 - 79 + 242, 244, 247, 250, 254, 257, 261, 265, // cases 80 - 87 + 270, 273, 277, 281, 286, 290, 295, 300, // cases 88 - 95 + 304, 306, 309, 312, 316, 319, 323, 325, // cases 96 - 103 + 328, 331, 335, 339, 344, 348, 353, 356, // cases 104 - 111 + 358, 361, 365, 369, 372, 376, 381, 384, // cases 112 - 119 + 386, 390, 395, 400, 404, 409, 411, 415, // cases 120 - 127 + 416, 417, 419, 421, 424, 426, 429, 432, // cases 128 - 135 + 436, 438, 441, 444, 448, 451, 455, 459, // cases 136 - 143 + 462, 464, 467, 470, 474, 477, 481, 485, // cases 144 - 151 + 490, 493, 495, 499, 502, 506, 509, 514, // cases 152 - 159 + 516, 518, 521, 524, 528, 531, 535, 539, // cases 160 - 167 + 544, 547, 551, 555, 560, 564, 569, 574, // cases 168 - 175 + 578, 581, 585, 589, 592, 596, 601, 606, // cases 176 - 183 + 610, 614, 617, 622, 624, 629, 633, 635, // cases 184 - 191 + 636, 638, 641, 644, 648, 651, 655, 659, // cases 192 - 199 + 664, 667, 671, 675, 680, 682, 685, 688, // cases 200 - 207 + 690, 693, 697, 701, 706, 710, 715, 720, // cases 208 - 215 + 722, 726, 729, 734, 738, 741, 743, 747, // cases 216 - 223 + 748, 751, 755, 759, 764, 768, 773, 776, // cases 224 - 231 + 780, 784, 789, 794, 796, 799, 803, 805, // cases 232 - 239 + 806, 808, 811, 814, 816, 819, 823, 825, // cases 240 - 247 + 826, 829, 831, 835, 836, 838, 839, 840, // cases 248 - 255 + // CELL_SHAPE_WEDGE + 840, 840, 841, 842, 844, 845, 847, 849, // cases 0 - 7 + 850, 851, 853, 855, 858, 860, 863, 866, // cases 8 - 15 + 868, 869, 871, 873, 876, 878, 881, 884, // cases 16 - 23 + 886, 888, 891, 894, 896, 899, 903, 907, // cases 24 - 31 + 908, 909, 911, 913, 916, 918, 921, 924, // cases 32 - 39 + 926, 928, 931, 934, 938, 941, 943, 947, // cases 40 - 47 + 948, 950, 953, 956, 960, 963, 967, 969, // cases 48 - 55 + 970, 971, 973, 975, 976, 978, 979, 980, // cases 56 - 63 + // CELL_SHAPE_PYRAMID + 980, 980, 981, 982, 984, 985, 987, 989, // cases 0 - 7 + 992, 993, 995, 997, 1000, 1002, 1005, 1008, // cases 8 - 15 + 1010, 1012, 1015, 1018, 1020, 1023, 1027, 1029, // cases 16 - 23 + 1030, 1033, 1035, 1039, 1040, 1042, 1043, 1044, // cases 24 - 31 + 0 // dummy }; + // clang-format on - template - ExecObject PrepareForExecution(DeviceAdapter, vtkm::cont::Token& token) - { - ExecObject execObject; - execObject.EdgeTablePortal = this->EdgeTableArray.PrepareForInput(DeviceAdapter(), token); - execObject.EdgeTableOffsetPortal = - this->EdgeTableOffsetArray.PrepareForInput(DeviceAdapter(), token); - execObject.TriangleTablePortal = - this->TriangleTableArray.PrepareForInput(DeviceAdapter(), token); - execObject.TriangleTableOffsetPortal = - this->TriangleTableOffsetArray.PrepareForInput(DeviceAdapter(), token); - return execObject; - } + vtkm::IdComponent offset = GetNumTrianglesOffset(cellType); + return triTableOffset[offset + caseNumber]; +} - TriangleGenerationTable() - : EdgeTableArray(vtkm::cont::make_ArrayHandle(EdgeTable, - sizeof(EdgeTable) / sizeof(EdgeTable[0]), - vtkm::CopyFlag::Off)) - , EdgeTableOffsetArray( - vtkm::cont::make_ArrayHandle(EdgeTableOffset, - sizeof(EdgeTableOffset) / sizeof(EdgeTableOffset[0]), - vtkm::CopyFlag::Off)) - , TriangleTableArray( - vtkm::cont::make_ArrayHandle(TriangleTable, - sizeof(TriangleTable) / sizeof(TriangleTable[0]), - vtkm::CopyFlag::Off)) - , TriangleTableOffsetArray( - vtkm::cont::make_ArrayHandle(TriangleTableOffset, - sizeof(TriangleTableOffset) / sizeof(TriangleTableOffset[0]), - vtkm::CopyFlag::Off)) - { - } +VTKM_EXEC inline const vtkm::UInt8* GetTriangleEdges(vtkm::UInt8 cellType, + vtkm::UInt8 caseNumber, + vtkm::UInt8 triangleNumber) +{ + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 triTable[][3] = { + // CELL_SHAPE_EMPTY + // CELL_SHAPE_VERTEX + // CELL_SHAPE_POLY_VERTEX + // CELL_SHAPE_LINE + // CELL_SHAPE_POLY_LINE + // CELL_SHAPE_TRIANGLE + // CELL_SHAPE_TRIANGLE_STRIP + // CELL_SHAPE_POLYGON + // CELL_SHAPE_PIXEL + // CELL_SHAPE_QUAD + // CELL_SHAPE_TETRA + // case 0 + { 0, 3, 2 }, // case 1 + { 0, 1, 4 }, // case 2 + { 1, 4, 2 }, { 2, 4, 3 }, // case 3 + { 1, 2, 5 }, // case 4 + { 0, 3, 5 }, { 0, 5, 1 }, // case 5 + { 0, 2, 5 }, { 0, 5, 4 }, // case 6 + { 5, 4, 3 }, // case 7 + { 3, 4, 5 }, // case 8 + { 4, 5, 0 }, { 5, 2, 0 }, // case 9 + { 1, 5, 0 }, { 5, 3, 0 }, // case 10 + { 5, 2, 1 }, // case 11 + { 3, 4, 2 }, { 2, 4, 1 }, // case 12 + { 4, 1, 0 }, // case 13 + { 2, 3, 0 }, // case 14 + // case 15 + // CELL_SHAPE_VOXEL + // CELL_SHAPE_HEXAHEDRON + // case 0 + { 0, 8, 3 }, // case 1 + { 0, 1, 9 }, // case 2 + { 1, 8, 3 }, { 9, 8, 1 }, // case 3 + { 1, 2, 11 }, // case 4 + { 0, 8, 3 }, { 1, 2, 11 }, // case 5 + { 9, 2, 11 }, { 0, 2, 9 }, // case 6 + { 2, 8, 3 }, { 2, 11, 8 }, { 11, 9, 8 }, // case 7 + { 3, 10, 2 }, // case 8 + { 0, 10, 2 }, { 8, 10, 0 }, // case 9 + { 1, 9, 0 }, { 2, 3, 10 }, // case 10 + { 1, 10, 2 }, { 1, 9, 10 }, { 9, 8, 10 }, // case 11 + { 3, 11, 1 }, { 10, 11, 3 }, // case 12 + { 0, 11, 1 }, { 0, 8, 11 }, { 8, 10, 11 }, // case 13 + { 3, 9, 0 }, { 3, 10, 9 }, { 10, 11, 9 }, // case 14 + { 9, 8, 11 }, { 11, 8, 10 }, // case 15 + { 4, 7, 8 }, // case 16 + { 4, 3, 0 }, { 7, 3, 4 }, // case 17 + { 0, 1, 9 }, { 8, 4, 7 }, // case 18 + { 4, 1, 9 }, { 4, 7, 1 }, { 7, 3, 1 }, // case 19 + { 1, 2, 11 }, { 8, 4, 7 }, // case 20 + { 3, 4, 7 }, { 3, 0, 4 }, { 1, 2, 11 }, // case 21 + { 9, 2, 11 }, { 9, 0, 2 }, { 8, 4, 7 }, // case 22 + { 2, 11, 9 }, { 2, 9, 7 }, { 2, 7, 3 }, { 7, 9, 4 }, // case 23 + { 8, 4, 7 }, { 3, 10, 2 }, // case 24 + { 10, 4, 7 }, { 10, 2, 4 }, { 2, 0, 4 }, // case 25 + { 9, 0, 1 }, { 8, 4, 7 }, { 2, 3, 10 }, // case 26 + { 4, 7, 10 }, { 9, 4, 10 }, { 9, 10, 2 }, { 9, 2, 1 }, // case 27 + { 3, 11, 1 }, { 3, 10, 11 }, { 7, 8, 4 }, // case 28 + { 1, 10, 11 }, { 1, 4, 10 }, { 1, 0, 4 }, { 7, 10, 4 }, // case 29 + { 4, 7, 8 }, { 9, 0, 10 }, { 9, 10, 11 }, { 10, 0, 3 }, // case 30 + { 4, 7, 10 }, { 4, 10, 9 }, { 9, 10, 11 }, // case 31 + { 9, 5, 4 }, // case 32 + { 9, 5, 4 }, { 0, 8, 3 }, // case 33 + { 0, 5, 4 }, { 1, 5, 0 }, // case 34 + { 8, 5, 4 }, { 8, 3, 5 }, { 3, 1, 5 }, // case 35 + { 1, 2, 11 }, { 9, 5, 4 }, // case 36 + { 3, 0, 8 }, { 1, 2, 11 }, { 4, 9, 5 }, // case 37 + { 5, 2, 11 }, { 5, 4, 2 }, { 4, 0, 2 }, // case 38 + { 2, 11, 5 }, { 3, 2, 5 }, { 3, 5, 4 }, { 3, 4, 8 }, // case 39 + { 9, 5, 4 }, { 2, 3, 10 }, // case 40 + { 0, 10, 2 }, { 0, 8, 10 }, { 4, 9, 5 }, // case 41 + { 0, 5, 4 }, { 0, 1, 5 }, { 2, 3, 10 }, // case 42 + { 2, 1, 5 }, { 2, 5, 8 }, { 2, 8, 10 }, { 4, 8, 5 }, // case 43 + { 11, 3, 10 }, { 11, 1, 3 }, { 9, 5, 4 }, // case 44 + { 4, 9, 5 }, { 0, 8, 1 }, { 8, 11, 1 }, { 8, 10, 11 }, // case 45 + { 5, 4, 0 }, { 5, 0, 10 }, { 5, 10, 11 }, { 10, 0, 3 }, // case 46 + { 5, 4, 8 }, { 5, 8, 11 }, { 11, 8, 10 }, // case 47 + { 9, 7, 8 }, { 5, 7, 9 }, // case 48 + { 9, 3, 0 }, { 9, 5, 3 }, { 5, 7, 3 }, // case 49 + { 0, 7, 8 }, { 0, 1, 7 }, { 1, 5, 7 }, // case 50 + { 1, 5, 3 }, { 3, 5, 7 }, // case 51 + { 9, 7, 8 }, { 9, 5, 7 }, { 11, 1, 2 }, // case 52 + { 11, 1, 2 }, { 9, 5, 0 }, { 5, 3, 0 }, { 5, 7, 3 }, // case 53 + { 8, 0, 2 }, { 8, 2, 5 }, { 8, 5, 7 }, { 11, 5, 2 }, // case 54 + { 2, 11, 5 }, { 2, 5, 3 }, { 3, 5, 7 }, // case 55 + { 7, 9, 5 }, { 7, 8, 9 }, { 3, 10, 2 }, // case 56 + { 9, 5, 7 }, { 9, 7, 2 }, { 9, 2, 0 }, { 2, 7, 10 }, // case 57 + { 2, 3, 10 }, { 0, 1, 8 }, { 1, 7, 8 }, { 1, 5, 7 }, // case 58 + { 10, 2, 1 }, { 10, 1, 7 }, { 7, 1, 5 }, // case 59 + { 9, 5, 8 }, { 8, 5, 7 }, { 11, 1, 3 }, { 11, 3, 10 }, // case 60 + { 5, 7, 0 }, { 5, 0, 9 }, { 7, 10, 0 }, { 1, 0, 11 }, { 10, 11, 0 }, // case 61 + { 10, 11, 0 }, { 10, 0, 3 }, { 11, 5, 0 }, { 8, 0, 7 }, { 5, 7, 0 }, // case 62 + { 10, 11, 5 }, { 7, 10, 5 }, // case 63 + { 11, 6, 5 }, // case 64 + { 0, 8, 3 }, { 5, 11, 6 }, // case 65 + { 9, 0, 1 }, { 5, 11, 6 }, // case 66 + { 1, 8, 3 }, { 1, 9, 8 }, { 5, 11, 6 }, // case 67 + { 1, 6, 5 }, { 2, 6, 1 }, // case 68 + { 1, 6, 5 }, { 1, 2, 6 }, { 3, 0, 8 }, // case 69 + { 9, 6, 5 }, { 9, 0, 6 }, { 0, 2, 6 }, // case 70 + { 5, 9, 8 }, { 5, 8, 2 }, { 5, 2, 6 }, { 3, 2, 8 }, // case 71 + { 2, 3, 10 }, { 11, 6, 5 }, // case 72 + { 10, 0, 8 }, { 10, 2, 0 }, { 11, 6, 5 }, // case 73 + { 0, 1, 9 }, { 2, 3, 10 }, { 5, 11, 6 }, // case 74 + { 5, 11, 6 }, { 1, 9, 2 }, { 9, 10, 2 }, { 9, 8, 10 }, // case 75 + { 6, 3, 10 }, { 6, 5, 3 }, { 5, 1, 3 }, // case 76 + { 0, 8, 10 }, { 0, 10, 5 }, { 0, 5, 1 }, { 5, 10, 6 }, // case 77 + { 3, 10, 6 }, { 0, 3, 6 }, { 0, 6, 5 }, { 0, 5, 9 }, // case 78 + { 6, 5, 9 }, { 6, 9, 10 }, { 10, 9, 8 }, // case 79 + { 5, 11, 6 }, { 4, 7, 8 }, // case 80 + { 4, 3, 0 }, { 4, 7, 3 }, { 6, 5, 11 }, // case 81 + { 1, 9, 0 }, { 5, 11, 6 }, { 8, 4, 7 }, // case 82 + { 11, 6, 5 }, { 1, 9, 7 }, { 1, 7, 3 }, { 7, 9, 4 }, // case 83 + { 6, 1, 2 }, { 6, 5, 1 }, { 4, 7, 8 }, // case 84 + { 1, 2, 5 }, { 5, 2, 6 }, { 3, 0, 4 }, { 3, 4, 7 }, // case 85 + { 8, 4, 7 }, { 9, 0, 5 }, { 0, 6, 5 }, { 0, 2, 6 }, // case 86 + { 7, 3, 9 }, { 7, 9, 4 }, { 3, 2, 9 }, { 5, 9, 6 }, { 2, 6, 9 }, // case 87 + { 3, 10, 2 }, { 7, 8, 4 }, { 11, 6, 5 }, // case 88 + { 5, 11, 6 }, { 4, 7, 2 }, { 4, 2, 0 }, { 2, 7, 10 }, // case 89 + { 0, 1, 9 }, { 4, 7, 8 }, { 2, 3, 10 }, { 5, 11, 6 }, // case 90 + { 9, 2, 1 }, { 9, 10, 2 }, { 9, 4, 10 }, { 7, 10, 4 }, { 5, 11, 6 }, // case 91 + { 8, 4, 7 }, { 3, 10, 5 }, { 3, 5, 1 }, { 5, 10, 6 }, // case 92 + { 5, 1, 10 }, { 5, 10, 6 }, { 1, 0, 10 }, { 7, 10, 4 }, { 0, 4, 10 }, // case 93 + { 0, 5, 9 }, { 0, 6, 5 }, { 0, 3, 6 }, { 10, 6, 3 }, { 8, 4, 7 }, // case 94 + { 6, 5, 9 }, { 6, 9, 10 }, { 4, 7, 9 }, { 7, 10, 9 }, // case 95 + { 11, 4, 9 }, { 6, 4, 11 }, // case 96 + { 4, 11, 6 }, { 4, 9, 11 }, { 0, 8, 3 }, // case 97 + { 11, 0, 1 }, { 11, 6, 0 }, { 6, 4, 0 }, // case 98 + { 8, 3, 1 }, { 8, 1, 6 }, { 8, 6, 4 }, { 6, 1, 11 }, // case 99 + { 1, 4, 9 }, { 1, 2, 4 }, { 2, 6, 4 }, // case 100 + { 3, 0, 8 }, { 1, 2, 9 }, { 2, 4, 9 }, { 2, 6, 4 }, // case 101 + { 0, 2, 4 }, { 4, 2, 6 }, // case 102 + { 8, 3, 2 }, { 8, 2, 4 }, { 4, 2, 6 }, // case 103 + { 11, 4, 9 }, { 11, 6, 4 }, { 10, 2, 3 }, // case 104 + { 0, 8, 2 }, { 2, 8, 10 }, { 4, 9, 11 }, { 4, 11, 6 }, // case 105 + { 3, 10, 2 }, { 0, 1, 6 }, { 0, 6, 4 }, { 6, 1, 11 }, // case 106 + { 6, 4, 1 }, { 6, 1, 11 }, { 4, 8, 1 }, { 2, 1, 10 }, { 8, 10, 1 }, // case 107 + { 9, 6, 4 }, { 9, 3, 6 }, { 9, 1, 3 }, { 10, 6, 3 }, // case 108 + { 8, 10, 1 }, { 8, 1, 0 }, { 10, 6, 1 }, { 9, 1, 4 }, { 6, 4, 1 }, // case 109 + { 3, 10, 6 }, { 3, 6, 0 }, { 0, 6, 4 }, // case 110 + { 6, 4, 8 }, { 10, 6, 8 }, // case 111 + { 7, 11, 6 }, { 7, 8, 11 }, { 8, 9, 11 }, // case 112 + { 0, 7, 3 }, { 0, 11, 7 }, { 0, 9, 11 }, { 6, 7, 11 }, // case 113 + { 11, 6, 7 }, { 1, 11, 7 }, { 1, 7, 8 }, { 1, 8, 0 }, // case 114 + { 11, 6, 7 }, { 11, 7, 1 }, { 1, 7, 3 }, // case 115 + { 1, 2, 6 }, { 1, 6, 8 }, { 1, 8, 9 }, { 8, 6, 7 }, // case 116 + { 2, 6, 9 }, { 2, 9, 1 }, { 6, 7, 9 }, { 0, 9, 3 }, { 7, 3, 9 }, // case 117 + { 7, 8, 0 }, { 7, 0, 6 }, { 6, 0, 2 }, // case 118 + { 7, 3, 2 }, { 6, 7, 2 }, // case 119 + { 2, 3, 10 }, { 11, 6, 8 }, { 11, 8, 9 }, { 8, 6, 7 }, // case 120 + { 2, 0, 7 }, { 2, 7, 10 }, { 0, 9, 7 }, { 6, 7, 11 }, { 9, 11, 7 }, // case 121 + { 1, 8, 0 }, { 1, 7, 8 }, { 1, 11, 7 }, { 6, 7, 11 }, { 2, 3, 10 }, // case 122 + { 10, 2, 1 }, { 10, 1, 7 }, { 11, 6, 1 }, { 6, 7, 1 }, // case 123 + { 8, 9, 6 }, { 8, 6, 7 }, { 9, 1, 6 }, { 10, 6, 3 }, { 1, 3, 6 }, // case 124 + { 0, 9, 1 }, { 10, 6, 7 }, // case 125 + { 7, 8, 0 }, { 7, 0, 6 }, { 3, 10, 0 }, { 10, 6, 0 }, // case 126 + { 7, 10, 6 }, // case 127 + { 7, 6, 10 }, // case 128 + { 3, 0, 8 }, { 10, 7, 6 }, // case 129 + { 0, 1, 9 }, { 10, 7, 6 }, // case 130 + { 8, 1, 9 }, { 8, 3, 1 }, { 10, 7, 6 }, // case 131 + { 11, 1, 2 }, { 6, 10, 7 }, // case 132 + { 1, 2, 11 }, { 3, 0, 8 }, { 6, 10, 7 }, // case 133 + { 2, 9, 0 }, { 2, 11, 9 }, { 6, 10, 7 }, // case 134 + { 6, 10, 7 }, { 2, 11, 3 }, { 11, 8, 3 }, { 11, 9, 8 }, // case 135 + { 7, 2, 3 }, { 6, 2, 7 }, // case 136 + { 7, 0, 8 }, { 7, 6, 0 }, { 6, 2, 0 }, // case 137 + { 2, 7, 6 }, { 2, 3, 7 }, { 0, 1, 9 }, // case 138 + { 1, 6, 2 }, { 1, 8, 6 }, { 1, 9, 8 }, { 8, 7, 6 }, // case 139 + { 11, 7, 6 }, { 11, 1, 7 }, { 1, 3, 7 }, // case 140 + { 11, 7, 6 }, { 1, 7, 11 }, { 1, 8, 7 }, { 1, 0, 8 }, // case 141 + { 0, 3, 7 }, { 0, 7, 11 }, { 0, 11, 9 }, { 6, 11, 7 }, // case 142 + { 7, 6, 11 }, { 7, 11, 8 }, { 8, 11, 9 }, // case 143 + { 6, 8, 4 }, { 10, 8, 6 }, // case 144 + { 3, 6, 10 }, { 3, 0, 6 }, { 0, 4, 6 }, // case 145 + { 8, 6, 10 }, { 8, 4, 6 }, { 9, 0, 1 }, // case 146 + { 9, 4, 6 }, { 9, 6, 3 }, { 9, 3, 1 }, { 10, 3, 6 }, // case 147 + { 6, 8, 4 }, { 6, 10, 8 }, { 2, 11, 1 }, // case 148 + { 1, 2, 11 }, { 3, 0, 10 }, { 0, 6, 10 }, { 0, 4, 6 }, // case 149 + { 4, 10, 8 }, { 4, 6, 10 }, { 0, 2, 9 }, { 2, 11, 9 }, // case 150 + { 11, 9, 3 }, { 11, 3, 2 }, { 9, 4, 3 }, { 10, 3, 6 }, { 4, 6, 3 }, // case 151 + { 8, 2, 3 }, { 8, 4, 2 }, { 4, 6, 2 }, // case 152 + { 0, 4, 2 }, { 4, 6, 2 }, // case 153 + { 1, 9, 0 }, { 2, 3, 4 }, { 2, 4, 6 }, { 4, 3, 8 }, // case 154 + { 1, 9, 4 }, { 1, 4, 2 }, { 2, 4, 6 }, // case 155 + { 8, 1, 3 }, { 8, 6, 1 }, { 8, 4, 6 }, { 6, 11, 1 }, // case 156 + { 11, 1, 0 }, { 11, 0, 6 }, { 6, 0, 4 }, // case 157 + { 4, 6, 3 }, { 4, 3, 8 }, { 6, 11, 3 }, { 0, 3, 9 }, { 11, 9, 3 }, // case 158 + { 11, 9, 4 }, { 6, 11, 4 }, // case 159 + { 4, 9, 5 }, { 7, 6, 10 }, // case 160 + { 0, 8, 3 }, { 4, 9, 5 }, { 10, 7, 6 }, // case 161 + { 5, 0, 1 }, { 5, 4, 0 }, { 7, 6, 10 }, // case 162 + { 10, 7, 6 }, { 8, 3, 4 }, { 3, 5, 4 }, { 3, 1, 5 }, // case 163 + { 9, 5, 4 }, { 11, 1, 2 }, { 7, 6, 10 }, // case 164 + { 6, 10, 7 }, { 1, 2, 11 }, { 0, 8, 3 }, { 4, 9, 5 }, // case 165 + { 7, 6, 10 }, { 5, 4, 11 }, { 4, 2, 11 }, { 4, 0, 2 }, // case 166 + { 3, 4, 8 }, { 3, 5, 4 }, { 3, 2, 5 }, { 11, 5, 2 }, { 10, 7, 6 }, // case 167 + { 7, 2, 3 }, { 7, 6, 2 }, { 5, 4, 9 }, // case 168 + { 9, 5, 4 }, { 0, 8, 6 }, { 0, 6, 2 }, { 6, 8, 7 }, // case 169 + { 3, 6, 2 }, { 3, 7, 6 }, { 1, 5, 0 }, { 5, 4, 0 }, // case 170 + { 6, 2, 8 }, { 6, 8, 7 }, { 2, 1, 8 }, { 4, 8, 5 }, { 1, 5, 8 }, // case 171 + { 9, 5, 4 }, { 11, 1, 6 }, { 1, 7, 6 }, { 1, 3, 7 }, // case 172 + { 1, 6, 11 }, { 1, 7, 6 }, { 1, 0, 7 }, { 8, 7, 0 }, { 9, 5, 4 }, // case 173 + { 4, 0, 11 }, { 4, 11, 5 }, { 0, 3, 11 }, { 6, 11, 7 }, { 3, 7, 11 }, // case 174 + { 7, 6, 11 }, { 7, 11, 8 }, { 5, 4, 11 }, { 4, 8, 11 }, // case 175 + { 6, 9, 5 }, { 6, 10, 9 }, { 10, 8, 9 }, // case 176 + { 3, 6, 10 }, { 0, 6, 3 }, { 0, 5, 6 }, { 0, 9, 5 }, // case 177 + { 0, 10, 8 }, { 0, 5, 10 }, { 0, 1, 5 }, { 5, 6, 10 }, // case 178 + { 6, 10, 3 }, { 6, 3, 5 }, { 5, 3, 1 }, // case 179 + { 1, 2, 11 }, { 9, 5, 10 }, { 9, 10, 8 }, { 10, 5, 6 }, // case 180 + { 0, 10, 3 }, { 0, 6, 10 }, { 0, 9, 6 }, { 5, 6, 9 }, { 1, 2, 11 }, // case 181 + { 10, 8, 5 }, { 10, 5, 6 }, { 8, 0, 5 }, { 11, 5, 2 }, { 0, 2, 5 }, // case 182 + { 6, 10, 3 }, { 6, 3, 5 }, { 2, 11, 3 }, { 11, 5, 3 }, // case 183 + { 5, 8, 9 }, { 5, 2, 8 }, { 5, 6, 2 }, { 3, 8, 2 }, // case 184 + { 9, 5, 6 }, { 9, 6, 0 }, { 0, 6, 2 }, // case 185 + { 1, 5, 8 }, { 1, 8, 0 }, { 5, 6, 8 }, { 3, 8, 2 }, { 6, 2, 8 }, // case 186 + { 1, 5, 6 }, { 2, 1, 6 }, // case 187 + { 1, 3, 6 }, { 1, 6, 11 }, { 3, 8, 6 }, { 5, 6, 9 }, { 8, 9, 6 }, // case 188 + { 11, 1, 0 }, { 11, 0, 6 }, { 9, 5, 0 }, { 5, 6, 0 }, // case 189 + { 0, 3, 8 }, { 5, 6, 11 }, // case 190 + { 11, 5, 6 }, // case 191 + { 10, 5, 11 }, { 7, 5, 10 }, // case 192 + { 10, 5, 11 }, { 10, 7, 5 }, { 8, 3, 0 }, // case 193 + { 5, 10, 7 }, { 5, 11, 10 }, { 1, 9, 0 }, // case 194 + { 11, 7, 5 }, { 11, 10, 7 }, { 9, 8, 1 }, { 8, 3, 1 }, // case 195 + { 10, 1, 2 }, { 10, 7, 1 }, { 7, 5, 1 }, // case 196 + { 0, 8, 3 }, { 1, 2, 7 }, { 1, 7, 5 }, { 7, 2, 10 }, // case 197 + { 9, 7, 5 }, { 9, 2, 7 }, { 9, 0, 2 }, { 2, 10, 7 }, // case 198 + { 7, 5, 2 }, { 7, 2, 10 }, { 5, 9, 2 }, { 3, 2, 8 }, { 9, 8, 2 }, // case 199 + { 2, 5, 11 }, { 2, 3, 5 }, { 3, 7, 5 }, // case 200 + { 8, 2, 0 }, { 8, 5, 2 }, { 8, 7, 5 }, { 11, 2, 5 }, // case 201 + { 9, 0, 1 }, { 5, 11, 3 }, { 5, 3, 7 }, { 3, 11, 2 }, // case 202 + { 9, 8, 2 }, { 9, 2, 1 }, { 8, 7, 2 }, { 11, 2, 5 }, { 7, 5, 2 }, // case 203 + { 1, 3, 5 }, { 3, 7, 5 }, // case 204 + { 0, 8, 7 }, { 0, 7, 1 }, { 1, 7, 5 }, // case 205 + { 9, 0, 3 }, { 9, 3, 5 }, { 5, 3, 7 }, // case 206 + { 9, 8, 7 }, { 5, 9, 7 }, // case 207 + { 5, 8, 4 }, { 5, 11, 8 }, { 11, 10, 8 }, // case 208 + { 5, 0, 4 }, { 5, 10, 0 }, { 5, 11, 10 }, { 10, 3, 0 }, // case 209 + { 0, 1, 9 }, { 8, 4, 11 }, { 8, 11, 10 }, { 11, 4, 5 }, // case 210 + { 11, 10, 4 }, { 11, 4, 5 }, { 10, 3, 4 }, { 9, 4, 1 }, { 3, 1, 4 }, // case 211 + { 2, 5, 1 }, { 2, 8, 5 }, { 2, 10, 8 }, { 4, 5, 8 }, // case 212 + { 0, 4, 10 }, { 0, 10, 3 }, { 4, 5, 10 }, { 2, 10, 1 }, { 5, 1, 10 }, // case 213 + { 0, 2, 5 }, { 0, 5, 9 }, { 2, 10, 5 }, { 4, 5, 8 }, { 10, 8, 5 }, // case 214 + { 9, 4, 5 }, { 2, 10, 3 }, // case 215 + { 2, 5, 11 }, { 3, 5, 2 }, { 3, 4, 5 }, { 3, 8, 4 }, // case 216 + { 5, 11, 2 }, { 5, 2, 4 }, { 4, 2, 0 }, // case 217 + { 3, 11, 2 }, { 3, 5, 11 }, { 3, 8, 5 }, { 4, 5, 8 }, { 0, 1, 9 }, // case 218 + { 5, 11, 2 }, { 5, 2, 4 }, { 1, 9, 2 }, { 9, 4, 2 }, // case 219 + { 8, 4, 5 }, { 8, 5, 3 }, { 3, 5, 1 }, // case 220 + { 0, 4, 5 }, { 1, 0, 5 }, // case 221 + { 8, 4, 5 }, { 8, 5, 3 }, { 9, 0, 5 }, { 0, 3, 5 }, // case 222 + { 9, 4, 5 }, // case 223 + { 4, 10, 7 }, { 4, 9, 10 }, { 9, 11, 10 }, // case 224 + { 0, 8, 3 }, { 4, 9, 7 }, { 9, 10, 7 }, { 9, 11, 10 }, // case 225 + { 1, 11, 10 }, { 1, 10, 4 }, { 1, 4, 0 }, { 7, 4, 10 }, // case 226 + { 3, 1, 4 }, { 3, 4, 8 }, { 1, 11, 4 }, { 7, 4, 10 }, { 11, 10, 4 }, // case 227 + { 4, 10, 7 }, { 9, 10, 4 }, { 9, 2, 10 }, { 9, 1, 2 }, // case 228 + { 9, 7, 4 }, { 9, 10, 7 }, { 9, 1, 10 }, { 2, 10, 1 }, { 0, 8, 3 }, // case 229 + { 10, 7, 4 }, { 10, 4, 2 }, { 2, 4, 0 }, // case 230 + { 10, 7, 4 }, { 10, 4, 2 }, { 8, 3, 4 }, { 3, 2, 4 }, // case 231 + { 2, 9, 11 }, { 2, 7, 9 }, { 2, 3, 7 }, { 7, 4, 9 }, // case 232 + { 9, 11, 7 }, { 9, 7, 4 }, { 11, 2, 7 }, { 8, 7, 0 }, { 2, 0, 7 }, // case 233 + { 3, 7, 11 }, { 3, 11, 2 }, { 7, 4, 11 }, { 1, 11, 0 }, { 4, 0, 11 }, // case 234 + { 1, 11, 2 }, { 8, 7, 4 }, // case 235 + { 4, 9, 1 }, { 4, 1, 7 }, { 7, 1, 3 }, // case 236 + { 4, 9, 1 }, { 4, 1, 7 }, { 0, 8, 1 }, { 8, 7, 1 }, // case 237 + { 4, 0, 3 }, { 7, 4, 3 }, // case 238 + { 4, 8, 7 }, // case 239 + { 9, 11, 8 }, { 11, 10, 8 }, // case 240 + { 3, 0, 9 }, { 3, 9, 10 }, { 10, 9, 11 }, // case 241 + { 0, 1, 11 }, { 0, 11, 8 }, { 8, 11, 10 }, // case 242 + { 3, 1, 11 }, { 10, 3, 11 }, // case 243 + { 1, 2, 10 }, { 1, 10, 9 }, { 9, 10, 8 }, // case 244 + { 3, 0, 9 }, { 3, 9, 10 }, { 1, 2, 9 }, { 2, 10, 9 }, // case 245 + { 0, 2, 10 }, { 8, 0, 10 }, // case 246 + { 3, 2, 10 }, // case 247 + { 2, 3, 8 }, { 2, 8, 11 }, { 11, 8, 9 }, // case 248 + { 9, 11, 2 }, { 0, 9, 2 }, // case 249 + { 2, 3, 8 }, { 2, 8, 11 }, { 0, 1, 8 }, { 1, 11, 8 }, // case 250 + { 1, 11, 2 }, // case 251 + { 1, 3, 8 }, { 9, 1, 8 }, // case 252 + { 0, 9, 1 }, // case 253 + { 0, 3, 8 }, // case 254 + // case 255 + // CELL_SHAPE_WEDGE + // case 0 + { 0, 6, 2 }, // case 1 + { 0, 1, 7 }, // case 2 + { 6, 1, 7 }, { 6, 2, 1 }, // case 3 + { 1, 2, 8 }, // case 4 + { 6, 1, 0 }, { 6, 8, 1 }, // case 5 + { 0, 2, 8 }, { 7, 0, 8 }, // case 6 + { 7, 6, 8 }, // case 7 + { 3, 5, 6 }, // case 8 + { 3, 5, 0 }, { 5, 2, 0 }, // case 9 + { 0, 1, 7 }, { 6, 3, 5 }, // case 10 + { 1, 7, 3 }, { 1, 3, 5 }, { 1, 5, 2 }, // case 11 + { 2, 8, 1 }, { 6, 3, 5 }, // case 12 + { 0, 3, 1 }, { 1, 3, 5 }, { 1, 5, 8 }, // case 13 + { 6, 3, 5 }, { 0, 8, 7 }, { 0, 2, 8 }, // case 14 + { 7, 3, 5 }, { 7, 5, 8 }, // case 15 + { 7, 4, 3 }, // case 16 + { 7, 4, 3 }, { 0, 6, 2 }, // case 17 + { 0, 1, 3 }, { 1, 4, 3 }, // case 18 + { 1, 4, 3 }, { 1, 3, 6 }, { 1, 6, 2 }, // case 19 + { 7, 4, 3 }, { 2, 8, 1 }, // case 20 + { 7, 4, 3 }, { 6, 1, 0 }, { 6, 8, 1 }, // case 21 + { 0, 4, 3 }, { 0, 8, 4 }, { 0, 2, 8 }, // case 22 + { 6, 8, 3 }, { 3, 8, 4 }, // case 23 + { 6, 7, 4 }, { 6, 4, 5 }, // case 24 + { 0, 7, 5 }, { 7, 4, 5 }, { 2, 0, 5 }, // case 25 + { 1, 6, 0 }, { 1, 5, 6 }, { 1, 4, 5 }, // case 26 + { 2, 1, 5 }, { 5, 1, 4 }, // case 27 + { 2, 8, 1 }, { 6, 7, 5 }, { 7, 4, 5 }, // case 28 + { 0, 7, 5 }, { 7, 4, 5 }, { 0, 5, 1 }, { 1, 5, 8 }, // case 29 + { 0, 2, 8 }, { 0, 8, 4 }, { 0, 4, 5 }, { 0, 5, 6 }, // case 30 + { 8, 4, 5 }, // case 31 + { 4, 8, 5 }, // case 32 + { 4, 8, 5 }, { 0, 6, 2 }, // case 33 + { 4, 8, 5 }, { 0, 1, 7 }, // case 34 + { 4, 8, 5 }, { 6, 1, 7 }, { 6, 2, 1 }, // case 35 + { 1, 5, 4 }, { 2, 5, 1 }, // case 36 + { 1, 5, 4 }, { 1, 6, 5 }, { 1, 0, 6 }, // case 37 + { 5, 4, 7 }, { 5, 7, 0 }, { 5, 0, 2 }, // case 38 + { 6, 4, 7 }, { 6, 5, 4 }, // case 39 + { 6, 3, 8 }, { 3, 4, 8 }, // case 40 + { 0, 3, 4 }, { 0, 4, 8 }, { 0, 8, 2 }, // case 41 + { 7, 0, 1 }, { 6, 3, 4 }, { 6, 4, 8 }, // case 42 + { 1, 7, 3 }, { 1, 3, 2 }, { 2, 3, 8 }, { 8, 3, 4 }, // case 43 + { 2, 6, 1 }, { 6, 3, 1 }, { 3, 4, 1 }, // case 44 + { 0, 3, 1 }, { 1, 3, 4 }, // case 45 + { 7, 0, 4 }, { 4, 0, 2 }, { 4, 2, 3 }, { 3, 2, 6 }, // case 46 + { 7, 3, 4 }, // case 47 + { 7, 8, 5 }, { 7, 5, 3 }, // case 48 + { 0, 6, 2 }, { 7, 8, 5 }, { 7, 5, 3 }, // case 49 + { 0, 1, 3 }, { 1, 5, 3 }, { 1, 8, 5 }, // case 50 + { 2, 1, 6 }, { 6, 1, 3 }, { 5, 1, 8 }, { 3, 1, 5 }, // case 51 + { 1, 3, 7 }, { 1, 5, 3 }, { 1, 2, 5 }, // case 52 + { 1, 0, 6 }, { 1, 6, 5 }, { 1, 5, 7 }, { 7, 5, 3 }, // case 53 + { 0, 2, 5 }, { 0, 5, 3 }, // case 54 + { 3, 6, 5 }, // case 55 + { 7, 8, 6 }, // case 56 + { 0, 7, 8 }, { 0, 8, 2 }, // case 57 + { 0, 1, 6 }, { 1, 8, 6 }, // case 58 + { 2, 1, 8 }, // case 59 + { 6, 7, 1 }, { 6, 1, 2 }, // case 60 + { 0, 7, 1 }, // case 61 + { 0, 2, 6 }, // case 62 + // case 63 + // CELL_SHAPE_PYRAMID + // case 0 + { 3, 4, 0 }, // case 1 + { 5, 1, 0 }, // case 2 + { 5, 1, 4 }, { 1, 3, 4 }, // case 3 + { 6, 2, 1 }, // case 4 + { 3, 4, 0 }, { 6, 2, 1 }, // case 5 + { 5, 2, 0 }, { 6, 2, 5 }, // case 6 + { 2, 3, 4 }, { 2, 4, 6 }, { 4, 5, 6 }, // case 7 + { 2, 7, 3 }, // case 8 + { 2, 7, 4 }, { 4, 0, 2 }, // case 9 + { 5, 1, 0 }, { 2, 7, 3 }, // case 10 + { 5, 7, 4 }, { 1, 7, 5 }, { 2, 7, 1 }, // case 11 + { 6, 3, 1 }, { 7, 3, 6 }, // case 12 + { 4, 6, 7 }, { 0, 6, 4 }, { 1, 6, 0 }, // case 13 + { 7, 5, 6 }, { 3, 5, 7 }, { 0, 5, 3 }, // case 14 + { 7, 4, 5 }, { 7, 5, 6 }, // case 15 + { 7, 5, 4 }, { 7, 6, 5 }, // case 16 + { 5, 0, 3 }, { 6, 5, 3 }, { 7, 6, 3 }, // case 17 + { 1, 0, 4 }, { 7, 1, 4 }, { 6, 1, 7 }, // case 18 + { 6, 1, 3 }, { 7, 6, 3 }, // case 19 + { 7, 5, 4 }, { 7, 1, 5 }, { 7, 2, 1 }, // case 20 + { 3, 7, 0 }, { 7, 5, 0 }, { 7, 2, 5 }, { 2, 1, 5 }, // case 21 + { 4, 2, 0 }, { 7, 2, 4 }, // case 22 + { 7, 2, 3 }, // case 23 + { 2, 4, 3 }, { 5, 4, 2 }, { 6, 5, 2 }, // case 24 + { 2, 5, 0 }, { 2, 6, 5 }, // case 25 + { 6, 1, 0 }, { 4, 6, 0 }, { 3, 6, 4 }, { 3, 2, 6 }, // case 26 + { 2, 6, 1 }, // case 27 + { 1, 4, 3 }, { 1, 5, 4 }, // case 28 + { 1, 5, 0 }, // case 29 + { 4, 3, 0 }, // case 30 + // case 31 + { 0, 0, 0 } // dummy + }; + // clang-format on -private: - vtkm::cont::ArrayHandle EdgeTableArray; - vtkm::cont::ArrayHandle EdgeTableOffsetArray; - vtkm::cont::ArrayHandle TriangleTableArray; - vtkm::cont::ArrayHandle TriangleTableOffsetArray; -}; + vtkm::IdComponent offset = GetTriTableOffset(cellType, caseNumber); + return triTable[offset + triangleNumber]; } } } -#endif // vtk_m_MarchingCellTables_h +} // namespace vtkm::worklet::marching_cells + +#endif //vtk_m_MarchingCellTables_h diff --git a/vtkm/filter/contour/worklet/contour/MarchingCellTables.h.in b/vtkm/filter/contour/worklet/contour/MarchingCellTables.h.in new file mode 100644 index 0000000000..fe10790601 --- /dev/null +++ b/vtkm/filter/contour/worklet/contour/MarchingCellTables.h.in @@ -0,0 +1,588 @@ +//============================================================================= +// +// 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. +// +//============================================================================= + +$# This file uses the pyexpander macro processing utility to build the +$# FunctionInterface facilities that use a variable number of arguments. +$# Information, documentation, and downloads for pyexpander can be found at: +$# +$# http://pyexpander.sourceforge.net/ +$# +$# To build the source code, execute the following (after installing +$# pyexpander, of course): +$# +$# expander.py VariantDetail.h.in > VariantDetail.h +$# +$# Ignore the following comment. It is meant for the generated file. +// **** DO NOT EDIT THIS FILE!!! **** +// This file is automatically generated by VariantDetail.h.in +#ifndef vtk_m_MarchingCellTables_h +#define vtk_m_MarchingCellTables_h +$py( +shape_names = ( + "CELL_SHAPE_EMPTY", + "CELL_SHAPE_VERTEX", + "CELL_SHAPE_POLY_VERTEX", + "CELL_SHAPE_LINE", + "CELL_SHAPE_POLY_LINE", + "CELL_SHAPE_TRIANGLE", + "CELL_SHAPE_TRIANGLE_STRIP", + "CELL_SHAPE_POLYGON", + "CELL_SHAPE_PIXEL", + "CELL_SHAPE_QUAD", + "CELL_SHAPE_TETRA", + "CELL_SHAPE_VOXEL", + "CELL_SHAPE_HEXAHEDRON", + "CELL_SHAPE_WEDGE", + "CELL_SHAPE_PYRAMID", +) +shape_tables = { + "CELL_SHAPE_EMPTY" : [], + "CELL_SHAPE_VERTEX" : [], + # TODO + "CELL_SHAPE_LINE" : [], + # Special implementation + "CELL_SHAPE_POLY_LINE" : [], + # TODO + "CELL_SHAPE_TRIANGLE" : [], + # Special implementation + "CELL_SHAPE_POLYGON" : [], + # TODO + "CELL_SHAPE_QUAD" : [], + "CELL_SHAPE_TETRA" : [ + [], + [(0, 3, 2)], + [(0, 1, 4)], + [(1, 4, 2), (2, 4, 3)], + [(1, 2, 5)], + [(0, 3, 5), (0, 5, 1)], + [(0, 2, 5), (0, 5, 4)], + [(5, 4, 3)], + [(3, 4, 5)], + [(4, 5, 0), (5, 2, 0)], + [(1, 5, 0), (5, 3, 0)], + [(5, 2, 1)], + [(3, 4, 2), (2, 4, 1)], + [(4, 1, 0)], + [(2, 3, 0)], + [] + ], + # TODO: Edges 11 and 10 may need to be swapped + "CELL_SHAPE_HEXAHEDRON" : [ + [], + [(0, 8, 3)], + [(0, 1, 9)], + [(1, 8, 3), (9, 8, 1)], + [(1, 2, 11)], + [(0, 8, 3), (1, 2, 11)], + [(9, 2, 11), (0, 2, 9)], + [(2, 8, 3), (2, 11, 8), (11, 9, 8)], + [(3, 10, 2)], + [(0, 10, 2), (8, 10, 0)], + [(1, 9, 0), (2, 3, 10)], + [(1, 10, 2), (1, 9, 10), (9, 8, 10)], + [(3, 11, 1), (10, 11, 3)], + [(0, 11, 1), (0, 8, 11), (8, 10, 11)], + [(3, 9, 0), (3, 10, 9), (10, 11, 9)], + [(9, 8, 11), (11, 8, 10)], + [(4, 7, 8)], + [(4, 3, 0), (7, 3, 4)], + [(0, 1, 9), (8, 4, 7)], + [(4, 1, 9), (4, 7, 1), (7, 3, 1)], + [(1, 2, 11), (8, 4, 7)], + [(3, 4, 7), (3, 0, 4), (1, 2, 11)], + [(9, 2, 11), (9, 0, 2), (8, 4, 7)], + [(2, 11, 9), (2, 9, 7), (2, 7, 3), (7, 9, 4)], + [(8, 4, 7), (3, 10, 2)], + [(10, 4, 7), (10, 2, 4), (2, 0, 4)], + [(9, 0, 1), (8, 4, 7), (2, 3, 10)], + [(4, 7, 10), (9, 4, 10), (9, 10, 2), (9, 2, 1)], + [(3, 11, 1), (3, 10, 11), (7, 8, 4)], + [(1, 10, 11), (1, 4, 10), (1, 0, 4), (7, 10, 4)], + [(4, 7, 8), (9, 0, 10), (9, 10, 11), (10, 0, 3)], + [(4, 7, 10), (4, 10, 9), (9, 10, 11)], + [(9, 5, 4)], + [(9, 5, 4), (0, 8, 3)], + [(0, 5, 4), (1, 5, 0)], + [(8, 5, 4), (8, 3, 5), (3, 1, 5)], + [(1, 2, 11), (9, 5, 4)], + [(3, 0, 8), (1, 2, 11), (4, 9, 5)], + [(5, 2, 11), (5, 4, 2), (4, 0, 2)], + [(2, 11, 5), (3, 2, 5), (3, 5, 4), (3, 4, 8)], + [(9, 5, 4), (2, 3, 10)], + [(0, 10, 2), (0, 8, 10), (4, 9, 5)], + [(0, 5, 4), (0, 1, 5), (2, 3, 10)], + [(2, 1, 5), (2, 5, 8), (2, 8, 10), (4, 8, 5)], + [(11, 3, 10), (11, 1, 3), (9, 5, 4)], + [(4, 9, 5), (0, 8, 1), (8, 11, 1), (8, 10, 11)], + [(5, 4, 0), (5, 0, 10), (5, 10, 11), (10, 0, 3)], + [(5, 4, 8), (5, 8, 11), (11, 8, 10)], + [(9, 7, 8), (5, 7, 9)], + [(9, 3, 0), (9, 5, 3), (5, 7, 3)], + [(0, 7, 8), (0, 1, 7), (1, 5, 7)], + [(1, 5, 3), (3, 5, 7)], + [(9, 7, 8), (9, 5, 7), (11, 1, 2)], + [(11, 1, 2), (9, 5, 0), (5, 3, 0), (5, 7, 3)], + [(8, 0, 2), (8, 2, 5), (8, 5, 7), (11, 5, 2)], + [(2, 11, 5), (2, 5, 3), (3, 5, 7)], + [(7, 9, 5), (7, 8, 9), (3, 10, 2)], + [(9, 5, 7), (9, 7, 2), (9, 2, 0), (2, 7, 10)], + [(2, 3, 10), (0, 1, 8), (1, 7, 8), (1, 5, 7)], + [(10, 2, 1), (10, 1, 7), (7, 1, 5)], + [(9, 5, 8), (8, 5, 7), (11, 1, 3), (11, 3, 10)], + [(5, 7, 0), (5, 0, 9), (7, 10, 0), (1, 0, 11), (10, 11, 0)], + [(10, 11, 0), (10, 0, 3), (11, 5, 0), (8, 0, 7), (5, 7, 0)], + [(10, 11, 5), (7, 10, 5)], + [(11, 6, 5)], + [(0, 8, 3), (5, 11, 6)], + [(9, 0, 1), (5, 11, 6)], + [(1, 8, 3), (1, 9, 8), (5, 11, 6)], + [(1, 6, 5), (2, 6, 1)], + [(1, 6, 5), (1, 2, 6), (3, 0, 8)], + [(9, 6, 5), (9, 0, 6), (0, 2, 6)], + [(5, 9, 8), (5, 8, 2), (5, 2, 6), (3, 2, 8)], + [(2, 3, 10), (11, 6, 5)], + [(10, 0, 8), (10, 2, 0), (11, 6, 5)], + [(0, 1, 9), (2, 3, 10), (5, 11, 6)], + [(5, 11, 6), (1, 9, 2), (9, 10, 2), (9, 8, 10)], + [(6, 3, 10), (6, 5, 3), (5, 1, 3)], + [(0, 8, 10), (0, 10, 5), (0, 5, 1), (5, 10, 6)], + [(3, 10, 6), (0, 3, 6), (0, 6, 5), (0, 5, 9)], + [(6, 5, 9), (6, 9, 10), (10, 9, 8)], + [(5, 11, 6), (4, 7, 8)], + [(4, 3, 0), (4, 7, 3), (6, 5, 11)], + [(1, 9, 0), (5, 11, 6), (8, 4, 7)], + [(11, 6, 5), (1, 9, 7), (1, 7, 3), (7, 9, 4)], + [(6, 1, 2), (6, 5, 1), (4, 7, 8)], + [(1, 2, 5), (5, 2, 6), (3, 0, 4), (3, 4, 7)], + [(8, 4, 7), (9, 0, 5), (0, 6, 5), (0, 2, 6)], + [(7, 3, 9), (7, 9, 4), (3, 2, 9), (5, 9, 6), (2, 6, 9)], + [(3, 10, 2), (7, 8, 4), (11, 6, 5)], + [(5, 11, 6), (4, 7, 2), (4, 2, 0), (2, 7, 10)], + [(0, 1, 9), (4, 7, 8), (2, 3, 10), (5, 11, 6)], + [(9, 2, 1), (9, 10, 2), (9, 4, 10), (7, 10, 4), (5, 11, 6)], + [(8, 4, 7), (3, 10, 5), (3, 5, 1), (5, 10, 6)], + [(5, 1, 10), (5, 10, 6), (1, 0, 10), (7, 10, 4), (0, 4, 10)], + [(0, 5, 9), (0, 6, 5), (0, 3, 6), (10, 6, 3), (8, 4, 7)], + [(6, 5, 9), (6, 9, 10), (4, 7, 9), (7, 10, 9)], + [(11, 4, 9), (6, 4, 11)], + [(4, 11, 6), (4, 9, 11), (0, 8, 3)], + [(11, 0, 1), (11, 6, 0), (6, 4, 0)], + [(8, 3, 1), (8, 1, 6), (8, 6, 4), (6, 1, 11)], + [(1, 4, 9), (1, 2, 4), (2, 6, 4)], + [(3, 0, 8), (1, 2, 9), (2, 4, 9), (2, 6, 4)], + [(0, 2, 4), (4, 2, 6)], + [(8, 3, 2), (8, 2, 4), (4, 2, 6)], + [(11, 4, 9), (11, 6, 4), (10, 2, 3)], + [(0, 8, 2), (2, 8, 10), (4, 9, 11), (4, 11, 6)], + [(3, 10, 2), (0, 1, 6), (0, 6, 4), (6, 1, 11)], + [(6, 4, 1), (6, 1, 11), (4, 8, 1), (2, 1, 10), (8, 10, 1)], + [(9, 6, 4), (9, 3, 6), (9, 1, 3), (10, 6, 3)], + [(8, 10, 1), (8, 1, 0), (10, 6, 1), (9, 1, 4), (6, 4, 1)], + [(3, 10, 6), (3, 6, 0), (0, 6, 4)], + [(6, 4, 8), (10, 6, 8)], + [(7, 11, 6), (7, 8, 11), (8, 9, 11)], + [(0, 7, 3), (0, 11, 7), (0, 9, 11), (6, 7, 11)], + [(11, 6, 7), (1, 11, 7), (1, 7, 8), (1, 8, 0)], + [(11, 6, 7), (11, 7, 1), (1, 7, 3)], + [(1, 2, 6), (1, 6, 8), (1, 8, 9), (8, 6, 7)], + [(2, 6, 9), (2, 9, 1), (6, 7, 9), (0, 9, 3), (7, 3, 9)], + [(7, 8, 0), (7, 0, 6), (6, 0, 2)], + [(7, 3, 2), (6, 7, 2)], + [(2, 3, 10), (11, 6, 8), (11, 8, 9), (8, 6, 7)], + [(2, 0, 7), (2, 7, 10), (0, 9, 7), (6, 7, 11), (9, 11, 7)], + [(1, 8, 0), (1, 7, 8), (1, 11, 7), (6, 7, 11), (2, 3, 10)], + [(10, 2, 1), (10, 1, 7), (11, 6, 1), (6, 7, 1)], + [(8, 9, 6), (8, 6, 7), (9, 1, 6), (10, 6, 3), (1, 3, 6)], + [(0, 9, 1), (10, 6, 7)], + [(7, 8, 0), (7, 0, 6), (3, 10, 0), (10, 6, 0)], + [(7, 10, 6)], + [(7, 6, 10)], + [(3, 0, 8), (10, 7, 6)], + [(0, 1, 9), (10, 7, 6)], + [(8, 1, 9), (8, 3, 1), (10, 7, 6)], + [(11, 1, 2), (6, 10, 7)], + [(1, 2, 11), (3, 0, 8), (6, 10, 7)], + [(2, 9, 0), (2, 11, 9), (6, 10, 7)], + [(6, 10, 7), (2, 11, 3), (11, 8, 3), (11, 9, 8)], + [(7, 2, 3), (6, 2, 7)], + [(7, 0, 8), (7, 6, 0), (6, 2, 0)], + [(2, 7, 6), (2, 3, 7), (0, 1, 9)], + [(1, 6, 2), (1, 8, 6), (1, 9, 8), (8, 7, 6)], + [(11, 7, 6), (11, 1, 7), (1, 3, 7)], + [(11, 7, 6), (1, 7, 11), (1, 8, 7), (1, 0, 8)], + [(0, 3, 7), (0, 7, 11), (0, 11, 9), (6, 11, 7)], + [(7, 6, 11), (7, 11, 8), (8, 11, 9)], + [(6, 8, 4), (10, 8, 6)], + [(3, 6, 10), (3, 0, 6), (0, 4, 6)], + [(8, 6, 10), (8, 4, 6), (9, 0, 1)], + [(9, 4, 6), (9, 6, 3), (9, 3, 1), (10, 3, 6)], + [(6, 8, 4), (6, 10, 8), (2, 11, 1)], + [(1, 2, 11), (3, 0, 10), (0, 6, 10), (0, 4, 6)], + [(4, 10, 8), (4, 6, 10), (0, 2, 9), (2, 11, 9)], + [(11, 9, 3), (11, 3, 2), (9, 4, 3), (10, 3, 6), (4, 6, 3)], + [(8, 2, 3), (8, 4, 2), (4, 6, 2)], + [(0, 4, 2), (4, 6, 2)], + [(1, 9, 0), (2, 3, 4), (2, 4, 6), (4, 3, 8)], + [(1, 9, 4), (1, 4, 2), (2, 4, 6)], + [(8, 1, 3), (8, 6, 1), (8, 4, 6), (6, 11, 1)], + [(11, 1, 0), (11, 0, 6), (6, 0, 4)], + [(4, 6, 3), (4, 3, 8), (6, 11, 3), (0, 3, 9), (11, 9, 3)], + [(11, 9, 4), (6, 11, 4)], + [(4, 9, 5), (7, 6, 10)], + [(0, 8, 3), (4, 9, 5), (10, 7, 6)], + [(5, 0, 1), (5, 4, 0), (7, 6, 10)], + [(10, 7, 6), (8, 3, 4), (3, 5, 4), (3, 1, 5)], + [(9, 5, 4), (11, 1, 2), (7, 6, 10)], + [(6, 10, 7), (1, 2, 11), (0, 8, 3), (4, 9, 5)], + [(7, 6, 10), (5, 4, 11), (4, 2, 11), (4, 0, 2)], + [(3, 4, 8), (3, 5, 4), (3, 2, 5), (11, 5, 2), (10, 7, 6)], + [(7, 2, 3), (7, 6, 2), (5, 4, 9)], + [(9, 5, 4), (0, 8, 6), (0, 6, 2), (6, 8, 7)], + [(3, 6, 2), (3, 7, 6), (1, 5, 0), (5, 4, 0)], + [(6, 2, 8), (6, 8, 7), (2, 1, 8), (4, 8, 5), (1, 5, 8)], + [(9, 5, 4), (11, 1, 6), (1, 7, 6), (1, 3, 7)], + [(1, 6, 11), (1, 7, 6), (1, 0, 7), (8, 7, 0), (9, 5, 4)], + [(4, 0, 11), (4, 11, 5), (0, 3, 11), (6, 11, 7), (3, 7, 11)], + [(7, 6, 11), (7, 11, 8), (5, 4, 11), (4, 8, 11)], + [(6, 9, 5), (6, 10, 9), (10, 8, 9)], + [(3, 6, 10), (0, 6, 3), (0, 5, 6), (0, 9, 5)], + [(0, 10, 8), (0, 5, 10), (0, 1, 5), (5, 6, 10)], + [(6, 10, 3), (6, 3, 5), (5, 3, 1)], + [(1, 2, 11), (9, 5, 10), (9, 10, 8), (10, 5, 6)], + [(0, 10, 3), (0, 6, 10), (0, 9, 6), (5, 6, 9), (1, 2, 11)], + [(10, 8, 5), (10, 5, 6), (8, 0, 5), (11, 5, 2), (0, 2, 5)], + [(6, 10, 3), (6, 3, 5), (2, 11, 3), (11, 5, 3)], + [(5, 8, 9), (5, 2, 8), (5, 6, 2), (3, 8, 2)], + [(9, 5, 6), (9, 6, 0), (0, 6, 2)], + [(1, 5, 8), (1, 8, 0), (5, 6, 8), (3, 8, 2), (6, 2, 8)], + [(1, 5, 6), (2, 1, 6)], + [(1, 3, 6), (1, 6, 11), (3, 8, 6), (5, 6, 9), (8, 9, 6)], + [(11, 1, 0), (11, 0, 6), (9, 5, 0), (5, 6, 0)], + [(0, 3, 8), (5, 6, 11)], + [(11, 5, 6)], + [(10, 5, 11), (7, 5, 10)], + [(10, 5, 11), (10, 7, 5), (8, 3, 0)], + [(5, 10, 7), (5, 11, 10), (1, 9, 0)], + [(11, 7, 5), (11, 10, 7), (9, 8, 1), (8, 3, 1)], + [(10, 1, 2), (10, 7, 1), (7, 5, 1)], + [(0, 8, 3), (1, 2, 7), (1, 7, 5), (7, 2, 10)], + [(9, 7, 5), (9, 2, 7), (9, 0, 2), (2, 10, 7)], + [(7, 5, 2), (7, 2, 10), (5, 9, 2), (3, 2, 8), (9, 8, 2)], + [(2, 5, 11), (2, 3, 5), (3, 7, 5)], + [(8, 2, 0), (8, 5, 2), (8, 7, 5), (11, 2, 5)], + [(9, 0, 1), (5, 11, 3), (5, 3, 7), (3, 11, 2)], + [(9, 8, 2), (9, 2, 1), (8, 7, 2), (11, 2, 5), (7, 5, 2)], + [(1, 3, 5), (3, 7, 5)], + [(0, 8, 7), (0, 7, 1), (1, 7, 5)], + [(9, 0, 3), (9, 3, 5), (5, 3, 7)], + [(9, 8, 7), (5, 9, 7)], + [(5, 8, 4), (5, 11, 8), (11, 10, 8)], + [(5, 0, 4), (5, 10, 0), (5, 11, 10), (10, 3, 0)], + [(0, 1, 9), (8, 4, 11), (8, 11, 10), (11, 4, 5)], + [(11, 10, 4), (11, 4, 5), (10, 3, 4), (9, 4, 1), (3, 1, 4)], + [(2, 5, 1), (2, 8, 5), (2, 10, 8), (4, 5, 8)], + [(0, 4, 10), (0, 10, 3), (4, 5, 10), (2, 10, 1), (5, 1, 10)], + [(0, 2, 5), (0, 5, 9), (2, 10, 5), (4, 5, 8), (10, 8, 5)], + [(9, 4, 5), (2, 10, 3)], + [(2, 5, 11), (3, 5, 2), (3, 4, 5), (3, 8, 4)], + [(5, 11, 2), (5, 2, 4), (4, 2, 0)], + [(3, 11, 2), (3, 5, 11), (3, 8, 5), (4, 5, 8), (0, 1, 9)], + [(5, 11, 2), (5, 2, 4), (1, 9, 2), (9, 4, 2)], + [(8, 4, 5), (8, 5, 3), (3, 5, 1)], + [(0, 4, 5), (1, 0, 5)], + [(8, 4, 5), (8, 5, 3), (9, 0, 5), (0, 3, 5)], + [(9, 4, 5)], + [(4, 10, 7), (4, 9, 10), (9, 11, 10)], + [(0, 8, 3), (4, 9, 7), (9, 10, 7), (9, 11, 10)], + [(1, 11, 10), (1, 10, 4), (1, 4, 0), (7, 4, 10)], + [(3, 1, 4), (3, 4, 8), (1, 11, 4), (7, 4, 10), (11, 10, 4)], + [(4, 10, 7), (9, 10, 4), (9, 2, 10), (9, 1, 2)], + [(9, 7, 4), (9, 10, 7), (9, 1, 10), (2, 10, 1), (0, 8, 3)], + [(10, 7, 4), (10, 4, 2), (2, 4, 0)], + [(10, 7, 4), (10, 4, 2), (8, 3, 4), (3, 2, 4)], + [(2, 9, 11), (2, 7, 9), (2, 3, 7), (7, 4, 9)], + [(9, 11, 7), (9, 7, 4), (11, 2, 7), (8, 7, 0), (2, 0, 7)], + [(3, 7, 11), (3, 11, 2), (7, 4, 11), (1, 11, 0), (4, 0, 11)], + [(1, 11, 2), (8, 7, 4)], + [(4, 9, 1), (4, 1, 7), (7, 1, 3)], + [(4, 9, 1), (4, 1, 7), (0, 8, 1), (8, 7, 1)], + [(4, 0, 3), (7, 4, 3)], + [(4, 8, 7)], + [(9, 11, 8), (11, 10, 8)], + [(3, 0, 9), (3, 9, 10), (10, 9, 11)], + [(0, 1, 11), (0, 11, 8), (8, 11, 10)], + [(3, 1, 11), (10, 3, 11)], + [(1, 2, 10), (1, 10, 9), (9, 10, 8)], + [(3, 0, 9), (3, 9, 10), (1, 2, 9), (2, 10, 9)], + [(0, 2, 10), (8, 0, 10)], + [(3, 2, 10)], + [(2, 3, 8), (2, 8, 11), (11, 8, 9)], + [(9, 11, 2), (0, 9, 2)], + [(2, 3, 8), (2, 8, 11), (0, 1, 8), (1, 11, 8)], + [(1, 11, 2)], + [(1, 3, 8), (9, 1, 8)], + [(0, 9, 1)], + [(0, 3, 8)], + [], + ], + "CELL_SHAPE_WEDGE" : [ + [], + [(0, 6, 2)], + [(0, 1, 7)], + [(6, 1, 7), (6, 2, 1)], + [(1, 2, 8)], + [(6, 1, 0), (6, 8, 1)], + [(0, 2, 8), (7, 0, 8)], + [(7, 6, 8)], + [(3, 5, 6)], + [(3, 5, 0), (5, 2, 0)], + [(0, 1, 7), (6, 3, 5)], + [(1, 7, 3), (1, 3, 5), (1, 5, 2)], + [(2, 8, 1), (6, 3, 5)], + [(0, 3, 1), (1, 3, 5), (1, 5, 8)], + [(6, 3, 5), (0, 8, 7), (0, 2, 8)], + [(7, 3, 5), (7, 5, 8)], + [(7, 4, 3)], + [(7, 4, 3), (0, 6, 2)], + [(0, 1, 3), (1, 4, 3)], + [(1, 4, 3), (1, 3, 6), (1, 6, 2)], + [(7, 4, 3), (2, 8, 1)], + [(7, 4, 3), (6, 1, 0), (6, 8, 1)], + [(0, 4, 3), (0, 8, 4), (0, 2, 8)], + [(6, 8, 3), (3, 8, 4)], + [(6, 7, 4), (6, 4, 5)], + [(0, 7, 5), (7, 4, 5), (2, 0, 5)], + [(1, 6, 0), (1, 5, 6), (1, 4, 5)], + [(2, 1, 5), (5, 1, 4)], + [(2, 8, 1), (6, 7, 5), (7, 4, 5)], + [(0, 7, 5), (7, 4, 5), (0, 5, 1), (1, 5, 8)], + [(0, 2, 8), (0, 8, 4), (0, 4, 5), (0, 5, 6)], + [(8, 4, 5)], + [(4, 8, 5)], + [(4, 8, 5), (0, 6, 2)], + [(4, 8, 5), (0, 1, 7)], + [(4, 8, 5), (6, 1, 7), (6, 2, 1)], + [(1, 5, 4), (2, 5, 1)], + [(1, 5, 4), (1, 6, 5), (1, 0, 6)], + [(5, 4, 7), (5, 7, 0), (5, 0, 2)], + [(6, 4, 7), (6, 5, 4)], + [(6, 3, 8), (3, 4, 8)], + [(0, 3, 4), (0, 4, 8), (0, 8, 2)], + [(7, 0, 1), (6, 3, 4), (6, 4, 8)], + [(1, 7, 3), (1, 3, 2), (2, 3, 8), (8, 3, 4)], + [(2, 6, 1), (6, 3, 1), (3, 4, 1)], + [(0, 3, 1), (1, 3, 4)], + [(7, 0, 4), (4, 0, 2), (4, 2, 3), (3, 2, 6)], + [(7, 3, 4)], + [(7, 8, 5), (7, 5, 3)], + [(0, 6, 2), (7, 8, 5), (7, 5, 3)], + [(0, 1, 3), (1, 5, 3), (1, 8, 5)], + [(2, 1, 6), (6, 1, 3), (5, 1, 8), (3, 1, 5)], + [(1, 3, 7), (1, 5, 3), (1, 2, 5)], + [(1, 0, 6), (1, 6, 5), (1, 5, 7), (7, 5, 3)], + [(0, 2, 5), (0, 5, 3)], + [(3, 6, 5)], + [(7, 8, 6)], + [(0, 7, 8), (0, 8, 2)], + [(0, 1, 6), (1, 8, 6)], + [(2, 1, 8)], + [(6, 7, 1), (6, 1, 2)], + [(0, 7, 1)], + [(0, 2, 6)], + [], + ], + "CELL_SHAPE_PYRAMID" : [ + [], + [(3, 4, 0)], + [(5, 1, 0)], + [(5, 1, 4), (1, 3, 4)], + [(6, 2, 1)], + [(3, 4, 0), (6, 2, 1)], + [(5, 2, 0), (6, 2, 5)], + [(2, 3, 4), (2, 4, 6), (4, 5, 6)], + [(2, 7, 3)], + [(2, 7, 4), (4, 0, 2)], + [(5, 1, 0), (2, 7, 3)], + [(5, 7, 4), (1, 7, 5), (2, 7, 1)], + [(6, 3, 1), (7, 3, 6)], + [(4, 6, 7), (0, 6, 4), (1, 6, 0)], + [(7, 5, 6), (3, 5, 7), (0, 5, 3)], + [(7, 4, 5), (7, 5, 6)], + [(7, 5, 4), (7, 6, 5)], + [(5, 0, 3), (6, 5, 3), (7, 6, 3)], + [(1, 0, 4), (7, 1, 4), (6, 1, 7)], + [(6, 1, 3), (7, 6, 3)], + [(7, 5, 4), (7, 1, 5), (7, 2, 1)], + [(3, 7, 0), (7, 5, 0), (7, 2, 5), (2, 1, 5)], + [(4, 2, 0), (7, 2, 4)], + [(7, 2, 3)], + [(2, 4, 3), (5, 4, 2), (6, 5, 2)], + [(2, 5, 0), (2, 6, 5)], + [(6, 1, 0), (4, 6, 0), (3, 6, 4), (3, 2, 6)], + [(2, 6, 1)], + [(1, 4, 3), (1, 5, 4)], + [(1, 5, 0)], + [(4, 3, 0)], + [], + ], +} +)\ + +#include +#include +#include + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace marching_cells +{ + +// Make sure the table indices are the same as VTK-m's shape IDs. +$for(shape_index in range(len(shape_names)))\ +$if(shape_names[shape_index] in shape_tables)\ +VTKM_STATIC_ASSERT(vtkm::$(shape_names[shape_index]) == $(shape_index)); +$endif\ +$endfor\ + +VTKM_EXEC inline vtkm::IdComponent GetNumTrianglesOffset(vtkm::UInt8 cellType) +{ + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent offsets[] = { +$py(offset = 0)\ +$for(shape_name in shape_names)\ + $(offset), // $(shape_name) +$py( +if shape_name in shape_tables: + offset = offset + len(shape_tables[shape_name]) +else: + shape_tables[shape_name] = [] +)\ +$endfor\ + 0 // dummy + }; + // clang-format on + + return offsets[cellType]; +} + +VTKM_EXEC inline vtkm::UInt8 GetNumTriangles(vtkm::UInt8 cellType, vtkm::UInt8 caseNumber) +{ + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 numTriangles[] = { +$py( +def num_triangles_list(shape_name): + result = " " + start_case = 0 + display_case = 0 + for triangle_list in shape_tables[shape_name]: + result = result + str(len(triangle_list)) + ", " + display_case += 1 + if display_case == 16: + result = result + " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n " + start_case += display_case + display_case = 0 + if display_case == 0: + result = result[:-4] + else: + result += " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n" + return result +)\ +$extend(num_triangles_list)\ +$for(shape_name in shape_names)\ + // $(shape_name) +$num_triangles_list(shape_name)\ +$endfor\ + 0 // dummy + }; + // clang-format on + + vtkm::IdComponent offset = GetNumTrianglesOffset(cellType); + return numTriangles[offset + caseNumber]; +} + +VTKM_EXEC inline vtkm::IdComponent GetTriTableOffset(vtkm::UInt8 cellType, vtkm::UInt8 caseNumber) +{ + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent triTableOffset[] = { +$py( +offset = 0 +def table_offset_list(shape_name): + global offset + result = " " + start_case = 0 + display_case = 0 + for triangle_list in shape_tables[shape_name]: + result = result + str(offset) + ", " + offset += len(triangle_list) + display_case += 1 + if display_case == 8: + result = result + " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n " + start_case += display_case + display_case = 0 + if display_case == 0: + result = result[:-4] + else: + result += " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n" + return result +)\ +$extend(table_offset_list)\ +$for(shape_name in shape_names)\ + // $(shape_name) +$table_offset_list(shape_name)\ +$endfor\ + 0 // dummy + }; + // clang-format on + + vtkm::IdComponent offset = GetNumTrianglesOffset(cellType); + return triTableOffset[offset + caseNumber]; +} + +VTKM_EXEC inline const vtkm::UInt8* GetTriangleEdges(vtkm::UInt8 cellType, + vtkm::UInt8 caseNumber, + vtkm::UInt8 triangleNumber) +{ + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 triTable[][3] = { +$py( +def triangles(triangle_list): + result = "" + for tri in triangle_list: + result += "{ " + str(tri[0]) + ", " + str(tri[1]) + ", " + str(tri[2]) + " }, " + return result +)\ +$extend(triangles)\ +$for(shape_name in shape_names)\ + // $(shape_name) +$py(casenum = 0)\ +$for(triangle_list in shape_tables[shape_name])\ + $triangles(triangle_list)// case $(casenum) +$py(casenum += 1)\ +$endfor\ +$endfor\ + { 0, 0, 0 } // dummy + }; + // clang-format on + + vtkm::IdComponent offset = GetTriTableOffset(cellType, caseNumber); + return triTable[offset + triangleNumber]; +} +} +} +} // namespace vtkm::worklet::marching_cells + +#endif //vtk_m_MarchingCellTables_h diff --git a/vtkm/filter/contour/worklet/contour/MarchingCells.h b/vtkm/filter/contour/worklet/contour/MarchingCells.h index 770307b7cd..68666b2dde 100644 --- a/vtkm/filter/contour/worklet/contour/MarchingCells.h +++ b/vtkm/filter/contour/worklet/contour/MarchingCells.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -80,34 +81,29 @@ public: using ControlSignature = void(WholeArrayIn isoValues, FieldInPoint fieldIn, CellSetIn cellSet, - FieldOutCell outNumTriangles, - ExecObject classifyTable); - using ExecutionSignature = void(CellShape, _1, _2, _4, _5); + FieldOutCell outNumTriangles); + using ExecutionSignature = void(CellShape, PointCount, _1, _2, _4); using InputDomain = _3; - template + template VTKM_EXEC void operator()(CellShapeType shape, + vtkm::IdComponent numVertices, const IsoValuesType& isovalues, const FieldInType& fieldIn, - vtkm::IdComponent& numTriangles, - const ClassifyTableType& classifyTable) const + vtkm::IdComponent& numTriangles) const { vtkm::IdComponent sum = 0; vtkm::IdComponent numIsoValues = static_cast(isovalues.GetNumberOfValues()); - vtkm::IdComponent numVerticesPerCell = classifyTable.GetNumVerticesPerCell(shape.Id); for (vtkm::Id i = 0; i < numIsoValues; ++i) { vtkm::IdComponent caseNumber = 0; - for (vtkm::IdComponent j = 0; j < numVerticesPerCell; ++j) + for (vtkm::IdComponent j = 0; j < numVertices; ++j) { caseNumber |= (fieldIn[j] > isovalues.Get(i)) << j; } - sum += classifyTable.GetNumTriangles(shape.Id, caseNumber); + sum += vtkm::worklet::marching_cells::GetNumTriangles(shape.Id, caseNumber); } numTriangles = sum; } @@ -205,26 +201,21 @@ public: typedef void ControlSignature(CellSetIn cellset, // Cell set WholeArrayIn isoValues, FieldInPoint fieldIn, // Input point field defining the contour - ExecObject metaData, // Metadata for edge weight generation - ExecObject classifyTable, - ExecObject triTable); + ExecObject metaData); // Metadata for edge weight generation using ExecutionSignature = - void(CellShape, _2, _3, _4, _5, _6, InputIndex, WorkIndex, VisitIndex, PointIndices); + void(CellShape, PointCount, _2, _3, _4, InputIndex, WorkIndex, VisitIndex, PointIndices); using InputDomain = _1; template VTKM_EXEC void operator()(const CellShape shape, + vtkm::IdComponent numVertices, const IsoValuesType& isovalues, const FieldInType& fieldIn, // Input point field defining the contour const EdgeWeightGenerateMetaData::ExecObject& metaData, - const ClassifyTableType& classifyTable, - const TriTableType& triTable, vtkm::Id inputCellId, vtkm::Id outputCellId, vtkm::IdComponent visitIndex, @@ -236,7 +227,6 @@ public: vtkm::IdComponent sum = 0, caseNumber = 0; vtkm::IdComponent i = 0, numIsoValues = static_cast(isovalues.GetNumberOfValues()); - vtkm::IdComponent numVerticesPerCell = classifyTable.GetNumVerticesPerCell(shape.Id); for (i = 0; i < numIsoValues; ++i) { @@ -245,12 +235,12 @@ public: // the isovalues until the sum >= our visit index. But we need to make // sure the caseNumber is correct before stopping caseNumber = 0; - for (vtkm::IdComponent j = 0; j < numVerticesPerCell; ++j) + for (vtkm::IdComponent j = 0; j < numVertices; ++j) { caseNumber |= (fieldIn[j] > ivalue) << j; } - sum += classifyTable.GetNumTriangles(shape.Id, caseNumber); + sum += GetNumTriangles(shape.Id, caseNumber); if (sum > visitIndex) { break; @@ -260,11 +250,22 @@ public: visitIndex = sum - visitIndex - 1; // Interpolate for vertex positions and associated scalar values + auto edges = vtkm::worklet::marching_cells::GetTriangleEdges(shape.Id, caseNumber, visitIndex); for (vtkm::IdComponent triVertex = 0; triVertex < 3; triVertex++) { - auto edgeVertices = triTable.GetEdgeVertices(shape.Id, caseNumber, visitIndex, triVertex); - const FieldType fieldValue0 = fieldIn[edgeVertices.first]; - const FieldType fieldValue1 = fieldIn[edgeVertices.second]; + vtkm::IdComponent2 edgeVertices; + vtkm::Vec fieldValues; + for (vtkm::IdComponent edgePointId = 0; edgePointId < 2; ++edgePointId) + { + vtkm::ErrorCode errorCode = vtkm::exec::CellEdgeLocalIndex( + numVertices, edgePointId, edges[triVertex], shape, edgeVertices[edgePointId]); + if (errorCode != vtkm::ErrorCode::Success) + { + this->RaiseError(vtkm::ErrorString(errorCode)); + return; + } + fieldValues[edgePointId] = fieldIn[edgeVertices[edgePointId]]; + } // Store the input cell id so that we can properly generate the normals // in a subsequent call, after we have merged duplicate points @@ -272,13 +273,12 @@ public: metaData.InterpContourPortal.Set(outputPointId + triVertex, static_cast(i)); - metaData.InterpIdPortal.Set( - outputPointId + triVertex, - vtkm::Id2(indices[edgeVertices.first], indices[edgeVertices.second])); + metaData.InterpIdPortal.Set(outputPointId + triVertex, + vtkm::Id2(indices[edgeVertices[0]], indices[edgeVertices[1]])); vtkm::FloatDefault interpolant = - static_cast(isovalues.Get(i) - fieldValue0) / - static_cast(fieldValue1 - fieldValue0); + static_cast(isovalues.Get(i) - fieldValues[0]) / + static_cast(fieldValues[1] - fieldValues[0]); metaData.InterpWeightsPortal.Set(outputPointId + triVertex, interpolant); } @@ -610,9 +610,6 @@ vtkm::cont::CellSetSingleType<> execute( using vtkm::worklet::marching_cells::EdgeWeightGenerate; using vtkm::worklet::marching_cells::EdgeWeightGenerateMetaData; - vtkm::worklet::marching_cells::CellClassifyTable classTable; - vtkm::worklet::marching_cells::TriangleGenerationTable triTable; - // Setup the invoker vtkm::cont::Invoker invoker; @@ -624,7 +621,7 @@ vtkm::cont::CellSetSingleType<> execute( vtkm::cont::ArrayHandle numOutputTrisPerCell; { marching_cells::ClassifyCell classifyCell; - invoker(classifyCell, isoValuesHandle, inputField, cells, numOutputTrisPerCell, classTable); + invoker(classifyCell, isoValuesHandle, inputField, cells, numOutputTrisPerCell); } //Pass 2 Generate the edges @@ -649,9 +646,7 @@ vtkm::cont::CellSetSingleType<> execute( //cast to a scalar field if not one, as cellderivative only works on those isoValuesHandle, inputField, - metaData, - classTable, - triTable); + metaData); } if (isovalues.size() <= 1 || !sharedState.MergeDuplicatePoints) -- GitLab From 9eaaa77aeebf21c1eb88e7901d993453de2b8110 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Thu, 12 Dec 2024 17:27:05 -0500 Subject: [PATCH 49/73] Automatically initialize Kokkos Calling `vtkm::cont::Initialize()` is supposed to be optional. However, Kokkos needs to have `Kokkos::initialize()` called before using some devices such as HIP. To make sure that Kokkos is properly initialized, the VTK-m allocation for the Kokkos device now checks to see if `Kokkos::is_initialized()` is true. If it is not, then `vtkm::cont::Initialize()` is called. --- docs/changelog/kokkos-force-init.md | 7 +++++++ vtkm/cont/kokkos/internal/KokkosAlloc.cxx | 9 +++++++++ 2 files changed, 16 insertions(+) create mode 100644 docs/changelog/kokkos-force-init.md diff --git a/docs/changelog/kokkos-force-init.md b/docs/changelog/kokkos-force-init.md new file mode 100644 index 0000000000..89f34b6511 --- /dev/null +++ b/docs/changelog/kokkos-force-init.md @@ -0,0 +1,7 @@ +## Automatically initialize Kokkos + +Calling `vtkm::cont::Initialize()` is supposed to be optional. However, Kokkos +needs to have `Kokkos::initialize()` called before using some devices such as +HIP. To make sure that Kokkos is properly initialized, the VTK-m allocation for +the Kokkos device now checks to see if `Kokkos::is_initialized()` is true. If it +is not, then `vtkm::cont::Initialize()` is called. diff --git a/vtkm/cont/kokkos/internal/KokkosAlloc.cxx b/vtkm/cont/kokkos/internal/KokkosAlloc.cxx index 37ba6f1149..0ec9e903c5 100644 --- a/vtkm/cont/kokkos/internal/KokkosAlloc.cxx +++ b/vtkm/cont/kokkos/internal/KokkosAlloc.cxx @@ -10,6 +10,8 @@ #include #include +#include +#include #include #include @@ -25,6 +27,13 @@ namespace internal void* Allocate(std::size_t size) { + if (!Kokkos::is_initialized()) + { + VTKM_LOG_F(vtkm::cont::LogLevel::Info, + "Allocating device memory before Kokkos has been initialized. Calling " + "vtkm::cont::Initialize."); + vtkm::cont::Initialize(); + } try { return Kokkos::kokkos_malloc(size); -- GitLab From e8be42cc7e422dc2bf223fdcb2f5900a044f30d0 Mon Sep 17 00:00:00 2001 From: Vicente Adolfo Bolea Sanchez Date: Fri, 13 Dec 2024 13:02:16 -0500 Subject: [PATCH 50/73] ci: remove large-memory windows tag --- .gitlab/ci/windows10.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab/ci/windows10.yml b/.gitlab/ci/windows10.yml index 0f86de33e8..776cb0d085 100644 --- a/.gitlab/ci/windows10.yml +++ b/.gitlab/ci/windows10.yml @@ -109,7 +109,6 @@ build:windows_vs2019: - shell - vs2019 - msvc-19.28 # sync with VCVARSVERSION above - - large-memory extends: - .cmake_build_windows - .run_automatically -- GitLab From a75d3854bd4569f52e5fae2e9ee345b3fd777f84 Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Mon, 16 Dec 2024 14:48:05 -0500 Subject: [PATCH 51/73] Fix compile err for serial. --- .../flow/internal/AdvectAlgorithmTerminator.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h index b22ffd3e2a..3ef2ef45b9 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -35,12 +35,20 @@ public: , State(STATE_0) #else AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) + : HaveWork(false) #endif { this->FirstCall = true; } - bool Done() const { return this->State == AdvectAlgorithmTerminatorState::DONE; } + bool Done() const + { +#ifdef VTKM_ENABLE_MPI + return this->State == AdvectAlgorithmTerminatorState::DONE; +#else + return this->HaveWork; +#endif + } void Control(bool haveLocalWork) { @@ -91,12 +99,12 @@ public: } } #else - if (!haveLocalWork) - this->State = DONE; + this->HaveWork = haveLocalWork; #endif } private: +#ifdef VTKM_ENABLE_MPI enum AdvectAlgorithmTerminatorState { STATE_0, @@ -106,7 +114,6 @@ private: DONE }; -#ifdef VTKM_ENABLE_MPI int AllDirty; int BarrierCnt = 0; int IReduceCnt = 0; @@ -118,6 +125,8 @@ private: vtkm::Id Rank; AdvectAlgorithmTerminatorState State = AdvectAlgorithmTerminatorState::STATE_0; MPI_Request StateReq; +#else + bool HaveWork; #endif }; -- GitLab From 58ba49efcd8514c9f9611880a0110d4c7dfe1926 Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Fri, 20 Dec 2024 12:14:46 -0500 Subject: [PATCH 52/73] Fix for serial builds. --- vtkm/filter/flow/internal/AdvectAlgorithm.h | 2 ++ vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index c8110bc994..a51bba96f2 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -105,6 +105,7 @@ public: //Advect all the particles. virtual void Go() { + this->Terminator.Control(this->HaveWork()); while (!this->Terminator.Done()) { std::vector v; @@ -121,6 +122,7 @@ public: } this->ExchangeParticles(); + this->Terminator.Control(this->HaveWork()); } } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h index 3ef2ef45b9..4d038fa1f2 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -46,7 +46,7 @@ public: #ifdef VTKM_ENABLE_MPI return this->State == AdvectAlgorithmTerminatorState::DONE; #else - return this->HaveWork; + return !this->HaveWork; #endif } -- GitLab From 678c353921bdfdac4b641e17916e29ce89c272ba Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Mon, 23 Dec 2024 12:55:45 -0500 Subject: [PATCH 53/73] Remove some unused debug flags. --- vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h index 4d038fa1f2..d1bf8ea402 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -22,8 +22,6 @@ namespace internal class AdvectAlgorithmTerminator { - bool FirstCall; - public: #ifdef VTKM_ENABLE_MPI AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) @@ -82,7 +80,6 @@ public: MPI_Iallreduce( &this->LocalDirty, &this->AllDirty, 1, MPI_INT, MPI_LOR, this->MPIComm, &this->StateReq); this->State = STATE_2; - this->BarrierCnt++; } else if (this->State == STATE_2) { @@ -95,7 +92,6 @@ public: this->State = DONE; else this->State = STATE_0; //reset. - this->IReduceCnt++; } } #else @@ -104,6 +100,8 @@ public: } private: + bool FirstCall; + #ifdef VTKM_ENABLE_MPI enum AdvectAlgorithmTerminatorState { @@ -115,8 +113,6 @@ private: }; int AllDirty; - int BarrierCnt = 0; - int IReduceCnt = 0; //Dirty: Has this rank seen any work since entering state? std::atomic Dirty; int LocalDirty; -- GitLab From 6a6e27cbef9d2ff043b81a49bfbcd0664035482e Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Mon, 23 Dec 2024 15:45:51 -0500 Subject: [PATCH 54/73] More cleanup... --- vtkm/filter/flow/internal/AdvectAlgorithm.h | 7 ++++--- vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx | 6 ------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index a51bba96f2..1e77b33c66 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -51,6 +51,8 @@ public: { this->SetStepSize(stepSize); this->SetSeeds(seeds); + this->Terminator.Control(this->HaveWork()); + this->Go(); } @@ -105,13 +107,11 @@ public: //Advect all the particles. virtual void Go() { - this->Terminator.Control(this->HaveWork()); while (!this->Terminator.Done()) { std::vector v; vtkm::Id blockId = -1; - this->Terminator.Control(this->HaveWork()); if (this->GetActiveParticles(v, blockId)) { //make this a pointer to avoid the copy? @@ -122,7 +122,6 @@ public: } this->ExchangeParticles(); - this->Terminator.Control(this->HaveWork()); } } @@ -223,6 +222,8 @@ public: this->ParticleBlockIDsMap.erase(p.GetID()); this->UpdateActive(incoming, incomingBlockIDs); + + this->Terminator.Control(this->HaveWork()); } void GetOutgoingParticles(std::vector& outgoing, diff --git a/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx b/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx index 8544a22a94..23e470d601 100644 --- a/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx +++ b/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx @@ -59,9 +59,6 @@ void DoTest() for (vtkm::Id nPerRank = 1; nPerRank < 3; ++nPerRank) { - if (nPerRank != 1) - continue; - for (bool useGhost : { true, false }) { if (useGhost) @@ -70,9 +67,6 @@ void DoTest() { for (bool useBlockIds : { true, false }) { - if (useBlockIds) - continue; - //Run blockIds with and without block duplication. if (useBlockIds && comm.size() > 1) { -- GitLab From 5983e189ec053756934af1b80c600bc045566f38 Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Fri, 3 Jan 2025 12:22:00 -0500 Subject: [PATCH 55/73] Cleanup the MPI version of filters. --- vtkm/filter/flow/internal/AdvectAlgorithm.h | 82 ++++++++++++++----- .../flow/internal/AdvectAlgorithmThreaded.h | 34 +++++--- vtkm/filter/flow/internal/ParticleExchanger.h | 9 +- .../flow/testing/UnitTestStreamlineFilter.cxx | 28 ++++--- 4 files changed, 103 insertions(+), 50 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithm.h b/vtkm/filter/flow/internal/AdvectAlgorithm.h index 1e77b33c66..e12348a8a1 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -13,11 +13,11 @@ #include -#include #include #include -#include #ifdef VTKM_ENABLE_MPI +#include +#include #include #include #endif @@ -40,10 +40,12 @@ public: AdvectAlgorithm(const vtkm::filter::flow::internal::BoundsMap& bm, std::vector& blocks) : Blocks(blocks) , BoundsMap(bm) +#ifdef VTKM_ENABLE_MPI + , Exchanger(this->Comm) + , Terminator(this->Comm) +#endif , NumRanks(this->Comm.size()) , Rank(this->Comm.rank()) - , Terminator(this->Comm) - , Exchanger(this->Comm) { } @@ -51,7 +53,6 @@ public: { this->SetStepSize(stepSize); this->SetSeeds(seeds); - this->Terminator.Control(this->HaveWork()); this->Go(); } @@ -100,14 +101,27 @@ public: virtual bool HaveWork() { - return !this->Active.empty() || !this->Inactive.empty() || - this->Exchanger.GetNumberOfBufferedSends() > 0; + const bool haveParticles = !this->Active.empty() || !this->Inactive.empty(); +#ifndef VTKM_ENABLE_MPI + return haveParticles; +#else + return haveParticles || this->Exchanger.HaveWork(); +#endif + } + + virtual bool GetDone() + { +#ifndef VTKM_ENABLE_MPI + return !this->HaveWork(); +#else + return this->Terminator.Done(); +#endif } //Advect all the particles. virtual void Go() { - while (!this->Terminator.Done()) + while (!this->GetDone()) { std::vector v; vtkm::Id blockId = -1; @@ -206,26 +220,48 @@ public: void ExchangeParticles() { - std::vector outgoing; - std::vector outgoingRanks; +#ifndef VTKM_ENABLE_MPI + this->SerialExchange(); +#else + // MPI with only 1 rank. + if (this->NumRanks == 1) + this->SerialExchange(); + else + { + std::vector outgoing; + std::vector outgoingRanks; - this->GetOutgoingParticles(outgoing, outgoingRanks); + this->GetOutgoingParticles(outgoing, outgoingRanks); - std::vector incoming; - std::unordered_map> incomingBlockIDs; + std::vector incoming; + std::unordered_map> incomingBlockIDs; - this->Exchanger.Exchange( - outgoing, outgoingRanks, this->ParticleBlockIDsMap, incoming, incomingBlockIDs); + this->Exchanger.Exchange( + outgoing, outgoingRanks, this->ParticleBlockIDsMap, incoming, incomingBlockIDs); - //Cleanup what was sent. - for (const auto& p : outgoing) - this->ParticleBlockIDsMap.erase(p.GetID()); + //Cleanup what was sent. + for (const auto& p : outgoing) + this->ParticleBlockIDsMap.erase(p.GetID()); - this->UpdateActive(incoming, incomingBlockIDs); + this->UpdateActive(incoming, incomingBlockIDs); + } this->Terminator.Control(this->HaveWork()); +#endif + } + + void SerialExchange() + { + for (const auto& p : this->Inactive) + { + const auto& bid = this->ParticleBlockIDsMap[p.GetID()]; + VTKM_ASSERT(!bid.empty()); + this->Active[bid[0]].emplace_back(std::move(p)); + } + this->Inactive.clear(); } +#ifdef VTKM_ENABLE_MPI void GetOutgoingParticles(std::vector& outgoing, std::vector& outgoingRanks) { @@ -286,6 +322,7 @@ public: if (!particlesStaying.empty()) this->UpdateActive(particlesStaying, particlesStayingBlockIDs); } +#endif virtual void UpdateActive(const std::vector& particles, const std::unordered_map>& idsMap) @@ -340,6 +377,10 @@ public: std::vector Blocks; vtkm::filter::flow::internal::BoundsMap BoundsMap; vtkmdiy::mpi::communicator Comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); +#ifdef VTKM_ENABLE_MPI + ParticleExchanger Exchanger; + AdvectAlgorithmTerminator Terminator; +#endif std::vector Inactive; vtkm::Id MaxNumberOfSteps = 0; vtkm::Id NumRanks; @@ -347,9 +388,6 @@ public: std::unordered_map> ParticleBlockIDsMap; vtkm::Id Rank; vtkm::FloatDefault StepSize; - AdvectAlgorithmTerminator Terminator; - - ParticleExchanger Exchanger; }; } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h index ed1d5fa3de..a0e823dae6 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h @@ -47,7 +47,6 @@ public: void Go() override { - this->Terminator.Control(this->HaveWork()); std::vector workerThreads; workerThreads.emplace_back(std::thread(AdvectAlgorithmThreaded::Worker, this)); this->Manage(); @@ -65,6 +64,22 @@ protected: return this->AdvectAlgorithm::HaveWork() || this->WorkerActivate; } + virtual bool GetDone() override + { + std::lock_guard lock(this->Mutex); +#ifndef VTKM_ENABLE_MPI + return !this->CheckHaveWork(); +#else + return this->Terminator.Done(); +#endif + } + + bool WorkerGetDone() + { + std::lock_guard lock(this->Mutex); + return this->Done; + } + bool GetActiveParticles(std::vector& particles, vtkm::Id& blockId) override { std::lock_guard lock(this->Mutex); @@ -87,12 +102,6 @@ protected: } } - bool CheckDone() - { - std::lock_guard lock(this->Mutex); - return this->Done; - } - void SetDone() { std::lock_guard lock(this->Mutex); @@ -117,7 +126,7 @@ protected: void Work() { - while (!this->CheckDone()) + while (!this->WorkerGetDone()) { std::vector v; vtkm::Id blockId = -1; @@ -135,7 +144,7 @@ protected: void Manage() { - while (!this->Terminator.Done()) + while (!this->GetDone()) { std::unordered_map>> workerResults; this->GetWorkerResults(workerResults); @@ -146,7 +155,6 @@ protected: numTerm += this->UpdateResult(r); this->ExchangeParticles(); - this->Terminator.Control(this->HaveWork()); } this->SetDone(); } @@ -164,6 +172,12 @@ protected: } } +private: + bool CheckHaveWork() + { + return this->AdvectAlgorithm::HaveWork() || this->WorkerActivate; + } + std::atomic Done; std::mutex Mutex; bool WorkerActivate = false; diff --git a/vtkm/filter/flow/internal/ParticleExchanger.h b/vtkm/filter/flow/internal/ParticleExchanger.h index 3dc8749bab..b8a0964c9e 100644 --- a/vtkm/filter/flow/internal/ParticleExchanger.h +++ b/vtkm/filter/flow/internal/ParticleExchanger.h @@ -38,14 +38,7 @@ public: ~ParticleExchanger() {} //{ this->CleanupSendBuffers(false); } #endif - vtkm::Id GetNumberOfBufferedSends() const - { -#ifdef VTKM_ENABLE_MPI - return static_cast(this->SendBuffers.size()); -#else - return 0; -#endif - } + bool HaveWork() const { return !this->SendBuffers.empty(); } void Exchange(const std::vector& outData, const std::vector& outRanks, diff --git a/vtkm/filter/flow/testing/UnitTestStreamlineFilter.cxx b/vtkm/filter/flow/testing/UnitTestStreamlineFilter.cxx index 4e24d59178..90d7d423d2 100644 --- a/vtkm/filter/flow/testing/UnitTestStreamlineFilter.cxx +++ b/vtkm/filter/flow/testing/UnitTestStreamlineFilter.cxx @@ -49,7 +49,7 @@ void AddVectorFields(vtkm::cont::PartitionedDataSet& pds, ds.AddPointField(fieldName, CreateConstantVectorField(ds.GetNumberOfPoints(), vec)); } -void TestStreamline() +void TestStreamline(bool useThreaded) { const vtkm::Id3 dims(5, 5, 5); const vtkm::Bounds bounds(0, 4, 0, 4, 0, 4); @@ -68,6 +68,7 @@ void TestStreamline() vtkm::filter::flow::Streamline streamline; + streamline.SetUseThreadedAlgorithm(useThreaded); streamline.SetStepSize(0.1f); streamline.SetNumberOfSteps(20); streamline.SetSeeds(seedArray); @@ -87,7 +88,7 @@ void TestStreamline() } } -void TestPathline() +void TestPathline(bool useThreaded) { const vtkm::Id3 dims(5, 5, 5); const vtkm::Vec3f vecX(1, 0, 0); @@ -126,6 +127,7 @@ void TestPathline() if (fType == 0) { vtkm::filter::flow::Pathline filt; + filt.SetUseThreadedAlgorithm(useThreaded); filt.SetActiveField(var); filt.SetStepSize(stepSize); filt.SetNumberOfSteps(numSteps); @@ -139,6 +141,7 @@ void TestPathline() else { vtkm::filter::flow::PathParticle filt; + filt.SetUseThreadedAlgorithm(useThreaded); filt.SetActiveField(var); filt.SetStepSize(stepSize); filt.SetNumberOfSteps(numSteps); @@ -162,7 +165,7 @@ void TestPathline() } } -void TestAMRStreamline(bool useSL) +void TestAMRStreamline(bool useSL, bool useThreaded) { vtkm::Bounds outerBounds(0, 10, 0, 10, 0, 10); vtkm::Id3 outerDims(11, 11, 11); @@ -225,6 +228,7 @@ void TestAMRStreamline(bool useSL) if (useSL) { vtkm::filter::flow::Streamline filter; + filter.SetUseThreadedAlgorithm(useThreaded); filter.SetStepSize(0.1f); filter.SetNumberOfSteps(100000); filter.SetSeeds(seedArray); @@ -297,6 +301,7 @@ void TestAMRStreamline(bool useSL) else { vtkm::filter::flow::ParticleAdvection filter; + filter.SetUseThreadedAlgorithm(useThreaded); filter.SetStepSize(0.1f); filter.SetNumberOfSteps(100000); filter.SetSeeds(seedArray); @@ -326,7 +331,7 @@ void TestAMRStreamline(bool useSL) } } -void TestPartitionedDataSet(vtkm::Id num, bool useGhost, FilterType fType) +void TestPartitionedDataSet(vtkm::Id num, bool useGhost, FilterType fType, bool useThreaded) { vtkm::Id numDims = 5; vtkm::FloatDefault x0 = 0; @@ -378,6 +383,7 @@ void TestPartitionedDataSet(vtkm::Id num, bool useGhost, FilterType fType) if (fType == FilterType::STREAMLINE) { vtkm::filter::flow::Streamline streamline; + streamline.SetUseThreadedAlgorithm(useThreaded); streamline.SetStepSize(0.1f); streamline.SetNumberOfSteps(100000); streamline.SetSeeds(seedArray); @@ -390,6 +396,7 @@ void TestPartitionedDataSet(vtkm::Id num, bool useGhost, FilterType fType) AddVectorFields(pds2, fieldName, vecX); vtkm::filter::flow::Pathline pathline; + pathline.SetUseThreadedAlgorithm(useThreaded); pathline.SetPreviousTime(0); pathline.SetNextTime(1000); pathline.SetNextDataSet(pds2); @@ -587,19 +594,20 @@ void TestStreamlineFilters() FilterType::STREAMLINE, FilterType::PATHLINE, FilterType::PATH_PARTICLE }; - for (int n = 1; n < 3; n++) { for (auto useGhost : flags) for (auto ft : fTypes) - TestPartitionedDataSet(n, useGhost, ft); + TestPartitionedDataSet(n, useGhost, ft, false); } - TestStreamline(); - TestPathline(); - + for (auto useThreaded : flags) + { + TestStreamline(useThreaded); + TestPathline(useThreaded); + } for (auto useSL : flags) - TestAMRStreamline(useSL); + TestAMRStreamline(useSL, false); { //Rotate test. -- GitLab From 3b57dc06de0d3d1e910c436b7499222f09ff3822 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 3 Feb 2025 14:25:36 -0500 Subject: [PATCH 56/73] Add ArraySetValues --- vtkm/cont/ArraySetValues.cxx | 76 ++++++ vtkm/cont/ArraySetValues.h | 261 +++++++++++++++++++ vtkm/cont/CMakeLists.txt | 2 + vtkm/cont/testing/CMakeLists.txt | 1 + vtkm/cont/testing/UnitTestArraySetValues.cxx | 185 +++++++++++++ 5 files changed, 525 insertions(+) create mode 100644 vtkm/cont/ArraySetValues.cxx create mode 100644 vtkm/cont/ArraySetValues.h create mode 100644 vtkm/cont/testing/UnitTestArraySetValues.cxx diff --git a/vtkm/cont/ArraySetValues.cxx b/vtkm/cont/ArraySetValues.cxx new file mode 100644 index 0000000000..54c8ebd321 --- /dev/null +++ b/vtkm/cont/ArraySetValues.cxx @@ -0,0 +1,76 @@ +//============================================================================ +// 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 + +void vtkm::cont::internal::ArraySetValuesImpl(const vtkm::cont::UnknownArrayHandle& ids, + const vtkm::cont::UnknownArrayHandle& values, + const vtkm::cont::UnknownArrayHandle& data, + std::false_type) +{ + auto idArray = ids.ExtractComponent(0, vtkm::CopyFlag::On); + VTKM_ASSERT(ids.GetNumberOfValues() == values.GetNumberOfValues()); + + bool copied = false; + vtkm::ListForEach( + [&](auto base) { + using T = decltype(base); + if (!copied && data.IsBaseComponentType()) + { + vtkm::IdComponent numComponents = data.GetNumberOfComponentsFlat(); + VTKM_ASSERT(values.GetNumberOfComponentsFlat() == numComponents); + + for (vtkm::IdComponent componentIdx = 0; componentIdx < numComponents; ++componentIdx) + { + auto valuesArray = values.ExtractComponent(componentIdx, vtkm::CopyFlag::On); + auto dataArray = data.ExtractComponent(componentIdx, vtkm::CopyFlag::Off); + auto permutedArray = vtkm::cont::make_ArrayHandlePermutation(idArray, dataArray); + + bool copiedComponent = false; + copiedComponent = vtkm::cont::TryExecute([&](auto device) { + if (dataArray.IsOnDevice(device)) + { + vtkm::cont::DeviceAdapterAlgorithm::Copy(valuesArray, + permutedArray); + return true; + } + return false; + }); + + if (!copiedComponent) + { // Fallback to control-side copy + const vtkm::Id numVals = ids.GetNumberOfValues(); + auto inPortal = valuesArray.ReadPortal(); + auto outPortal = permutedArray.WritePortal(); + for (vtkm::Id i = 0; i < numVals; ++i) + { + outPortal.Set(i, inPortal.Get(i)); + } + } + } + + copied = true; + } + }, + vtkm::TypeListBaseC{}); + + if (!copied) + { + throw vtkm::cont::ErrorBadType("Unable to set values in array of type " + + data.GetArrayTypeName()); + } +} diff --git a/vtkm/cont/ArraySetValues.h b/vtkm/cont/ArraySetValues.h new file mode 100644 index 0000000000..fa7f882444 --- /dev/null +++ b/vtkm/cont/ArraySetValues.h @@ -0,0 +1,261 @@ +//============================================================================ +// 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. +//============================================================================ + +#ifndef vtk_m_cont_ArraySetValues_h +#define vtk_m_cont_ArraySetValues_h + +#include + +#include +#include + +#include +#include + +namespace vtkm +{ +namespace cont +{ + +namespace internal +{ + +VTKM_CONT_EXPORT void ArraySetValuesImpl(const vtkm::cont::UnknownArrayHandle& ids, + const vtkm::cont::UnknownArrayHandle& values, + const vtkm::cont::UnknownArrayHandle& data, + std::false_type extractComponentInefficient); + +template +void ArraySetValuesImpl(const IdsArrayHandle& ids, + const ValuesArrayHandle& values, + const DataArrayHandle& data, + std::true_type vtkmNotUsed(extractComponentInefficient)) +{ + // Fallback implementation using control portals when device operations would be inefficient + vtkm::Id numValues = ids.GetNumberOfValues(); + VTKM_ASSERT(values.GetNumberOfValues() == numValues); + + auto idsPortal = ids.ReadPortal(); + auto valuesPortal = values.ReadPortal(); + auto dataPortal = data.WritePortal(); + + for (vtkm::Id index = 0; index < numValues; ++index) + { + dataPortal.Set(idsPortal.Get(index), valuesPortal.Get(index)); + } +} + +} // namespace internal + +/// \brief Set a small set of values in an ArrayHandle with minimal device transfers. +/// +/// The values in @a values are copied into @a data at the indices specified in @a ids. +/// This is useful for updating a subset of an array on a device without transferring +/// the entire array. +/// +/// These functions should not be called repeatedly in a loop to set all values in +/// an array handle. The much more efficient way to do this is to use the proper +/// control-side portals (ArrayHandle::WritePortal()) or to do so in a worklet. +/// +/// This method will attempt to copy the data using the device that the input +/// data is already valid on. If the input data is only valid in the control +/// environment or the device copy fails, a control-side copy is performed. +/// +/// Since a serial control-side copy may be used, this method is only intended +/// for copying small subsets of the input data. Larger subsets that would +/// benefit from parallelization should prefer using ArrayCopy with an +/// ArrayHandlePermutation. +/// +/// This utility provides several convenient overloads: +/// +/// A single id and value may be passed into ArraySetValue, or multiple ids and values +/// may be specified to ArraySetValues as ArrayHandles, std::vectors, c-arrays +/// (pointer and size), or as brace-enclosed initializer lists. +/// +/// Examples: +/// +/// ```cpp +/// vtkm::cont::ArrayHandle data = ...; +/// +/// // Set a single value in an array handle: +/// vtkm::cont::ArraySetValue(0, T{42}, data); +/// +/// // Set the first and third values in an array handle: +/// vtkm::cont::ArraySetValues({0, 2}, {T{10}, T{30}}, data); +/// +/// // Set values using std::vector +/// std::vector ids{0, 1, 2, 3}; +/// std::vector values{T{10}, T{20}, T{30}, T{40}}; +/// vtkm::cont::ArraySetValues(ids, values, data); +/// +/// // Set values using array handles directly +/// vtkm::cont::ArrayHandle idsHandle; +/// vtkm::cont::ArrayHandle valuesHandle; +/// // ... populate handles ... +/// vtkm::cont::ArraySetValues(idsHandle, valuesHandle, data); +/// +/// // Set values using raw pointers +/// vtkm::Id rawIds[] = {0, 1, 2}; +/// T rawValues[] = {T{10}, T{20}, T{30}}; +/// vtkm::cont::ArraySetValues(rawIds, 3, rawValues, 3, data); +/// ``` +/// +///@{ +/// +template +VTKM_CONT void ArraySetValues(const vtkm::cont::ArrayHandle& ids, + const vtkm::cont::ArrayHandle& values, + const vtkm::cont::ArrayHandle& data) +{ + using DataArrayHandle = vtkm::cont::ArrayHandle; + using InefficientExtract = + vtkm::cont::internal::ArrayExtractComponentIsInefficient; + internal::ArraySetValuesImpl(ids, values, data, InefficientExtract{}); +} + +/// Specialization for ArrayHandleCasts +template +VTKM_CONT void ArraySetValues( + const vtkm::cont::ArrayHandle& ids, + const vtkm::cont::ArrayHandle& values, + const vtkm::cont::ArrayHandle>& data) +{ + vtkm::cont::ArrayHandleBasic tempValues; + tempValues.Allocate(values.GetNumberOfValues()); + auto inp = values.ReadPortal(); + auto outp = tempValues.WritePortal(); + for (vtkm::Id i = 0; i < values.GetNumberOfValues(); ++i) + { + outp.Set(i, static_cast(inp.Get(i))); + } + + vtkm::cont::ArrayHandleCast> castArray = data; + ArraySetValues(ids, tempValues, castArray.GetSourceArray()); +} + +template +VTKM_CONT void ArraySetValues(const vtkm::cont::ArrayHandle& ids, + const std::vector& values, + const vtkm::cont::ArrayHandle& data) +{ + const auto valuesAH = vtkm::cont::make_ArrayHandle(values, vtkm::CopyFlag::Off); + ArraySetValues(ids, valuesAH, data); +} + +template +VTKM_CONT void ArraySetValues(const std::vector& ids, + const vtkm::cont::ArrayHandle& values, + const vtkm::cont::ArrayHandle& data) +{ + const auto idsAH = vtkm::cont::make_ArrayHandle(ids, vtkm::CopyFlag::Off); + ArraySetValues(idsAH, values, data); +} + +template +VTKM_CONT void ArraySetValues(const std::vector& ids, + const std::vector& values, + const vtkm::cont::ArrayHandle& data) +{ + const auto idsAH = vtkm::cont::make_ArrayHandle(ids, vtkm::CopyFlag::Off); + const auto valuesAH = vtkm::cont::make_ArrayHandle(values, vtkm::CopyFlag::Off); + ArraySetValues(idsAH, valuesAH, data); +} + +template +VTKM_CONT void ArraySetValues(const std::initializer_list& ids, + const std::vector& values, + const vtkm::cont::ArrayHandle& data) +{ + const auto idsAH = vtkm::cont::make_ArrayHandle( + ids.begin(), static_cast(ids.size()), vtkm::CopyFlag::Off); + const auto valuesAH = vtkm::cont::make_ArrayHandle(values, vtkm::CopyFlag::Off); + ArraySetValues(idsAH, valuesAH, data); +} + +template +VTKM_CONT void ArraySetValues(const std::initializer_list& ids, + const std::initializer_list& values, + const vtkm::cont::ArrayHandle& data) +{ + const auto idsAH = vtkm::cont::make_ArrayHandle( + ids.begin(), static_cast(ids.size()), vtkm::CopyFlag::Off); + const auto valuesAH = vtkm::cont::make_ArrayHandle( + values.begin(), static_cast(values.size()), vtkm::CopyFlag::Off); + ArraySetValues(idsAH, valuesAH, data); +} + +template +VTKM_CONT void ArraySetValues(const std::initializer_list& ids, + const vtkm::cont::ArrayHandle& values, + const vtkm::cont::ArrayHandle& data) +{ + const auto idsAH = vtkm::cont::make_ArrayHandle( + ids.begin(), static_cast(ids.size()), vtkm::CopyFlag::Off); + ArraySetValues(idsAH, values, data); +} + +template +VTKM_CONT void ArraySetValues(const vtkm::Id* ids, + const vtkm::Id numIds, + const std::vector& values, + const vtkm::cont::ArrayHandle& data) +{ + VTKM_ASSERT(numIds == static_cast(values.size())); + const auto idsAH = vtkm::cont::make_ArrayHandle(ids, numIds, vtkm::CopyFlag::Off); + const auto valuesAH = + vtkm::cont::make_ArrayHandle(values.data(), values.size(), vtkm::CopyFlag::Off); + ArraySetValues(idsAH, valuesAH, data); +} + +template +VTKM_CONT void ArraySetValues(const vtkm::Id* ids, + const vtkm::Id numIds, + const T* values, + const vtkm::Id numValues, + const vtkm::cont::ArrayHandle& data) +{ + VTKM_ASSERT(numIds == numValues); + const auto idsAH = vtkm::cont::make_ArrayHandle(ids, numIds, vtkm::CopyFlag::Off); + const auto valuesAH = vtkm::cont::make_ArrayHandle(values, numValues, vtkm::CopyFlag::Off); + ArraySetValues(idsAH, valuesAH, data); +} + +template +VTKM_CONT void ArraySetValues(const vtkm::Id* ids, + const vtkm::Id numIds, + const vtkm::cont::ArrayHandle& values, + const vtkm::cont::ArrayHandle& data) +{ + VTKM_ASSERT(numIds == values.GetNumberOfValues()); + const auto idsAH = vtkm::cont::make_ArrayHandle(ids, numIds, vtkm::CopyFlag::Off); + ArraySetValues(idsAH, values, data); +} + +/// \brief Set a single value in an ArrayHandle at the specified index. +/// +/// This is a convenience function that sets a single value at the given index. +/// It is equivalent to calling ArraySetValues with single-element arrays. +/// +template +VTKM_CONT void ArraySetValue(vtkm::Id id, + const T& value, + const vtkm::cont::ArrayHandle& data) +{ + const auto idAH = vtkm::cont::make_ArrayHandle(&id, 1, vtkm::CopyFlag::Off); + const auto valueAH = vtkm::cont::make_ArrayHandle(&value, 1, vtkm::CopyFlag::Off); + ArraySetValues(idAH, valueAH, data); +} + +///@} + +} // namespace cont +} // namespace vtkm + +#endif //vtk_m_cont_ArraySetValues_h diff --git a/vtkm/cont/CMakeLists.txt b/vtkm/cont/CMakeLists.txt index 6499853705..ca47f3cbb6 100644 --- a/vtkm/cont/CMakeLists.txt +++ b/vtkm/cont/CMakeLists.txt @@ -52,6 +52,7 @@ set(headers ArrayRangeCompute.h ArrayRangeComputeTemplate.h ArrayRangeComputeTemplateInstantiationsIncludes.h + ArraySetValues.h AssignerPartitionedDataSet.h AtomicArray.h BitField.h @@ -190,6 +191,7 @@ set(device_sources ArrayHandleIndex.cxx ArrayHandleUniformPointCoordinates.cxx ArrayRangeCompute.cxx + ArraySetValues.cxx CellLocatorBoundingIntervalHierarchy.cxx CellLocatorUniformBins.cxx CellLocatorTwoLevel.cxx diff --git a/vtkm/cont/testing/CMakeLists.txt b/vtkm/cont/testing/CMakeLists.txt index 850ecd4972..250a5f787a 100644 --- a/vtkm/cont/testing/CMakeLists.txt +++ b/vtkm/cont/testing/CMakeLists.txt @@ -29,6 +29,7 @@ set(unit_tests UnitTestArrayHandleUniformPointCoordinates.cxx UnitTestArrayPortalFromIterators.cxx UnitTestArrayPortalToIterators.cxx + UnitTestArraySetValues.cxx UnitTestBuffer.cxx UnitTestComputeRange.cxx UnitTestControlSignatureTag.cxx diff --git a/vtkm/cont/testing/UnitTestArraySetValues.cxx b/vtkm/cont/testing/UnitTestArraySetValues.cxx new file mode 100644 index 0000000000..a92ee64594 --- /dev/null +++ b/vtkm/cont/testing/UnitTestArraySetValues.cxx @@ -0,0 +1,185 @@ +//============================================================================ +// 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 + +namespace +{ + +static constexpr vtkm::Id ARRAY_SIZE = 10; + +template +VTKM_CONT void TestValues(const vtkm::cont::ArrayHandle& ah, + const std::initializer_list& expected) +{ + auto portal = ah.ReadPortal(); + VTKM_TEST_ASSERT(expected.size() == static_cast(ah.GetNumberOfValues())); + for (vtkm::Id i = 0; i < ah.GetNumberOfValues(); ++i) + { + VTKM_TEST_ASSERT(expected.begin()[static_cast(i)] == portal.Get(i)); + } +} + +template +void TryCopy() +{ + std::cout << "Trying type: " << vtkm::testing::TypeName::Name() << std::endl; + + auto createData = []() -> vtkm::cont::ArrayHandle { + vtkm::cont::ArrayHandle data; + // Create and initialize the ValueType array + vtkm::cont::ArrayHandleIndex values(ARRAY_SIZE); + vtkm::cont::ArrayCopy(values, data); + return data; + }; + + { // ArrayHandle ids + const auto ids = vtkm::cont::make_ArrayHandle({ 3, 8, 7 }); + { // Pass vector + const auto data = createData(); + std::vector values{ 30, 80, 70 }; + vtkm::cont::ArraySetValues(ids, values, data); + TestValues(data, { 0, 1, 2, 30, 4, 5, 6, 70, 80, 9 }); + } + { // Pass Handle + const auto data = createData(); + const auto newValues = vtkm::cont::make_ArrayHandle({ 30, 80, 70 }); + vtkm::cont::ArraySetValues(ids, newValues, data); + TestValues(data, { 0, 1, 2, 30, 4, 5, 6, 70, 80, 9 }); + } + { // Test the specialization for ArrayHandleCast + const auto data = createData(); + auto castedData = vtkm::cont::make_ArrayHandleCast(data); + const auto doubleValues = vtkm::cont::make_ArrayHandle({ 3.0, 8.0, 7.0 }); + vtkm::cont::ArraySetValues(ids, doubleValues, castedData); + TestValues(data, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); + } + } + + { // vector ids + const std::vector ids{ 3, 8, 7 }; + { // Pass vector + const auto data = createData(); + const std::vector values{ 30, 80, 70 }; + vtkm::cont::ArraySetValues(ids, values, data); + TestValues(data, { 0, 1, 2, 30, 4, 5, 6, 70, 80, 9 }); + } + { // Pass handle + const auto data = createData(); + const auto newValues = vtkm::cont::make_ArrayHandle({ 30, 80, 70 }); + vtkm::cont::ArraySetValues(ids, newValues, data); + TestValues(data, { 0, 1, 2, 30, 4, 5, 6, 70, 80, 9 }); + } + } + + { // Initializer list ids + { // Pass vector: + const auto data = createData(); + const std::vector values{ 30, 80, 70 }; + vtkm::cont::ArraySetValues({ 3, 8, 7 }, values, data); + TestValues(data, { 0, 1, 2, 30, 4, 5, 6, 70, 80, 9 }); +} +{ // Pass initializer list + const auto data = createData(); + vtkm::cont::ArraySetValues( + { 3, 8, 7 }, + { static_cast(30), static_cast(80), static_cast(70) }, + data); + TestValues(data, { 0, 1, 2, 30, 4, 5, 6, 70, 80, 9 }); +} +{ // Pass handle: + const auto data = createData(); + const auto newValues = vtkm::cont::make_ArrayHandle({ 30, 80, 70 }); + vtkm::cont::ArraySetValues({ 3, 8, 7 }, newValues, data); + TestValues(data, { 0, 1, 2, 30, 4, 5, 6, 70, 80, 9 }); +} +} + +{ // c-array ids + const std::vector idVec{ 3, 8, 7 }; + const vtkm::Id* ids = idVec.data(); + const auto numIds = static_cast(idVec.size()); + const std::vector valueVec{ 30, 80, 70 }; + const ValueType* values = valueVec.data(); + const auto nValues = static_cast(valueVec.size()); + { // Pass c-array + const auto data = createData(); + vtkm::cont::ArraySetValues(ids, numIds, values, nValues, data); + TestValues(data, { 0, 1, 2, 30, 4, 5, 6, 70, 80, 9 }); + } + { // Pass vector + const auto data = createData(); + vtkm::cont::ArraySetValues(ids, numIds, valueVec, data); + TestValues(data, { 0, 1, 2, 30, 4, 5, 6, 70, 80, 9 }); + } + { // Pass Handle + const auto data = createData(); + const auto newValues = vtkm::cont::make_ArrayHandle(valueVec, vtkm::CopyFlag::Off); + vtkm::cont::ArraySetValues(ids, numIds, newValues, data); + TestValues(data, { 0, 1, 2, 30, 4, 5, 6, 70, 80, 9 }); + } +} + +{ // single value + const auto data = createData(); + vtkm::cont::ArraySetValue(8, static_cast(88), data); + TestValues(data, { 0, 1, 2, 3, 4, 5, 6, 7, 88, 9 }); +} +} + +void TryRange() +{ + std::cout << "Trying vtkm::Range" << std::endl; + + vtkm::cont::ArrayHandle values = + vtkm::cont::make_ArrayHandle({ { 0.0, 1.0 }, { 1.0, 2.0 }, { 2.0, 4.0 } }); + + vtkm::cont::ArraySetValue(1, vtkm::Range{ 5.0, 6.0 }, values); + auto portal = values.ReadPortal(); + VTKM_TEST_ASSERT(portal.Get(1) == vtkm::Range{ 5.0, 6.0 }); +} + +void TryBounds() +{ + std::cout << "Trying vtkm::Bounds" << std::endl; + + vtkm::cont::ArrayHandle values = + vtkm::cont::make_ArrayHandle({ { { 0.0, 1.0 }, { 0.0, 1.0 }, { 0.0, 1.0 } }, + { { 1.0, 2.0 }, { 1.0, 2.0 }, { 1.0, 2.0 } }, + { { 2.0, 4.0 }, { 2.0, 4.0 }, { 2.0, 4.0 } } }); + + vtkm::cont::ArraySetValue(1, vtkm::Bounds{ { 5.0, 6.0 }, { 5.0, 6.0 }, { 5.0, 6.0 } }, values); + auto portal = values.ReadPortal(); + VTKM_TEST_ASSERT(portal.Get(1) == vtkm::Bounds{ { 5.0, 6.0 }, { 5.0, 6.0 }, { 5.0, 6.0 } }); +} + +void Test() +{ + TryCopy(); + TryCopy(); + TryCopy(); + TryRange(); + TryBounds(); +} + +} // anonymous namespace + +int UnitTestArraySetValues(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(Test, argc, argv); +} -- GitLab From d641d2766eeed0b672683fec4ecfa8b0340834d5 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 3 Feb 2025 14:28:01 -0500 Subject: [PATCH 57/73] Add changelog --- docs/changelog/add-array-set-values.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 docs/changelog/add-array-set-values.md diff --git a/docs/changelog/add-array-set-values.md b/docs/changelog/add-array-set-values.md new file mode 100644 index 0000000000..fc837e57dd --- /dev/null +++ b/docs/changelog/add-array-set-values.md @@ -0,0 +1,4 @@ +## Add ArraySetValues + +ArraySetValues.h is a new header file that includes functions to set 1 or many values in an array +similarly to how ArrayGetValues.h gets 1 or many values from an array. -- GitLab From 0445b71d89dc50c1fdfb676d8a0418e610ce5c3a Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Tue, 4 Feb 2025 11:27:58 -0500 Subject: [PATCH 58/73] Add comment on termination algorithm. --- .../flow/internal/AdvectAlgorithmTerminator.h | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h index d1bf8ea402..3f35bf448c 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -20,6 +20,36 @@ namespace flow namespace internal { +// This is based on: +// D. Morozov, et al., "IExchange: Asynchronous Communication and Termination Detection for Iterative Algorithms," +// 2021 IEEE 11th Symposium on Large Data Analysis and Visualization (LDAV), New Orleans, LA, USA, 2021, pp. 12-21, +// doi: 10.1109/LDAV53230.2021.00009. +// +// The challenge for async termination is to determine when all work is complete and no messages remain in flight. +// The algorithm uses a number of states to determine when this occurs. +// State 0: a process is working. +// State 1: Process is done and waiting +// State 2: All done and checking for cancelation +// State 3: Done +// +// State 0: ----- if no work ----> State 1: (locally done. call ibarrier). +// | +// | ibarrier done +// | dirty = "have new work since entering State 1" +// | call iallreduce(dirty) +// | +// State 2: (all done, checking for cancel) +// | +// | if dirty == 1 : GOTO State 0. +// | else: goto State 3 (DONE) +// +// A process begins in State 0 and remains until it has no more work to do. +// Process calls ibarrier and enters State 1. When the ibarrier is satisfied, this means that all processes are in State 1. +// When all processes are in State 1, each process sets a dirty flag to true if any work has arrived since entering State 1. +// Each procces call iallreduce(dirty) and enter State 2. +// In State 2, if the iallreduce returns true, there is new work, so return to State 0. +// If the iallreduce returns false, then all work is complete and we can terminate. +// class AdvectAlgorithmTerminator { public: @@ -72,14 +102,12 @@ public: int flag; MPI_Test(&this->StateReq, &flag, &status); if (flag == 1) - this->State = STATE_1B; - } - else if (this->State == STATE_1B) - { - this->LocalDirty = this->Dirty; - MPI_Iallreduce( - &this->LocalDirty, &this->AllDirty, 1, MPI_INT, MPI_LOR, this->MPIComm, &this->StateReq); - this->State = STATE_2; + { + this->LocalDirty = this->Dirty; + MPI_Iallreduce( + &this->LocalDirty, &this->AllDirty, 1, MPI_INT, MPI_LOR, this->MPIComm, &this->StateReq); + this->State = STATE_2; + } } else if (this->State == STATE_2) { @@ -107,7 +135,6 @@ private: { STATE_0, STATE_1, - STATE_1B, STATE_2, DONE }; -- GitLab From 10c6336799ce6d27ab52e9d95a00e1463378706e Mon Sep 17 00:00:00 2001 From: David Pugmire Date: Tue, 4 Feb 2025 11:36:08 -0500 Subject: [PATCH 59/73] update comments. --- vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h index 3f35bf448c..3a0895d9b7 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -30,7 +30,6 @@ namespace internal // State 0: a process is working. // State 1: Process is done and waiting // State 2: All done and checking for cancelation -// State 3: Done // // State 0: ----- if no work ----> State 1: (locally done. call ibarrier). // | @@ -41,7 +40,7 @@ namespace internal // State 2: (all done, checking for cancel) // | // | if dirty == 1 : GOTO State 0. -// | else: goto State 3 (DONE) +// | else: Done // // A process begins in State 0 and remains until it has no more work to do. // Process calls ibarrier and enters State 1. When the ibarrier is satisfied, this means that all processes are in State 1. @@ -92,6 +91,8 @@ public: if (this->State == STATE_0 && !haveLocalWork) { + //No more work for this rank. + //Set Dirty = 0 (to see if any work arrives while in STATE_1) MPI_Ibarrier(this->MPIComm, &this->StateReq); this->Dirty = 0; this->State = STATE_1; @@ -116,10 +117,12 @@ public: MPI_Test(&this->StateReq, &flag, &status); if (flag == 1) { - if (this->AllDirty == 0) //done + //If no rank has had any new work since the iBarrier, work is complete. + //Otherwise, return to STATE_0. + if (this->AllDirty == 0) this->State = DONE; else - this->State = STATE_0; //reset. + this->State = STATE_0; } } #else -- GitLab From 441b1c94248bbacd1543e65a7a5385b9a6d17c22 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 22 Nov 2024 21:20:12 -0500 Subject: [PATCH 60/73] Clip: Improve performance and memory consumption The following set of improvements have been implemented: 1. Input points that are kept are determined by comparing their scalar value with the isovalue, instead of checking the output cells' connectivity. 2. Output arrays are written only once, and they are not transformed. Due to that, no auxiliary arrays are needed to perform the transformations. 3. A fast path for discarded and kept cells has been added, which are the most common cell cases. 4. ClipTables are now more descriptive, and the non-inverted case tables have been imported from VTK, such that both VTK and VTK-m produce the same results. --- vtkm/filter/contour/ClipWithField.cxx | 3 +- .../contour/ClipWithImplicitFunction.cxx | 5 +- vtkm/filter/contour/worklet/Clip.h | 1150 +-- vtkm/filter/contour/worklet/MIR.h | 4 +- vtkm/filter/contour/worklet/clip/ClipTables.h | 7709 +++++++++++------ 5 files changed, 5682 insertions(+), 3189 deletions(-) diff --git a/vtkm/filter/contour/ClipWithField.cxx b/vtkm/filter/contour/ClipWithField.cxx index 71d32bba5e..b1b93843e0 100644 --- a/vtkm/filter/contour/ClipWithField.cxx +++ b/vtkm/filter/contour/ClipWithField.cxx @@ -77,7 +77,8 @@ vtkm::cont::DataSet ClipWithField::DoExecute(const vtkm::cont::DataSet& input) vtkm::cont::CellSetExplicit<> outputCellSet; auto resolveFieldType = [&](const auto& concrete) { - outputCellSet = worklet.Run(inputCellSet, concrete, this->ClipValue, this->Invert); + outputCellSet = this->Invert ? worklet.Run(inputCellSet, concrete, this->ClipValue) + : worklet.Run(inputCellSet, concrete, this->ClipValue); }; this->CastAndCallScalarField(this->GetFieldFromDataSet(input).GetData(), resolveFieldType); diff --git a/vtkm/filter/contour/ClipWithImplicitFunction.cxx b/vtkm/filter/contour/ClipWithImplicitFunction.cxx index 24f2f69913..48df7aafa2 100644 --- a/vtkm/filter/contour/ClipWithImplicitFunction.cxx +++ b/vtkm/filter/contour/ClipWithImplicitFunction.cxx @@ -70,8 +70,9 @@ vtkm::cont::DataSet ClipWithImplicitFunction::DoExecute(const vtkm::cont::DataSe vtkm::worklet::Clip worklet; - vtkm::cont::CellSetExplicit<> outputCellSet = - worklet.Run(inputCellSet, this->Function, this->Offset, inputCoords, this->Invert); + vtkm::cont::CellSetExplicit<> outputCellSet = this->Invert + ? worklet.Run(inputCellSet, this->Function, this->Offset, inputCoords) + : worklet.Run(inputCellSet, this->Function, this->Offset, inputCoords); auto mapper = [&](auto& result, const auto& f) { DoMapField(result, f, worklet); }; return this->CreateResult(input, outputCellSet, mapper); diff --git a/vtkm/filter/contour/worklet/Clip.h b/vtkm/filter/contour/worklet/Clip.h index ce630a88be..84be4428e6 100644 --- a/vtkm/filter/contour/worklet/Clip.h +++ b/vtkm/filter/contour/worklet/Clip.h @@ -10,28 +10,24 @@ #ifndef vtkm_m_worklet_Clip_h #define vtkm_m_worklet_Clip_h -#include -#include -#include -#include -#include -#include +#include +#include #include -#include -#include -#include +#include +#include +#include #include -#include #include #include -#include -#include -#include +#include + +#include +#include +#include +#include -#include -#include #if defined(THRUST_MAJOR_VERSION) && THRUST_MAJOR_VERSION == 1 && THRUST_MINOR_VERSION == 8 && \ THRUST_SUBMINOR_VERSION < 3 @@ -48,725 +44,521 @@ namespace vtkm { namespace worklet { -struct ClipStats +class Clip { - vtkm::Id NumberOfCells = 0; - vtkm::Id NumberOfIndices = 0; - vtkm::Id NumberOfEdgeIndices = 0; - - // Stats for interpolating new points within cell. - vtkm::Id NumberOfInCellPoints = 0; - vtkm::Id NumberOfInCellIndices = 0; - vtkm::Id NumberOfInCellInterpPoints = 0; - vtkm::Id NumberOfInCellEdgeIndices = 0; - - struct SumOp +public: + struct ClipStats { - VTKM_EXEC_CONT - ClipStats operator()(const ClipStats& stat1, const ClipStats& stat2) const + vtkm::Id NumberOfCells = 0; + vtkm::Id NumberOfCellIndices = 0; + vtkm::Id NumberOfEdges = 0; + vtkm::Id NumberOfCentroids = 0; + vtkm::Id NumberOfCentroidIndices = 0; + + struct SumOp { - ClipStats sum = stat1; - sum.NumberOfCells += stat2.NumberOfCells; - sum.NumberOfIndices += stat2.NumberOfIndices; - sum.NumberOfEdgeIndices += stat2.NumberOfEdgeIndices; - sum.NumberOfInCellPoints += stat2.NumberOfInCellPoints; - sum.NumberOfInCellIndices += stat2.NumberOfInCellIndices; - sum.NumberOfInCellInterpPoints += stat2.NumberOfInCellInterpPoints; - sum.NumberOfInCellEdgeIndices += stat2.NumberOfInCellEdgeIndices; - return sum; - } + VTKM_EXEC_CONT + ClipStats operator()(const ClipStats& stat1, const ClipStats& stat2) const + { + ClipStats sum = stat1; + sum.NumberOfCells += stat2.NumberOfCells; + sum.NumberOfCellIndices += stat2.NumberOfCellIndices; + sum.NumberOfEdges += stat2.NumberOfEdges; + sum.NumberOfCentroids += stat2.NumberOfCentroids; + sum.NumberOfCentroidIndices += stat2.NumberOfCentroidIndices; + return sum; + } + }; }; -}; - -struct EdgeInterpolation -{ - vtkm::Id Vertex1 = -1; - vtkm::Id Vertex2 = -1; - vtkm::Float64 Weight = 0; - struct LessThanOp + struct EdgeInterpolation { - VTKM_EXEC - bool operator()(const EdgeInterpolation& v1, const EdgeInterpolation& v2) const + vtkm::Id Vertex1 = -1; + vtkm::Id Vertex2 = -1; + vtkm::Float64 Weight = 0; + + struct LessThanOp { - return (v1.Vertex1 < v2.Vertex1) || (v1.Vertex1 == v2.Vertex1 && v1.Vertex2 < v2.Vertex2); - } - }; + VTKM_EXEC + bool operator()(const EdgeInterpolation& v1, const EdgeInterpolation& v2) const + { + return (v1.Vertex1 < v2.Vertex1) || (v1.Vertex1 == v2.Vertex1 && v1.Vertex2 < v2.Vertex2); + } + }; - struct EqualToOp - { - VTKM_EXEC - bool operator()(const EdgeInterpolation& v1, const EdgeInterpolation& v2) const + struct EqualToOp { - return v1.Vertex1 == v2.Vertex1 && v1.Vertex2 == v2.Vertex2; - } + VTKM_EXEC + bool operator()(const EdgeInterpolation& v1, const EdgeInterpolation& v2) const + { + return v1.Vertex1 == v2.Vertex1 && v1.Vertex2 == v2.Vertex2; + } + }; }; -}; - -namespace internal -{ - -template -VTKM_EXEC_CONT T Scale(const T& val, vtkm::Float64 scale) -{ - return static_cast(scale * static_cast(val)); -} - -template -VTKM_EXEC_CONT vtkm::Vec Scale(const vtkm::Vec& val, - vtkm::Float64 scale) -{ - return val * scale; -} - -template -class ExecutionConnectivityExplicit -{ -private: - using UInt8Portal = typename vtkm::cont::ArrayHandle::WritePortalType; - using IdComponentPortal = typename vtkm::cont::ArrayHandle::WritePortalType; - using IdPortal = typename vtkm::cont::ArrayHandle::WritePortalType; - -public: - VTKM_CONT - ExecutionConnectivityExplicit() = default; - - VTKM_CONT - ExecutionConnectivityExplicit(vtkm::cont::ArrayHandle shapes, - vtkm::cont::ArrayHandle numberOfIndices, - vtkm::cont::ArrayHandle connectivity, - vtkm::cont::ArrayHandle offsets, - ClipStats stats, - vtkm::cont::Token& token) - : Shapes(shapes.PrepareForOutput(stats.NumberOfCells, Device(), token)) - , NumberOfIndices(numberOfIndices.PrepareForOutput(stats.NumberOfCells, Device(), token)) - , Connectivity(connectivity.PrepareForOutput(stats.NumberOfIndices, Device(), token)) - , Offsets(offsets.PrepareForOutput(stats.NumberOfCells, Device(), token)) - { - } - - VTKM_EXEC - void SetCellShape(vtkm::Id cellIndex, vtkm::UInt8 shape) { this->Shapes.Set(cellIndex, shape); } - VTKM_EXEC - void SetNumberOfIndices(vtkm::Id cellIndex, vtkm::IdComponent numIndices) + /** + * This worklet identifies the input points that are kept, i.e. are inside the implicit function. + */ + template + class MarkKeptPoints : public vtkm::worklet::WorkletMapField { - this->NumberOfIndices.Set(cellIndex, numIndices); - } + public: + using ControlSignature = void(FieldIn scalar, FieldOut keptPointMask); + using ExecutionSignature = _2(_1); - VTKM_EXEC - void SetIndexOffset(vtkm::Id cellIndex, vtkm::Id indexOffset) - { - this->Offsets.Set(cellIndex, indexOffset); - } + VTKM_CONT + explicit MarkKeptPoints(vtkm::Float64 isoValue) + : IsoValue(isoValue) + { + } - VTKM_EXEC - void SetConnectivity(vtkm::Id connectivityIndex, vtkm::Id pointIndex) - { - this->Connectivity.Set(connectivityIndex, pointIndex); - } + template + VTKM_EXEC vtkm::UInt8 operator()(const T& scalar) const + { + return Invert ? scalar < this->IsoValue : scalar >= this->IsoValue; + } -private: - UInt8Portal Shapes; - IdComponentPortal NumberOfIndices; - IdPortal Connectivity; - IdPortal Offsets; -}; + private: + vtkm::Float64 IsoValue; + }; -class ConnectivityExplicit : vtkm::cont::ExecutionObjectBase -{ -public: - VTKM_CONT - ConnectivityExplicit() = default; - - VTKM_CONT - ConnectivityExplicit(const vtkm::cont::ArrayHandle& shapes, - const vtkm::cont::ArrayHandle& numberOfIndices, - const vtkm::cont::ArrayHandle& connectivity, - const vtkm::cont::ArrayHandle& offsets, - const ClipStats& stats) - : Shapes(shapes) - , NumberOfIndices(numberOfIndices) - , Connectivity(connectivity) - , Offsets(offsets) - , Stats(stats) + template + class ComputeClipStats : public vtkm::worklet::WorkletVisitCellsWithPoints { - } + public: + using ControlSignature = void(CellSetIn cellSet, + FieldInPoint pointMask, + FieldOutCell clipStat, + FieldOutCell clippedMask, + FieldOutCell keptOrClippedMask, + FieldOutCell caseIndex); - template - VTKM_CONT ExecutionConnectivityExplicit PrepareForExecution( - Device, - vtkm::cont::Token& token) const - { - ExecutionConnectivityExplicit execConnectivity( - this->Shapes, this->NumberOfIndices, this->Connectivity, this->Offsets, this->Stats, token); - return execConnectivity; - } + using ExecutionSignature = void(CellShape, PointCount, _2, _3, _4, _5, _6); -private: - vtkm::cont::ArrayHandle Shapes; - vtkm::cont::ArrayHandle NumberOfIndices; - vtkm::cont::ArrayHandle Connectivity; - vtkm::cont::ArrayHandle Offsets; - vtkm::worklet::ClipStats Stats; -}; + using CT = internal::ClipTables; + template + VTKM_EXEC void operator()(const CellShapeTag& shape, + const vtkm::IdComponent pointCount, + const KeptPointsMask& keptPointsMask, + ClipStats& clipStat, + vtkm::UInt8& clippedMask, + vtkm::UInt8& keptOrClippedMask, + vtkm::UInt8& caseIndex) const + { + namespace CTI = vtkm::worklet::internal::ClipTablesInformation; + // compute case index + caseIndex = 0; + for (vtkm::IdComponent ptId = pointCount - 1; ptId >= 0; --ptId) + { + static constexpr auto InvertUint8 = static_cast(Invert); + caseIndex |= (InvertUint8 != keptPointsMask[ptId]) << ptId; + } -} // namespace internal + if (CT::IsCellDiscarded(pointCount, caseIndex)) // discarded cell + { + // we do that to determine if a cell is discarded using only the caseIndex + caseIndex = CT::GetDiscardedCellCase(); + } + else if (CT::IsCellKept(pointCount, caseIndex)) // kept cell + { + // we do that to determine if a cell is kept using only the caseIndex + caseIndex = CT::GetKeptCellCase(); + clipStat.NumberOfCells = 1; + clipStat.NumberOfCellIndices = pointCount; + } + else // clipped cell + { + vtkm::Id index = CT::GetCaseIndex(shape.Id, caseIndex); + const vtkm::UInt8 numberOfShapes = CT::ValueAt(index++); -class Clip -{ - // Add support for invert -public: - using TypeClipStats = vtkm::List; + clipStat.NumberOfCells = numberOfShapes; + for (vtkm::IdComponent shapeId = 0; shapeId < numberOfShapes; ++shapeId) + { + const vtkm::UInt8 cellShape = CT::ValueAt(index++); + const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); - using TypeEdgeInterp = vtkm::List; + for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; pointId++, index++) + { + // Find how many points need to be calculated using edge interpolation. + const vtkm::UInt8 pointIndex = CT::ValueAt(index); + clipStat.NumberOfEdges += (pointIndex >= CTI::E00 && pointIndex <= CTI::E11); + } + if (cellShape != CTI::ST_PNT) // normal cell + { + // Collect number of indices required for storing current shape + clipStat.NumberOfCellIndices += numberOfCellIndices; + } + else // cellShape == ST_PNT + { + --clipStat.NumberOfCells; // decrement since this is a centroid shape + clipStat.NumberOfCentroids++; + clipStat.NumberOfCentroidIndices += numberOfCellIndices; + } + } + } + keptOrClippedMask = caseIndex != CT::GetDiscardedCellCase(); + clippedMask = keptOrClippedMask && caseIndex != CT::GetKeptCellCase(); + } + }; - class ComputeStats : public vtkm::worklet::WorkletVisitCellsWithPoints + template + class ExtractEdges : public vtkm::worklet::WorkletVisitCellsWithPoints { public: VTKM_CONT - ComputeStats(vtkm::Float64 value, bool invert) - : Value(value) - , Invert(invert) + explicit ExtractEdges(vtkm::Float64 isoValue) + : IsoValue(isoValue) { } - using ControlSignature = - void(CellSetIn, FieldInPoint, ExecObject clippingData, FieldOutCell, FieldOutCell); + using ControlSignature = void(CellSetIn cellSet, + FieldInPoint scalars, + FieldInCell clipStatOffsets, + FieldInCell caseIndex, + WholeArrayOut edges); - using ExecutionSignature = void(CellShape, PointCount, _2, _3, _4, _5); + using ExecutionSignature = void(CellShape, PointIndices, _2, _3, _4, _5); - using InputDomain = _1; + using MaskType = vtkm::worklet::MaskSelect; - template - VTKM_EXEC void operator()(CellShapeTag shape, - vtkm::IdComponent pointCount, - const ScalarFieldVec& scalars, - const internal::ClipTables::DevicePortal& clippingData, - ClipStats& clipStat, - vtkm::Id& clipDataIndex) const + using CT = internal::ClipTables; + + template + VTKM_EXEC void operator()(const CellShapeTag& shape, + const PointIndicesVec& points, + const PointScalars& scalars, + const ClipStats& clipStat, + const vtkm::UInt8& caseIndex, + EdgesArray& edges) const { - (void)shape; // C4100 false positive workaround - vtkm::Id caseId = 0; - for (vtkm::IdComponent iter = pointCount - 1; iter >= 0; iter--) - { - if (!this->Invert && static_cast(scalars[iter]) <= this->Value) - { - caseId++; - } - else if (this->Invert && static_cast(scalars[iter]) >= this->Value) - { - caseId++; - } - if (iter > 0) - caseId *= 2; - } - vtkm::Id index = clippingData.GetCaseIndex(shape.Id, caseId); - clipDataIndex = index; - vtkm::Id numberOfCells = clippingData.ValueAt(index++); - clipStat.NumberOfCells = numberOfCells; - for (vtkm::IdComponent shapes = 0; shapes < numberOfCells; shapes++) + namespace CTI = vtkm::worklet::internal::ClipTablesInformation; + vtkm::Id edgeOffset = clipStat.NumberOfEdges; + + // only clipped cells have edges + vtkm::Id index = CT::GetCaseIndex(shape.Id, caseIndex); + const vtkm::UInt8 numberOfShapes = CT::ValueAt(index++); + + for (vtkm::IdComponent shapeId = 0; shapeId < numberOfShapes; shapeId++) { - vtkm::Id cellShape = clippingData.ValueAt(index++); - vtkm::Id numberOfIndices = clippingData.ValueAt(index++); - if (cellShape == 0) - { - --clipStat.NumberOfCells; - // Shape is 0, which is a case of interpolating new point within a cell - // Gather stats for later operation. - clipStat.NumberOfInCellPoints = 1; - clipStat.NumberOfInCellInterpPoints = numberOfIndices; - for (vtkm::IdComponent points = 0; points < numberOfIndices; points++, index++) - { - //Find how many points need to be calculated using edge interpolation. - vtkm::Id element = clippingData.ValueAt(index); - clipStat.NumberOfInCellEdgeIndices += (element < 100) ? 1 : 0; - } - } - else + /*vtkm::UInt8 cellShape = */ CT::ValueAt(index++); + const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); + + for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; pointId++, index++) { - // Collect number of indices required for storing current shape - clipStat.NumberOfIndices += numberOfIndices; - // Collect number of new points - for (vtkm::IdComponent points = 0; points < numberOfIndices; points++, index++) + // Find how many points need to be calculated using edge interpolation. + const vtkm::UInt8 pointIndex = CT::ValueAt(index); + if (pointIndex >= CTI::E00 && pointIndex <= CTI::E11) { - //Find how many points need to found using edge interpolation. - vtkm::Id element = clippingData.ValueAt(index); - if (element == 255) + typename CT::EdgeVec edge = CT::GetEdge(shape.Id, pointIndex - CTI::E00); + EdgeInterpolation ei; + ei.Vertex1 = points[edge[0]]; + ei.Vertex2 = points[edge[1]]; + // For consistency purposes keep the points ordered. + if (ei.Vertex1 > ei.Vertex2) { - clipStat.NumberOfInCellIndices++; - } - else if (element < 100) - { - clipStat.NumberOfEdgeIndices++; + vtkm::Swap(ei.Vertex1, ei.Vertex2); + vtkm::Swap(edge[0], edge[1]); } + ei.Weight = (static_cast(scalars[edge[0]]) - this->IsoValue) / + static_cast(scalars[edge[1]] - scalars[edge[0]]); + // Add edge to the list of edges. + edges.Set(edgeOffset++, ei); } } } } private: - vtkm::Float64 Value; - bool Invert; + vtkm::Float64 IsoValue; }; + template class GenerateCellSet : public vtkm::worklet::WorkletVisitCellsWithPoints { public: VTKM_CONT - GenerateCellSet(vtkm::Float64 value) - : Value(value) + GenerateCellSet(vtkm::Id edgePointsOffset, vtkm::Id centroidPointsOffset) + : EdgePointsOffset(edgePointsOffset) + , CentroidPointsOffset(centroidPointsOffset) { } - using ControlSignature = void(CellSetIn, - FieldInPoint, - FieldInCell clipTableIndices, - FieldInCell clipStats, - ExecObject clipTables, - ExecObject connectivityObject, - WholeArrayOut pointsOnlyConnectivityIndices, - WholeArrayOut edgePointReverseConnectivity, - WholeArrayOut edgePointInterpolation, - WholeArrayOut inCellReverseConnectivity, - WholeArrayOut inCellEdgeReverseConnectivity, - WholeArrayOut inCellEdgeInterpolation, - WholeArrayOut inCellInterpolationKeys, - WholeArrayOut inCellInterpolationInfo, - WholeArrayOut cellMapOutputToInput); - - using ExecutionSignature = void(CellShape, - WorkIndex, - PointIndices, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15); + using ControlSignature = void(CellSetIn cellSet, + FieldInCell caseIndex, + FieldInCell clipStatOffsets, + WholeArrayIn pointMapOutputToInput, + WholeArrayIn edgeIndexToUnique, + WholeArrayOut centroidOffsets, + WholeArrayOut centroidConnectivity, + WholeArrayOut cellMapOutputToInput, + WholeArrayOut shapes, + WholeArrayOut offsets, + WholeArrayOut connectivity); + using ExecutionSignature = + void(InputIndex, CellShape, PointIndices, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11); + + using MaskType = vtkm::worklet::MaskSelect; + + using CT = internal::ClipTables; template - VTKM_EXEC void operator()(CellShapeTag shape, - vtkm::Id workIndex, + typename PointMapInputToOutput, + typename EdgeIndexToUnique, + typename CentroidOffsets, + typename CentroidConnectivity, + typename CellMapOutputToInput, + typename Shapes, + typename Offsets, + typename Connectivity> + VTKM_EXEC void operator()(vtkm::Id cellId, + const CellShapeTag& shape, const PointVecType& points, - const ScalarVecType& scalars, - vtkm::Id clipDataIndex, - const ClipStats& clipStats, - const internal::ClipTables::DevicePortal& clippingData, - ConnectivityObject& connectivityObject, - IdArrayType& pointsOnlyConnectivityIndices, - IdArrayType& edgePointReverseConnectivity, - EdgeInterpolationPortalType& edgePointInterpolation, - IdArrayType& inCellReverseConnectivity, - IdArrayType& inCellEdgeReverseConnectivity, - EdgeInterpolationPortalType& inCellEdgeInterpolation, - IdArrayType& inCellInterpolationKeys, - IdArrayType& inCellInterpolationInfo, - IdArrayType& cellMapOutputToInput) const + const vtkm::UInt8& caseIndex, + const ClipStats& clipStatOffsets, + const PointMapInputToOutput pointMapInputToOutput, + const EdgeIndexToUnique& edgeIndexToUnique, + CentroidOffsets& centroidOffsets, + CentroidConnectivity& centroidConnectivity, + CellMapOutputToInput& cellMapOutputToInput, + Shapes& shapes, + Offsets& offsets, + Connectivity& connectivity) const { - (void)shape; - vtkm::Id clipIndex = clipDataIndex; - // Start index for the cells of this case. - vtkm::Id cellIndex = clipStats.NumberOfCells; - // Start index to store connevtivity of this case. - vtkm::Id connectivityIndex = clipStats.NumberOfIndices; - // Start indices for reverse mapping into connectivity for this case. - vtkm::Id edgeIndex = clipStats.NumberOfEdgeIndices; - vtkm::Id inCellIndex = clipStats.NumberOfInCellIndices; - vtkm::Id inCellPoints = clipStats.NumberOfInCellPoints; - // Start Indices to keep track of interpolation points for new cell. - vtkm::Id inCellInterpPointIndex = clipStats.NumberOfInCellInterpPoints; - vtkm::Id inCellEdgeInterpIndex = clipStats.NumberOfInCellEdgeIndices; - // Start index of connectivityPointsOnly - vtkm::Id pointsOnlyConnectivityIndicesIndex = connectivityIndex - edgeIndex - inCellIndex; - - // Iterate over the shapes for the current cell and begin to fill connectivity. - vtkm::Id numberOfCells = clippingData.ValueAt(clipIndex++); - for (vtkm::Id cell = 0; cell < numberOfCells; ++cell) + namespace CTI = vtkm::worklet::internal::ClipTablesInformation; + vtkm::Id cellsOffset = clipStatOffsets.NumberOfCells; + vtkm::Id cellIndicesOffset = clipStatOffsets.NumberOfCellIndices; + vtkm::Id edgeOffset = clipStatOffsets.NumberOfEdges; + vtkm::Id centroidOffset = clipStatOffsets.NumberOfCentroids; + vtkm::Id centroidIndicesOffset = clipStatOffsets.NumberOfCentroidIndices; + + if (caseIndex == CT::GetKeptCellCase()) // kept cell + { + cellMapOutputToInput.Set(cellsOffset, cellId); + shapes.Set(cellsOffset, static_cast(shape.Id)); + offsets.Set(cellsOffset, cellIndicesOffset); + for (vtkm::IdComponent pointId = 0; pointId < points.GetNumberOfComponents(); ++pointId) + { + connectivity.Set(cellIndicesOffset++, pointMapInputToOutput.Get(points[pointId])); + } + } + else // clipped cell { - vtkm::UInt8 cellShape = clippingData.ValueAt(clipIndex++); - vtkm::IdComponent numberOfPoints = clippingData.ValueAt(clipIndex++); - if (cellShape == 0) + vtkm::Id centroidIndex = 0; + + vtkm::Id index = CT::GetCaseIndex(shape.Id, caseIndex); + const vtkm::UInt8 numberOfShapes = CT::ValueAt(index++); + + for (vtkm::IdComponent shapeId = 0; shapeId < numberOfShapes; shapeId++) { - // Case for a new cell point. - - // 1. Output the input cell id for which we need to generate new point. - // 2. Output number of points used for interpolation. - // 3. If vertex - // - Add vertex to connectivity interpolation information. - // 4. If edge - // - Add edge interpolation information for new points. - // - Reverse connectivity map for new points. - // Make an array which has all the elements that need to be used - // for interpolation. - for (vtkm::IdComponent point = 0; point < numberOfPoints; - point++, inCellInterpPointIndex++, clipIndex++) + const vtkm::UInt8 cellShape = CT::ValueAt(index++); + const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); + + if (cellShape != CTI::ST_PNT) // normal cell { - vtkm::IdComponent entry = - static_cast(clippingData.ValueAt(clipIndex)); - inCellInterpolationKeys.Set(inCellInterpPointIndex, workIndex); - if (entry >= 100) - { - inCellInterpolationInfo.Set(inCellInterpPointIndex, points[entry - 100]); - } - else + // Store the cell data + cellMapOutputToInput.Set(cellsOffset, cellId); + shapes.Set(cellsOffset, cellShape); + offsets.Set(cellsOffset++, cellIndicesOffset); + + for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; pointId++, index++) { - internal::ClipTables::EdgeVec edge = clippingData.GetEdge(shape.Id, entry); - VTKM_ASSERT(edge[0] != 255); - VTKM_ASSERT(edge[1] != 255); - EdgeInterpolation ei; - ei.Vertex1 = points[edge[0]]; - ei.Vertex2 = points[edge[1]]; - // For consistency purposes keep the points ordered. - if (ei.Vertex1 > ei.Vertex2) + // Find how many points need to be calculated using edge interpolation. + const vtkm::UInt8 pointIndex = CT::ValueAt(index); + if (pointIndex <= CTI::P7) // Input Point { - this->swap(ei.Vertex1, ei.Vertex2); - this->swap(edge[0], edge[1]); + // We know pt P0 must be > P0 since we already + // assume P0 == 0. This is why we do not + // bother subtracting P0 from pt here. + connectivity.Set(cellIndicesOffset++, + pointMapInputToOutput.Get(points[pointIndex])); + } + else if (/*pointIndex >= CTI::E00 &&*/ pointIndex <= CTI::E11) // Mid-Edge Point + { + connectivity.Set(cellIndicesOffset++, + this->EdgePointsOffset + edgeIndexToUnique.Get(edgeOffset++)); + } + else // pointIndex == CTI::N0 // Centroid Point + { + connectivity.Set(cellIndicesOffset++, centroidIndex); } - ei.Weight = (static_cast(scalars[edge[0]]) - this->Value) / - static_cast(scalars[edge[1]] - scalars[edge[0]]); - - inCellEdgeReverseConnectivity.Set(inCellEdgeInterpIndex, inCellInterpPointIndex); - inCellEdgeInterpolation.Set(inCellEdgeInterpIndex, ei); - inCellEdgeInterpIndex++; } } - } - else - { - // Just a normal cell, generate edge representations, - - // 1. Add cell type to connectivity information. - // 2. If vertex - // - Add vertex to connectivity information. - // 3. If edge point - // - Add edge to edge points - // - Add edge point index to edge point reverse connectivity. - // 4. If cell point - // - Add cell point index to connectivity - // (as there is only one cell point per required cell) - // 5. Store input cell index against current cell for mapping cell data. - connectivityObject.SetCellShape(cellIndex, cellShape); - connectivityObject.SetNumberOfIndices(cellIndex, numberOfPoints); - connectivityObject.SetIndexOffset(cellIndex, connectivityIndex); - for (vtkm::IdComponent point = 0; point < numberOfPoints; point++, clipIndex++) + else // cellShape == CTI::ST_PNT { - vtkm::IdComponent entry = - static_cast(clippingData.ValueAt(clipIndex)); - if (entry == 255) // case of cell point interpolation - { - // Add index of the corresponding cell point. - inCellReverseConnectivity.Set(inCellIndex++, connectivityIndex); - connectivityObject.SetConnectivity(connectivityIndex, inCellPoints); - connectivityIndex++; - } - else if (entry >= 100) // existing vertex - { - pointsOnlyConnectivityIndices.Set(pointsOnlyConnectivityIndicesIndex++, - connectivityIndex); - connectivityObject.SetConnectivity(connectivityIndex++, points[entry - 100]); - } - else // case of a new edge point + // Store the centroid data + centroidIndex = this->CentroidPointsOffset + centroidOffset; + centroidOffsets.Set(centroidOffset++, centroidIndicesOffset); + + for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; pointId++, index++) { - internal::ClipTables::EdgeVec edge = clippingData.GetEdge(shape.Id, entry); - VTKM_ASSERT(edge[0] != 255); - VTKM_ASSERT(edge[1] != 255); - EdgeInterpolation ei; - ei.Vertex1 = points[edge[0]]; - ei.Vertex2 = points[edge[1]]; - // For consistency purposes keep the points ordered. - if (ei.Vertex1 > ei.Vertex2) + // Find how many points need to be calculated using edge interpolation. + const vtkm::UInt8 pointIndex = CT::ValueAt(index); + if (pointIndex <= CTI::P7) // Input Point { - this->swap(ei.Vertex1, ei.Vertex2); - this->swap(edge[0], edge[1]); + // We know pt P0 must be > P0 since we already + // assume P0 == 0. This is why we do not + // bother subtracting P0 from pt here. + centroidConnectivity.Set(centroidIndicesOffset++, + pointMapInputToOutput.Get(points[pointIndex])); + } + else /*pointIndex >= CTI::E00 && pointIndex <= CTI::E11*/ // Mid-Edge Point + { + centroidConnectivity.Set(centroidIndicesOffset++, + this->EdgePointsOffset + + edgeIndexToUnique.Get(edgeOffset++)); } - ei.Weight = (static_cast(scalars[edge[0]]) - this->Value) / - static_cast(scalars[edge[1]] - scalars[edge[0]]); - //Add to set of new edge points - //Add reverse connectivity; - edgePointReverseConnectivity.Set(edgeIndex, connectivityIndex++); - edgePointInterpolation.Set(edgeIndex, ei); - edgeIndex++; } } - cellMapOutputToInput.Set(cellIndex, workIndex); - ++cellIndex; } } } - template - VTKM_EXEC void swap(T& v1, T& v2) const - { - T temp = v1; - v1 = v2; - v2 = temp; - } - private: - vtkm::Float64 Value; + vtkm::Id EdgePointsOffset; + vtkm::Id CentroidPointsOffset; }; - class ScatterEdgeConnectivity : public vtkm::worklet::WorkletMapField - { - public: - VTKM_CONT - ScatterEdgeConnectivity(vtkm::Id edgePointOffset) - : EdgePointOffset(edgePointOffset) - { - } - - using ControlSignature = void(FieldIn sourceValue, - FieldIn destinationIndices, - WholeArrayOut destinationData); - - using ExecutionSignature = void(_1, _2, _3); + Clip() = default; - using InputDomain = _1; - - template - VTKM_EXEC void operator()(vtkm::Id sourceValue, - vtkm::Id destinationIndex, - ConnectivityDataType& destinationData) const - { - destinationData.Set(destinationIndex, (sourceValue + EdgePointOffset)); - } - - private: - vtkm::Id EdgePointOffset; - }; - - class ScatterInCellConnectivity : public vtkm::worklet::WorkletMapField - { - public: - VTKM_CONT - ScatterInCellConnectivity(vtkm::Id inCellPointOffset) - : InCellPointOffset(inCellPointOffset) - { - } - - using ControlSignature = void(FieldIn destinationIndices, WholeArrayOut destinationData); - - using ExecutionSignature = void(_1, _2); - - using InputDomain = _1; - - template - VTKM_EXEC void operator()(vtkm::Id destinationIndex, - ConnectivityDataType& destinationData) const - { - auto sourceValue = destinationData.Get(destinationIndex); - destinationData.Set(destinationIndex, (sourceValue + InCellPointOffset)); - } - - private: - vtkm::Id InCellPointOffset; - }; - - Clip() - : ClipTablesInstance() - , EdgePointsInterpolation() - , InCellInterpolationKeys() - , InCellInterpolationInfo() - , CellMapOutputToInput() - , EdgePointsOffset() - , InCellPointsOffset() - { - } - - template + template vtkm::cont::CellSetExplicit<> Run(const CellSetType& cellSet, const ScalarsArrayHandle& scalars, - vtkm::Float64 value, - bool invert) + vtkm::Float64 value) { + const vtkm::Id numberOfInputPoints = scalars.GetNumberOfValues(); + const vtkm::Id numberOfInputCells = cellSet.GetNumberOfCells(); + + // Create an invoker. vtkm::cont::Invoker invoke; - // Create the required output fields. - vtkm::cont::ArrayHandle clipStats; - vtkm::cont::ArrayHandle clipTableIndices; + // Create an array to store the mask of kept points. + vtkm::cont::ArrayHandle keptPointsMask; + keptPointsMask.Allocate(numberOfInputPoints); - //Send this CellSet to process - ComputeStats statsWorklet(value, invert); - invoke(statsWorklet, cellSet, scalars, this->ClipTablesInstance, clipStats, clipTableIndices); + // Mark the points that are kept. + invoke(MarkKeptPoints(value), scalars, keptPointsMask); - ClipStats zero; - vtkm::cont::ArrayHandle cellSetStats; - ClipStats total = - vtkm::cont::Algorithm::ScanExclusive(clipStats, cellSetStats, ClipStats::SumOp(), zero); - clipStats.ReleaseResources(); + // Create an array to save the caseIndex for each cell. + vtkm::cont::ArrayHandle caseIndices; + caseIndices.Allocate(numberOfInputCells); - vtkm::cont::ArrayHandle shapes; - vtkm::cont::ArrayHandle numberOfIndices; - vtkm::cont::ArrayHandle connectivity; - vtkm::cont::ArrayHandle offsets; - internal::ConnectivityExplicit connectivityObject( - shapes, numberOfIndices, connectivity, offsets, total); + // Create an array to store the statistics of the clip operation. + vtkm::cont::ArrayHandle clipStats; + clipStats.Allocate(numberOfInputCells); - //Begin Process of Constructing the new CellSet. - vtkm::cont::ArrayHandle pointsOnlyConnectivityIndices; - pointsOnlyConnectivityIndices.Allocate(total.NumberOfIndices - total.NumberOfEdgeIndices - - total.NumberOfInCellIndices); + // Create a mask to only process the cells that are clipped, to extract the edges. + vtkm::cont::ArrayHandle clippedMask; - vtkm::cont::ArrayHandle edgePointReverseConnectivity; - edgePointReverseConnectivity.Allocate(total.NumberOfEdgeIndices); - vtkm::cont::ArrayHandle edgeInterpolation; - edgeInterpolation.Allocate(total.NumberOfEdgeIndices); + // Create a mask to only process the kept or clipped cells. + vtkm::cont::ArrayHandle keptOrClippedMask; - vtkm::cont::ArrayHandle cellPointReverseConnectivity; - cellPointReverseConnectivity.Allocate(total.NumberOfInCellIndices); - vtkm::cont::ArrayHandle cellPointEdgeReverseConnectivity; - cellPointEdgeReverseConnectivity.Allocate(total.NumberOfInCellEdgeIndices); - vtkm::cont::ArrayHandle cellPointEdgeInterpolation; - cellPointEdgeInterpolation.Allocate(total.NumberOfInCellEdgeIndices); - - this->InCellInterpolationKeys.Allocate(total.NumberOfInCellInterpPoints); - this->InCellInterpolationInfo.Allocate(total.NumberOfInCellInterpPoints); - this->CellMapOutputToInput.Allocate(total.NumberOfCells); + // Compute the statistics of the clip operation. + invoke(ComputeClipStats(), + cellSet, + keptPointsMask, + clipStats, + clippedMask, + keptOrClippedMask, + caseIndices); + + // Create ScatterCounting on the keptPointsMask. + vtkm::worklet::ScatterCounting scatterCullDiscardedPoints(keptPointsMask, true); + auto pointMapInputToOutput = scatterCullDiscardedPoints.GetInputToOutputMap(); + this->PointMapOutputToInput = scatterCullDiscardedPoints.GetOutputToInputMap(); + keptPointsMask.ReleaseResources(); // Release keptPointsMask since it's no longer needed. + + // Compute the total of clipStats, and convert clipStats to offsets in-place. + const ClipStats total = + vtkm::cont::Algorithm::ScanExclusive(clipStats, clipStats, ClipStats::SumOp(), ClipStats{}); + + // Create an array to store the edge interpolations. + vtkm::cont::ArrayHandle edgeInterpolation; + edgeInterpolation.Allocate(total.NumberOfEdges); - //Send this CellSet to process - GenerateCellSet cellSetWorklet(value); - invoke(cellSetWorklet, + // Extract the edges. + invoke(ExtractEdges(value), + vtkm::worklet::MaskSelect(clippedMask), cellSet, scalars, - clipTableIndices, - cellSetStats, - this->ClipTablesInstance, - connectivityObject, - pointsOnlyConnectivityIndices, - edgePointReverseConnectivity, - edgeInterpolation, - cellPointReverseConnectivity, - cellPointEdgeReverseConnectivity, - cellPointEdgeInterpolation, - this->InCellInterpolationKeys, - this->InCellInterpolationInfo, - this->CellMapOutputToInput); - this->InterpolationKeysBuilt = false; - - clipTableIndices.ReleaseResources(); - cellSetStats.ReleaseResources(); - - // extract only the used points from the input - { - vtkm::cont::ArrayHandle pointMask; - pointMask.AllocateAndFill(scalars.GetNumberOfValues(), 0); - - auto pointsOnlyConnectivity = - vtkm::cont::make_ArrayHandlePermutation(pointsOnlyConnectivityIndices, connectivity); - - invoke( - vtkm::worklet::RemoveUnusedPoints::GeneratePointMask{}, pointsOnlyConnectivity, pointMask); - - vtkm::worklet::ScatterCounting scatter(pointMask, true); - auto pointMapInputToOutput = scatter.GetInputToOutputMap(); - this->PointMapOutputToInput = scatter.GetOutputToInputMap(); - pointMask.ReleaseResources(); - - invoke(vtkm::worklet::RemoveUnusedPoints::TransformPointIndices{}, - pointsOnlyConnectivity, - pointMapInputToOutput, - pointsOnlyConnectivity); - - pointsOnlyConnectivityIndices.ReleaseResources(); - - // We want to find the entries in `InCellInterpolationInfo` that point to exisiting points. - // `cellPointEdgeReverseConnectivity` map to entries that point to edges. - vtkm::cont::ArrayHandle stencil; - stencil.AllocateAndFill(this->InCellInterpolationInfo.GetNumberOfValues(), 1); - auto edgeOnlyStencilEntries = - vtkm::cont::make_ArrayHandlePermutation(cellPointEdgeReverseConnectivity, stencil); - vtkm::cont::Algorithm::Fill(edgeOnlyStencilEntries, vtkm::UInt8{}); - vtkm::cont::ArrayHandle idxsToPoints; - vtkm::cont::Algorithm::CopyIf( - vtkm::cont::ArrayHandleIndex(this->InCellInterpolationInfo.GetNumberOfValues()), - stencil, - idxsToPoints); - stencil.ReleaseResources(); - - // Remap the point indices in `InCellInterpolationInfo`, to the used-only point indices - // computed above. - // This only works if the points needed for interpolating centroids are included in the - // `connectivity` array. This has been verified to be true for all cases in the clip tables. - auto inCellInterpolationInfoPointsOnly = - vtkm::cont::make_ArrayHandlePermutation(idxsToPoints, this->InCellInterpolationInfo); - invoke(vtkm::worklet::RemoveUnusedPoints::TransformPointIndices{}, - inCellInterpolationInfoPointsOnly, - pointMapInputToOutput, - inCellInterpolationInfoPointsOnly); - } - - // Get unique EdgeInterpolation : unique edge points. - // LowerBound for edgeInterpolation : get index into new edge points array. - // LowerBound for cellPointEdgeInterpolation : get index into new edge points array. - vtkm::cont::Algorithm::SortByKey( - edgeInterpolation, edgePointReverseConnectivity, EdgeInterpolation::LessThanOp()); + clipStats, // clipStatOffsets + caseIndices, + edgeInterpolation); + clippedMask.ReleaseResources(); // Release clippedMask since it's no longer needed. + + // Sort the edge interpolations. + vtkm::cont::Algorithm::Sort(edgeInterpolation, EdgeInterpolation::LessThanOp()); + // Copy the edge interpolations to the output. vtkm::cont::Algorithm::Copy(edgeInterpolation, this->EdgePointsInterpolation); + // Remove duplicates. vtkm::cont::Algorithm::Unique(this->EdgePointsInterpolation, EdgeInterpolation::EqualToOp()); - + // Get the edge index to unique index. vtkm::cont::ArrayHandle edgeInterpolationIndexToUnique; vtkm::cont::Algorithm::LowerBounds(this->EdgePointsInterpolation, edgeInterpolation, edgeInterpolationIndexToUnique, EdgeInterpolation::LessThanOp()); - edgeInterpolation.ReleaseResources(); - - // This only works if the edges in `cellPointEdgeInterpolation` also exist in - // `EdgePointsInterpolation`. This has been verified to be true for all cases in the clip - // tables. - vtkm::cont::ArrayHandle cellInterpolationIndexToUnique; - vtkm::cont::Algorithm::LowerBounds(this->EdgePointsInterpolation, - cellPointEdgeInterpolation, - cellInterpolationIndexToUnique, - EdgeInterpolation::LessThanOp()); - cellPointEdgeInterpolation.ReleaseResources(); + edgeInterpolation.ReleaseResources(); // Release edgeInterpolation since it's no longer needed. + + // Get the number of kept points, unique edge points, centroids, and output points. + const vtkm::Id numberOfKeptPoints = this->PointMapOutputToInput.GetNumberOfValues(); + const vtkm::Id numberOfUniqueEdgePoints = this->EdgePointsInterpolation.GetNumberOfValues(); + const vtkm::Id numberOfCentroids = total.NumberOfCentroids; + const vtkm::Id numberOfOutputPoints = + numberOfKeptPoints + numberOfUniqueEdgePoints + numberOfCentroids; + // Create the offsets to write the point indices. + this->EdgePointsOffset = numberOfKeptPoints; + this->CentroidPointsOffset = this->EdgePointsOffset + numberOfUniqueEdgePoints; + + // Allocate the centroids. + vtkm::cont::ArrayHandle centroidOffsets; + centroidOffsets.Allocate(numberOfCentroids + 1); + vtkm::cont::ArrayHandle centroidConnectivity; + centroidConnectivity.Allocate(total.NumberOfCentroidIndices); + this->CentroidPointsInterpolation = + vtkm::cont::make_ArrayHandleGroupVecVariable(centroidConnectivity, centroidOffsets); + + // Allocate the output cell set. + vtkm::cont::ArrayHandle shapes; + shapes.Allocate(total.NumberOfCells); + vtkm::cont::ArrayHandle offsets; + offsets.Allocate(total.NumberOfCells + 1); + vtkm::cont::ArrayHandle connectivity; + connectivity.Allocate(total.NumberOfCellIndices); - this->EdgePointsOffset = this->PointMapOutputToInput.GetNumberOfValues(); - this->InCellPointsOffset = - this->EdgePointsOffset + this->EdgePointsInterpolation.GetNumberOfValues(); + // Allocate Cell Map output to Input. + this->CellMapOutputToInput.Allocate(total.NumberOfCells); - // Scatter these values into the connectivity array, - // scatter indices are given in reverse connectivity. - ScatterEdgeConnectivity scatterEdgePointConnectivity(this->EdgePointsOffset); - invoke(scatterEdgePointConnectivity, + // Generate the output cell set. + invoke(GenerateCellSet(this->EdgePointsOffset, this->CentroidPointsOffset), + vtkm::worklet::MaskSelect(keptOrClippedMask), + cellSet, + caseIndices, + clipStats, // clipStatOffsets + pointMapInputToOutput, edgeInterpolationIndexToUnique, - edgePointReverseConnectivity, + centroidOffsets, + centroidConnectivity, + this->CellMapOutputToInput, + shapes, + offsets, connectivity); - invoke(scatterEdgePointConnectivity, - cellInterpolationIndexToUnique, - cellPointEdgeReverseConnectivity, - this->InCellInterpolationInfo); + // All no longer needed arrays will be released at the end of this function. - // Add offset in connectivity of all new in-cell points. - ScatterInCellConnectivity scatterInCellPointConnectivity(this->InCellPointsOffset); - invoke(scatterInCellPointConnectivity, cellPointReverseConnectivity, connectivity); + // Set the last offset to the size of the connectivity. + vtkm::cont::ArraySetValue(total.NumberOfCells, total.NumberOfCellIndices, offsets); + vtkm::cont::ArraySetValue(numberOfCentroids, total.NumberOfCentroidIndices, centroidOffsets); vtkm::cont::CellSetExplicit<> output; - vtkm::Id numberOfPoints = this->PointMapOutputToInput.GetNumberOfValues() + - this->EdgePointsInterpolation.GetNumberOfValues() + total.NumberOfInCellPoints; - - vtkm::cont::ConvertNumComponentsToOffsets(numberOfIndices, offsets); - - output.Fill(numberOfPoints, shapes, connectivity, offsets); + output.Fill(numberOfOutputPoints, shapes, connectivity, offsets); return output; } - template + template class ClipWithImplicitFunction { public: @@ -775,13 +567,11 @@ public: const CellSetType& cellSet, const ImplicitFunction& function, vtkm::Float64 offset, - bool invert, vtkm::cont::CellSetExplicit<>* result) : Clipper(clipper) , CellSet(&cellSet) , Function(function) , Offset(offset) - , Invert(invert) , Result(result) { } @@ -796,7 +586,9 @@ public: clipScalars(handle, this->Function); // Clip at locations where the implicit function evaluates to `Offset` - *this->Result = this->Clipper->Run(*this->CellSet, clipScalars, this->Offset, this->Invert); + *this->Result = Invert + ? this->Clipper->template Run(*this->CellSet, clipScalars, this->Offset) + : this->Clipper->template Run(*this->CellSet, clipScalars, this->Offset); } private: @@ -804,33 +596,30 @@ public: const CellSetType* CellSet; ImplicitFunction Function; vtkm::Float64 Offset; - bool Invert; vtkm::cont::CellSetExplicit<>* Result; }; - template + template vtkm::cont::CellSetExplicit<> Run(const CellSetType& cellSet, const ImplicitFunction& clipFunction, vtkm::Float64 offset, - const vtkm::cont::CoordinateSystem& coords, - bool invert) + const vtkm::cont::CoordinateSystem& coords) { vtkm::cont::CellSetExplicit<> output; - ClipWithImplicitFunction clip( - this, cellSet, clipFunction, offset, invert, &output); + ClipWithImplicitFunction clip( + this, cellSet, clipFunction, offset, &output); CastAndCall(coords, clip); return output; } - template + template vtkm::cont::CellSetExplicit<> Run(const CellSetType& cellSet, const ImplicitFunction& clipFunction, - const vtkm::cont::CoordinateSystem& coords, - bool invert) + const vtkm::cont::CoordinateSystem& coords) { - return this->Run(cellSet, clipFunction, 0.0, coords, invert); + return this->Run(cellSet, clipFunction, 0.0, coords); } struct PerformEdgeInterpolations : public vtkm::worklet::WorkletMapField @@ -845,8 +634,8 @@ public: const FieldPortal& originalField, T& output) const { - T v1 = originalField.Get(edgeInterp.Vertex1); - T v2 = originalField.Get(edgeInterp.Vertex2); + const T v1 = originalField.Get(edgeInterp.Vertex1); + const T v2 = originalField.Get(edgeInterp.Vertex2); // Interpolate per-vertex because some vec-like objects do not allow intermediate variables using VTraits = vtkm::VecTraits; @@ -856,37 +645,42 @@ public: for (vtkm::IdComponent component = 0; component < VTraits::GetNumberOfComponents(output); ++component) { - CType c1 = VTraits::GetComponent(v1, component); - CType c2 = VTraits::GetComponent(v2, component); - CType o = static_cast(((c1 - c2) * edgeInterp.Weight) + c1); + const CType c1 = VTraits::GetComponent(v1, component); + const CType c2 = VTraits::GetComponent(v2, component); + const CType o = static_cast(((c1 - c2) * edgeInterp.Weight) + c1); VTraits::SetComponent(output, component, o); } } }; - struct PerformInCellInterpolations : public vtkm::worklet::WorkletReduceByKey + struct PerformCentroidInterpolations : public vtkm::worklet::WorkletMapField { - using ControlSignature = void(KeysIn keys, ValuesIn toReduce, ReducedValuesOut centroids); - using ExecutionSignature = void(_2, _3); + using ControlSignature = void(FieldIn centroidInterpolation, + WholeArrayIn outputField, + FieldOut output); + using ExecutionSignature = void(_1, _2, _3); - template - VTKM_EXEC void operator()(const MappedValueVecType& toReduce, MappedValueType& centroid) const + template + VTKM_EXEC void operator()(const CentroidInterpolation& centroid, + const OutputFieldArray& outputField, + OutputFieldValue& output) const { - const vtkm::IdComponent numValues = toReduce.GetNumberOfComponents(); + const vtkm::IdComponent numValues = centroid.GetNumberOfComponents(); // Interpolate per-vertex because some vec-like objects do not allow intermediate variables - using VTraits = vtkm::VecTraits; + using VTraits = vtkm::VecTraits; using CType = typename VTraits::ComponentType; - for (vtkm::IdComponent component = 0; component < VTraits::GetNumberOfComponents(centroid); + for (vtkm::IdComponent component = 0; component < VTraits::GetNumberOfComponents(output); ++component) { - CType sum = VTraits::GetComponent(toReduce[0], component); - for (vtkm::IdComponent reduceI = 1; reduceI < numValues; ++reduceI) + CType sum = VTraits::GetComponent(outputField.Get(centroid[0]), component); + for (vtkm::IdComponent i = 1; i < numValues; ++i) { - // static_cast is for when MappedValueType is a small int that gets promoted to int32. - sum = static_cast(sum + VTraits::GetComponent(toReduce[reduceI], component)); + // static_cast is for when OutputFieldValue is a small int that gets promoted to int32. + sum = static_cast(sum + + VTraits::GetComponent(outputField.Get(centroid[i]), component)); } - VTraits::SetComponent(centroid, component, static_cast(sum / numValues)); + VTraits::SetComponent(output, component, static_cast(sum / numValues)); } } }; @@ -894,40 +688,32 @@ public: template void ProcessPointField(const InputType& input, OutputType& output) { - if (!this->InterpolationKeysBuilt) - { - this->InterpolationKeys.BuildArrays(this->InCellInterpolationKeys, KeysSortType::Unstable); - } + const vtkm::Id numberOfKeptPoints = this->PointMapOutputToInput.GetNumberOfValues(); + const vtkm::Id numberOfEdgePoints = this->EdgePointsInterpolation.GetNumberOfValues(); + const vtkm::Id numberOfCentroidPoints = this->CentroidPointsInterpolation.GetNumberOfValues(); - vtkm::Id numberOfVertexPoints = this->PointMapOutputToInput.GetNumberOfValues(); - vtkm::Id numberOfEdgePoints = this->EdgePointsInterpolation.GetNumberOfValues(); - vtkm::Id numberOfInCellPoints = this->InterpolationKeys.GetUniqueKeys().GetNumberOfValues(); - - output.Allocate(numberOfVertexPoints + numberOfEdgePoints + numberOfInCellPoints); + output.Allocate(numberOfKeptPoints + numberOfEdgePoints + numberOfCentroidPoints); // Copy over the original values that are still part of the output. vtkm::cont::Algorithm::CopySubRange( vtkm::cont::make_ArrayHandlePermutation(this->PointMapOutputToInput, input), 0, - numberOfVertexPoints, + numberOfKeptPoints, output); // Interpolate all new points that lie on edges of the input mesh. vtkm::cont::Invoker invoke; - invoke(PerformEdgeInterpolations{}, + invoke(PerformEdgeInterpolations(), this->EdgePointsInterpolation, input, - vtkm::cont::make_ArrayHandleView(output, numberOfVertexPoints, numberOfEdgePoints)); - - // Perform a gather on the output to get all the required values for calculation of centroids - // using the interpolation info array. - auto toReduceValues = - vtkm::cont::make_ArrayHandlePermutation(this->InCellInterpolationInfo, output); - invoke(PerformInCellInterpolations{}, - this->InterpolationKeys, - toReduceValues, - vtkm::cont::make_ArrayHandleView( - output, numberOfVertexPoints + numberOfEdgePoints, numberOfInCellPoints)); + vtkm::cont::make_ArrayHandleView(output, this->EdgePointsOffset, numberOfEdgePoints)); + + // interpolate all new points that lie as centroids of input meshes + invoke( + PerformCentroidInterpolations(), + this->CentroidPointsInterpolation, + output, + vtkm::cont::make_ArrayHandleView(output, this->CentroidPointsOffset, numberOfCentroidPoints)); } vtkm::cont::ArrayHandle GetCellMapOutputToInput() const @@ -936,16 +722,14 @@ public: } private: - internal::ClipTables ClipTablesInstance; + vtkm::cont::ArrayHandle PointMapOutputToInput; vtkm::cont::ArrayHandle EdgePointsInterpolation; - vtkm::cont::ArrayHandle InCellInterpolationKeys; - vtkm::cont::ArrayHandle InCellInterpolationInfo; + vtkm::cont::ArrayHandleGroupVecVariable, + vtkm::cont::ArrayHandle> + CentroidPointsInterpolation; vtkm::cont::ArrayHandle CellMapOutputToInput; - vtkm::cont::ArrayHandle PointMapOutputToInput; - vtkm::Id EdgePointsOffset; - vtkm::Id InCellPointsOffset; - vtkm::worklet::Keys InterpolationKeys; - bool InterpolationKeysBuilt = false; + vtkm::Id EdgePointsOffset = 0; + vtkm::Id CentroidPointsOffset = 0; }; } } // namespace vtkm::worklet diff --git a/vtkm/filter/contour/worklet/MIR.h b/vtkm/filter/contour/worklet/MIR.h index 4ceea7af09..376039ec2c 100644 --- a/vtkm/filter/contour/worklet/MIR.h +++ b/vtkm/filter/contour/worklet/MIR.h @@ -528,7 +528,7 @@ public: } else { - internal::ClipTables::EdgeVec edge = + internal::ClipTablesBase::EdgeVec edge = MIRData.GetEdge(shape.Id, entry - MIRCases::EA, pointcount); if (edge[0] == 255 || edge[1] == 255) { @@ -592,7 +592,7 @@ public: } else // case of a new edge point { - internal::ClipTables::EdgeVec edge = + internal::ClipTablesBase::EdgeVec edge = MIRData.GetEdge(shape.Id, entry - MIRCases::EA, pointcount); if (edge[0] == 255 || edge[1] == 255) { diff --git a/vtkm/filter/contour/worklet/clip/ClipTables.h b/vtkm/filter/contour/worklet/clip/ClipTables.h index 7860ae9a69..1c46f857d1 100644 --- a/vtkm/filter/contour/worklet/clip/ClipTables.h +++ b/vtkm/filter/contour/worklet/clip/ClipTables.h @@ -13,2536 +13,5243 @@ #include #include -#include -#include - namespace vtkm { namespace worklet { namespace internal { -// clang-format off -// table format: -// ncells, {{celltype, nverts, {edge/verts(>=100), ...}}, ...}, \n -// values < 100 represent edges where the corresponding vertex lies -// values >= 100 reresent existing vertices of the input cell (vertex = value - 100) -VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 ClipTablesData[] = { -// Vtx -1, // Case : 0 -1, 1, 100, -0, // Case : 1 -// Lin -1, // Case : 0 -3, 2, 100, 101, -1, // Case : 1 -3, 2, 0, 101, -1, // Case : 2 -3, 2, 0, 100, -0, // Case : 3 -// Tri -1, // Case : 0 -5, 3, 100, 101, 102, -1, // Case : 1 -9, 4, 101, 102, 2, 0, -1, // Case : 2 -9, 4, 102, 100, 0, 1, -1, // Case : 3 -5, 3, 2, 1, 102, -1, // Case : 4 -9, 4, 100, 101, 1, 2, -1, // Case : 5 -5, 3, 1, 0, 101, -1, // Case : 6 -5, 3, 0, 2, 100, -0, // Case : 7 -// Qua -1, // Case : 0 -9, 4, 100, 101, 102, 103, -2, // Case : 1 -9, 4, 3, 0, 101, 103, -5, 3, 103, 101, 102, -2, // Case : 2 -9, 4, 0, 1, 102, 100, -5, 3, 100, 102, 103, -1, // Case : 3 -9, 4, 3, 1, 102, 103, -2, // Case : 4 -9, 4, 1, 2, 103, 101, -5, 3, 101, 103, 100, -2, // Case : 5 -9, 4, 103, 101, 1, 2, -9, 4, 101, 103, 3, 0, -1, // Case : 6 -9, 4, 0, 2, 103, 100, -1, // Case : 7 -5, 3, 3, 2, 103, -2, // Case : 8 -9, 4, 2, 3, 100, 102, -5, 3, 102, 100, 101, -1, // Case : 9 -9, 4, 2, 0, 101, 102, -2, // Case : 10 -5, 3, 0, 3, 100, -5, 3, 2, 1, 102, -1, // Case : 11 -5, 3, 2, 1, 102, -1, // Case : 12 -9, 4, 1, 3, 100, 101, -1, // Case : 13 -5, 3, 1, 0, 101, -1, // Case : 14 -5, 3, 0, 3, 100, -0, // Case : 15 -// Tet -1, // Case : 0 -10, 4, 100, 101, 102, 103, -1, // Case : 1 -13, 6, 0, 3, 2, 101, 103, 102, -1, // Case : 2 -13, 6, 100, 103, 102, 0, 4, 1, -1, // Case : 3 -13, 6, 103, 3, 4, 102, 2, 1, -1, // Case : 4 -13, 6, 2, 5, 1, 100, 103, 101, -1, // Case : 5 -13, 6, 101, 0, 1, 103, 3, 5, -1, // Case : 6 -13, 6, 103, 4, 5, 100, 0, 2, -1, // Case : 7 -10, 4, 3, 4, 5, 103, -1, // Case : 8 -13, 6, 100, 102, 101, 3, 5, 4, -1, // Case : 9 -13, 6, 102, 2, 5, 101, 0, 4, -1, // Case : 10 -13, 6, 100, 0, 3, 102, 1, 5, -1, // Case : 11 -10, 4, 2, 5, 1, 102, -1, // Case : 12 -13, 6, 101, 1, 4, 100, 2, 3, -1, // Case : 13 -10, 4, 0, 1, 4, 101, -1, // Case : 14 -10, 4, 0, 3, 2, 100, -0, // Case : 15 -// Hex -1, // Case : 0 -12, 8, 100, 101, 102, 103, 104, 105, 106, 107, -9, // Case : 1 -0, 7, 101, 102, 103, 104, 105, 106, 107, -13, 6, 101, 103, 104, 0, 3, 8, -10, 4, 101, 103, 104, 255, -10, 4, 101, 102, 103, 255, -14, 5, 106, 107, 103, 102, 255, -14, 5, 105, 106, 102, 101, 255, -14, 5, 104, 107, 106, 105, 255, -10, 4, 103, 107, 104, 255, -10, 4, 104, 105, 101, 255, -9, // Case : 2 -0, 7, 105, 104, 100, 102, 106, 107, 103, -13, 6, 9, 0, 1, 105, 100, 102, -10, 4, 105, 102, 100, 255, -10, 4, 105, 100, 104, 255, -14, 5, 107, 104, 100, 103, 255, -14, 5, 106, 105, 104, 107, 255, -14, 5, 102, 106, 107, 103, 255, -10, 4, 100, 102, 103, 255, -10, 4, 102, 105, 106, 255, -2, // Case : 3 -12, 8, 1, 102, 103, 3, 9, 105, 104, 8, -13, 6, 102, 106, 105, 103, 107, 104, -9, // Case : 4 -0, 7, 106, 105, 101, 103, 107, 104, 100, -13, 6, 11, 1, 2, 106, 101, 103, -10, 4, 106, 103, 101, 255, -10, 4, 106, 101, 105, 255, -14, 5, 104, 105, 101, 100, 255, -14, 5, 107, 106, 105, 104, 255, -14, 5, 103, 107, 104, 100, 255, -10, 4, 101, 103, 100, 255, -10, 4, 103, 106, 107, 255, -12, // Case : 5 -0, 2, 8, 11, -14, 5, 104, 107, 106, 105, 255, -10, 4, 105, 106, 101, 255, -10, 4, 104, 105, 101, 255, -10, 4, 103, 107, 104, 255, -10, 4, 106, 107, 103, 255, -14, 5, 106, 103, 2, 11, 255, -14, 5, 101, 106, 11, 1, 255, -10, 4, 101, 1, 0, 255, -14, 5, 104, 101, 0, 8, 255, -14, 5, 104, 8, 3, 103, 255, -10, 4, 103, 3, 2, 255, -2, // Case : 6 -12, 8, 2, 103, 100, 0, 11, 106, 105, 9, -13, 6, 103, 107, 106, 100, 104, 105, -9, // Case : 7 -0, 5, 8, 9, 3, 2, 11, -14, 5, 104, 107, 106, 105, 255, -10, 4, 106, 103, 255, 107, -14, 5, 105, 106, 11, 9, 255, -14, 5, 8, 104, 105, 9, 255, -10, 4, 103, 107, 104, 255, -14, 5, 103, 104, 8, 3, 255, -10, 4, 103, 3, 2, 255, -14, 5, 11, 106, 103, 2, 255, -9, // Case : 8 -0, 7, 102, 101, 100, 107, 106, 105, 104, -13, 6, 2, 3, 10, 102, 100, 107, -10, 4, 102, 107, 100, 255, -10, 4, 102, 100, 101, 255, -14, 5, 105, 101, 100, 104, 255, -14, 5, 106, 102, 101, 105, 255, -14, 5, 107, 106, 105, 104, 255, -10, 4, 100, 107, 104, 255, -10, 4, 107, 102, 106, 255, -2, // Case : 9 -12, 8, 10, 107, 104, 8, 2, 102, 101, 0, -13, 6, 107, 106, 102, 104, 105, 101, -12, // Case : 10 -0, 2, 10, 9, -14, 5, 107, 106, 105, 104, 255, -10, 4, 106, 102, 105, 255, -10, 4, 107, 102, 106, 255, -10, 4, 100, 107, 104, 255, -10, 4, 105, 100, 104, 255, -14, 5, 105, 9, 0, 100, 255, -14, 5, 102, 1, 9, 105, 255, -10, 4, 102, 2, 1, 255, -14, 5, 107, 10, 2, 102, 255, -14, 5, 107, 100, 3, 10, 255, -10, 4, 100, 0, 3, 255, -9, // Case : 11 -0, 5, 9, 8, 1, 2, 10, -14, 5, 105, 104, 107, 106, 255, -10, 4, 107, 255, 102, 106, -14, 5, 104, 8, 10, 107, 255, -14, 5, 9, 8, 104, 105, 255, -10, 4, 102, 105, 106, 255, -14, 5, 102, 1, 9, 105, 255, -10, 4, 102, 2, 1, 255, -14, 5, 10, 2, 102, 107, 255, -2, // Case : 12 -12, 8, 11, 106, 107, 10, 1, 101, 100, 3, -13, 6, 100, 104, 107, 101, 105, 106, -9, // Case : 13 -0, 5, 8, 10, 0, 1, 11, -14, 5, 104, 107, 106, 105, 255, -10, 4, 106, 255, 101, 105, -14, 5, 107, 10, 11, 106, 255, -14, 5, 8, 10, 107, 104, 255, -10, 4, 101, 104, 105, 255, -14, 5, 101, 0, 8, 104, 255, -10, 4, 101, 1, 0, 255, -14, 5, 11, 1, 101, 106, 255, -9, // Case : 14 -0, 5, 10, 11, 3, 0, 9, -14, 5, 107, 106, 105, 104, 255, -10, 4, 105, 255, 100, 104, -14, 5, 106, 11, 9, 105, 255, -14, 5, 10, 11, 106, 107, 255, -10, 4, 100, 107, 104, 255, -14, 5, 100, 3, 10, 107, 255, -10, 4, 100, 0, 3, 255, -14, 5, 9, 0, 100, 105, 255, -1, // Case : 15 -12, 8, 8, 9, 11, 10, 104, 105, 106, 107, -9, // Case : 16 -0, 7, 105, 101, 100, 107, 106, 102, 103, -13, 6, 105, 100, 107, 4, 8, 7, -10, 4, 105, 100, 107, 255, -10, 4, 105, 101, 100, 255, -14, 5, 102, 103, 100, 101, 255, -14, 5, 106, 102, 101, 105, 255, -14, 5, 107, 103, 102, 106, 255, -10, 4, 100, 103, 107, 255, -10, 4, 107, 106, 105, 255, -2, // Case : 17 -12, 8, 4, 105, 101, 0, 7, 107, 103, 3, -13, 6, 103, 102, 101, 107, 106, 105, -12, // Case : 18 -0, 2, 7, 1, -14, 5, 107, 103, 102, 106, 255, -10, 4, 106, 102, 105, 255, -10, 4, 107, 106, 105, 255, -10, 4, 100, 103, 107, 255, -10, 4, 102, 103, 100, 255, -14, 5, 102, 100, 0, 1, 255, -14, 5, 105, 102, 1, 9, 255, -10, 4, 105, 9, 4, 255, -14, 5, 107, 105, 4, 7, 255, -14, 5, 107, 7, 8, 100, 255, -10, 4, 100, 8, 0, 255, -9, // Case : 19 -0, 5, 1, 3, 9, 4, 7, -14, 5, 102, 106, 107, 103, 255, -10, 4, 107, 105, 255, 106, -14, 5, 103, 107, 7, 3, 255, -14, 5, 1, 102, 103, 3, 255, -10, 4, 105, 106, 102, 255, -14, 5, 105, 102, 1, 9, 255, -10, 4, 105, 9, 4, 255, -14, 5, 7, 107, 105, 4, 255, -6, // Case : 20 -13, 6, 1, 2, 11, 101, 103, 106, -13, 6, 100, 107, 105, 8, 7, 4, -10, 4, 103, 101, 106, 107, -10, 4, 105, 107, 106, 101, -10, 4, 100, 105, 101, 107, -10, 4, 103, 107, 100, 101, -11, // Case : 21 -0, 4, 4, 7, 11, 11, -14, 5, 106, 103, 2, 11, 255, -10, 4, 2, 103, 3, 255, -14, 5, 107, 7, 3, 103, 255, -10, 4, 106, 107, 103, 255, -10, 4, 101, 1, 0, 255, -10, 4, 105, 106, 101, 255, -14, 5, 101, 106, 11, 1, 255, -10, 4, 105, 107, 106, 255, -14, 5, 105, 4, 7, 107, 255, -14, 5, 105, 101, 0, 4, 255, -11, // Case : 22 -0, 4, 11, 2, 7, 7, -14, 5, 107, 7, 8, 100, 255, -10, 4, 8, 0, 100, 255, -14, 5, 103, 100, 0, 2, 255, -10, 4, 107, 100, 103, 255, -10, 4, 105, 9, 4, 255, -10, 4, 106, 105, 107, 255, -14, 5, 105, 4, 7, 107, 255, -10, 4, 106, 107, 103, 255, -14, 5, 106, 103, 2, 11, 255, -14, 5, 106, 11, 9, 105, 255, -9, // Case : 23 -0, 6, 3, 2, 11, 9, 4, 7, -10, 4, 106, 105, 107, 255, -14, 5, 106, 11, 9, 105, 255, -10, 4, 103, 106, 107, 255, -14, 5, 103, 2, 11, 106, 255, -10, 4, 3, 2, 103, 255, -14, 5, 103, 107, 7, 3, 255, -14, 5, 7, 107, 105, 4, 255, -10, 4, 105, 9, 4, 255, -12, // Case : 24 -0, 2, 2, 4, -14, 5, 102, 101, 105, 106, 255, -10, 4, 106, 105, 107, 255, -10, 4, 102, 106, 107, 255, -10, 4, 100, 101, 102, 255, -10, 4, 105, 101, 100, 255, -14, 5, 105, 100, 8, 4, 255, -14, 5, 107, 105, 4, 7, 255, -10, 4, 107, 7, 10, 255, -14, 5, 102, 107, 10, 2, 255, -14, 5, 102, 2, 3, 100, 255, -10, 4, 100, 3, 8, 255, -9, // Case : 25 -0, 5, 4, 0, 7, 10, 2, -14, 5, 105, 106, 102, 101, 255, -10, 4, 102, 107, 255, 106, -14, 5, 101, 102, 2, 0, 255, -14, 5, 4, 105, 101, 0, 255, -10, 4, 107, 106, 105, 255, -14, 5, 107, 105, 4, 7, 255, -10, 4, 107, 7, 10, 255, -14, 5, 2, 102, 107, 10, 255, -6, // Case : 26 -10, 4, 100, 0, 3, 8, -10, 4, 105, 107, 106, 102, -14, 5, 2, 102, 107, 10, 7, -14, 5, 1, 9, 105, 102, 4, -14, 5, 107, 105, 4, 7, 102, -14, 5, 7, 4, 1, 2, 102, -5, // Case : 27 -10, 4, 105, 107, 106, 102, -14, 5, 2, 102, 107, 10, 7, -14, 5, 1, 9, 105, 102, 4, -14, 5, 107, 105, 4, 7, 102, -14, 5, 7, 4, 1, 2, 102, -11, // Case : 28 -0, 4, 11, 1, 4, 4, -14, 5, 105, 100, 8, 4, 255, -10, 4, 8, 100, 3, 255, -14, 5, 101, 1, 3, 100, 255, -10, 4, 105, 101, 100, 255, -10, 4, 107, 7, 10, 255, -10, 4, 106, 105, 107, 255, -14, 5, 107, 105, 4, 7, 255, -10, 4, 106, 101, 105, 255, -14, 5, 106, 11, 1, 101, 255, -14, 5, 106, 107, 10, 11, 255, -9, // Case : 29 -0, 6, 0, 1, 11, 10, 7, 4, -10, 4, 106, 105, 107, 255, -14, 5, 106, 107, 10, 11, 255, -10, 4, 101, 105, 106, 255, -14, 5, 101, 106, 11, 1, 255, -10, 4, 0, 101, 1, 255, -14, 5, 101, 0, 4, 105, 255, -14, 5, 4, 7, 107, 105, 255, -10, 4, 107, 7, 10, 255, -8, // Case : 30 -0, 5, 11, 9, 10, 7, 4, -10, 4, 100, 0, 3, 8, -14, 5, 105, 106, 11, 9, 255, -14, 5, 106, 107, 10, 11, 255, -10, 4, 107, 7, 10, 255, -10, 4, 106, 105, 107, 255, -14, 5, 107, 105, 4, 7, 255, -10, 4, 105, 9, 4, 255, -7, // Case : 31 -0, 5, 9, 11, 10, 4, 7, -14, 5, 106, 107, 10, 11, 255, -10, 4, 107, 7, 10, 255, -14, 5, 105, 106, 11, 9, 255, -10, 4, 4, 105, 9, 255, -14, 5, 7, 107, 105, 4, 255, -10, 4, 107, 106, 105, 255, -9, // Case : 32 -0, 7, 106, 102, 101, 104, 107, 103, 100, -13, 6, 106, 101, 104, 5, 9, 4, -10, 4, 106, 101, 104, 255, -10, 4, 106, 102, 101, 255, -14, 5, 103, 100, 101, 102, 255, -14, 5, 107, 103, 102, 106, 255, -14, 5, 104, 100, 103, 107, 255, -10, 4, 101, 100, 104, 255, -10, 4, 104, 107, 106, 255, -12, // Case : 33 -0, 2, 3, 5, -14, 5, 103, 102, 106, 107, 255, -10, 4, 102, 101, 106, 255, -10, 4, 103, 101, 102, 255, -10, 4, 104, 103, 107, 255, -10, 4, 106, 104, 107, 255, -14, 5, 106, 5, 4, 104, 255, -14, 5, 101, 9, 5, 106, 255, -10, 4, 101, 0, 9, 255, -14, 5, 103, 3, 0, 101, 255, -14, 5, 103, 104, 8, 3, 255, -10, 4, 104, 4, 8, 255, -2, // Case : 34 -12, 8, 5, 106, 102, 1, 4, 104, 100, 0, -13, 6, 100, 103, 102, 104, 107, 106, -9, // Case : 35 -0, 5, 3, 1, 8, 4, 5, -14, 5, 103, 102, 106, 107, 255, -10, 4, 106, 255, 104, 107, -14, 5, 102, 1, 5, 106, 255, -14, 5, 3, 1, 102, 103, 255, -10, 4, 104, 103, 107, 255, -14, 5, 104, 8, 3, 103, 255, -10, 4, 104, 4, 8, 255, -14, 5, 5, 4, 104, 106, 255, -12, // Case : 36 -0, 2, 2, 4, -14, 5, 103, 107, 104, 100, 255, -10, 4, 107, 106, 104, 255, -10, 4, 103, 106, 107, 255, -10, 4, 101, 103, 100, 255, -10, 4, 104, 101, 100, 255, -14, 5, 104, 4, 9, 101, 255, -14, 5, 106, 5, 4, 104, 255, -10, 4, 106, 11, 5, 255, -14, 5, 103, 2, 11, 106, 255, -14, 5, 103, 101, 1, 2, 255, -10, 4, 101, 9, 1, 255, -6, // Case : 37 -10, 4, 101, 0, 9, 1, -10, 4, 106, 107, 103, 104, -14, 5, 8, 3, 103, 104, 2, -14, 5, 4, 104, 106, 5, 11, -14, 5, 103, 2, 11, 106, 104, -14, 5, 2, 8, 4, 11, 104, -9, // Case : 38 -0, 5, 4, 0, 5, 11, 2, -14, 5, 104, 100, 103, 107, 255, -10, 4, 103, 255, 106, 107, -14, 5, 100, 0, 2, 103, 255, -14, 5, 4, 0, 100, 104, 255, -10, 4, 106, 104, 107, 255, -14, 5, 106, 5, 4, 104, 255, -10, 4, 106, 11, 5, 255, -14, 5, 2, 11, 106, 103, 255, -5, // Case : 39 -10, 4, 106, 107, 103, 104, -14, 5, 8, 3, 103, 104, 2, -14, 5, 4, 104, 106, 5, 11, -14, 5, 103, 2, 11, 106, 104, -14, 5, 2, 8, 4, 11, 104, -6, // Case : 40 -13, 6, 101, 104, 106, 9, 4, 5, -13, 6, 3, 10, 2, 100, 107, 102, -10, 4, 104, 106, 101, 107, -10, 4, 102, 106, 107, 101, -10, 4, 100, 101, 102, 107, -10, 4, 104, 100, 107, 101, -11, // Case : 41 -0, 4, 2, 10, 5, 5, -14, 5, 106, 5, 4, 104, 255, -10, 4, 4, 8, 104, 255, -14, 5, 107, 104, 8, 10, 255, -10, 4, 106, 104, 107, 255, -10, 4, 101, 0, 9, 255, -10, 4, 102, 101, 106, 255, -14, 5, 101, 9, 5, 106, 255, -10, 4, 102, 106, 107, 255, -14, 5, 102, 107, 10, 2, 255, -14, 5, 102, 2, 0, 101, 255, -11, // Case : 42 -0, 4, 5, 4, 10, 10, -14, 5, 107, 100, 3, 10, 255, -10, 4, 3, 100, 0, 255, -14, 5, 104, 4, 0, 100, 255, -10, 4, 107, 104, 100, 255, -10, 4, 102, 2, 1, 255, -10, 4, 106, 107, 102, 255, -14, 5, 102, 107, 10, 2, 255, -10, 4, 106, 104, 107, 255, -14, 5, 106, 5, 4, 104, 255, -14, 5, 106, 102, 1, 5, 255, -9, // Case : 43 -0, 6, 8, 4, 5, 1, 2, 10, -10, 4, 106, 107, 102, 255, -14, 5, 106, 102, 1, 5, 255, -10, 4, 104, 107, 106, 255, -14, 5, 104, 106, 5, 4, 255, -10, 4, 8, 104, 4, 255, -14, 5, 104, 8, 10, 107, 255, -14, 5, 10, 2, 102, 107, 255, -10, 4, 102, 2, 1, 255, -11, // Case : 44 -0, 4, 10, 3, 4, 4, -14, 5, 104, 4, 9, 101, 255, -10, 4, 9, 1, 101, 255, -14, 5, 100, 101, 1, 3, 255, -10, 4, 104, 101, 100, 255, -10, 4, 106, 11, 5, 255, -10, 4, 107, 106, 104, 255, -14, 5, 106, 5, 4, 104, 255, -10, 4, 107, 104, 100, 255, -14, 5, 107, 100, 3, 10, 255, -14, 5, 107, 10, 11, 106, 255, -8, // Case : 45 -0, 5, 10, 11, 8, 4, 5, -10, 4, 101, 1, 0, 9, -14, 5, 106, 107, 10, 11, 255, -14, 5, 107, 104, 8, 10, 255, -10, 4, 104, 4, 8, 255, -10, 4, 107, 106, 104, 255, -14, 5, 104, 106, 5, 4, 255, -10, 4, 106, 11, 5, 255, -9, // Case : 46 -0, 6, 0, 3, 10, 11, 5, 4, -10, 4, 107, 106, 104, 255, -14, 5, 107, 10, 11, 106, 255, -10, 4, 100, 107, 104, 255, -14, 5, 100, 3, 10, 107, 255, -10, 4, 0, 3, 100, 255, -14, 5, 100, 104, 4, 0, 255, -14, 5, 4, 104, 106, 5, 255, -10, 4, 106, 11, 5, 255, -7, // Case : 47 -0, 5, 11, 10, 8, 5, 4, -14, 5, 107, 104, 8, 10, 255, -10, 4, 104, 4, 8, 255, -14, 5, 106, 107, 10, 11, 255, -10, 4, 5, 106, 11, 255, -14, 5, 4, 104, 106, 5, 255, -10, 4, 104, 107, 106, 255, -2, // Case : 48 -12, 8, 9, 101, 100, 8, 5, 106, 107, 7, -13, 6, 101, 102, 106, 100, 103, 107, -9, // Case : 49 -0, 5, 3, 7, 0, 9, 5, -14, 5, 103, 102, 106, 107, 255, -10, 4, 106, 101, 255, 102, -14, 5, 107, 106, 5, 7, 255, -14, 5, 3, 103, 107, 7, 255, -10, 4, 101, 102, 103, 255, -14, 5, 101, 103, 3, 0, 255, -10, 4, 101, 0, 9, 255, -14, 5, 5, 106, 101, 9, 255, -9, // Case : 50 -0, 5, 7, 5, 8, 0, 1, -14, 5, 107, 103, 102, 106, 255, -10, 4, 102, 100, 255, 103, -14, 5, 106, 102, 1, 5, 255, -14, 5, 7, 107, 106, 5, 255, -10, 4, 100, 103, 107, 255, -14, 5, 100, 107, 7, 8, 255, -10, 4, 100, 8, 0, 255, -14, 5, 1, 102, 100, 0, 255, -1, // Case : 51 -12, 8, 103, 102, 106, 107, 3, 1, 5, 7, -11, // Case : 52 -0, 4, 7, 8, 2, 2, -14, 5, 103, 101, 1, 2, 255, -10, 4, 1, 101, 9, 255, -14, 5, 100, 8, 9, 101, 255, -10, 4, 103, 100, 101, 255, -10, 4, 106, 11, 5, 255, -10, 4, 107, 103, 106, 255, -14, 5, 106, 103, 2, 11, 255, -10, 4, 107, 100, 103, 255, -14, 5, 107, 7, 8, 100, 255, -14, 5, 107, 106, 5, 7, 255, -8, // Case : 53 -0, 5, 7, 5, 3, 2, 11, -10, 4, 101, 0, 9, 1, -14, 5, 106, 5, 7, 107, 255, -14, 5, 107, 7, 3, 103, 255, -10, 4, 103, 3, 2, 255, -10, 4, 107, 103, 106, 255, -14, 5, 103, 2, 11, 106, 255, -10, 4, 106, 11, 5, 255, -9, // Case : 54 -0, 6, 0, 8, 7, 5, 11, 2, -10, 4, 107, 103, 106, 255, -14, 5, 107, 106, 5, 7, 255, -10, 4, 100, 103, 107, 255, -14, 5, 100, 107, 7, 8, 255, -10, 4, 0, 100, 8, 255, -14, 5, 100, 0, 2, 103, 255, -14, 5, 2, 11, 106, 103, 255, -10, 4, 106, 11, 5, 255, -7, // Case : 55 -0, 5, 5, 7, 3, 11, 2, -14, 5, 107, 7, 3, 103, 255, -10, 4, 103, 3, 2, 255, -14, 5, 106, 5, 7, 107, 255, -10, 4, 11, 5, 106, 255, -14, 5, 2, 11, 106, 103, 255, -10, 4, 103, 106, 107, 255, -11, // Case : 56 -0, 4, 5, 9, 2, 2, -14, 5, 102, 2, 3, 100, 255, -10, 4, 3, 8, 100, 255, -14, 5, 101, 100, 8, 9, 255, -10, 4, 102, 100, 101, 255, -10, 4, 107, 7, 10, 255, -10, 4, 106, 107, 102, 255, -14, 5, 107, 10, 2, 102, 255, -10, 4, 106, 102, 101, 255, -14, 5, 106, 101, 9, 5, 255, -14, 5, 106, 5, 7, 107, 255, -9, // Case : 57 -0, 6, 0, 9, 5, 7, 10, 2, -10, 4, 106, 107, 102, 255, -14, 5, 106, 5, 7, 107, 255, -10, 4, 101, 106, 102, 255, -14, 5, 101, 9, 5, 106, 255, -10, 4, 0, 9, 101, 255, -14, 5, 101, 102, 2, 0, 255, -14, 5, 2, 102, 107, 10, 255, -10, 4, 107, 7, 10, 255, -8, // Case : 58 -0, 5, 5, 1, 7, 10, 2, -10, 4, 100, 8, 0, 3, -14, 5, 102, 1, 5, 106, 255, -14, 5, 106, 5, 7, 107, 255, -10, 4, 107, 7, 10, 255, -10, 4, 106, 107, 102, 255, -14, 5, 107, 10, 2, 102, 255, -10, 4, 102, 2, 1, 255, -7, // Case : 59 -0, 5, 1, 5, 7, 2, 10, -14, 5, 106, 5, 7, 107, 255, -10, 4, 107, 7, 10, 255, -14, 5, 102, 1, 5, 106, 255, -10, 4, 2, 1, 102, 255, -14, 5, 10, 2, 102, 107, 255, -10, 4, 107, 102, 106, 255, -2, // Case : 60 -13, 6, 101, 1, 9, 100, 3, 8, -13, 6, 106, 5, 11, 107, 7, 10, -2, // Case : 61 -13, 6, 7, 107, 10, 5, 106, 11, -10, 4, 0, 101, 1, 9, -2, // Case : 62 -13, 6, 7, 107, 10, 5, 106, 11, -10, 4, 0, 3, 100, 8, -1, // Case : 63 -13, 6, 107, 10, 7, 106, 11, 5, -9, // Case : 64 -0, 7, 107, 104, 105, 102, 103, 100, 101, -13, 6, 6, 5, 11, 107, 105, 102, -10, 4, 107, 102, 105, 255, -10, 4, 107, 105, 104, 255, -14, 5, 100, 104, 105, 101, 255, -14, 5, 103, 107, 104, 100, 255, -14, 5, 102, 103, 100, 101, 255, -10, 4, 105, 102, 101, 255, -10, 4, 102, 107, 103, 255, -6, // Case : 65 -13, 6, 105, 107, 102, 5, 6, 11, -13, 6, 8, 3, 0, 104, 103, 101, -10, 4, 107, 102, 105, 103, -10, 4, 101, 102, 103, 105, -10, 4, 104, 105, 101, 103, -10, 4, 107, 104, 103, 105, -12, // Case : 66 -0, 2, 0, 6, -14, 5, 100, 103, 107, 104, 255, -10, 4, 104, 107, 105, 255, -10, 4, 100, 104, 105, 255, -10, 4, 102, 103, 100, 255, -10, 4, 107, 103, 102, 255, -14, 5, 107, 102, 11, 6, 255, -14, 5, 105, 107, 6, 5, 255, -10, 4, 105, 5, 9, 255, -14, 5, 100, 105, 9, 0, 255, -14, 5, 100, 0, 1, 102, 255, -10, 4, 102, 1, 11, 255, -11, // Case : 67 -0, 4, 8, 3, 6, 6, -14, 5, 107, 102, 11, 6, 255, -10, 4, 11, 102, 1, 255, -14, 5, 103, 3, 1, 102, 255, -10, 4, 107, 103, 102, 255, -10, 4, 105, 5, 9, 255, -10, 4, 104, 107, 105, 255, -14, 5, 105, 107, 6, 5, 255, -10, 4, 104, 103, 107, 255, -14, 5, 104, 8, 3, 103, 255, -14, 5, 104, 105, 9, 8, 255, -2, // Case : 68 -12, 8, 6, 107, 103, 2, 5, 105, 101, 1, -13, 6, 101, 100, 103, 105, 104, 107, -11, // Case : 69 -0, 4, 6, 5, 8, 8, -14, 5, 104, 101, 0, 8, 255, -10, 4, 0, 101, 1, 255, -14, 5, 105, 5, 1, 101, 255, -10, 4, 104, 105, 101, 255, -10, 4, 103, 3, 2, 255, -10, 4, 107, 104, 103, 255, -14, 5, 103, 104, 8, 3, 255, -10, 4, 107, 105, 104, 255, -14, 5, 107, 6, 5, 105, 255, -14, 5, 107, 103, 2, 6, 255, -9, // Case : 70 -0, 5, 0, 2, 9, 5, 6, -14, 5, 100, 103, 107, 104, 255, -10, 4, 107, 255, 105, 104, -14, 5, 103, 2, 6, 107, 255, -14, 5, 0, 2, 103, 100, 255, -10, 4, 105, 100, 104, 255, -14, 5, 105, 9, 0, 100, 255, -10, 4, 105, 5, 9, 255, -14, 5, 6, 5, 105, 107, 255, -9, // Case : 71 -0, 6, 9, 5, 6, 2, 3, 8, -10, 4, 107, 104, 103, 255, -14, 5, 107, 103, 2, 6, 255, -10, 4, 105, 104, 107, 255, -14, 5, 105, 107, 6, 5, 255, -10, 4, 9, 105, 5, 255, -14, 5, 105, 9, 8, 104, 255, -14, 5, 8, 3, 103, 104, 255, -10, 4, 103, 3, 2, 255, -12, // Case : 72 -0, 2, 3, 5, -14, 5, 100, 104, 105, 101, 255, -10, 4, 101, 105, 102, 255, -10, 4, 100, 101, 102, 255, -10, 4, 107, 104, 100, 255, -10, 4, 105, 104, 107, 255, -14, 5, 105, 107, 6, 5, 255, -14, 5, 102, 105, 5, 11, 255, -10, 4, 102, 11, 2, 255, -14, 5, 100, 102, 2, 3, 255, -14, 5, 100, 3, 10, 107, 255, -10, 4, 107, 10, 6, 255, -11, // Case : 73 -0, 4, 0, 8, 5, 5, -14, 5, 105, 107, 6, 5, 255, -10, 4, 6, 107, 10, 255, -14, 5, 104, 8, 10, 107, 255, -10, 4, 105, 104, 107, 255, -10, 4, 102, 11, 2, 255, -10, 4, 101, 105, 102, 255, -14, 5, 102, 105, 5, 11, 255, -10, 4, 101, 104, 105, 255, -14, 5, 101, 0, 8, 104, 255, -14, 5, 101, 102, 2, 0, 255, -6, // Case : 74 -10, 4, 102, 1, 11, 2, -10, 4, 107, 104, 100, 105, -14, 5, 9, 0, 100, 105, 3, -14, 5, 5, 105, 107, 6, 10, -14, 5, 100, 3, 10, 107, 105, -14, 5, 3, 9, 5, 10, 105, -8, // Case : 75 -0, 5, 8, 10, 9, 5, 6, -10, 4, 102, 2, 1, 11, -14, 5, 107, 104, 8, 10, 255, -14, 5, 104, 105, 9, 8, 255, -10, 4, 105, 5, 9, 255, -10, 4, 104, 107, 105, 255, -14, 5, 105, 107, 6, 5, 255, -10, 4, 107, 10, 6, 255, -9, // Case : 76 -0, 5, 3, 1, 10, 6, 5, -14, 5, 100, 104, 105, 101, 255, -10, 4, 105, 107, 255, 104, -14, 5, 101, 105, 5, 1, 255, -14, 5, 3, 100, 101, 1, 255, -10, 4, 107, 104, 100, 255, -14, 5, 107, 100, 3, 10, 255, -10, 4, 107, 10, 6, 255, -14, 5, 5, 105, 107, 6, 255, -9, // Case : 77 -0, 6, 10, 6, 5, 1, 0, 8, -10, 4, 105, 101, 104, 255, -14, 5, 105, 5, 1, 101, 255, -10, 4, 107, 105, 104, 255, -14, 5, 107, 6, 5, 105, 255, -10, 4, 10, 6, 107, 255, -14, 5, 107, 104, 8, 10, 255, -14, 5, 8, 104, 101, 0, 255, -10, 4, 101, 1, 0, 255, -5, // Case : 78 -10, 4, 107, 104, 100, 105, -14, 5, 9, 0, 100, 105, 3, -14, 5, 5, 105, 107, 6, 10, -14, 5, 100, 3, 10, 107, 105, -14, 5, 3, 9, 5, 10, 105, -7, // Case : 79 -0, 5, 10, 8, 9, 6, 5, -14, 5, 104, 105, 9, 8, 255, -10, 4, 105, 5, 9, 255, -14, 5, 107, 104, 8, 10, 255, -10, 4, 6, 107, 10, 255, -14, 5, 5, 105, 107, 6, 255, -10, 4, 105, 104, 107, 255, -12, // Case : 80 -0, 2, 8, 11, -14, 5, 100, 101, 102, 103, 255, -10, 4, 101, 105, 102, 255, -10, 4, 100, 105, 101, 255, -10, 4, 107, 100, 103, 255, -10, 4, 102, 107, 103, 255, -14, 5, 102, 11, 6, 107, 255, -14, 5, 105, 5, 11, 102, 255, -10, 4, 105, 4, 5, 255, -14, 5, 100, 8, 4, 105, 255, -14, 5, 100, 107, 7, 8, 255, -10, 4, 107, 6, 7, 255, -11, // Case : 81 -0, 4, 0, 3, 11, 11, -14, 5, 102, 11, 6, 107, 255, -10, 4, 6, 7, 107, 255, -14, 5, 103, 107, 7, 3, 255, -10, 4, 102, 107, 103, 255, -10, 4, 105, 4, 5, 255, -10, 4, 101, 105, 102, 255, -14, 5, 105, 5, 11, 102, 255, -10, 4, 101, 102, 103, 255, -14, 5, 101, 103, 3, 0, 255, -14, 5, 101, 0, 4, 105, 255, -6, // Case : 82 -10, 4, 105, 5, 9, 4, -10, 4, 107, 100, 103, 102, -14, 5, 1, 102, 100, 0, 8, -14, 5, 11, 6, 107, 102, 7, -14, 5, 100, 107, 7, 8, 102, -14, 5, 8, 7, 11, 1, 102, -8, // Case : 83 -0, 5, 3, 7, 1, 11, 6, -10, 4, 105, 9, 4, 5, -14, 5, 107, 7, 3, 103, 255, -14, 5, 103, 3, 1, 102, 255, -10, 4, 102, 1, 11, 255, -10, 4, 103, 102, 107, 255, -14, 5, 102, 11, 6, 107, 255, -10, 4, 107, 6, 7, 255, -11, // Case : 84 -0, 4, 2, 1, 8, 8, -14, 5, 100, 8, 4, 105, 255, -10, 4, 4, 5, 105, 255, -14, 5, 101, 105, 5, 1, 255, -10, 4, 100, 105, 101, 255, -10, 4, 107, 6, 7, 255, -10, 4, 103, 107, 100, 255, -14, 5, 107, 7, 8, 100, 255, -10, 4, 103, 100, 101, 255, -14, 5, 103, 101, 1, 2, 255, -14, 5, 103, 2, 6, 107, 255, -2, // Case : 85 -13, 6, 107, 7, 6, 103, 3, 2, -13, 6, 105, 5, 4, 101, 1, 0, -8, // Case : 86 -0, 5, 2, 6, 0, 8, 7, -10, 4, 105, 5, 9, 4, -14, 5, 107, 103, 2, 6, 255, -14, 5, 103, 100, 0, 2, 255, -10, 4, 100, 8, 0, 255, -10, 4, 103, 107, 100, 255, -14, 5, 100, 107, 7, 8, 255, -10, 4, 107, 6, 7, 255, -2, // Case : 87 -13, 6, 6, 107, 7, 2, 103, 3, -10, 4, 9, 4, 105, 5, -6, // Case : 88 -10, 4, 107, 6, 7, 10, -10, 4, 102, 100, 101, 105, -14, 5, 4, 105, 100, 8, 3, -14, 5, 5, 11, 102, 105, 2, -14, 5, 100, 102, 2, 3, 105, -14, 5, 3, 2, 5, 4, 105, -8, // Case : 89 -0, 5, 0, 2, 4, 5, 11, -10, 4, 107, 7, 10, 6, -14, 5, 102, 2, 0, 101, 255, -14, 5, 101, 0, 4, 105, 255, -10, 4, 105, 4, 5, 255, -10, 4, 101, 105, 102, 255, -14, 5, 105, 5, 11, 102, 255, -10, 4, 102, 11, 2, 255, -4, // Case : 90 -10, 4, 7, 6, 10, 107, -10, 4, 8, 3, 0, 100, -10, 4, 4, 9, 5, 105, -10, 4, 1, 2, 11, 102, -3, // Case : 91 -10, 4, 4, 9, 5, 105, -10, 4, 7, 10, 107, 6, -10, 4, 1, 102, 2, 11, -8, // Case : 92 -0, 5, 1, 5, 3, 8, 4, -10, 4, 107, 10, 6, 7, -14, 5, 105, 5, 1, 101, 255, -14, 5, 101, 1, 3, 100, 255, -10, 4, 100, 3, 8, 255, -10, 4, 101, 100, 105, 255, -14, 5, 100, 8, 4, 105, 255, -10, 4, 105, 4, 5, 255, -2, // Case : 93 -13, 6, 1, 101, 0, 5, 105, 4, -10, 4, 10, 107, 7, 6, -3, // Case : 94 -10, 4, 10, 7, 6, 107, -10, 4, 3, 100, 0, 8, -10, 4, 5, 9, 105, 4, -2, // Case : 95 -10, 4, 6, 10, 7, 107, -10, 4, 5, 4, 9, 105, -2, // Case : 96 -12, 8, 11, 102, 101, 9, 6, 107, 104, 4, -13, 6, 102, 103, 107, 101, 100, 104, -11, // Case : 97 -0, 4, 6, 11, 3, 3, -14, 5, 103, 3, 0, 101, 255, -10, 4, 0, 9, 101, 255, -14, 5, 102, 101, 9, 11, 255, -10, 4, 103, 101, 102, 255, -10, 4, 104, 4, 8, 255, -10, 4, 107, 104, 103, 255, -14, 5, 104, 8, 3, 103, 255, -10, 4, 107, 103, 102, 255, -14, 5, 107, 102, 11, 6, 255, -14, 5, 107, 6, 4, 104, 255, -9, // Case : 98 -0, 5, 0, 4, 1, 11, 6, -14, 5, 100, 103, 107, 104, 255, -10, 4, 107, 102, 255, 103, -14, 5, 104, 107, 6, 4, 255, -14, 5, 0, 100, 104, 4, 255, -10, 4, 102, 103, 100, 255, -14, 5, 102, 100, 0, 1, 255, -10, 4, 102, 1, 11, 255, -14, 5, 6, 107, 102, 11, 255, -9, // Case : 99 -0, 6, 1, 11, 6, 4, 8, 3, -10, 4, 107, 104, 103, 255, -14, 5, 107, 6, 4, 104, 255, -10, 4, 102, 107, 103, 255, -14, 5, 102, 11, 6, 107, 255, -10, 4, 1, 11, 102, 255, -14, 5, 102, 103, 3, 1, 255, -14, 5, 3, 103, 104, 8, 255, -10, 4, 104, 4, 8, 255, -9, // Case : 100 -0, 5, 2, 6, 1, 9, 4, -14, 5, 103, 107, 104, 100, 255, -10, 4, 104, 255, 101, 100, -14, 5, 107, 6, 4, 104, 255, -14, 5, 2, 6, 107, 103, 255, -10, 4, 101, 103, 100, 255, -14, 5, 101, 1, 2, 103, 255, -10, 4, 101, 9, 1, 255, -14, 5, 4, 9, 101, 104, 255, -8, // Case : 101 -0, 5, 6, 4, 2, 3, 8, -10, 4, 101, 9, 1, 0, -14, 5, 104, 107, 6, 4, 255, -14, 5, 107, 103, 2, 6, 255, -10, 4, 103, 3, 2, 255, -10, 4, 107, 104, 103, 255, -14, 5, 103, 104, 8, 3, 255, -10, 4, 104, 4, 8, 255, -1, // Case : 102 -12, 8, 0, 4, 6, 2, 100, 104, 107, 103, -7, // Case : 103 -0, 5, 4, 6, 2, 8, 3, -14, 5, 107, 103, 2, 6, 255, -10, 4, 103, 3, 2, 255, -14, 5, 104, 107, 6, 4, 255, -10, 4, 8, 104, 4, 255, -14, 5, 3, 103, 104, 8, 255, -10, 4, 103, 107, 104, 255, -11, // Case : 104 -0, 4, 4, 9, 3, 3, -14, 5, 100, 102, 2, 3, 255, -10, 4, 2, 102, 11, 255, -14, 5, 101, 9, 11, 102, 255, -10, 4, 100, 101, 102, 255, -10, 4, 107, 10, 6, 255, -10, 4, 104, 100, 107, 255, -14, 5, 107, 100, 3, 10, 255, -10, 4, 104, 101, 100, 255, -14, 5, 104, 4, 9, 101, 255, -14, 5, 104, 107, 6, 4, 255, -2, // Case : 105 -13, 6, 104, 8, 4, 107, 10, 6, -13, 6, 101, 9, 0, 102, 11, 2, -8, // Case : 106 -0, 5, 4, 6, 0, 3, 10, -10, 4, 102, 1, 11, 2, -14, 5, 107, 6, 4, 104, 255, -14, 5, 104, 4, 0, 100, 255, -10, 4, 100, 0, 3, 255, -10, 4, 104, 100, 107, 255, -14, 5, 100, 3, 10, 107, 255, -10, 4, 107, 10, 6, 255, -2, // Case : 107 -13, 6, 4, 104, 8, 6, 107, 10, -10, 4, 1, 102, 2, 11, -9, // Case : 108 -0, 6, 1, 9, 4, 6, 10, 3, -10, 4, 104, 100, 107, 255, -14, 5, 104, 107, 6, 4, 255, -10, 4, 101, 100, 104, 255, -14, 5, 101, 104, 4, 9, 255, -10, 4, 1, 101, 9, 255, -14, 5, 101, 1, 3, 100, 255, -14, 5, 3, 10, 107, 100, 255, -10, 4, 107, 10, 6, 255, -2, // Case : 109 -13, 6, 4, 104, 8, 6, 107, 10, -10, 4, 1, 0, 101, 9, -7, // Case : 110 -0, 5, 6, 4, 0, 10, 3, -14, 5, 104, 4, 0, 100, 255, -10, 4, 100, 0, 3, 255, -14, 5, 107, 6, 4, 104, 255, -10, 4, 10, 6, 107, 255, -14, 5, 3, 10, 107, 100, 255, -10, 4, 100, 107, 104, 255, -1, // Case : 111 -13, 6, 104, 8, 4, 107, 10, 6, -9, // Case : 112 -0, 5, 8, 9, 7, 6, 11, -14, 5, 100, 101, 102, 103, 255, -10, 4, 102, 255, 107, 103, -14, 5, 101, 9, 11, 102, 255, -14, 5, 8, 9, 101, 100, 255, -10, 4, 107, 100, 103, 255, -14, 5, 107, 7, 8, 100, 255, -10, 4, 107, 6, 7, 255, -14, 5, 11, 6, 107, 102, 255, -9, // Case : 113 -0, 6, 7, 6, 11, 9, 0, 3, -10, 4, 102, 103, 101, 255, -14, 5, 102, 101, 9, 11, 255, -10, 4, 107, 103, 102, 255, -14, 5, 107, 102, 11, 6, 255, -10, 4, 7, 107, 6, 255, -14, 5, 107, 7, 3, 103, 255, -14, 5, 3, 0, 101, 103, 255, -10, 4, 101, 0, 9, 255, -5, // Case : 114 -10, 4, 107, 100, 103, 102, -14, 5, 1, 102, 100, 0, 8, -14, 5, 11, 6, 107, 102, 7, -14, 5, 100, 107, 7, 8, 102, -14, 5, 8, 7, 11, 1, 102, -7, // Case : 115 -0, 5, 7, 3, 1, 6, 11, -14, 5, 103, 3, 1, 102, 255, -10, 4, 102, 1, 11, 255, -14, 5, 107, 7, 3, 103, 255, -10, 4, 6, 7, 107, 255, -14, 5, 11, 6, 107, 102, 255, -10, 4, 102, 107, 103, 255, -9, // Case : 116 -0, 6, 9, 1, 2, 6, 7, 8, -10, 4, 103, 107, 100, 255, -14, 5, 103, 2, 6, 107, 255, -10, 4, 101, 103, 100, 255, -14, 5, 101, 1, 2, 103, 255, -10, 4, 9, 1, 101, 255, -14, 5, 101, 100, 8, 9, 255, -14, 5, 8, 100, 107, 7, 255, -10, 4, 107, 6, 7, 255, -2, // Case : 117 -13, 6, 6, 107, 7, 2, 103, 3, -10, 4, 9, 101, 0, 1, -7, // Case : 118 -0, 5, 6, 2, 0, 7, 8, -14, 5, 103, 100, 0, 2, 255, -10, 4, 100, 8, 0, 255, -14, 5, 107, 103, 2, 6, 255, -10, 4, 7, 107, 6, 255, -14, 5, 8, 100, 107, 7, 255, -10, 4, 100, 103, 107, 255, -1, // Case : 119 -13, 6, 107, 7, 6, 103, 3, 2, -8, // Case : 120 -0, 5, 9, 11, 8, 3, 2, -10, 4, 107, 6, 7, 10, -14, 5, 102, 101, 9, 11, 255, -14, 5, 101, 100, 8, 9, 255, -10, 4, 100, 3, 8, 255, -10, 4, 101, 102, 100, 255, -14, 5, 100, 102, 2, 3, 255, -10, 4, 102, 11, 2, 255, -2, // Case : 121 -13, 6, 11, 102, 2, 9, 101, 0, -10, 4, 7, 10, 107, 6, -3, // Case : 122 -10, 4, 7, 6, 10, 107, -10, 4, 8, 0, 100, 3, -10, 4, 11, 102, 1, 2, -2, // Case : 123 -10, 4, 6, 10, 7, 107, -10, 4, 11, 1, 2, 102, -2, // Case : 124 -13, 6, 3, 100, 8, 1, 101, 9, -10, 4, 6, 7, 107, 10, -2, // Case : 125 -10, 4, 6, 10, 7, 107, -10, 4, 9, 0, 1, 101, -2, // Case : 126 -10, 4, 8, 3, 0, 100, -10, 4, 7, 6, 10, 107, -1, // Case : 127 -10, 4, 7, 6, 10, 107, -9, // Case : 128 -0, 7, 106, 105, 104, 103, 102, 101, 100, -13, 6, 106, 104, 103, 6, 7, 10, -10, 4, 106, 104, 103, 255, -10, 4, 106, 105, 104, 255, -14, 5, 101, 100, 104, 105, 255, -14, 5, 102, 101, 105, 106, 255, -14, 5, 103, 100, 101, 102, 255, -10, 4, 104, 100, 103, 255, -10, 4, 103, 102, 106, 255, -12, // Case : 129 -0, 2, 0, 6, -14, 5, 101, 105, 106, 102, 255, -10, 4, 105, 104, 106, 255, -10, 4, 101, 104, 105, 255, -10, 4, 103, 101, 102, 255, -10, 4, 106, 103, 102, 255, -14, 5, 106, 6, 10, 103, 255, -14, 5, 104, 7, 6, 106, 255, -10, 4, 104, 8, 7, 255, -14, 5, 101, 0, 8, 104, 255, -14, 5, 101, 103, 3, 0, 255, -10, 4, 103, 10, 3, 255, -6, // Case : 130 -13, 6, 104, 103, 106, 7, 10, 6, -13, 6, 0, 1, 9, 100, 102, 105, -10, 4, 103, 106, 104, 102, -10, 4, 105, 106, 102, 104, -10, 4, 100, 104, 105, 102, -10, 4, 103, 100, 102, 104, -11, // Case : 131 -0, 4, 9, 1, 6, 6, -14, 5, 106, 6, 10, 103, 255, -10, 4, 10, 3, 103, 255, -14, 5, 102, 103, 3, 1, 255, -10, 4, 106, 103, 102, 255, -10, 4, 104, 8, 7, 255, -10, 4, 105, 104, 106, 255, -14, 5, 104, 7, 6, 106, 255, -10, 4, 105, 106, 102, 255, -14, 5, 105, 102, 1, 9, 255, -14, 5, 105, 9, 8, 104, 255, -12, // Case : 132 -0, 2, 7, 1, -14, 5, 104, 105, 101, 100, 255, -10, 4, 105, 106, 101, 255, -10, 4, 104, 106, 105, 255, -10, 4, 103, 104, 100, 255, -10, 4, 101, 103, 100, 255, -14, 5, 101, 1, 2, 103, 255, -14, 5, 106, 11, 1, 101, 255, -10, 4, 106, 6, 11, 255, -14, 5, 104, 7, 6, 106, 255, -14, 5, 104, 103, 10, 7, 255, -10, 4, 103, 2, 10, 255, -6, // Case : 133 -10, 4, 103, 3, 2, 10, -10, 4, 106, 105, 104, 101, -14, 5, 0, 8, 104, 101, 7, -14, 5, 1, 101, 106, 11, 6, -14, 5, 104, 7, 6, 106, 101, -14, 5, 7, 0, 1, 6, 101, -11, // Case : 134 -0, 4, 9, 0, 7, 7, -14, 5, 104, 103, 10, 7, 255, -10, 4, 10, 103, 2, 255, -14, 5, 100, 0, 2, 103, 255, -10, 4, 104, 100, 103, 255, -10, 4, 106, 6, 11, 255, -10, 4, 105, 104, 106, 255, -14, 5, 106, 104, 7, 6, 255, -10, 4, 105, 100, 104, 255, -14, 5, 105, 9, 0, 100, 255, -14, 5, 105, 106, 11, 9, 255, -8, // Case : 135 -0, 5, 9, 11, 8, 7, 6, -10, 4, 103, 3, 2, 10, -14, 5, 106, 11, 9, 105, 255, -14, 5, 105, 9, 8, 104, 255, -10, 4, 104, 8, 7, 255, -10, 4, 105, 104, 106, 255, -14, 5, 104, 7, 6, 106, 255, -10, 4, 106, 6, 11, 255, -2, // Case : 136 -12, 8, 7, 104, 100, 3, 6, 106, 102, 2, -13, 6, 104, 105, 106, 100, 101, 102, -9, // Case : 137 -0, 5, 0, 2, 8, 7, 6, -14, 5, 101, 105, 106, 102, 255, -10, 4, 106, 104, 255, 105, -14, 5, 102, 106, 6, 2, 255, -14, 5, 0, 101, 102, 2, 255, -10, 4, 104, 105, 101, 255, -14, 5, 104, 101, 0, 8, 255, -10, 4, 104, 8, 7, 255, -14, 5, 6, 106, 104, 7, 255, -11, // Case : 138 -0, 4, 6, 7, 9, 9, -14, 5, 105, 9, 0, 100, 255, -10, 4, 0, 3, 100, 255, -14, 5, 104, 100, 3, 7, 255, -10, 4, 105, 100, 104, 255, -10, 4, 102, 2, 1, 255, -10, 4, 106, 102, 105, 255, -14, 5, 102, 1, 9, 105, 255, -10, 4, 106, 105, 104, 255, -14, 5, 106, 104, 7, 6, 255, -14, 5, 106, 6, 2, 102, 255, -9, // Case : 139 -0, 6, 8, 7, 6, 2, 1, 9, -10, 4, 106, 102, 105, 255, -14, 5, 106, 6, 2, 102, 255, -10, 4, 104, 106, 105, 255, -14, 5, 104, 7, 6, 106, 255, -10, 4, 8, 7, 104, 255, -14, 5, 104, 105, 9, 8, 255, -14, 5, 9, 105, 102, 1, 255, -10, 4, 102, 2, 1, 255, -9, // Case : 140 -0, 5, 1, 3, 11, 6, 7, -14, 5, 101, 100, 104, 105, 255, -10, 4, 104, 255, 106, 105, -14, 5, 100, 3, 7, 104, 255, -14, 5, 1, 3, 100, 101, 255, -10, 4, 106, 101, 105, 255, -14, 5, 106, 11, 1, 101, 255, -10, 4, 106, 6, 11, 255, -14, 5, 7, 6, 106, 104, 255, -5, // Case : 141 -10, 4, 106, 105, 104, 101, -14, 5, 0, 8, 104, 101, 7, -14, 5, 1, 101, 106, 11, 6, -14, 5, 104, 7, 6, 106, 101, -14, 5, 7, 0, 1, 6, 101, -9, // Case : 142 -0, 6, 3, 0, 9, 11, 6, 7, -10, 4, 105, 104, 106, 255, -14, 5, 105, 106, 11, 9, 255, -10, 4, 100, 104, 105, 255, -14, 5, 100, 105, 9, 0, 255, -10, 4, 3, 100, 0, 255, -14, 5, 100, 3, 7, 104, 255, -14, 5, 7, 6, 106, 104, 255, -10, 4, 106, 6, 11, 255, -7, // Case : 143 -0, 5, 11, 9, 8, 6, 7, -14, 5, 105, 9, 8, 104, 255, -10, 4, 104, 8, 7, 255, -14, 5, 106, 11, 9, 105, 255, -10, 4, 6, 11, 106, 255, -14, 5, 7, 6, 106, 104, 255, -10, 4, 104, 106, 105, 255, -2, // Case : 144 -12, 8, 6, 106, 105, 4, 10, 103, 100, 8, -13, 6, 100, 101, 105, 103, 102, 106, -9, // Case : 145 -0, 5, 0, 4, 3, 10, 6, -14, 5, 101, 105, 106, 102, 255, -10, 4, 106, 255, 103, 102, -14, 5, 105, 4, 6, 106, 255, -14, 5, 0, 4, 105, 101, 255, -10, 4, 103, 101, 102, 255, -14, 5, 103, 3, 0, 101, 255, -10, 4, 103, 10, 3, 255, -14, 5, 6, 10, 103, 106, 255, -11, // Case : 146 -0, 4, 6, 10, 1, 1, -14, 5, 102, 100, 0, 1, 255, -10, 4, 0, 100, 8, 255, -14, 5, 103, 10, 8, 100, 255, -10, 4, 102, 103, 100, 255, -10, 4, 105, 9, 4, 255, -10, 4, 106, 102, 105, 255, -14, 5, 105, 102, 1, 9, 255, -10, 4, 106, 103, 102, 255, -14, 5, 106, 6, 10, 103, 255, -14, 5, 106, 105, 4, 6, 255, -9, // Case : 147 -0, 6, 3, 10, 6, 4, 9, 1, -10, 4, 106, 102, 105, 255, -14, 5, 106, 105, 4, 6, 255, -10, 4, 103, 102, 106, 255, -14, 5, 103, 106, 6, 10, 255, -10, 4, 3, 103, 10, 255, -14, 5, 103, 3, 1, 102, 255, -14, 5, 1, 9, 105, 102, 255, -10, 4, 105, 9, 4, 255, -11, // Case : 148 -0, 4, 4, 8, 1, 1, -14, 5, 101, 1, 2, 103, 255, -10, 4, 2, 10, 103, 255, -14, 5, 100, 103, 10, 8, 255, -10, 4, 101, 103, 100, 255, -10, 4, 106, 6, 11, 255, -10, 4, 105, 106, 101, 255, -14, 5, 106, 11, 1, 101, 255, -10, 4, 105, 101, 100, 255, -14, 5, 105, 100, 8, 4, 255, -14, 5, 105, 4, 6, 106, 255, -8, // Case : 149 -0, 5, 4, 6, 0, 1, 11, -10, 4, 103, 10, 3, 2, -14, 5, 106, 105, 4, 6, 255, -14, 5, 105, 101, 0, 4, 255, -10, 4, 101, 1, 0, 255, -10, 4, 105, 106, 101, 255, -14, 5, 101, 106, 11, 1, 255, -10, 4, 106, 6, 11, 255, -2, // Case : 150 -13, 6, 103, 10, 2, 100, 8, 0, -13, 6, 106, 11, 6, 105, 9, 4, -2, // Case : 151 -13, 6, 9, 105, 4, 11, 106, 6, -10, 4, 3, 103, 10, 2, -9, // Case : 152 -0, 5, 2, 6, 3, 8, 4, -14, 5, 102, 101, 105, 106, 255, -10, 4, 105, 100, 255, 101, -14, 5, 106, 105, 4, 6, 255, -14, 5, 2, 102, 106, 6, 255, -10, 4, 100, 101, 102, 255, -14, 5, 100, 102, 2, 3, 255, -10, 4, 100, 3, 8, 255, -14, 5, 4, 105, 100, 8, 255, -1, // Case : 153 -12, 8, 101, 105, 106, 102, 0, 4, 6, 2, -8, // Case : 154 -0, 5, 6, 4, 2, 1, 9, -10, 4, 100, 3, 8, 0, -14, 5, 105, 4, 6, 106, 255, -14, 5, 106, 6, 2, 102, 255, -10, 4, 102, 2, 1, 255, -10, 4, 106, 102, 105, 255, -14, 5, 102, 1, 9, 105, 255, -10, 4, 105, 9, 4, 255, -7, // Case : 155 -0, 5, 4, 6, 2, 9, 1, -14, 5, 106, 6, 2, 102, 255, -10, 4, 102, 2, 1, 255, -14, 5, 105, 4, 6, 106, 255, -10, 4, 9, 4, 105, 255, -14, 5, 1, 9, 105, 102, 255, -10, 4, 102, 105, 106, 255, -9, // Case : 156 -0, 6, 3, 8, 4, 6, 11, 1, -10, 4, 105, 106, 101, 255, -14, 5, 105, 4, 6, 106, 255, -10, 4, 100, 105, 101, 255, -14, 5, 100, 8, 4, 105, 255, -10, 4, 3, 8, 100, 255, -14, 5, 100, 101, 1, 3, 255, -14, 5, 1, 101, 106, 11, 255, -10, 4, 106, 6, 11, 255, -7, // Case : 157 -0, 5, 6, 4, 0, 11, 1, -14, 5, 105, 101, 0, 4, 255, -10, 4, 101, 1, 0, 255, -14, 5, 106, 105, 4, 6, 255, -10, 4, 11, 106, 6, 255, -14, 5, 1, 101, 106, 11, 255, -10, 4, 101, 105, 106, 255, -2, // Case : 158 -13, 6, 9, 105, 4, 11, 106, 6, -10, 4, 3, 8, 100, 0, -1, // Case : 159 -13, 6, 105, 4, 9, 106, 6, 11, -12, // Case : 160 -0, 2, 10, 9, -14, 5, 103, 100, 101, 102, 255, -10, 4, 102, 101, 106, 255, -10, 4, 103, 102, 106, 255, -10, 4, 104, 100, 103, 255, -10, 4, 101, 100, 104, 255, -14, 5, 101, 104, 4, 9, 255, -14, 5, 106, 101, 9, 5, 255, -10, 4, 106, 5, 6, 255, -14, 5, 103, 106, 6, 10, 255, -14, 5, 103, 10, 7, 104, 255, -10, 4, 104, 7, 4, 255, -6, // Case : 161 -10, 4, 104, 4, 8, 7, -10, 4, 106, 103, 102, 101, -14, 5, 0, 101, 103, 3, 10, -14, 5, 9, 5, 106, 101, 6, -14, 5, 103, 106, 6, 10, 101, -14, 5, 10, 6, 9, 0, 101, -11, // Case : 162 -0, 4, 1, 0, 10, 10, -14, 5, 103, 10, 7, 104, 255, -10, 4, 7, 4, 104, 255, -14, 5, 100, 104, 4, 0, 255, -10, 4, 103, 104, 100, 255, -10, 4, 106, 5, 6, 255, -10, 4, 102, 106, 103, 255, -14, 5, 106, 6, 10, 103, 255, -10, 4, 102, 103, 100, 255, -14, 5, 102, 100, 0, 1, 255, -14, 5, 102, 1, 5, 106, 255, -8, // Case : 163 -0, 5, 1, 5, 3, 10, 6, -10, 4, 104, 4, 8, 7, -14, 5, 106, 102, 1, 5, 255, -14, 5, 102, 103, 3, 1, 255, -10, 4, 103, 10, 3, 255, -10, 4, 102, 106, 103, 255, -14, 5, 103, 106, 6, 10, 255, -10, 4, 106, 5, 6, 255, -6, // Case : 164 -10, 4, 106, 5, 6, 11, -10, 4, 103, 100, 101, 104, -14, 5, 4, 9, 101, 104, 1, -14, 5, 7, 104, 103, 10, 2, -14, 5, 101, 1, 2, 103, 104, -14, 5, 1, 4, 7, 2, 104, -4, // Case : 165 -10, 4, 7, 8, 4, 104, -10, 4, 10, 2, 3, 103, -10, 4, 6, 5, 11, 106, -10, 4, 1, 9, 0, 101, -8, // Case : 166 -0, 5, 0, 2, 4, 7, 10, -10, 4, 106, 11, 5, 6, -14, 5, 103, 100, 0, 2, 255, -14, 5, 100, 104, 4, 0, 255, -10, 4, 104, 7, 4, 255, -10, 4, 100, 103, 104, 255, -14, 5, 104, 103, 10, 7, 255, -10, 4, 103, 2, 10, 255, -3, // Case : 167 -10, 4, 11, 6, 5, 106, -10, 4, 2, 103, 3, 10, -10, 4, 4, 8, 104, 7, -11, // Case : 168 -0, 4, 2, 3, 9, 9, -14, 5, 101, 104, 4, 9, 255, -10, 4, 4, 104, 7, 255, -14, 5, 100, 3, 7, 104, 255, -10, 4, 101, 100, 104, 255, -10, 4, 106, 5, 6, 255, -10, 4, 102, 101, 106, 255, -14, 5, 106, 101, 9, 5, 255, -10, 4, 102, 100, 101, 255, -14, 5, 102, 2, 3, 100, 255, -14, 5, 102, 106, 6, 2, 255, -8, // Case : 169 -0, 5, 2, 6, 0, 9, 5, -10, 4, 104, 8, 7, 4, -14, 5, 106, 6, 2, 102, 255, -14, 5, 102, 2, 0, 101, 255, -10, 4, 101, 0, 9, 255, -10, 4, 102, 101, 106, 255, -14, 5, 101, 9, 5, 106, 255, -10, 4, 106, 5, 6, 255, -2, // Case : 170 -13, 6, 100, 3, 0, 104, 7, 4, -13, 6, 102, 1, 2, 106, 5, 6, -2, // Case : 171 -13, 6, 5, 106, 6, 1, 102, 2, -10, 4, 8, 7, 104, 4, -8, // Case : 172 -0, 5, 3, 7, 1, 9, 4, -10, 4, 106, 6, 11, 5, -14, 5, 104, 100, 3, 7, 255, -14, 5, 100, 101, 1, 3, 255, -10, 4, 101, 9, 1, 255, -10, 4, 100, 104, 101, 255, -14, 5, 101, 104, 4, 9, 255, -10, 4, 104, 7, 4, 255, -3, // Case : 173 -10, 4, 6, 5, 11, 106, -10, 4, 7, 104, 8, 4, -10, 4, 1, 0, 101, 9, -2, // Case : 174 -13, 6, 7, 104, 4, 3, 100, 0, -10, 4, 11, 5, 106, 6, -2, // Case : 175 -10, 4, 4, 7, 8, 104, -10, 4, 5, 11, 6, 106, -9, // Case : 176 -0, 5, 9, 8, 5, 6, 10, -14, 5, 101, 102, 103, 100, 255, -10, 4, 103, 106, 255, 102, -14, 5, 100, 103, 10, 8, 255, -14, 5, 9, 101, 100, 8, 255, -10, 4, 106, 102, 101, 255, -14, 5, 106, 101, 9, 5, 255, -10, 4, 106, 5, 6, 255, -14, 5, 10, 103, 106, 6, 255, -5, // Case : 177 -10, 4, 106, 103, 102, 101, -14, 5, 0, 101, 103, 3, 10, -14, 5, 9, 5, 106, 101, 6, -14, 5, 103, 106, 6, 10, 101, -14, 5, 10, 6, 9, 0, 101, -9, // Case : 178 -0, 6, 8, 0, 1, 5, 6, 10, -10, 4, 102, 106, 103, 255, -14, 5, 102, 1, 5, 106, 255, -10, 4, 100, 102, 103, 255, -14, 5, 100, 0, 1, 102, 255, -10, 4, 8, 0, 100, 255, -14, 5, 100, 103, 10, 8, 255, -14, 5, 10, 103, 106, 6, 255, -10, 4, 106, 5, 6, 255, -7, // Case : 179 -0, 5, 5, 1, 3, 6, 10, -14, 5, 102, 103, 3, 1, 255, -10, 4, 103, 10, 3, 255, -14, 5, 106, 102, 1, 5, 255, -10, 4, 6, 106, 5, 255, -14, 5, 10, 103, 106, 6, 255, -10, 4, 103, 102, 106, 255, -8, // Case : 180 -0, 5, 8, 10, 9, 1, 2, -10, 4, 106, 5, 6, 11, -14, 5, 103, 10, 8, 100, 255, -14, 5, 100, 8, 9, 101, 255, -10, 4, 101, 9, 1, 255, -10, 4, 100, 101, 103, 255, -14, 5, 101, 1, 2, 103, 255, -10, 4, 103, 2, 10, 255, -3, // Case : 181 -10, 4, 6, 5, 11, 106, -10, 4, 10, 3, 103, 2, -10, 4, 9, 101, 0, 1, -2, // Case : 182 -13, 6, 8, 100, 0, 10, 103, 2, -10, 4, 5, 106, 11, 6, -2, // Case : 183 -10, 4, 2, 3, 10, 103, -10, 4, 11, 6, 5, 106, -9, // Case : 184 -0, 6, 8, 3, 2, 6, 5, 9, -10, 4, 102, 101, 106, 255, -14, 5, 102, 106, 6, 2, 255, -10, 4, 100, 101, 102, 255, -14, 5, 100, 102, 2, 3, 255, -10, 4, 8, 100, 3, 255, -14, 5, 100, 8, 9, 101, 255, -14, 5, 9, 5, 106, 101, 255, -10, 4, 106, 5, 6, 255, -7, // Case : 185 -0, 5, 6, 2, 0, 5, 9, -14, 5, 102, 2, 0, 101, 255, -10, 4, 101, 0, 9, 255, -14, 5, 106, 6, 2, 102, 255, -10, 4, 5, 6, 106, 255, -14, 5, 9, 5, 106, 101, 255, -10, 4, 101, 106, 102, 255, -2, // Case : 186 -13, 6, 5, 106, 6, 1, 102, 2, -10, 4, 8, 100, 3, 0, -1, // Case : 187 -13, 6, 106, 6, 5, 102, 2, 1, -2, // Case : 188 -13, 6, 3, 100, 8, 1, 101, 9, -10, 4, 6, 106, 5, 11, -2, // Case : 189 -10, 4, 9, 0, 1, 101, -10, 4, 5, 11, 6, 106, -2, // Case : 190 -10, 4, 0, 8, 3, 100, -10, 4, 11, 6, 5, 106, -1, // Case : 191 -10, 4, 5, 11, 6, 106, -2, // Case : 192 -12, 8, 5, 105, 104, 7, 11, 102, 103, 10, -13, 6, 105, 101, 102, 104, 100, 103, -11, // Case : 193 -0, 4, 11, 5, 0, 0, -14, 5, 101, 0, 8, 104, 255, -10, 4, 8, 7, 104, 255, -14, 5, 105, 104, 7, 5, 255, -10, 4, 101, 104, 105, 255, -10, 4, 103, 10, 3, 255, -10, 4, 102, 103, 101, 255, -14, 5, 103, 3, 0, 101, 255, -10, 4, 102, 101, 105, 255, -14, 5, 102, 105, 5, 11, 255, -14, 5, 102, 11, 10, 103, 255, -11, // Case : 194 -0, 4, 10, 7, 0, 0, -14, 5, 100, 105, 9, 0, 255, -10, 4, 9, 105, 5, 255, -14, 5, 104, 7, 5, 105, 255, -10, 4, 100, 104, 105, 255, -10, 4, 102, 1, 11, 255, -10, 4, 103, 100, 102, 255, -14, 5, 102, 100, 0, 1, 255, -10, 4, 103, 104, 100, 255, -14, 5, 103, 10, 7, 104, 255, -14, 5, 103, 102, 11, 10, 255, -2, // Case : 195 -13, 6, 103, 3, 10, 102, 1, 11, -13, 6, 104, 7, 8, 105, 5, 9, -9, // Case : 196 -0, 5, 7, 5, 10, 2, 1, -14, 5, 104, 105, 101, 100, 255, -10, 4, 101, 255, 103, 100, -14, 5, 105, 5, 1, 101, 255, -14, 5, 7, 5, 105, 104, 255, -10, 4, 103, 104, 100, 255, -14, 5, 103, 10, 7, 104, 255, -10, 4, 103, 2, 10, 255, -14, 5, 1, 2, 103, 101, 255, -8, // Case : 197 -0, 5, 5, 1, 7, 8, 0, -10, 4, 103, 2, 10, 3, -14, 5, 101, 105, 5, 1, 255, -14, 5, 105, 104, 7, 5, 255, -10, 4, 104, 8, 7, 255, -10, 4, 105, 101, 104, 255, -14, 5, 104, 101, 0, 8, 255, -10, 4, 101, 1, 0, 255, -9, // Case : 198 -0, 6, 2, 10, 7, 5, 9, 0, -10, 4, 104, 105, 100, 255, -14, 5, 104, 7, 5, 105, 255, -10, 4, 103, 104, 100, 255, -14, 5, 103, 10, 7, 104, 255, -10, 4, 2, 10, 103, 255, -14, 5, 103, 100, 0, 2, 255, -14, 5, 0, 100, 105, 9, 255, -10, 4, 105, 5, 9, 255, -2, // Case : 199 -13, 6, 5, 105, 9, 7, 104, 8, -10, 4, 2, 103, 3, 10, -9, // Case : 200 -0, 5, 3, 7, 2, 11, 5, -14, 5, 100, 104, 105, 101, 255, -10, 4, 105, 255, 102, 101, -14, 5, 104, 7, 5, 105, 255, -14, 5, 3, 7, 104, 100, 255, -10, 4, 102, 100, 101, 255, -14, 5, 102, 2, 3, 100, 255, -10, 4, 102, 11, 2, 255, -14, 5, 5, 11, 102, 105, 255, -9, // Case : 201 -0, 6, 2, 11, 5, 7, 8, 0, -10, 4, 105, 101, 104, 255, -14, 5, 105, 104, 7, 5, 255, -10, 4, 102, 101, 105, 255, -14, 5, 102, 105, 5, 11, 255, -10, 4, 2, 102, 11, 255, -14, 5, 102, 2, 0, 101, 255, -14, 5, 0, 8, 104, 101, 255, -10, 4, 104, 8, 7, 255, -8, // Case : 202 -0, 5, 7, 5, 3, 0, 9, -10, 4, 102, 11, 2, 1, -14, 5, 105, 104, 7, 5, 255, -14, 5, 104, 100, 3, 7, 255, -10, 4, 100, 0, 3, 255, -10, 4, 104, 105, 100, 255, -14, 5, 100, 105, 9, 0, 255, -10, 4, 105, 5, 9, 255, -2, // Case : 203 -13, 6, 5, 105, 9, 7, 104, 8, -10, 4, 2, 1, 102, 11, -1, // Case : 204 -12, 8, 3, 1, 5, 7, 100, 101, 105, 104, -7, // Case : 205 -0, 5, 1, 5, 7, 0, 8, -14, 5, 105, 104, 7, 5, 255, -10, 4, 104, 8, 7, 255, -14, 5, 101, 105, 5, 1, 255, -10, 4, 0, 101, 1, 255, -14, 5, 8, 104, 101, 0, 255, -10, 4, 104, 105, 101, 255, -7, // Case : 206 -0, 5, 5, 7, 3, 9, 0, -14, 5, 104, 100, 3, 7, 255, -10, 4, 100, 0, 3, 255, -14, 5, 105, 104, 7, 5, 255, -10, 4, 9, 105, 5, 255, -14, 5, 0, 100, 105, 9, 255, -10, 4, 100, 104, 105, 255, -1, // Case : 207 -13, 6, 105, 9, 5, 104, 8, 7, -9, // Case : 208 -0, 5, 8, 10, 4, 5, 11, -14, 5, 100, 101, 102, 103, 255, -10, 4, 102, 105, 255, 101, -14, 5, 103, 102, 11, 10, 255, -14, 5, 8, 100, 103, 10, 255, -10, 4, 105, 101, 100, 255, -14, 5, 105, 100, 8, 4, 255, -10, 4, 105, 4, 5, 255, -14, 5, 11, 102, 105, 5, 255, -9, // Case : 209 -0, 6, 4, 5, 11, 10, 3, 0, -10, 4, 102, 103, 101, 255, -14, 5, 102, 11, 10, 103, 255, -10, 4, 105, 102, 101, 255, -14, 5, 105, 5, 11, 102, 255, -10, 4, 4, 5, 105, 255, -14, 5, 105, 101, 0, 4, 255, -14, 5, 0, 101, 103, 3, 255, -10, 4, 103, 10, 3, 255, -8, // Case : 210 -0, 5, 10, 11, 8, 0, 1, -10, 4, 105, 4, 5, 9, -14, 5, 102, 11, 10, 103, 255, -14, 5, 103, 10, 8, 100, 255, -10, 4, 100, 8, 0, 255, -10, 4, 103, 100, 102, 255, -14, 5, 100, 0, 1, 102, 255, -10, 4, 102, 1, 11, 255, -2, // Case : 211 -13, 6, 10, 103, 3, 11, 102, 1, -10, 4, 4, 105, 9, 5, -9, // Case : 212 -0, 6, 10, 2, 1, 5, 4, 8, -10, 4, 101, 100, 105, 255, -14, 5, 101, 105, 5, 1, 255, -10, 4, 103, 100, 101, 255, -14, 5, 103, 101, 1, 2, 255, -10, 4, 10, 103, 2, 255, -14, 5, 103, 10, 8, 100, 255, -14, 5, 8, 4, 105, 100, 255, -10, 4, 105, 4, 5, 255, -2, // Case : 213 -13, 6, 1, 101, 0, 5, 105, 4, -10, 4, 10, 3, 103, 2, -2, // Case : 214 -13, 6, 8, 100, 0, 10, 103, 2, -10, 4, 5, 9, 105, 4, -2, // Case : 215 -10, 4, 5, 4, 9, 105, -10, 4, 2, 3, 10, 103, -5, // Case : 216 -10, 4, 102, 100, 101, 105, -14, 5, 4, 105, 100, 8, 3, -14, 5, 5, 11, 102, 105, 2, -14, 5, 100, 102, 2, 3, 105, -14, 5, 3, 2, 5, 4, 105, -7, // Case : 217 -0, 5, 2, 0, 4, 11, 5, -14, 5, 101, 0, 4, 105, 255, -10, 4, 105, 4, 5, 255, -14, 5, 102, 2, 0, 101, 255, -10, 4, 11, 2, 102, 255, -14, 5, 5, 11, 102, 105, 255, -10, 4, 105, 102, 101, 255, -3, // Case : 218 -10, 4, 2, 11, 1, 102, -10, 4, 3, 8, 100, 0, -10, 4, 5, 105, 4, 9, -2, // Case : 219 -10, 4, 11, 1, 2, 102, -10, 4, 5, 4, 9, 105, -7, // Case : 220 -0, 5, 5, 1, 3, 4, 8, -14, 5, 101, 1, 3, 100, 255, -10, 4, 100, 3, 8, 255, -14, 5, 105, 5, 1, 101, 255, -10, 4, 4, 5, 105, 255, -14, 5, 8, 4, 105, 100, 255, -10, 4, 100, 105, 101, 255, -1, // Case : 221 -13, 6, 101, 0, 1, 105, 4, 5, -2, // Case : 222 -10, 4, 0, 8, 3, 100, -10, 4, 9, 5, 4, 105, -1, // Case : 223 -10, 4, 9, 5, 4, 105, -9, // Case : 224 -0, 5, 10, 11, 7, 4, 9, -14, 5, 103, 100, 101, 102, 255, -10, 4, 101, 104, 255, 100, -14, 5, 102, 101, 9, 11, 255, -14, 5, 10, 103, 102, 11, 255, -10, 4, 104, 100, 103, 255, -14, 5, 104, 103, 10, 7, 255, -10, 4, 104, 7, 4, 255, -14, 5, 9, 101, 104, 4, 255, -8, // Case : 225 -0, 5, 11, 9, 10, 3, 0, -10, 4, 104, 7, 4, 8, -14, 5, 101, 9, 11, 102, 255, -14, 5, 102, 11, 10, 103, 255, -10, 4, 103, 10, 3, 255, -10, 4, 102, 103, 101, 255, -14, 5, 103, 3, 0, 101, 255, -10, 4, 101, 0, 9, 255, -9, // Case : 226 -0, 6, 4, 7, 10, 11, 1, 0, -10, 4, 103, 100, 102, 255, -14, 5, 103, 102, 11, 10, 255, -10, 4, 104, 100, 103, 255, -14, 5, 104, 103, 10, 7, 255, -10, 4, 4, 104, 7, 255, -14, 5, 104, 4, 0, 100, 255, -14, 5, 0, 1, 102, 100, 255, -10, 4, 102, 1, 11, 255, -2, // Case : 227 -13, 6, 10, 103, 3, 11, 102, 1, -10, 4, 4, 8, 104, 7, -5, // Case : 228 -10, 4, 103, 100, 101, 104, -14, 5, 4, 9, 101, 104, 1, -14, 5, 7, 104, 103, 10, 2, -14, 5, 101, 1, 2, 103, 104, -14, 5, 1, 4, 7, 2, 104, -3, // Case : 229 -10, 4, 2, 3, 10, 103, -10, 4, 1, 101, 9, 0, -10, 4, 7, 4, 104, 8, -7, // Case : 230 -0, 5, 2, 0, 4, 10, 7, -14, 5, 100, 104, 4, 0, 255, -10, 4, 104, 7, 4, 255, -14, 5, 103, 100, 0, 2, 255, -10, 4, 10, 103, 2, 255, -14, 5, 7, 104, 103, 10, 255, -10, 4, 104, 100, 103, 255, -2, // Case : 231 -10, 4, 10, 2, 3, 103, -10, 4, 7, 8, 4, 104, -9, // Case : 232 -0, 6, 7, 4, 9, 11, 2, 3, -10, 4, 101, 102, 100, 255, -14, 5, 101, 9, 11, 102, 255, -10, 4, 104, 101, 100, 255, -14, 5, 104, 4, 9, 101, 255, -10, 4, 7, 4, 104, 255, -14, 5, 104, 100, 3, 7, 255, -14, 5, 3, 100, 102, 2, 255, -10, 4, 102, 11, 2, 255, -2, // Case : 233 -13, 6, 11, 102, 2, 9, 101, 0, -10, 4, 7, 104, 8, 4, -2, // Case : 234 -13, 6, 7, 104, 4, 3, 100, 0, -10, 4, 11, 102, 1, 2, -2, // Case : 235 -10, 4, 11, 1, 2, 102, -10, 4, 4, 7, 8, 104, -7, // Case : 236 -0, 5, 7, 3, 1, 4, 9, -14, 5, 100, 101, 1, 3, 255, -10, 4, 101, 9, 1, 255, -14, 5, 104, 100, 3, 7, 255, -10, 4, 4, 104, 7, 255, -14, 5, 9, 101, 104, 4, 255, -10, 4, 101, 100, 104, 255, -2, // Case : 237 -10, 4, 4, 7, 8, 104, -10, 4, 9, 0, 1, 101, -1, // Case : 238 -13, 6, 104, 4, 7, 100, 0, 3, -1, // Case : 239 -10, 4, 7, 8, 4, 104, -1, // Case : 240 -12, 8, 100, 101, 102, 103, 8, 9, 11, 10, -7, // Case : 241 -0, 5, 9, 11, 10, 0, 3, -14, 5, 102, 11, 10, 103, 255, -10, 4, 103, 10, 3, 255, -14, 5, 101, 9, 11, 102, 255, -10, 4, 0, 9, 101, 255, -14, 5, 3, 0, 101, 103, 255, -10, 4, 103, 101, 102, 255, -7, // Case : 242 -0, 5, 11, 10, 8, 1, 0, -14, 5, 103, 10, 8, 100, 255, -10, 4, 100, 8, 0, 255, -14, 5, 102, 11, 10, 103, 255, -10, 4, 1, 11, 102, 255, -14, 5, 0, 1, 102, 100, 255, -10, 4, 100, 102, 103, 255, -1, // Case : 243 -13, 6, 103, 3, 10, 102, 1, 11, -7, // Case : 244 -0, 5, 10, 8, 9, 2, 1, -14, 5, 100, 8, 9, 101, 255, -10, 4, 101, 9, 1, 255, -14, 5, 103, 10, 8, 100, 255, -10, 4, 2, 10, 103, 255, -14, 5, 1, 2, 103, 101, 255, -10, 4, 101, 103, 100, 255, -2, // Case : 245 -10, 4, 2, 3, 10, 103, -10, 4, 1, 9, 0, 101, -1, // Case : 246 -13, 6, 100, 0, 8, 103, 2, 10, -1, // Case : 247 -10, 4, 10, 2, 3, 103, -7, // Case : 248 -0, 5, 11, 9, 8, 2, 3, -14, 5, 101, 100, 8, 9, 255, -10, 4, 100, 3, 8, 255, -14, 5, 102, 101, 9, 11, 255, -10, 4, 2, 102, 11, 255, -14, 5, 3, 100, 102, 2, 255, -10, 4, 100, 101, 102, 255, -1, // Case : 249 -13, 6, 102, 2, 11, 101, 0, 9, -2, // Case : 250 -10, 4, 0, 8, 3, 100, -10, 4, 1, 2, 11, 102, -1, // Case : 251 -10, 4, 1, 2, 11, 102, -1, // Case : 252 -13, 6, 100, 8, 3, 101, 9, 1, -1, // Case : 253 -10, 4, 1, 9, 0, 101, -1, // Case : 254 -10, 4, 3, 0, 8, 100, -0, // Case : 255 -// Wdg -1, // Case : 0 -13, 6, 100, 101, 102, 103, 104, 105, -2, // Case : 1 -13, 6, 0, 2, 6, 101, 102, 103, -14, 5, 101, 102, 105, 104, 103, -2, // Case : 2 -13, 6, 1, 0, 7, 102, 100, 104, -14, 5, 102, 100, 103, 105, 104, -8, // Case : 3 -0, 7, 102, 103, 104, 1, 2, 6, 7, -10, 4, 104, 105, 103, 102, -10, 4, 102, 103, 104, 255, -14, 5, 6, 7, 104, 103, 255, -14, 5, 1, 7, 6, 2, 255, -10, 4, 102, 1, 2, 255, -14, 5, 102, 2, 6, 103, 255, -14, 5, 7, 1, 102, 104, 255, -2, // Case : 4 -13, 6, 2, 1, 8, 100, 101, 105, -14, 5, 100, 101, 104, 103, 105, -8, // Case : 5 -0, 7, 101, 105, 103, 0, 1, 8, 6, -10, 4, 103, 104, 105, 101, -10, 4, 101, 105, 103, 255, -14, 5, 8, 6, 103, 105, 255, -14, 5, 0, 6, 8, 1, 255, -10, 4, 101, 0, 1, 255, -14, 5, 101, 1, 8, 105, 255, -14, 5, 6, 0, 101, 103, 255, -8, // Case : 6 -0, 7, 100, 104, 105, 2, 0, 7, 8, -10, 4, 105, 103, 104, 100, -10, 4, 100, 104, 105, 255, -14, 5, 7, 8, 105, 104, 255, -14, 5, 2, 8, 7, 0, 255, -10, 4, 100, 2, 0, 255, -14, 5, 100, 0, 7, 104, 255, -14, 5, 8, 2, 100, 105, 255, -1, // Case : 7 -13, 6, 6, 7, 8, 103, 104, 105, -2, // Case : 8 -13, 6, 104, 105, 100, 3, 5, 6, -14, 5, 104, 101, 102, 105, 100, -1, // Case : 9 -12, 8, 101, 102, 105, 104, 0, 2, 5, 3, -9, // Case : 10 -0, 6, 0, 1, 7, 3, 5, 6, -14, 5, 105, 100, 6, 5, 255, -10, 4, 100, 0, 6, 255, -14, 5, 100, 102, 1, 0, 255, -10, 4, 105, 102, 100, 255, -14, 5, 104, 7, 1, 102, 255, -10, 4, 105, 104, 102, 255, -14, 5, 5, 3, 104, 105, 255, -10, 4, 3, 7, 104, 255, -7, // Case : 11 -0, 5, 1, 2, 5, 3, 7, -14, 5, 104, 105, 5, 3, 255, -10, 4, 3, 7, 104, 255, -14, 5, 2, 5, 105, 102, 255, -14, 5, 1, 102, 104, 7, 255, -10, 4, 104, 102, 105, 255, -10, 4, 102, 1, 2, 255, -9, // Case : 12 -0, 6, 5, 3, 6, 2, 1, 8, -14, 5, 101, 1, 8, 105, 255, -10, 4, 105, 8, 5, 255, -14, 5, 105, 5, 3, 104, 255, -10, 4, 101, 105, 104, 255, -14, 5, 100, 104, 3, 6, 255, -10, 4, 101, 104, 100, 255, -14, 5, 1, 101, 100, 2, 255, -10, 4, 2, 100, 6, 255, -7, // Case : 13 -0, 5, 1, 0, 3, 5, 8, -14, 5, 105, 5, 3, 104, 255, -10, 4, 5, 105, 8, 255, -14, 5, 0, 101, 104, 3, 255, -14, 5, 1, 8, 105, 101, 255, -10, 4, 105, 104, 101, 255, -10, 4, 101, 0, 1, 255, -2, // Case : 14 -10, 4, 100, 2, 0, 6, -13, 6, 5, 8, 105, 3, 7, 104, -1, // Case : 15 -13, 6, 3, 104, 7, 5, 105, 8, -2, // Case : 16 -13, 6, 105, 103, 101, 4, 3, 7, -14, 5, 105, 102, 100, 103, 101, -9, // Case : 17 -0, 6, 3, 4, 7, 0, 2, 6, -14, 5, 102, 2, 6, 103, 255, -10, 4, 103, 6, 3, 255, -14, 5, 103, 3, 4, 105, 255, -10, 4, 102, 103, 105, 255, -14, 5, 101, 105, 4, 7, 255, -10, 4, 102, 105, 101, 255, -14, 5, 2, 102, 101, 0, 255, -10, 4, 0, 101, 7, 255, -1, // Case : 18 -12, 8, 102, 100, 103, 105, 1, 0, 3, 4, -7, // Case : 19 -0, 5, 2, 1, 4, 3, 6, -14, 5, 103, 3, 4, 105, 255, -10, 4, 3, 103, 6, 255, -14, 5, 1, 102, 105, 4, 255, -14, 5, 2, 6, 103, 102, 255, -10, 4, 103, 105, 102, 255, -10, 4, 102, 1, 2, 255, -9, // Case : 20 -0, 6, 1, 2, 8, 4, 3, 7, -14, 5, 103, 101, 7, 3, 255, -10, 4, 101, 1, 7, 255, -14, 5, 101, 100, 2, 1, 255, -10, 4, 103, 100, 101, 255, -14, 5, 105, 8, 2, 100, 255, -10, 4, 103, 105, 100, 255, -14, 5, 3, 4, 105, 103, 255, -10, 4, 4, 8, 105, 255, -2, // Case : 21 -10, 4, 101, 0, 1, 7, -13, 6, 3, 6, 103, 4, 8, 105, -7, // Case : 22 -0, 5, 2, 0, 3, 4, 8, -14, 5, 105, 103, 3, 4, 255, -10, 4, 4, 8, 105, 255, -14, 5, 0, 3, 103, 100, 255, -14, 5, 2, 100, 105, 8, 255, -10, 4, 105, 100, 103, 255, -10, 4, 100, 2, 0, 255, -1, // Case : 23 -13, 6, 4, 105, 8, 3, 103, 6, -8, // Case : 24 -0, 7, 105, 100, 101, 4, 5, 6, 7, -10, 4, 101, 100, 102, 105, -10, 4, 105, 101, 100, 255, -14, 5, 6, 100, 101, 7, 255, -14, 5, 4, 5, 6, 7, 255, -10, 4, 105, 5, 4, 255, -14, 5, 105, 100, 6, 5, 255, -14, 5, 7, 101, 105, 4, 255, -7, // Case : 25 -0, 5, 4, 5, 2, 0, 7, -14, 5, 101, 0, 2, 102, 255, -10, 4, 0, 101, 7, 255, -14, 5, 5, 105, 102, 2, 255, -14, 5, 4, 7, 101, 105, 255, -10, 4, 101, 102, 105, 255, -10, 4, 105, 5, 4, 255, -7, // Case : 26 -0, 5, 5, 4, 1, 0, 6, -14, 5, 100, 102, 1, 0, 255, -10, 4, 0, 6, 100, 255, -14, 5, 4, 1, 102, 105, 255, -14, 5, 5, 105, 100, 6, 255, -10, 4, 100, 105, 102, 255, -10, 4, 105, 5, 4, 255, -1, // Case : 27 -13, 6, 5, 105, 4, 2, 102, 1, -2, // Case : 28 -10, 4, 105, 5, 4, 8, -13, 6, 2, 6, 100, 1, 7, 101, -2, // Case : 29 -10, 4, 101, 0, 1, 7, -10, 4, 5, 4, 105, 8, -2, // Case : 30 -10, 4, 105, 5, 4, 8, -10, 4, 0, 100, 2, 6, -1, // Case : 31 -10, 4, 5, 8, 4, 105, -2, // Case : 32 -13, 6, 103, 104, 102, 5, 4, 8, -14, 5, 103, 100, 101, 104, 102, -9, // Case : 33 -0, 6, 2, 0, 6, 5, 4, 8, -14, 5, 104, 102, 8, 4, 255, -10, 4, 102, 2, 8, 255, -14, 5, 102, 101, 0, 2, 255, -10, 4, 104, 101, 102, 255, -14, 5, 103, 6, 0, 101, 255, -10, 4, 104, 103, 101, 255, -14, 5, 4, 5, 103, 104, 255, -10, 4, 5, 6, 103, 255, -9, // Case : 34 -0, 6, 4, 5, 8, 1, 0, 7, -14, 5, 100, 0, 7, 104, 255, -10, 4, 104, 7, 4, 255, -14, 5, 104, 4, 5, 103, 255, -10, 4, 100, 104, 103, 255, -14, 5, 102, 103, 5, 8, 255, -10, 4, 100, 103, 102, 255, -14, 5, 0, 100, 102, 1, 255, -10, 4, 1, 102, 8, 255, -2, // Case : 35 -10, 4, 102, 1, 2, 8, -13, 6, 4, 7, 104, 5, 6, 103, -1, // Case : 36 -12, 8, 100, 101, 104, 103, 2, 1, 4, 5, -7, // Case : 37 -0, 5, 0, 1, 4, 5, 6, -14, 5, 103, 104, 4, 5, 255, -10, 4, 5, 6, 103, 255, -14, 5, 1, 4, 104, 101, 255, -14, 5, 0, 101, 103, 6, 255, -10, 4, 103, 101, 104, 255, -10, 4, 101, 0, 1, 255, -7, // Case : 38 -0, 5, 0, 2, 5, 4, 7, -14, 5, 104, 4, 5, 103, 255, -10, 4, 4, 104, 7, 255, -14, 5, 2, 100, 103, 5, 255, -14, 5, 0, 7, 104, 100, 255, -10, 4, 104, 103, 100, 255, -10, 4, 100, 2, 0, 255, -1, // Case : 39 -13, 6, 5, 103, 6, 4, 104, 7, -8, // Case : 40 -0, 7, 104, 102, 100, 3, 4, 8, 6, -10, 4, 100, 102, 101, 104, -10, 4, 104, 100, 102, 255, -14, 5, 8, 102, 100, 6, 255, -14, 5, 3, 4, 8, 6, 255, -10, 4, 104, 4, 3, 255, -14, 5, 104, 102, 8, 4, 255, -14, 5, 6, 100, 104, 3, 255, -7, // Case : 41 -0, 5, 4, 3, 0, 2, 8, -14, 5, 102, 101, 0, 2, 255, -10, 4, 2, 8, 102, 255, -14, 5, 3, 0, 101, 104, 255, -14, 5, 4, 104, 102, 8, 255, -10, 4, 102, 104, 101, 255, -10, 4, 104, 4, 3, 255, -2, // Case : 42 -10, 4, 104, 4, 3, 7, -13, 6, 1, 8, 102, 0, 6, 100, -2, // Case : 43 -10, 4, 104, 4, 3, 7, -10, 4, 2, 102, 1, 8, -7, // Case : 44 -0, 5, 3, 4, 1, 2, 6, -14, 5, 100, 2, 1, 101, 255, -10, 4, 2, 100, 6, 255, -14, 5, 4, 104, 101, 1, 255, -14, 5, 3, 6, 100, 104, 255, -10, 4, 100, 101, 104, 255, -10, 4, 104, 4, 3, 255, -1, // Case : 45 -13, 6, 4, 104, 3, 1, 101, 0, -2, // Case : 46 -10, 4, 100, 2, 0, 6, -10, 4, 4, 3, 104, 7, -1, // Case : 47 -10, 4, 4, 7, 3, 104, -8, // Case : 48 -0, 7, 103, 101, 102, 5, 3, 7, 8, -10, 4, 102, 101, 100, 103, -10, 4, 103, 102, 101, 255, -14, 5, 7, 101, 102, 8, 255, -14, 5, 5, 3, 7, 8, 255, -10, 4, 103, 3, 5, 255, -14, 5, 103, 101, 7, 3, 255, -14, 5, 8, 102, 103, 5, 255, -2, // Case : 49 -10, 4, 103, 3, 5, 6, -13, 6, 0, 7, 101, 2, 8, 102, -7, // Case : 50 -0, 5, 5, 3, 0, 1, 8, -14, 5, 102, 1, 0, 100, 255, -10, 4, 1, 102, 8, 255, -14, 5, 3, 103, 100, 0, 255, -14, 5, 5, 8, 102, 103, 255, -10, 4, 102, 100, 103, 255, -10, 4, 103, 3, 5, 255, -2, // Case : 51 -10, 4, 102, 1, 2, 8, -10, 4, 3, 5, 103, 6, -7, // Case : 52 -0, 5, 3, 5, 2, 1, 7, -14, 5, 101, 100, 2, 1, 255, -10, 4, 1, 7, 101, 255, -14, 5, 5, 2, 100, 103, 255, -14, 5, 3, 103, 101, 7, 255, -10, 4, 101, 103, 100, 255, -10, 4, 103, 3, 5, 255, -2, // Case : 53 -10, 4, 103, 3, 5, 6, -10, 4, 1, 101, 0, 7, -1, // Case : 54 -13, 6, 3, 103, 5, 0, 100, 2, -1, // Case : 55 -10, 4, 3, 6, 5, 103, -1, // Case : 56 -13, 6, 100, 101, 102, 6, 7, 8, -1, // Case : 57 -13, 6, 2, 102, 8, 0, 101, 7, -1, // Case : 58 -13, 6, 0, 100, 6, 1, 102, 8, -1, // Case : 59 -10, 4, 2, 1, 8, 102, -1, // Case : 60 -13, 6, 1, 101, 7, 2, 100, 6, -1, // Case : 61 -10, 4, 1, 0, 7, 101, -1, // Case : 62 -10, 4, 0, 2, 6, 100, -0, // Case : 63 -// Pyr -1, // Case : 0 -14, 5, 100, 101, 102, 103, 104, -2, // Case : 1 -13, 6, 0, 4, 3, 101, 104, 103, -10, 4, 101, 102, 103, 104, -2, // Case : 2 -13, 6, 1, 5, 0, 102, 104, 100, -10, 4, 102, 103, 100, 104, -7, // Case : 3 -0, 7, 104, 5, 4, 1, 3, 102, 103, -10, 4, 4, 104, 5, 255, -14, 5, 1, 3, 4, 5, 255, -14, 5, 1, 5, 104, 102, 255, -10, 4, 102, 104, 103, 255, -14, 5, 103, 104, 4, 3, 255, -14, 5, 102, 103, 3, 1, 255, -2, // Case : 4 -13, 6, 2, 6, 1, 103, 104, 101, -10, 4, 103, 100, 101, 104, -2, // Case : 5 -13, 6, 4, 104, 6, 0, 101, 1, -13, 6, 104, 4, 6, 103, 3, 2, -7, // Case : 6 -0, 7, 104, 6, 5, 2, 0, 103, 100, -10, 4, 5, 104, 6, 255, -14, 5, 2, 0, 5, 6, 255, -14, 5, 2, 6, 104, 103, 255, -10, 4, 103, 104, 100, 255, -14, 5, 100, 104, 5, 0, 255, -14, 5, 103, 100, 0, 2, 255, -2, // Case : 7 -10, 4, 4, 5, 6, 104, -13, 6, 2, 3, 103, 6, 4, 104, -2, // Case : 8 -13, 6, 3, 7, 2, 100, 104, 102, -10, 4, 100, 101, 102, 104, -7, // Case : 9 -0, 7, 104, 4, 7, 0, 2, 101, 102, -10, 4, 7, 104, 4, 255, -14, 5, 0, 2, 7, 4, 255, -14, 5, 0, 4, 104, 101, 255, -10, 4, 101, 104, 102, 255, -14, 5, 102, 104, 7, 2, 255, -14, 5, 101, 102, 2, 0, 255, -2, // Case : 10 -13, 6, 7, 104, 5, 3, 100, 0, -13, 6, 104, 7, 5, 102, 2, 1, -2, // Case : 11 -10, 4, 7, 4, 5, 104, -13, 6, 1, 2, 102, 5, 7, 104, -7, // Case : 12 -0, 7, 104, 7, 6, 3, 1, 100, 101, -10, 4, 6, 104, 7, 255, -14, 5, 3, 1, 6, 7, 255, -14, 5, 3, 7, 104, 100, 255, -10, 4, 100, 104, 101, 255, -14, 5, 101, 104, 6, 1, 255, -14, 5, 100, 101, 1, 3, 255, -2, // Case : 13 -10, 4, 6, 7, 4, 104, -13, 6, 0, 1, 101, 4, 6, 104, -2, // Case : 14 -10, 4, 5, 6, 7, 104, -13, 6, 3, 0, 100, 7, 5, 104, -1, // Case : 15 -14, 5, 4, 5, 6, 7, 104, -1, // Case : 16 -12, 8, 100, 101, 102, 103, 4, 5, 6, 7, -2, // Case : 17 -13, 6, 3, 7, 103, 0, 5, 101, -13, 6, 5, 6, 7, 101, 102, 103, -2, // Case : 18 -13, 6, 0, 4, 100, 1, 6, 102, -13, 6, 6, 7, 4, 102, 103, 100, -1, // Case : 19 -13, 6, 3, 7, 103, 1, 6, 102, -2, // Case : 20 -13, 6, 1, 5, 101, 2, 7, 103, -13, 6, 7, 4, 5, 103, 100, 101, -2, // Case : 21 -10, 4, 0, 101, 1, 5, -10, 4, 103, 3, 2, 7, -1, // Case : 22 -13, 6, 0, 4, 100, 2, 7, 103, -1, // Case : 23 -10, 4, 103, 3, 2, 7, -2, // Case : 24 -13, 6, 2, 6, 102, 3, 4, 100, -13, 6, 4, 5, 6, 100, 101, 102, -1, // Case : 25 -13, 6, 2, 6, 102, 0, 5, 101, -2, // Case : 26 -10, 4, 3, 100, 0, 4, -10, 4, 102, 2, 1, 6, -1, // Case : 27 -10, 4, 102, 2, 1, 6, -1, // Case : 28 -13, 6, 1, 5, 101, 3, 4, 100, -1, // Case : 29 -10, 4, 101, 1, 0, 5, -1, // Case : 30 -10, 4, 100, 0, 3, 4, -0, // Case : 31 -}; +namespace ClipTablesInformation +{ +// Points of original cell (up to 8, for the hex) +// Note: we assume P0 is zero in several places. +// Note: we assume these values are contiguous and monotonic. +static constexpr vtkm::UInt8 P0 = 0; +static constexpr vtkm::UInt8 P1 = 1; +static constexpr vtkm::UInt8 P2 = 2; +static constexpr vtkm::UInt8 P3 = 3; +static constexpr vtkm::UInt8 P4 = 4; +static constexpr vtkm::UInt8 P5 = 5; +static constexpr vtkm::UInt8 P6 = 6; +static constexpr vtkm::UInt8 P7 = 7; -// Index into ClipTablesData for each shape and configuration -VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt16 ClipTablesIndices[] = { -//Vtx -0, 4, //cases 0 - 1 -//Lin -5, 10, 15, 20, //cases 0 - 3 -//Tri -21, 27, 34, 41, 47, 54, 60, 66, //cases 0 - 7 -//Qua -67, 74, 86, 98, 105, 117, 130, 137, //cases 0 - 7 -143, 155, 162, 173, 179, 186, 192, 198, //cases 8 - 15 -//Tet -199, 206, 215, 224, 233, 242, 251, 260, //cases 0 - 7 -267, 276, 285, 294, 301, 310, 317, 324, //cases 8 - 15 -//Hex -325, 336, 399, 462, 481, 544, 620, 639, //cases 0 - 7 -700, 763, 782, 858, 919, 938, 999, 1060, //cases 8 - 15 -1071, 1134, 1153, 1229, 1290, 1331, 1403, 1475, //cases 16 - 23 -1536, 1612, 1673, 1714, 1749, 1821, 1882, 1935, //cases 24 - 31 -1982, 2045, 2121, 2140, 2201, 2277, 2318, 2379, //cases 32 - 39 -2414, 2455, 2527, 2599, 2660, 2732, 2785, 2846, //cases 40 - 47 -2893, 2912, 2973, 3034, 3045, 3117, 3170, 3231, //cases 48 - 55 -3278, 3350, 3411, 3464, 3511, 3528, 3543, 3558, //cases 56 - 63 -3567, 3630, 3671, 3747, 3819, 3838, 3910, 3971, //cases 64 - 71 -4032, 4108, 4180, 4221, 4274, 4335, 4396, 4431, //cases 72 - 79 -4478, 4554, 4626, 4667, 4720, 4792, 4809, 4862, //cases 80 - 87 -4877, 4918, 4971, 4996, 5015, 5068, 5083, 5102, //cases 88 - 95 -5115, 5134, 5206, 5267, 5328, 5389, 5442, 5453, //cases 96 - 103 -5500, 5572, 5589, 5642, 5657, 5718, 5733, 5780, //cases 104 - 111 -5789, 5850, 5911, 5946, 5993, 6054, 6069, 6116, //cases 112 - 119 -6125, 6178, 6193, 6212, 6225, 6240, 6253, 6266, //cases 120 - 127 -6273, 6336, 6412, 6453, 6525, 6601, 6642, 6714, //cases 128 - 135 -6767, 6786, 6847, 6919, 6980, 7041, 7076, 7137, //cases 136 - 143 -7184, 7203, 7264, 7336, 7397, 7469, 7522, 7539, //cases 144 - 151 -7554, 7615, 7626, 7679, 7726, 7787, 7834, 7849, //cases 152 - 159 -7858, 7934, 7975, 8047, 8100, 8141, 8166, 8219, //cases 160 - 167 -8238, 8310, 8363, 8380, 8395, 8448, 8467, 8482, //cases 168 - 175 -8495, 8556, 8591, 8652, 8699, 8752, 8771, 8786, //cases 176 - 183 -8799, 8860, 8907, 8922, 8931, 8946, 8959, 8972, //cases 184 - 191 -8979, 8998, 9070, 9142, 9159, 9220, 9273, 9334, //cases 192 - 199 -9349, 9410, 9471, 9524, 9539, 9550, 9597, 9644, //cases 200 - 207 -9653, 9714, 9775, 9828, 9843, 9904, 9919, 9934, //cases 208 - 215 -9947, 9982, 10029, 10048, 10061, 10108, 10117, 10130, //cases 216 - 223 -10137, 10198, 10251, 10312, 10327, 10362, 10381, 10428, //cases 224 - 231 -10441, 10502, 10517, 10532, 10545, 10592, 10605, 10614, //cases 232 - 239 -10621, 10632, 10679, 10726, 10735, 10782, 10795, 10804, //cases 240 - 247 -10811, 10858, 10867, 10880, 10887, 10896, 10903, 10910, //cases 248 - 255 -//Wdg -10911, 10920, 10936, 10952, 11008, 11024, 11080, 11136, //cases 0 - 7 -11145, 11161, 11172, 11233, 11280, 11341, 11388, 11403, //cases 8 - 15 -11412, 11428, 11489, 11500, 11547, 11608, 11623, 11670, //cases 16 - 23 -11679, 11735, 11782, 11829, 11838, 11853, 11866, 11879, //cases 24 - 31 -11886, 11902, 11963, 12024, 12039, 12050, 12097, 12144, //cases 32 - 39 -12153, 12209, 12256, 12271, 12284, 12331, 12340, 12353, //cases 40 - 47 -12360, 12416, 12431, 12478, 12491, 12538, 12551, 12560, //cases 48 - 55 -12567, 12576, 12585, 12594, 12601, 12610, 12617, 12624, //cases 56 - 63 -//Pyr -12625, 12633, 12648, 12663, 12713, 12728, 12745, 12795, //cases 0 - 7 -12810, 12825, 12875, 12892, 12907, 12957, 12972, 12987, //cases 8 - 15 -12995, 13006, 13023, 13040, 13049, 13066, 13079, 13088, //cases 16 - 23 -13095, 13112, 13121, 13134, 13141, 13150, 13157, 13164, //cases 24 - 31 -}; +// Edges of original cell (up to 12, for the hex) +// Note: we assume these values are contiguous and monotonic. +static constexpr vtkm::UInt8 E00 = 8; +static constexpr vtkm::UInt8 E01 = 9; +static constexpr vtkm::UInt8 E02 = 10; +static constexpr vtkm::UInt8 E03 = 11; +static constexpr vtkm::UInt8 E04 = 12; +static constexpr vtkm::UInt8 E05 = 13; +static constexpr vtkm::UInt8 E06 = 14; +static constexpr vtkm::UInt8 E07 = 15; +static constexpr vtkm::UInt8 E08 = 16; +static constexpr vtkm::UInt8 E09 = 17; +static constexpr vtkm::UInt8 E10 = 18; +static constexpr vtkm::UInt8 E11 = 19; + +// New interpolated point (ST_PNT outputs) +static constexpr vtkm::UInt8 N0 = 20; + +// Shape types +static constexpr vtkm::UInt8 ST_VTX = vtkm::CELL_SHAPE_VERTEX; +static constexpr vtkm::UInt8 ST_LIN = vtkm::CELL_SHAPE_LINE; +static constexpr vtkm::UInt8 ST_TRI = vtkm::CELL_SHAPE_TRIANGLE; +static constexpr vtkm::UInt8 ST_QUA = vtkm::CELL_SHAPE_QUAD; +static constexpr vtkm::UInt8 ST_TET = vtkm::CELL_SHAPE_TETRA; +static constexpr vtkm::UInt8 ST_HEX = vtkm::CELL_SHAPE_HEXAHEDRON; +static constexpr vtkm::UInt8 ST_PYR = vtkm::CELL_SHAPE_PYRAMID; +static constexpr vtkm::UInt8 ST_WDG = vtkm::CELL_SHAPE_WEDGE; +// This is a special shape type for a centroid point +static constexpr vtkm::UInt8 ST_PNT = vtkm::CELL_SHAPE_EMPTY; -enum +static constexpr vtkm::UInt8 MAX_CELL_INDICES = 8; // Hexahedron +static constexpr vtkm::UInt8 MAX_CELL_EDGES = 12; // Hexahedron +static constexpr vtkm::UInt16 CELL_EDGES_SIZE = vtkm::NUMBER_OF_CELL_SHAPES * MAX_CELL_EDGES * 2; +} + +class ClipTablesBase { - CLIP_TABLES_DATA_SIZE = sizeof(ClipTablesData) / sizeof(ClipTablesData[0]), - CLIP_TABLES_INDICES_SIZE = sizeof(ClipTablesIndices) / sizeof(ClipTablesIndices[0]), - MAX_CELL_EDGES = 12, // Hexahedron - CELL_EDGES_SIZE = vtkm::NUMBER_OF_CELL_SHAPES * MAX_CELL_EDGES * 2 -}; +protected: + VTKM_EXEC inline static vtkm::Int32 GetCellClipTableIndex(vtkm::UInt8 shape) + { + // index into ClipTablesIndices for each shape + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Int32 CellIndexLookup[vtkm::NUMBER_OF_CELL_SHAPES] = { + -1, // 0 = vtkm::CELL_SHAPE_EMPTY_CELL + 0, // 1 = vtkm::CELL_SHAPE_VERTEX + -1, // 2 = vtkm::CELL_SHAPE_POLY_VERTEX + 2, // 3 = vtkm::CELL_SHAPE_LINE + -1, // 4 = vtkm::CELL_SHAPE_POLY_LINE + 6, // 5 = vtkm::CELL_SHAPE_TRIANGLE + -1, // 6 = vtkm::CELL_SHAPE_TRIANGLE_STRIP + -1, // 7 = vtkm::CELL_SHAPE_POLYGON + -1, // 8 = vtkm::CELL_SHAPE_PIXEL + 14, // 9 = vtkm::CELL_SHAPE_QUAD + 30, // 10 = vtkm::CELL_SHAPE_TETRA + -1, // 11 = vtkm::CELL_SHAPE_VOXEL + 46, // 12 = vtkm::CELL_SHAPE_HEXAHEDRON + 302, // 13 = vtkm::CELL_SHAPE_WEDGE + 366 // 14 = vtkm::CELL_SHAPE_PYRAMID + }; + return CellIndexLookup[shape]; + } + + VTKM_EXEC inline static vtkm::UInt8 GetMaxCellCase(vtkm::IdComponent numberOfPoints) + { + using namespace ClipTablesInformation; + // index into ClipTablesIndices for each shape + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 CellMaxCase[MAX_CELL_INDICES + 1] = { 0, 1, 3, + 7, 15, 31, + 63, 127, 255 }; + return CellMaxCase[numberOfPoints]; + } + +public: + using EdgeVec = vtkm::Vec; + + VTKM_EXEC inline static EdgeVec GetEdge(vtkm::UInt8 shape, vtkm::Id edgeId) + { + using namespace ClipTablesInformation; -#define X 255 -VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 CellEdges[CELL_EDGES_SIZE] = { - X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_EMPTY_CELL - X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_VERTEX - X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_POLY_VERTEX - 0, 1, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_LINE - X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_POLY_LINE - 0, 1, 1, 2, 2, 0, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_TRIANGLE - X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_TRIANGLE_STRIP - X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_POLYGON - X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_PIXEL - 0, 1, 1, 2, 3, 2, 0, 3, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_QUAD - 0, 1, 1, 2, 2, 0, 0, 3, 1, 3, 2, 3, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_TETRA - X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_VOXEL - 0, 1, 1, 2, 3, 2, 0, 3, 4, 5, 5, 6, - 7, 6, 4, 7, 0, 4, 1, 5, 3, 7, 2, 6, // vtkm::CELL_SHAPE_HEXAHEDRON - 0, 1, 1, 2, 2, 0, 3, 4, 4, 5, 5, 3, - 0, 3, 1, 4, 2, 5, X, X, X, X, X, X, // vtkm::CELL_SHAPE_WEDGE - 0, 1, 1, 2, 2, 3, 3, 0, 0, 4, 1, 4, - 2, 4, 3, 4, X, X, X, X, X, X, X, X // vtkm::CELL_SHAPE_PYRAMID + static constexpr uint8_t X = 255; + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 CellEdges[CELL_EDGES_SIZE] = { + X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_EMPTY_CELL + X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_VERTEX + X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_POLY_VERTEX + 0, 1, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_LINE + X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_POLY_LINE + 0, 1, 1, 2, 2, 0, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_TRIANGLE + X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_TRIANGLE_STRIP + X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_POLYGON + X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_PIXEL + 0, 1, 1, 2, 3, 2, 0, 3, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_QUAD + 0, 1, 1, 2, 2, 0, 0, 3, 1, 3, 2, 3, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_TETRA + X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, // vtkm::CELL_SHAPE_VOXEL + 0, 1, 1, 2, 3, 2, 0, 3, 4, 5, 5, 6, + 7, 6, 4, 7, 0, 4, 1, 5, 3, 7, 2, 6, // vtkm::CELL_SHAPE_HEXAHEDRON + 0, 1, 1, 2, 2, 0, 3, 4, 4, 5, 5, 3, + 0, 3, 1, 4, 2, 5, X, X, X, X, X, X, // vtkm::CELL_SHAPE_WEDGE + 0, 1, 1, 2, 2, 3, 3, 0, 0, 4, 1, 4, + 2, 4, 3, 4, X, X, X, X, X, X, X, X // vtkm::CELL_SHAPE_PYRAMID + }; + + const vtkm::Id index = ((shape * MAX_CELL_EDGES) + edgeId) * 2; + return { CellEdges[index], CellEdges[index + 1] }; + } + +protected: + ClipTablesBase() = default; }; -#undef X -// clang-format on -class ClipTables : public vtkm::cont::ExecutionObjectBase +// Primary template declaration +template +class ClipTables; + +// Specialization for false +template <> +class ClipTables : public ClipTablesBase { public: - using EdgeVec = vtkm::IdComponent2; + ClipTables() + : ClipTablesBase() + { + } + + /** + * Get the value at the given index. + */ + VTKM_EXEC inline static vtkm::UInt8 ValueAt(vtkm::Id idx) + { + using namespace ClipTablesInformation; + // clang-format off + // table format of an example case + // number of shapes + // {{shape-type, nverts, {point1, point2...}}, ...}, \n - template - class DevicePortal + // clip table for all cases of each shape + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 ClipTablesData[] = { + // vtkm::CELL_SHAPE_VERTEX + /* case 0 */ 0, + /* case 1 */ 1, + ST_VTX, 1, P0, + // vtkm::CELL_SHAPE_LINE + /* case 0 */ 0, + /* case 1 */ 1, + ST_LIN, 2, P0, E00, + /* case 2 */ 1, + ST_LIN, 2, P1, E00, + /* case 3 */ 1, + ST_LIN, 2, P0, P1, + // vtkm::CELL_SHAPE_TRIANGLE + /* case 0 */ 0, + /* case 1 */ 1, + ST_TRI, 3, P0, E00, E02, + /* case 2 */ 1, + ST_TRI, 3, P1, E01, E00, + /* case 3 */ 1, + ST_QUA, 4, P0, P1, E01, E02, + /* case 4 */ 1, + ST_TRI, 3, P2, E02, E01, + /* case 5 */ 1, + ST_QUA, 4, P2, P0, E00, E01, + /* case 6 */ 1, + ST_QUA, 4, P1, P2, E02, E00, + /* case 7 */ 1, + ST_TRI, 3, P0, P1, P2, + // vtkm::CELL_SHAPE_QUAD + /* case 0 */ 0, + /* case 1 */ 1, + ST_TRI, 3, P0, E00, E03, + /* case 2 */ 1, + ST_TRI, 3, P1, E01, E00, + /* case 3 */ 1, + ST_QUA, 4, P0, P1, E01, E03, + /* case 4 */ 1, + ST_TRI, 3, P2, E02, E01, + /* case 5 */ 2, + ST_QUA, 4, P2, P0, E00, E01, + ST_QUA, 4, P0, P2, E02, E03, + /* case 6 */ 1, + ST_QUA, 4, P1, P2, E02, E00, + /* case 7 */ 2, + ST_QUA, 4, P0, P2, E02, E03, + ST_TRI, 3, P1, P2, P0, + /* case 8 */ 1, + ST_TRI, 3, P3, E03, E02, + /* case 9 */ 1, + ST_QUA, 4, P3, P0, E00, E02, + /* case 10 */ 2, + ST_QUA, 4, P3, P1, E01, E02, + ST_QUA, 4, P1, P3, E03, E00, + /* case 11 */ 2, + ST_QUA, 4, P3, P1, E01, E02, + ST_TRI, 3, P0, P1, P3, + /* case 12 */ 1, + ST_QUA, 4, P2, P3, E03, E01, + /* case 13 */ 2, + ST_QUA, 4, P2, P0, E00, E01, + ST_TRI, 3, P3, P0, P2, + /* case 14 */ 2, + ST_QUA, 4, P1, P3, E03, E00, + ST_TRI, 3, P2, P3, P1, + /* case 15 */ 1, + ST_QUA, 4, P0, P1, P2, P3, + // vtkm::CELL_SHAPE_TETRA + /* case 0 */ 0, + /* case 1 */ 1, + ST_TET, 4, P0, E00, E02, E03, + /* case 2 */ 1, + ST_TET, 4, P1, E01, E00, E04, + /* case 3 */ 1, + ST_WDG, 6, P0, E03, E02, P1, E04, E01, + /* case 4 */ 1, + ST_TET, 4, P2, E02, E01, E05, + /* case 5 */ 1, + ST_WDG, 6, P2, E05, E01, P0, E03, E00, + /* case 6 */ 1, + ST_WDG, 6, P1, E04, E00, P2, E05, E02, + /* case 7 */ 1, + ST_WDG, 6, E03, E04, E05, P0, P1, P2, + /* case 8 */ 1, + ST_TET, 4, P3, E04, E03, E05, + /* case 9 */ 1, + ST_WDG, 6, P0, E02, E00, P3, E05, E04, + /* case 10 */ 1, + ST_WDG, 6, P3, E05, E03, P1, E01, E00, + /* case 11 */ 1, + ST_WDG, 6, P0, P1, P3, E02, E01, E05, + /* case 12 */ 1, + ST_WDG, 6, P2, E01, E02, P3, E04, E03, + /* case 13 */ 1, + ST_WDG, 6, E00, E01, E04, P0, P2, P3, + /* case 14 */ 1, + ST_WDG, 6, P1, P2, P3, E00, E02, E03, + /* case 15 */ 1, + ST_TET, 4, P0, P1, P2, P3, + // vtkm::CELL_SHAPE_HEXAHEDRON + /* case 0 */ 0, + /* case 1 */ 1, + ST_TET, 4, P0, E00, E03, E08, + /* case 2 */ 1, + ST_TET, 4, P1, E00, E09, E01, + /* case 3 */ 1, + ST_WDG, 6, P1, E01, E09, P0, E03, E08, + /* case 4 */ 1, + ST_TET, 4, P2, E01, E11, E02, + /* case 5 */ 7, + ST_PNT, 2, E08, E11, + ST_PYR, 5, P0, P2, E02, E03, N0, + ST_PYR, 5, E00, E01, P2, P0, N0, + ST_TET, 4, E01, E11, P2, N0, + ST_TET, 4, P2, E11, E02, N0, + ST_TET, 4, E00, N0, P0, E08, + ST_TET, 4, E03, E08, P0, N0, + /* case 6 */ 1, + ST_WDG, 6, P2, E02, E11, P1, E00, E09, + /* case 7 */ 7, + ST_PNT, 5, E08, E09, E03, E02, E11, + ST_PYR, 5, E09, E11, P2, P1, N0, + ST_PYR, 5, E08, E09, P1, P0, N0, + ST_TET, 4, E03, E08, P0, N0, + ST_TET, 4, P0, P1, P2, N0, + ST_PYR, 5, E03, P0, P2, E02, N0, + ST_TET, 4, P2, E11, E02, N0, + /* case 8 */ 1, + ST_TET, 4, P3, E03, E02, E10, + /* case 9 */ 1, + ST_WDG, 6, P3, E10, E02, P0, E08, E00, + /* case 10 */ 7, + ST_PNT, 2, E10, E09, + ST_PYR, 5, P3, E03, E00, P1, N0, + ST_PYR, 5, E02, P3, P1, E01, N0, + ST_TET, 4, E01, P1, E09, N0, + ST_TET, 4, P1, E00, E09, N0, + ST_TET, 4, E02, P3, N0, E10, + ST_TET, 4, E03, P3, E10, N0, + /* case 11 */ 7, + ST_PNT, 5, E09, E08, E01, E02, E10, + ST_PYR, 5, E08, P0, P3, E10, N0, + ST_PYR, 5, E09, P1, P0, E08, N0, + ST_TET, 4, E01, P1, E09, N0, + ST_TET, 4, P1, P3, P0, N0, + ST_PYR, 5, E01, E02, P3, P1, N0, + ST_TET, 4, P3, E02, E10, N0, + /* case 12 */ 1, + ST_WDG, 6, P3, E03, E10, P2, E01, E11, + /* case 13 */ 7, + ST_PNT, 5, E08, E10, E00, E01, E11, + ST_PYR, 5, E10, P3, P2, E11, N0, + ST_PYR, 5, E08, P0, P3, E10, N0, + ST_TET, 4, E00, P0, E08, N0, + ST_TET, 4, P0, P2, P3, N0, + ST_PYR, 5, E00, E01, P2, P0, N0, + ST_TET, 4, P2, E01, E11, N0, + /* case 14 */ 7, + ST_PNT, 5, E10, E11, E03, E00, E09, + ST_PYR, 5, E11, P2, P1, E09, N0, + ST_PYR, 5, E10, P3, P2, E11, N0, + ST_TET, 4, E03, P3, E10, N0, + ST_TET, 4, P3, P1, P2, N0, + ST_PYR, 5, E03, E00, P1, P3, N0, + ST_TET, 4, P1, E00, E09, N0, + /* case 15 */ 1, + ST_HEX, 8, P0, P1, P2, P3, E08, E09, E11, E10, + /* case 16 */ 1, + ST_TET, 4, P4, E04, E08, E07, + /* case 17 */ 1, + ST_WDG, 6, P0, E03, E00, P4, E07, E04, + /* case 18 */ 7, + ST_PNT, 2, E07, E01, + ST_PYR, 5, P4, P1, E00, E08, N0, + ST_PYR, 5, E04, E09, P1, P4, N0, + ST_TET, 4, E09, E01, P1, N0, + ST_TET, 4, P1, E01, E00, N0, + ST_TET, 4, E04, N0, P4, E07, + ST_TET, 4, E08, E07, P4, N0, + /* case 19 */ 7, + ST_PNT, 5, E01, E03, E09, E04, E07, + ST_PYR, 5, E03, E07, P4, P0, N0, + ST_PYR, 5, E01, E03, P0, P1, N0, + ST_TET, 4, E09, E01, P1, N0, + ST_TET, 4, P1, P0, P4, N0, + ST_PYR, 5, E09, P1, P4, E04, N0, + ST_TET, 4, P4, E07, E04, N0, + /* case 20 */ 2, + ST_TET, 4, P4, E04, E08, E07, + ST_TET, 4, P2, E02, E01, E11, + /* case 21 */ 8, + ST_PNT, 4, E04, E07, E11, E11, + ST_PYR, 5, P2, E02, E03, P0, N0, + ST_PYR, 5, E00, E01, P2, P0, N0, + ST_TET, 4, P2, E11, E02, N0, + ST_TET, 4, E01, E11, P2, N0, + ST_PYR, 5, E03, E07, P4, P0, N0, + ST_PYR, 5, P0, P4, E04, E00, N0, + ST_TET, 4, E04, P4, E07, N0, + /* case 22 */ 8, + ST_PNT, 4, E11, E02, E07, E07, + ST_PYR, 5, P4, P1, E00, E08, N0, + ST_PYR, 5, E09, P1, P4, E04, N0, + ST_TET, 4, P4, E08, E07, N0, + ST_TET, 4, E04, P4, E07, N0, + ST_PYR, 5, E00, P1, P2, E02, N0, + ST_PYR, 5, P1, E09, E11, P2, N0, + ST_TET, 4, E11, E02, P2, N0, + /* case 23 */ 9, + ST_PNT, 6, E03, E02, E11, E09, E04, E07, + ST_TET, 4, P0, P1, P2, N0, + ST_PYR, 5, E03, P0, P2, E02, N0, + ST_PYR, 5, E03, E07, P4, P0, N0, + ST_TET, 4, P4, P1, P0, N0, + ST_TET, 4, P4, E07, E04, N0, + ST_PYR, 5, P4, E04, E09, P1, N0, + ST_PYR, 5, E09, E11, P2, P1, N0, + ST_TET, 4, E11, E02, P2, N0, + /* case 24 */ 7, + ST_PNT, 2, E02, E04, + ST_PYR, 5, P3, P4, E08, E03, N0, + ST_PYR, 5, E10, E07, P4, P3, N0, + ST_TET, 4, E07, E04, P4, N0, + ST_TET, 4, P4, E04, E08, N0, + ST_TET, 4, E10, N0, P3, E02, + ST_TET, 4, E03, E02, P3, N0, + /* case 25 */ 7, + ST_PNT, 5, E04, E00, E07, E10, E02, + ST_PYR, 5, E00, E02, P3, P0, N0, + ST_PYR, 5, E04, E00, P0, P4, N0, + ST_TET, 4, E07, E04, P4, N0, + ST_TET, 4, P4, P0, P3, N0, + ST_PYR, 5, E07, P4, P3, E10, N0, + ST_TET, 4, P3, E02, E10, N0, + /* case 26 */ 5, + ST_WDG, 6, E03, E00, E08, P3, P1, P4, + ST_PYR, 5, P3, E10, E07, P4, E02, + ST_PYR, 5, E04, E09, P1, P4, E01, + ST_PYR, 5, E02, P3, P1, E01, P4, + ST_PYR, 5, E02, E01, E04, E07, P4, + /* case 27 */ 5, + ST_TET, 4, P0, P1, P3, P4, + ST_PYR, 5, E07, P4, P3, E10, E02, + ST_PYR, 5, E04, E09, P1, P4, E01, + ST_PYR, 5, P3, P1, E01, E02, P4, + ST_PYR, 5, E07, E02, E01, E04, P4, + /* case 28 */ 8, + ST_PNT, 4, E11, E01, E04, E04, + ST_PYR, 5, P4, E08, E03, P3, N0, + ST_PYR, 5, E10, E07, P4, P3, N0, + ST_TET, 4, P4, E04, E08, N0, + ST_TET, 4, E07, E04, P4, N0, + ST_PYR, 5, E03, E01, P2, P3, N0, + ST_PYR, 5, P3, P2, E11, E10, N0, + ST_TET, 4, E11, P2, E01, N0, + /* case 29 */ 9, + ST_PNT, 6, E00, E01, E11, E10, E07, E04, + ST_TET, 4, P0, P2, P3, N0, + ST_PYR, 5, E00, E01, P2, P0, N0, + ST_PYR, 5, E00, P0, P4, E04, N0, + ST_TET, 4, P4, P0, P3, N0, + ST_TET, 4, P4, E07, E04, N0, + ST_PYR, 5, P4, P3, E10, E07, N0, + ST_PYR, 5, E10, P3, P2, E11, N0, + ST_TET, 4, E11, P2, E01, N0, + /* case 30 */ 9, + ST_PNT, 5, E11, E09, E10, E07, E04, + ST_WDG, 6, E03, E00, E08, P3, P1, P4, + ST_TET, 4, P1, P3, P4, N0, + ST_PYR, 5, P3, E10, E07, P4, N0, + ST_TET, 4, P2, P3, P1, N0, + ST_PYR, 5, E09, E11, P2, P1, N0, + ST_PYR, 5, E11, E10, P3, P2, N0, + ST_PYR, 5, P4, E04, E09, P1, N0, + ST_TET, 4, E07, E04, P4, N0, + /* case 31 */ 9, + ST_PNT, 5, E09, E11, E10, E04, E07, + ST_PYR, 5, P0, P1, P2, P3, N0, + ST_TET, 4, P3, P4, P0, N0, + ST_TET, 4, P4, P1, P0, N0, + ST_PYR, 5, P4, E04, E09, P1, N0, + ST_PYR, 5, E09, E11, P2, P1, N0, + ST_PYR, 5, E11, E10, P3, P2, N0, + ST_PYR, 5, E10, E07, P4, P3, N0, + ST_TET, 4, E04, P4, E07, N0, + /* case 32 */ 1, + ST_TET, 4, P5, E05, E09, E04, + /* case 33 */ 7, + ST_PNT, 2, E03, E05, + ST_PYR, 5, P0, E08, E04, P5, N0, + ST_PYR, 5, E00, P0, P5, E09, N0, + ST_TET, 4, E09, P5, E05, N0, + ST_TET, 4, P5, E04, E05, N0, + ST_TET, 4, E00, P0, N0, E03, + ST_TET, 4, E08, P0, E03, N0, + /* case 34 */ 1, + ST_WDG, 6, P1, E00, E01, P5, E04, E05, + /* case 35 */ 7, + ST_PNT, 5, E03, E01, E08, E04, E05, + ST_PYR, 5, E01, P1, P5, E05, N0, + ST_PYR, 5, E03, P0, P1, E01, N0, + ST_TET, 4, E08, P0, E03, N0, + ST_TET, 4, P0, P5, P1, N0, + ST_PYR, 5, E08, E04, P5, P0, N0, + ST_TET, 4, P5, E04, E05, N0, + /* case 36 */ 7, + ST_PNT, 2, E02, E04, + ST_PYR, 5, P2, E01, E09, P5, N0, + ST_PYR, 5, E11, P2, P5, E05, N0, + ST_TET, 4, E05, P5, E04, N0, + ST_TET, 4, P5, E09, E04, N0, + ST_TET, 4, E11, P2, N0, E02, + ST_TET, 4, E01, P2, E02, N0, + /* case 37 */ 5, + ST_WDG, 6, P0, P5, P2, E00, E09, E01, + ST_PYR, 5, P0, P2, E02, E03, E08, + ST_PYR, 5, E11, P2, P5, E05, E04, + ST_PYR, 5, E08, E04, P5, P0, P2, + ST_PYR, 5, E08, E02, E11, E04, P2, + /* case 38 */ 7, + ST_PNT, 5, E04, E00, E05, E11, E02, + ST_PYR, 5, E00, P1, P2, E02, N0, + ST_PYR, 5, E04, P5, P1, E00, N0, + ST_TET, 4, E05, P5, E04, N0, + ST_TET, 4, P5, P2, P1, N0, + ST_PYR, 5, E05, E11, P2, P5, N0, + ST_TET, 4, P2, E11, E02, N0, + /* case 39 */ 5, + ST_TET, 4, P1, P0, P5, P2, + ST_PYR, 5, E02, E03, P0, P2, E08, + ST_PYR, 5, E11, P2, P5, E05, E04, + ST_PYR, 5, P0, E08, E04, P5, P2, + ST_PYR, 5, E02, E11, E04, E08, P2, + /* case 40 */ 2, + ST_TET, 4, P3, E03, E02, E10, + ST_TET, 4, P5, E09, E04, E05, + /* case 41 */ 8, + ST_PNT, 4, E02, E10, E05, E05, + ST_PYR, 5, P5, P0, E08, E04, N0, + ST_PYR, 5, E00, P0, P5, E09, N0, + ST_TET, 4, P5, E04, E05, N0, + ST_TET, 4, E09, P5, E05, N0, + ST_PYR, 5, E08, P0, P3, E10, N0, + ST_PYR, 5, P0, E00, E02, P3, N0, + ST_TET, 4, E02, E10, P3, N0, + /* case 42 */ 8, + ST_PNT, 4, E05, E04, E10, E10, + ST_PYR, 5, P3, E03, E00, P1, N0, + ST_PYR, 5, E01, E02, P3, P1, N0, + ST_TET, 4, P3, E10, E03, N0, + ST_TET, 4, E02, E10, P3, N0, + ST_PYR, 5, E00, E04, P5, P1, N0, + ST_PYR, 5, P1, P5, E05, E01, N0, + ST_TET, 4, E05, P5, E04, N0, + /* case 43 */ 9, + ST_PNT, 6, E08, E04, E05, E01, E02, E10, + ST_TET, 4, P0, P5, P1, N0, + ST_PYR, 5, E08, E04, P5, P0, N0, + ST_PYR, 5, E08, P0, P3, E10, N0, + ST_TET, 4, P3, P0, P1, N0, + ST_TET, 4, P3, E02, E10, N0, + ST_PYR, 5, P3, P1, E01, E02, N0, + ST_PYR, 5, E01, P1, P5, E05, N0, + ST_TET, 4, E05, P5, E04, N0, + /* case 44 */ 8, + ST_PNT, 4, E10, E03, E04, E04, + ST_PYR, 5, P5, P2, E01, E09, N0, + ST_PYR, 5, E11, P2, P5, E05, N0, + ST_TET, 4, P5, E09, E04, N0, + ST_TET, 4, E05, P5, E04, N0, + ST_PYR, 5, E01, P2, P3, E03, N0, + ST_PYR, 5, P2, E11, E10, P3, N0, + ST_TET, 4, E10, E03, P3, N0, + /* case 45 */ 9, + ST_PNT, 5, E10, E11, E08, E04, E05, + ST_WDG, 6, E00, E01, E09, P0, P2, P5, + ST_TET, 4, P2, P0, P5, N0, + ST_PYR, 5, P0, E08, E04, P5, N0, + ST_TET, 4, P3, P0, P2, N0, + ST_PYR, 5, E11, E10, P3, P2, N0, + ST_PYR, 5, E10, E08, P0, P3, N0, + ST_PYR, 5, P5, E05, E11, P2, N0, + ST_TET, 4, E04, E05, P5, N0, + /* case 46 */ 9, + ST_PNT, 6, E00, E03, E10, E11, E05, E04, + ST_TET, 4, P1, P2, P3, N0, + ST_PYR, 5, E00, P1, P3, E03, N0, + ST_PYR, 5, E00, E04, P5, P1, N0, + ST_TET, 4, P5, P2, P1, N0, + ST_TET, 4, P5, E04, E05, N0, + ST_PYR, 5, P5, E05, E11, P2, N0, + ST_PYR, 5, E11, E10, P3, P2, N0, + ST_TET, 4, E10, E03, P3, N0, + /* case 47 */ 9, + ST_PNT, 5, E11, E10, E08, E05, E04, + ST_PYR, 5, P1, P2, P3, P0, N0, + ST_TET, 4, P0, P5, P1, N0, + ST_TET, 4, P5, P2, P1, N0, + ST_PYR, 5, P5, E05, E11, P2, N0, + ST_PYR, 5, E11, E10, P3, P2, N0, + ST_PYR, 5, E10, E08, P0, P3, N0, + ST_PYR, 5, E08, E04, P5, P0, N0, + ST_TET, 4, E05, P5, E04, N0, + /* case 48 */ 1, + ST_WDG, 6, P5, E09, E05, P4, E08, E07, + /* case 49 */ 7, + ST_PNT, 5, E03, E07, E00, E09, E05, + ST_PYR, 5, E07, E05, P5, P4, N0, + ST_PYR, 5, E03, E07, P4, P0, N0, + ST_TET, 4, E00, E03, P0, N0, + ST_TET, 4, P0, P4, P5, N0, + ST_PYR, 5, E00, P0, P5, E09, N0, + ST_TET, 4, P5, E05, E09, N0, + /* case 50 */ 7, + ST_PNT, 5, E07, E05, E08, E00, E01, + ST_PYR, 5, E05, E01, P1, P5, N0, + ST_PYR, 5, E07, E05, P5, P4, N0, + ST_TET, 4, E08, E07, P4, N0, + ST_TET, 4, P4, P5, P1, N0, + ST_PYR, 5, E08, P4, P1, E00, N0, + ST_TET, 4, P1, E01, E00, N0, + /* case 51 */ 1, + ST_HEX, 8, E03, E01, E05, E07, P0, P1, P5, P4, + /* case 52 */ 8, + ST_PNT, 4, E07, E08, E02, E02, + ST_PYR, 5, P2, E01, E09, P5, N0, + ST_PYR, 5, E05, E11, P2, P5, N0, + ST_TET, 4, P2, E02, E01, N0, + ST_TET, 4, E11, E02, P2, N0, + ST_PYR, 5, E09, E08, P4, P5, N0, + ST_PYR, 5, P5, P4, E07, E05, N0, + ST_TET, 4, E07, P4, E08, N0, + /* case 53 */ 9, + ST_PNT, 5, E07, E05, E03, E02, E11, + ST_WDG, 6, P0, P5, P2, E00, E09, E01, + ST_TET, 4, P5, P2, P0, N0, + ST_PYR, 5, P0, P2, E02, E03, N0, + ST_TET, 4, P4, P5, P0, N0, + ST_PYR, 5, E05, P5, P4, E07, N0, + ST_PYR, 5, E07, P4, P0, E03, N0, + ST_PYR, 5, P2, P5, E05, E11, N0, + ST_TET, 4, E02, P2, E11, N0, + /* case 54 */ 9, + ST_PNT, 6, E00, E08, E07, E05, E11, E02, + ST_TET, 4, P1, P4, P5, N0, + ST_PYR, 5, E00, E08, P4, P1, N0, + ST_PYR, 5, E00, P1, P2, E02, N0, + ST_TET, 4, P2, P1, P5, N0, + ST_TET, 4, P2, E11, E02, N0, + ST_PYR, 5, P2, P5, E05, E11, N0, + ST_PYR, 5, E05, P5, P4, E07, N0, + ST_TET, 4, E07, P4, E08, N0, + /* case 55 */ 9, + ST_PNT, 5, E05, E07, E03, E11, E02, + ST_PYR, 5, P1, P0, P4, P5, N0, + ST_TET, 4, P0, P1, P2, N0, + ST_TET, 4, P2, P1, P5, N0, + ST_PYR, 5, P2, P5, E05, E11, N0, + ST_PYR, 5, E05, P5, P4, E07, N0, + ST_PYR, 5, E07, P4, P0, E03, N0, + ST_PYR, 5, E03, P0, P2, E02, N0, + ST_TET, 4, E11, E02, P2, N0, + /* case 56 */ 8, + ST_PNT, 4, E05, E09, E02, E02, + ST_PYR, 5, P3, P4, E08, E03, N0, + ST_PYR, 5, E07, P4, P3, E10, N0, + ST_TET, 4, P3, E03, E02, N0, + ST_TET, 4, E10, P3, E02, N0, + ST_PYR, 5, E08, P4, P5, E09, N0, + ST_PYR, 5, P4, E07, E05, P5, N0, + ST_TET, 4, E05, E09, P5, N0, + /* case 57 */ 9, + ST_PNT, 6, E00, E09, E05, E07, E10, E02, + ST_TET, 4, P0, P4, P5, N0, + ST_PYR, 5, E00, P0, P5, E09, N0, + ST_PYR, 5, E00, E02, P3, P0, N0, + ST_TET, 4, P3, P4, P0, N0, + ST_TET, 4, P3, E02, E10, N0, + ST_PYR, 5, P3, E10, E07, P4, N0, + ST_PYR, 5, E07, E05, P5, P4, N0, + ST_TET, 4, E05, E09, P5, N0, + /* case 58 */ 9, + ST_PNT, 5, E05, E01, E07, E10, E02, + ST_WDG, 6, P4, P1, P3, E08, E00, E03, + ST_TET, 4, P1, P3, P4, N0, + ST_PYR, 5, P4, P3, E10, E07, N0, + ST_TET, 4, P5, P1, P4, N0, + ST_PYR, 5, E01, P1, P5, E05, N0, + ST_PYR, 5, E05, P5, P4, E07, N0, + ST_PYR, 5, P3, P1, E01, E02, N0, + ST_TET, 4, E10, P3, E02, N0, + /* case 59 */ 9, + ST_PNT, 5, E01, E05, E07, E02, E10, + ST_PYR, 5, P0, P4, P5, P1, N0, + ST_TET, 4, P4, P0, P3, N0, + ST_TET, 4, P3, P0, P1, N0, + ST_PYR, 5, P3, P1, E01, E02, N0, + ST_PYR, 5, E01, P1, P5, E05, N0, + ST_PYR, 5, E05, P5, P4, E07, N0, + ST_PYR, 5, E07, P4, P3, E10, N0, + ST_TET, 4, E02, E10, P3, N0, + /* case 60 */ 2, + ST_HEX, 8, P3, P4, P5, P2, E10, E07, E05, E11, + ST_HEX, 8, E03, E08, E09, E01, P3, P4, P5, P2, + /* case 61 */ 11, + ST_PNT, 6, P0, P2, P3, P4, E05, E07, + ST_WDG, 6, P0, P5, P2, E00, E09, E01, + ST_PYR, 5, E07, E05, P5, P4, N0, + ST_TET, 4, P4, P5, P0, N0, + ST_TET, 4, P4, P0, P3, N0, + ST_PYR, 5, E10, E07, P4, P3, N0, + ST_PYR, 5, E11, E10, P3, P2, N0, + ST_TET, 4, P3, P0, P2, N0, + ST_PYR, 5, E05, E07, E10, E11, N0, + ST_TET, 4, P2, P0, P5, N0, + ST_PYR, 5, E05, E11, P2, P5, N0, + /* case 62 */ 11, + ST_PNT, 6, P1, P3, P2, P5, E07, E05, + ST_WDG, 6, E00, E08, E03, P1, P4, P3, + ST_PYR, 5, E05, P5, P4, E07, N0, + ST_TET, 4, P5, P1, P4, N0, + ST_TET, 4, P5, P2, P1, N0, + ST_PYR, 5, E11, P2, P5, E05, N0, + ST_PYR, 5, E10, P3, P2, E11, N0, + ST_TET, 4, P2, P3, P1, N0, + ST_PYR, 5, E07, E10, E11, E05, N0, + ST_TET, 4, P3, P4, P1, N0, + ST_PYR, 5, E07, P4, P3, E10, N0, + /* case 63 */ 2, + ST_HEX, 8, P3, P4, P5, P2, E10, E07, E05, E11, + ST_WDG, 6, P1, P2, P5, P0, P3, P4, + /* case 64 */ 1, + ST_TET, 4, P6, E05, E06, E11, + /* case 65 */ 2, + ST_TET, 4, P0, E08, E00, E03, + ST_TET, 4, P6, E05, E06, E11, + /* case 66 */ 7, + ST_PNT, 2, E00, E06, + ST_PYR, 5, P1, P6, E11, E01, N0, + ST_PYR, 5, E09, E05, P6, P1, N0, + ST_TET, 4, E05, E06, P6, N0, + ST_TET, 4, P6, E06, E11, N0, + ST_TET, 4, E09, N0, P1, E00, + ST_TET, 4, E01, E00, P1, N0, + /* case 67 */ 8, + ST_PNT, 4, E08, E03, E06, E06, + ST_PYR, 5, P6, E11, E01, P1, N0, + ST_PYR, 5, E09, E05, P6, P1, N0, + ST_TET, 4, P6, E06, E11, N0, + ST_TET, 4, E05, E06, P6, N0, + ST_PYR, 5, E01, E03, P0, P1, N0, + ST_PYR, 5, P1, P0, E08, E09, N0, + ST_TET, 4, E08, P0, E03, N0, + /* case 68 */ 1, + ST_WDG, 6, P2, E01, E02, P6, E05, E06, + /* case 69 */ 8, + ST_PNT, 4, E06, E05, E08, E08, + ST_PYR, 5, P0, E00, E01, P2, N0, + ST_PYR, 5, E02, E03, P0, P2, N0, + ST_TET, 4, P0, E08, E00, N0, + ST_TET, 4, E03, E08, P0, N0, + ST_PYR, 5, E01, E05, P6, P2, N0, + ST_PYR, 5, P2, P6, E06, E02, N0, + ST_TET, 4, E06, P6, E05, N0, + /* case 70 */ 7, + ST_PNT, 5, E00, E02, E09, E05, E06, + ST_PYR, 5, E02, P2, P6, E06, N0, + ST_PYR, 5, E00, P1, P2, E02, N0, + ST_TET, 4, E09, P1, E00, N0, + ST_TET, 4, P1, P6, P2, N0, + ST_PYR, 5, E09, E05, P6, P1, N0, + ST_TET, 4, P6, E05, E06, N0, + /* case 71 */ 9, + ST_PNT, 6, E09, E05, E06, E02, E03, E08, + ST_TET, 4, P1, P6, P2, N0, + ST_PYR, 5, E09, E05, P6, P1, N0, + ST_PYR, 5, E09, P1, P0, E08, N0, + ST_TET, 4, P0, P1, P2, N0, + ST_TET, 4, P0, E03, E08, N0, + ST_PYR, 5, P0, P2, E02, E03, N0, + ST_PYR, 5, E02, P2, P6, E06, N0, + ST_TET, 4, E06, P6, E05, N0, + /* case 72 */ 7, + ST_PNT, 2, E03, E05, + ST_PYR, 5, P3, P6, E06, E10, N0, + ST_PYR, 5, E02, E11, P6, P3, N0, + ST_TET, 4, E11, E05, P6, N0, + ST_TET, 4, P6, E05, E06, N0, + ST_TET, 4, E02, N0, P3, E03, + ST_TET, 4, E10, E03, P3, N0, + /* case 73 */ 8, + ST_PNT, 4, E00, E08, E05, E05, + ST_PYR, 5, P6, E06, E10, P3, N0, + ST_PYR, 5, E02, E11, P6, P3, N0, + ST_TET, 4, P6, E05, E06, N0, + ST_TET, 4, E11, E05, P6, N0, + ST_PYR, 5, E10, E08, P0, P3, N0, + ST_PYR, 5, P3, P0, E00, E02, N0, + ST_TET, 4, E00, P0, E08, N0, + /* case 74 */ 5, + ST_WDG, 6, P1, P6, P3, E01, E11, E02, + ST_PYR, 5, P1, P3, E03, E00, E09, + ST_PYR, 5, E10, P3, P6, E06, E05, + ST_PYR, 5, E09, E05, P6, P1, P3, + ST_PYR, 5, E09, E03, E10, E05, P3, + /* case 75 */ 9, + ST_PNT, 5, E08, E10, E09, E05, E06, + ST_WDG, 6, E01, E02, E11, P1, P3, P6, + ST_TET, 4, P3, P1, P6, N0, + ST_PYR, 5, P1, E09, E05, P6, N0, + ST_TET, 4, P0, P1, P3, N0, + ST_PYR, 5, E10, E08, P0, P3, N0, + ST_PYR, 5, E08, E09, P1, P0, N0, + ST_PYR, 5, P6, E06, E10, P3, N0, + ST_TET, 4, E05, E06, P6, N0, + /* case 76 */ 7, + ST_PNT, 5, E03, E01, E10, E06, E05, + ST_PYR, 5, E01, E05, P6, P2, N0, + ST_PYR, 5, E03, E01, P2, P3, N0, + ST_TET, 4, E10, E03, P3, N0, + ST_TET, 4, P3, P2, P6, N0, + ST_PYR, 5, E10, P3, P6, E06, N0, + ST_TET, 4, P6, E05, E06, N0, + /* case 77 */ 9, + ST_PNT, 6, E10, E06, E05, E01, E00, E08, + ST_TET, 4, P3, P2, P6, N0, + ST_PYR, 5, E10, P3, P6, E06, N0, + ST_PYR, 5, E10, E08, P0, P3, N0, + ST_TET, 4, P0, P2, P3, N0, + ST_TET, 4, P0, E08, E00, N0, + ST_PYR, 5, P0, E00, E01, P2, N0, + ST_PYR, 5, E01, E05, P6, P2, N0, + ST_TET, 4, E05, E06, P6, N0, + /* case 78 */ 5, + ST_TET, 4, P2, P1, P6, P3, + ST_PYR, 5, E03, E00, P1, P3, E09, + ST_PYR, 5, E10, P3, P6, E06, E05, + ST_PYR, 5, P1, E09, E05, P6, P3, + ST_PYR, 5, E03, E10, E05, E09, P3, + /* case 79 */ 9, + ST_PNT, 5, E10, E08, E09, E06, E05, + ST_PYR, 5, P2, P3, P0, P1, N0, + ST_TET, 4, P1, P6, P2, N0, + ST_TET, 4, P6, P3, P2, N0, + ST_PYR, 5, P6, E06, E10, P3, N0, + ST_PYR, 5, E10, E08, P0, P3, N0, + ST_PYR, 5, E08, E09, P1, P0, N0, + ST_PYR, 5, E09, E05, P6, P1, N0, + ST_TET, 4, E06, P6, E05, N0, + /* case 80 */ 7, + ST_PNT, 2, E08, E11, + ST_PYR, 5, P4, E07, E06, P6, N0, + ST_PYR, 5, E04, P4, P6, E05, N0, + ST_TET, 4, E05, P6, E11, N0, + ST_TET, 4, P6, E06, E11, N0, + ST_TET, 4, E04, P4, N0, E08, + ST_TET, 4, E07, P4, E08, N0, + /* case 81 */ 8, + ST_PNT, 4, E00, E03, E11, E11, + ST_PYR, 5, P6, P4, E07, E06, N0, + ST_PYR, 5, E04, P4, P6, E05, N0, + ST_TET, 4, P6, E06, E11, N0, + ST_TET, 4, E05, P6, E11, N0, + ST_PYR, 5, E07, P4, P0, E03, N0, + ST_PYR, 5, P4, E04, E00, P0, N0, + ST_TET, 4, E00, E03, P0, N0, + /* case 82 */ 5, + ST_WDG, 6, E09, E05, E04, P1, P6, P4, + ST_PYR, 5, P1, E00, E08, P4, E01, + ST_PYR, 5, E07, E06, P6, P4, E11, + ST_PYR, 5, E01, P1, P6, E11, P4, + ST_PYR, 5, E01, E11, E07, E08, P4, + /* case 83 */ 9, + ST_PNT, 5, E03, E07, E01, E11, E06, + ST_WDG, 6, P1, P4, P6, E09, E04, E05, + ST_TET, 4, P4, P6, P1, N0, + ST_PYR, 5, P1, P6, E11, E01, N0, + ST_TET, 4, P0, P4, P1, N0, + ST_PYR, 5, E07, P4, P0, E03, N0, + ST_PYR, 5, E03, P0, P1, E01, N0, + ST_PYR, 5, P6, P4, E07, E06, N0, + ST_TET, 4, E11, P6, E06, N0, + /* case 84 */ 8, + ST_PNT, 4, E02, E01, E08, E08, + ST_PYR, 5, P4, P6, E05, E04, N0, + ST_PYR, 5, E06, P6, P4, E07, N0, + ST_TET, 4, P4, E04, E08, N0, + ST_TET, 4, E07, P4, E08, N0, + ST_PYR, 5, E05, P6, P2, E01, N0, + ST_PYR, 5, P6, E06, E02, P2, N0, + ST_TET, 4, E02, E01, P2, N0, + /* case 85 */ 2, + ST_HEX, 8, P0, P2, P6, P4, E00, E01, E05, E04, + ST_HEX, 8, E03, E02, E06, E07, P0, P2, P6, P4, + /* case 86 */ 9, + ST_PNT, 5, E02, E06, E00, E08, E07, + ST_WDG, 6, E09, E05, E04, P1, P6, P4, + ST_TET, 4, P6, P1, P4, N0, + ST_PYR, 5, P1, E00, E08, P4, N0, + ST_TET, 4, P2, P1, P6, N0, + ST_PYR, 5, E06, E02, P2, P6, N0, + ST_PYR, 5, E02, E00, P1, P2, N0, + ST_PYR, 5, P4, E07, E06, P6, N0, + ST_TET, 4, E08, E07, P4, N0, + /* case 87 */ 11, + ST_PNT, 6, P1, P4, P0, P2, E06, E02, + ST_WDG, 6, E09, E05, E04, P1, P6, P4, + ST_PYR, 5, E02, P2, P6, E06, N0, + ST_TET, 4, P2, P1, P6, N0, + ST_TET, 4, P2, P0, P1, N0, + ST_PYR, 5, E03, P0, P2, E02, N0, + ST_PYR, 5, E07, P4, P0, E03, N0, + ST_TET, 4, P0, P4, P1, N0, + ST_PYR, 5, E06, E07, E03, E02, N0, + ST_TET, 4, P4, P6, P1, N0, + ST_PYR, 5, E06, P6, P4, E07, N0, + /* case 88 */ 5, + ST_WDG, 6, E07, E06, E10, P4, P6, P3, + ST_PYR, 5, P4, E08, E03, P3, E04, + ST_PYR, 5, E02, E11, P6, P3, E05, + ST_PYR, 5, E04, P4, P6, E05, P3, + ST_PYR, 5, E04, E05, E02, E03, P3, + /* case 89 */ 9, + ST_PNT, 5, E00, E02, E04, E05, E11, + ST_WDG, 6, P4, P3, P6, E07, E10, E06, + ST_TET, 4, P3, P6, P4, N0, + ST_PYR, 5, P4, P6, E05, E04, N0, + ST_TET, 4, P0, P3, P4, N0, + ST_PYR, 5, E02, P3, P0, E00, N0, + ST_PYR, 5, E00, P0, P4, E04, N0, + ST_PYR, 5, P6, P3, E02, E11, N0, + ST_TET, 4, E05, P6, E11, N0, + /* case 90 */ 5, + ST_WDG, 6, E01, E02, E11, P1, P3, P6, + ST_TET, 4, P1, P6, P3, P4, + ST_WDG, 6, P4, P6, P1, E04, E05, E09, + ST_WDG, 6, P3, P4, P1, E03, E08, E00, + ST_WDG, 6, P6, P4, P3, E06, E07, E10, + /* case 91 */ 5, + ST_WDG, 6, P6, P3, P1, E11, E02, E01, + ST_WDG, 6, E07, E06, E10, P4, P6, P3, + ST_WDG, 6, P4, P6, P1, E04, E05, E09, + ST_TET, 4, P6, P3, P1, P4, + ST_TET, 4, P4, P1, P0, P3, + /* case 92 */ 9, + ST_PNT, 5, E01, E05, E03, E08, E04, + ST_WDG, 6, P3, P6, P4, E10, E06, E07, + ST_TET, 4, P6, P4, P3, N0, + ST_PYR, 5, P3, P4, E08, E03, N0, + ST_TET, 4, P2, P6, P3, N0, + ST_PYR, 5, E05, P6, P2, E01, N0, + ST_PYR, 5, E01, P2, P3, E03, N0, + ST_PYR, 5, P4, P6, E05, E04, N0, + ST_TET, 4, E08, P4, E04, N0, + /* case 93 */ 11, + ST_PNT, 6, P3, P4, P0, P2, E05, E01, + ST_WDG, 6, P3, P6, P4, E10, E06, E07, + ST_PYR, 5, E01, E05, P6, P2, N0, + ST_TET, 4, P2, P6, P3, N0, + ST_TET, 4, P2, P3, P0, N0, + ST_PYR, 5, E00, E01, P2, P0, N0, + ST_PYR, 5, E04, E00, P0, P4, N0, + ST_TET, 4, P0, P3, P4, N0, + ST_PYR, 5, E05, E01, E00, E04, N0, + ST_TET, 4, P4, P3, P6, N0, + ST_PYR, 5, E05, E04, P4, P6, N0, + /* case 94 */ 5, + ST_WDG, 6, E04, E09, E05, P4, P1, P6, + ST_WDG, 6, P3, P4, P1, E03, E08, E00, + ST_WDG, 6, E10, E07, E06, P3, P4, P6, + ST_TET, 4, P4, P6, P1, P3, + ST_TET, 4, P3, P2, P6, P1, + /* case 95 */ 5, + ST_WDG, 6, P4, P6, P1, E04, E05, E09, + ST_WDG, 6, E07, E06, E10, P4, P6, P3, + ST_PYR, 5, P0, P1, P2, P3, P4, + ST_TET, 4, P6, P3, P2, P4, + ST_TET, 4, P6, P2, P1, P4, + /* case 96 */ 1, + ST_WDG, 6, P6, E11, E06, P5, E09, E04, + /* case 97 */ 8, + ST_PNT, 4, E06, E11, E03, E03, + ST_PYR, 5, P0, P5, E09, E00, N0, + ST_PYR, 5, E04, P5, P0, E08, N0, + ST_TET, 4, P0, E00, E03, N0, + ST_TET, 4, E08, P0, E03, N0, + ST_PYR, 5, E09, P5, P6, E11, N0, + ST_PYR, 5, P5, E04, E06, P6, N0, + ST_TET, 4, E06, E11, P6, N0, + /* case 98 */ 7, + ST_PNT, 5, E00, E04, E01, E11, E06, + ST_PYR, 5, E04, E06, P6, P5, N0, + ST_PYR, 5, E00, E04, P5, P1, N0, + ST_TET, 4, E01, E00, P1, N0, + ST_TET, 4, P1, P5, P6, N0, + ST_PYR, 5, E01, P1, P6, E11, N0, + ST_TET, 4, P6, E06, E11, N0, + /* case 99 */ 9, + ST_PNT, 6, E01, E11, E06, E04, E08, E03, + ST_TET, 4, P1, P5, P6, N0, + ST_PYR, 5, E01, P1, P6, E11, N0, + ST_PYR, 5, E01, E03, P0, P1, N0, + ST_TET, 4, P0, P5, P1, N0, + ST_TET, 4, P0, E03, E08, N0, + ST_PYR, 5, P0, E08, E04, P5, N0, + ST_PYR, 5, E04, E06, P6, P5, N0, + ST_TET, 4, E06, E11, P6, N0, + /* case 100 */ 7, + ST_PNT, 5, E02, E06, E01, E09, E04, + ST_PYR, 5, E06, P6, P5, E04, N0, + ST_PYR, 5, E02, P2, P6, E06, N0, + ST_TET, 4, E01, P2, E02, N0, + ST_TET, 4, P2, P5, P6, N0, + ST_PYR, 5, E01, E09, P5, P2, N0, + ST_TET, 4, P5, E09, E04, N0, + /* case 101 */ 9, + ST_PNT, 5, E06, E04, E02, E03, E08, + ST_WDG, 6, E01, E09, E00, P2, P5, P0, + ST_TET, 4, P5, P2, P0, N0, + ST_PYR, 5, P2, E02, E03, P0, N0, + ST_TET, 4, P6, P2, P5, N0, + ST_PYR, 5, E04, E06, P6, P5, N0, + ST_PYR, 5, E06, E02, P2, P6, N0, + ST_PYR, 5, P0, E08, E04, P5, N0, + ST_TET, 4, E03, E08, P0, N0, + /* case 102 */ 1, + ST_HEX, 8, P1, P5, P6, P2, E00, E04, E06, E02, + /* case 103 */ 9, + ST_PNT, 5, E04, E06, E02, E08, E03, + ST_PYR, 5, P1, P5, P6, P2, N0, + ST_TET, 4, P2, P0, P1, N0, + ST_TET, 4, P0, P5, P1, N0, + ST_PYR, 5, P0, E08, E04, P5, N0, + ST_PYR, 5, E04, E06, P6, P5, N0, + ST_PYR, 5, E06, E02, P2, P6, N0, + ST_PYR, 5, E02, E03, P0, P2, N0, + ST_TET, 4, E08, P0, E03, N0, + /* case 104 */ 8, + ST_PNT, 4, E04, E09, E03, E03, + ST_PYR, 5, P3, E02, E11, P6, N0, + ST_PYR, 5, E06, E10, P3, P6, N0, + ST_TET, 4, P3, E03, E02, N0, + ST_TET, 4, E10, E03, P3, N0, + ST_PYR, 5, E11, E09, P5, P6, N0, + ST_PYR, 5, P6, P5, E04, E06, N0, + ST_TET, 4, E04, P5, E09, N0, + /* case 105 */ 2, + ST_HEX, 8, E00, E09, E11, E02, P0, P5, P6, P3, + ST_HEX, 8, P0, P5, P6, P3, E08, E04, E06, E10, + /* case 106 */ 9, + ST_PNT, 5, E04, E06, E00, E03, E10, + ST_WDG, 6, P1, P6, P3, E01, E11, E02, + ST_TET, 4, P6, P3, P1, N0, + ST_PYR, 5, P1, P3, E03, E00, N0, + ST_TET, 4, P5, P6, P1, N0, + ST_PYR, 5, E06, P6, P5, E04, N0, + ST_PYR, 5, E04, P5, P1, E00, N0, + ST_PYR, 5, P3, P6, E06, E10, N0, + ST_TET, 4, E03, P3, E10, N0, + /* case 107 */ 11, + ST_PNT, 6, P1, P3, P0, P5, E06, E04, + ST_WDG, 6, P1, P6, P3, E01, E11, E02, + ST_PYR, 5, E04, E06, P6, P5, N0, + ST_TET, 4, P5, P6, P1, N0, + ST_TET, 4, P5, P1, P0, N0, + ST_PYR, 5, E08, E04, P5, P0, N0, + ST_PYR, 5, E10, E08, P0, P3, N0, + ST_TET, 4, P0, P1, P3, N0, + ST_PYR, 5, E06, E04, E08, E10, N0, + ST_TET, 4, P3, P1, P6, N0, + ST_PYR, 5, E06, E10, P3, P6, N0, + /* case 108 */ 9, + ST_PNT, 6, E01, E09, E04, E06, E10, E03, + ST_TET, 4, P2, P5, P6, N0, + ST_PYR, 5, E01, E09, P5, P2, N0, + ST_PYR, 5, E01, P2, P3, E03, N0, + ST_TET, 4, P3, P2, P6, N0, + ST_TET, 4, P3, E10, E03, N0, + ST_PYR, 5, P3, P6, E06, E10, N0, + ST_PYR, 5, E06, P6, P5, E04, N0, + ST_TET, 4, E04, P5, E09, N0, + /* case 109 */ 11, + ST_PNT, 6, P2, P0, P3, P6, E04, E06, + ST_WDG, 6, E01, E09, E00, P2, P5, P0, + ST_PYR, 5, E06, P6, P5, E04, N0, + ST_TET, 4, P6, P2, P5, N0, + ST_TET, 4, P6, P3, P2, N0, + ST_PYR, 5, E10, P3, P6, E06, N0, + ST_PYR, 5, E08, P0, P3, E10, N0, + ST_TET, 4, P3, P0, P2, N0, + ST_PYR, 5, E04, E08, E10, E06, N0, + ST_TET, 4, P0, P5, P2, N0, + ST_PYR, 5, E04, P5, P0, E08, N0, + /* case 110 */ 9, + ST_PNT, 5, E06, E04, E00, E10, E03, + ST_PYR, 5, P2, P1, P5, P6, N0, + ST_TET, 4, P1, P2, P3, N0, + ST_TET, 4, P3, P2, P6, N0, + ST_PYR, 5, P3, P6, E06, E10, N0, + ST_PYR, 5, E06, P6, P5, E04, N0, + ST_PYR, 5, E04, P5, P1, E00, N0, + ST_PYR, 5, E00, P1, P3, E03, N0, + ST_TET, 4, E10, E03, P3, N0, + /* case 111 */ 2, + ST_HEX, 8, P0, P5, P6, P3, E08, E04, E06, E10, + ST_WDG, 6, P2, P3, P6, P1, P0, P5, + /* case 112 */ 7, + ST_PNT, 5, E08, E09, E07, E06, E11, + ST_PYR, 5, E09, P5, P6, E11, N0, + ST_PYR, 5, E08, P4, P5, E09, N0, + ST_TET, 4, E07, P4, E08, N0, + ST_TET, 4, P4, P6, P5, N0, + ST_PYR, 5, E07, E06, P6, P4, N0, + ST_TET, 4, P6, E06, E11, N0, + /* case 113 */ 9, + ST_PNT, 6, E07, E06, E11, E09, E00, E03, + ST_TET, 4, P4, P6, P5, N0, + ST_PYR, 5, E07, E06, P6, P4, N0, + ST_PYR, 5, E07, P4, P0, E03, N0, + ST_TET, 4, P0, P4, P5, N0, + ST_TET, 4, P0, E00, E03, N0, + ST_PYR, 5, P0, P5, E09, E00, N0, + ST_PYR, 5, E09, P5, P6, E11, N0, + ST_TET, 4, E11, P6, E06, N0, + /* case 114 */ 5, + ST_TET, 4, P5, P6, P1, P4, + ST_PYR, 5, E08, P4, P1, E00, E01, + ST_PYR, 5, E07, E06, P6, P4, E11, + ST_PYR, 5, P1, P6, E11, E01, P4, + ST_PYR, 5, E08, E01, E11, E07, P4, + /* case 115 */ 9, + ST_PNT, 5, E07, E03, E01, E06, E11, + ST_PYR, 5, P5, P1, P0, P4, N0, + ST_TET, 4, P1, P5, P6, N0, + ST_TET, 4, P6, P5, P4, N0, + ST_PYR, 5, P6, P4, E07, E06, N0, + ST_PYR, 5, E07, P4, P0, E03, N0, + ST_PYR, 5, E03, P0, P1, E01, N0, + ST_PYR, 5, E01, P1, P6, E11, N0, + ST_TET, 4, E06, E11, P6, N0, + /* case 116 */ 9, + ST_PNT, 6, E09, E01, E02, E06, E07, E08, + ST_TET, 4, P5, P6, P2, N0, + ST_PYR, 5, E09, P5, P2, E01, N0, + ST_PYR, 5, E09, E08, P4, P5, N0, + ST_TET, 4, P4, P6, P5, N0, + ST_TET, 4, P4, E08, E07, N0, + ST_PYR, 5, P4, E07, E06, P6, N0, + ST_PYR, 5, E06, E02, P2, P6, N0, + ST_TET, 4, E02, E01, P2, N0, + /* case 117 */ 11, + ST_PNT, 6, P5, P0, P4, P6, E02, E06, + ST_WDG, 6, P5, P2, P0, E09, E01, E00, + ST_PYR, 5, E06, E02, P2, P6, N0, + ST_TET, 4, P6, P2, P5, N0, + ST_TET, 4, P6, P5, P4, N0, + ST_PYR, 5, E07, E06, P6, P4, N0, + ST_PYR, 5, E03, E07, P4, P0, N0, + ST_TET, 4, P4, P5, P0, N0, + ST_PYR, 5, E02, E06, E07, E03, N0, + ST_TET, 4, P0, P5, P2, N0, + ST_PYR, 5, E02, E03, P0, P2, N0, + /* case 118 */ 9, + ST_PNT, 5, E06, E02, E00, E07, E08, + ST_PYR, 5, P5, P6, P2, P1, N0, + ST_TET, 4, P1, P4, P5, N0, + ST_TET, 4, P4, P6, P5, N0, + ST_PYR, 5, P4, E07, E06, P6, N0, + ST_PYR, 5, E06, E02, P2, P6, N0, + ST_PYR, 5, E02, E00, P1, P2, N0, + ST_PYR, 5, E00, E08, P4, P1, N0, + ST_TET, 4, E07, P4, E08, N0, + /* case 119 */ 2, + ST_HEX, 8, E03, E02, E06, E07, P0, P2, P6, P4, + ST_WDG, 6, P1, P0, P2, P5, P4, P6, + /* case 120 */ 9, + ST_PNT, 5, E09, E11, E08, E03, E02, + ST_WDG, 6, E07, E06, E10, P4, P6, P3, + ST_TET, 4, P6, P4, P3, N0, + ST_PYR, 5, P4, E08, E03, P3, N0, + ST_TET, 4, P5, P4, P6, N0, + ST_PYR, 5, E11, E09, P5, P6, N0, + ST_PYR, 5, E09, E08, P4, P5, N0, + ST_PYR, 5, P3, E02, E11, P6, N0, + ST_TET, 4, E03, E02, P3, N0, + /* case 121 */ 11, + ST_PNT, 6, P4, P3, P0, P5, E11, E09, + ST_WDG, 6, E07, E06, E10, P4, P6, P3, + ST_PYR, 5, E09, P5, P6, E11, N0, + ST_TET, 4, P5, P4, P6, N0, + ST_TET, 4, P5, P0, P4, N0, + ST_PYR, 5, E00, P0, P5, E09, N0, + ST_PYR, 5, E02, P3, P0, E00, N0, + ST_TET, 4, P0, P3, P4, N0, + ST_PYR, 5, E11, E02, E00, E09, N0, + ST_TET, 4, P3, P6, P4, N0, + ST_PYR, 5, E11, P6, P3, E02, N0, + /* case 122 */ 5, + ST_WDG, 6, P3, P1, P6, E02, E01, E11, + ST_WDG, 6, E08, E03, E00, P4, P3, P1, + ST_WDG, 6, P4, P3, P6, E07, E10, E06, + ST_TET, 4, P3, P1, P6, P4, + ST_TET, 4, P4, P6, P5, P1, + /* case 123 */ 5, + ST_WDG, 6, E02, E11, E01, P3, P6, P1, + ST_WDG, 6, P3, P6, P4, E10, E06, E07, + ST_PYR, 5, P0, P4, P5, P1, P3, + ST_TET, 4, P6, P5, P4, P3, + ST_TET, 4, P6, P1, P5, P3, + /* case 124 */ 11, + ST_PNT, 6, P6, P4, P5, P2, E03, E01, + ST_WDG, 6, E06, E10, E07, P6, P3, P4, + ST_PYR, 5, E01, P2, P3, E03, N0, + ST_TET, 4, P2, P6, P3, N0, + ST_TET, 4, P2, P5, P6, N0, + ST_PYR, 5, E09, P5, P2, E01, N0, + ST_PYR, 5, E08, P4, P5, E09, N0, + ST_TET, 4, P5, P4, P6, N0, + ST_PYR, 5, E03, E08, E09, E01, N0, + ST_TET, 4, P4, P3, P6, N0, + ST_PYR, 5, E03, P3, P4, E08, N0, + /* case 125 */ 6, + ST_WDG, 6, E07, E06, E10, P4, P6, P3, + ST_WDG, 6, E09, E00, E01, P5, P0, P2, + ST_TET, 4, P2, P3, P0, P5, + ST_TET, 4, P5, P4, P6, P3, + ST_TET, 4, P4, P5, P0, P3, + ST_TET, 4, P5, P6, P2, P3, + /* case 126 */ 5, + ST_WDG, 6, E10, E07, E06, P3, P4, P6, + ST_WDG, 6, P3, P4, P1, E03, E08, E00, + ST_PYR, 5, P2, P1, P5, P6, P3, + ST_TET, 4, P4, P5, P1, P3, + ST_TET, 4, P4, P6, P5, P3, + /* case 127 */ 9, + ST_PNT, 7, P0, P1, P2, P3, P4, P5, P6, + ST_WDG, 6, E07, E06, E10, P4, P6, P3, + ST_TET, 4, P4, P3, P6, N0, + ST_PYR, 5, P5, P6, P2, P1, N0, + ST_TET, 4, P6, P3, P2, N0, + ST_PYR, 5, P0, P1, P2, P3, N0, + ST_TET, 4, P0, P3, P4, N0, + ST_PYR, 5, P0, P4, P5, P1, N0, + ST_TET, 4, P4, P6, P5, N0, + /* case 128 */ 1, + ST_TET, 4, P7, E06, E07, E10, + /* case 129 */ 7, + ST_PNT, 2, E00, E06, + ST_PYR, 5, P0, E03, E10, P7, N0, + ST_PYR, 5, E08, P0, P7, E07, N0, + ST_TET, 4, E07, P7, E06, N0, + ST_TET, 4, P7, E10, E06, N0, + ST_TET, 4, E08, P0, N0, E00, + ST_TET, 4, E03, P0, E00, N0, + /* case 130 */ 2, + ST_TET, 4, P1, E00, E09, E01, + ST_TET, 4, P7, E07, E10, E06, + /* case 131 */ 8, + ST_PNT, 4, E09, E01, E06, E06, + ST_PYR, 5, P7, P0, E03, E10, N0, + ST_PYR, 5, E08, P0, P7, E07, N0, + ST_TET, 4, P7, E10, E06, N0, + ST_TET, 4, E07, P7, E06, N0, + ST_PYR, 5, E03, P0, P1, E01, N0, + ST_PYR, 5, P0, E08, E09, P1, N0, + ST_TET, 4, E09, E01, P1, N0, + /* case 132 */ 7, + ST_PNT, 2, E07, E01, + ST_PYR, 5, P7, E10, E02, P2, N0, + ST_PYR, 5, E06, P7, P2, E11, N0, + ST_TET, 4, E11, P2, E01, N0, + ST_TET, 4, P2, E02, E01, N0, + ST_TET, 4, E06, P7, N0, E07, + ST_TET, 4, E10, P7, E07, N0, + /* case 133 */ 5, + ST_WDG, 6, P0, P2, P7, E03, E02, E10, + ST_PYR, 5, P0, P7, E07, E08, E00, + ST_PYR, 5, E06, P7, P2, E11, E01, + ST_PYR, 5, E00, E01, P2, P0, P7, + ST_PYR, 5, E00, E07, E06, E01, P7, + /* case 134 */ 8, + ST_PNT, 4, E09, E00, E07, E07, + ST_PYR, 5, P7, E10, E02, P2, N0, + ST_PYR, 5, E11, E06, P7, P2, N0, + ST_TET, 4, P7, E07, E10, N0, + ST_TET, 4, E06, E07, P7, N0, + ST_PYR, 5, E02, E00, P1, P2, N0, + ST_PYR, 5, P2, P1, E09, E11, N0, + ST_TET, 4, E09, P1, E00, N0, + /* case 135 */ 9, + ST_PNT, 5, E09, E11, E08, E07, E06, + ST_WDG, 6, P0, P2, P7, E03, E02, E10, + ST_TET, 4, P2, P7, P0, N0, + ST_PYR, 5, P0, P7, E07, E08, N0, + ST_TET, 4, P1, P2, P0, N0, + ST_PYR, 5, E11, P2, P1, E09, N0, + ST_PYR, 5, E09, P1, P0, E08, N0, + ST_PYR, 5, P7, P2, E11, E06, N0, + ST_TET, 4, E07, P7, E06, N0, + /* case 136 */ 1, + ST_WDG, 6, P7, E07, E06, P3, E03, E02, + /* case 137 */ 7, + ST_PNT, 5, E00, E02, E08, E07, E06, + ST_PYR, 5, E02, E06, P7, P3, N0, + ST_PYR, 5, E00, E02, P3, P0, N0, + ST_TET, 4, E08, E00, P0, N0, + ST_TET, 4, P0, P3, P7, N0, + ST_PYR, 5, E08, P0, P7, E07, N0, + ST_TET, 4, P7, E06, E07, N0, + /* case 138 */ 8, + ST_PNT, 4, E06, E07, E09, E09, + ST_PYR, 5, P1, P3, E03, E00, N0, + ST_PYR, 5, E02, P3, P1, E01, N0, + ST_TET, 4, P1, E00, E09, N0, + ST_TET, 4, E01, P1, E09, N0, + ST_PYR, 5, E03, P3, P7, E07, N0, + ST_PYR, 5, P3, E02, E06, P7, N0, + ST_TET, 4, E06, E07, P7, N0, + /* case 139 */ 9, + ST_PNT, 6, E08, E07, E06, E02, E01, E09, + ST_TET, 4, P0, P3, P7, N0, + ST_PYR, 5, E08, P0, P7, E07, N0, + ST_PYR, 5, E08, E09, P1, P0, N0, + ST_TET, 4, P1, P3, P0, N0, + ST_TET, 4, P1, E09, E01, N0, + ST_PYR, 5, P1, E01, E02, P3, N0, + ST_PYR, 5, E02, E06, P7, P3, N0, + ST_TET, 4, E06, E07, P7, N0, + /* case 140 */ 7, + ST_PNT, 5, E01, E03, E11, E06, E07, + ST_PYR, 5, E03, P3, P7, E07, N0, + ST_PYR, 5, E01, P2, P3, E03, N0, + ST_TET, 4, E11, P2, E01, N0, + ST_TET, 4, P2, P7, P3, N0, + ST_PYR, 5, E11, E06, P7, P2, N0, + ST_TET, 4, P7, E06, E07, N0, + /* case 141 */ 5, + ST_TET, 4, P3, P0, P2, P7, + ST_PYR, 5, E07, E08, P0, P7, E00, + ST_PYR, 5, E06, P7, P2, E11, E01, + ST_PYR, 5, P0, E00, E01, P2, P7, + ST_PYR, 5, E07, E06, E01, E00, P7, + /* case 142 */ 9, + ST_PNT, 6, E03, E00, E09, E11, E06, E07, + ST_TET, 4, P3, P1, P2, N0, + ST_PYR, 5, E03, E00, P1, P3, N0, + ST_PYR, 5, E03, P3, P7, E07, N0, + ST_TET, 4, P7, P3, P2, N0, + ST_TET, 4, P7, E06, E07, N0, + ST_PYR, 5, P7, P2, E11, E06, N0, + ST_PYR, 5, E11, P2, P1, E09, N0, + ST_TET, 4, E09, P1, E00, N0, + /* case 143 */ 9, + ST_PNT, 5, E11, E09, E08, E06, E07, + ST_PYR, 5, P3, P0, P1, P2, N0, + ST_TET, 4, P0, P3, P7, N0, + ST_TET, 4, P7, P3, P2, N0, + ST_PYR, 5, P7, P2, E11, E06, N0, + ST_PYR, 5, E11, P2, P1, E09, N0, + ST_PYR, 5, E09, P1, P0, E08, N0, + ST_PYR, 5, E08, P0, P7, E07, N0, + ST_TET, 4, E06, E07, P7, N0, + /* case 144 */ 1, + ST_WDG, 6, P4, E08, E04, P7, E10, E06, + /* case 145 */ 7, + ST_PNT, 5, E00, E04, E03, E10, E06, + ST_PYR, 5, E04, P4, P7, E06, N0, + ST_PYR, 5, E00, P0, P4, E04, N0, + ST_TET, 4, E03, P0, E00, N0, + ST_TET, 4, P0, P7, P4, N0, + ST_PYR, 5, E03, E10, P7, P0, N0, + ST_TET, 4, P7, E10, E06, N0, + /* case 146 */ 8, + ST_PNT, 4, E06, E10, E01, E01, + ST_PYR, 5, P1, E00, E08, P4, N0, + ST_PYR, 5, E04, E09, P1, P4, N0, + ST_TET, 4, P1, E01, E00, N0, + ST_TET, 4, E09, E01, P1, N0, + ST_PYR, 5, E08, E10, P7, P4, N0, + ST_PYR, 5, P4, P7, E06, E04, N0, + ST_TET, 4, E06, P7, E10, N0, + /* case 147 */ 9, + ST_PNT, 6, E03, E10, E06, E04, E09, E01, + ST_TET, 4, P0, P7, P4, N0, + ST_PYR, 5, E03, E10, P7, P0, N0, + ST_PYR, 5, E03, P0, P1, E01, N0, + ST_TET, 4, P1, P0, P4, N0, + ST_TET, 4, P1, E09, E01, N0, + ST_PYR, 5, P1, P4, E04, E09, N0, + ST_PYR, 5, E04, P4, P7, E06, N0, + ST_TET, 4, E06, P7, E10, N0, + /* case 148 */ 8, + ST_PNT, 4, E04, E08, E01, E01, + ST_PYR, 5, P2, P7, E10, E02, N0, + ST_PYR, 5, E06, P7, P2, E11, N0, + ST_TET, 4, P2, E02, E01, N0, + ST_TET, 4, E11, P2, E01, N0, + ST_PYR, 5, E10, P7, P4, E08, N0, + ST_PYR, 5, P7, E06, E04, P4, N0, + ST_TET, 4, E04, E08, P4, N0, + /* case 149 */ 9, + ST_PNT, 5, E04, E06, E00, E01, E11, + ST_WDG, 6, E03, E10, E02, P0, P7, P2, + ST_TET, 4, P7, P0, P2, N0, + ST_PYR, 5, P0, E00, E01, P2, N0, + ST_TET, 4, P4, P0, P7, N0, + ST_PYR, 5, E06, E04, P4, P7, N0, + ST_PYR, 5, E04, E00, P0, P4, N0, + ST_PYR, 5, P2, E11, E06, P7, N0, + ST_TET, 4, E01, E11, P2, N0, + /* case 150 */ 2, + ST_HEX, 8, P4, P1, P2, P7, E04, E09, E11, E06, + ST_HEX, 8, E08, E00, E02, E10, P4, P1, P2, P7, + /* case 151 */ 11, + ST_PNT, 6, P0, P7, P4, P1, E11, E09, + ST_WDG, 6, P0, P2, P7, E03, E02, E10, + ST_PYR, 5, E09, E11, P2, P1, N0, + ST_TET, 4, P1, P2, P0, N0, + ST_TET, 4, P1, P0, P4, N0, + ST_PYR, 5, E04, E09, P1, P4, N0, + ST_PYR, 5, E06, E04, P4, P7, N0, + ST_TET, 4, P4, P0, P7, N0, + ST_PYR, 5, E11, E09, E04, E06, N0, + ST_TET, 4, P7, P0, P2, N0, + ST_PYR, 5, E11, E06, P7, P2, N0, + /* case 152 */ 7, + ST_PNT, 5, E02, E06, E03, E08, E04, + ST_PYR, 5, E06, E04, P4, P7, N0, + ST_PYR, 5, E02, E06, P7, P3, N0, + ST_TET, 4, E03, E02, P3, N0, + ST_TET, 4, P3, P7, P4, N0, + ST_PYR, 5, E03, P3, P4, E08, N0, + ST_TET, 4, P4, E04, E08, N0, + /* case 153 */ 1, + ST_HEX, 8, E00, E04, E06, E02, P0, P4, P7, P3, + /* case 154 */ 9, + ST_PNT, 5, E06, E04, E02, E01, E09, + ST_WDG, 6, P3, P4, P1, E03, E08, E00, + ST_TET, 4, P4, P1, P3, N0, + ST_PYR, 5, P3, P1, E01, E02, N0, + ST_TET, 4, P7, P4, P3, N0, + ST_PYR, 5, E04, P4, P7, E06, N0, + ST_PYR, 5, E06, P7, P3, E02, N0, + ST_PYR, 5, P1, P4, E04, E09, N0, + ST_TET, 4, E01, P1, E09, N0, + /* case 155 */ 9, + ST_PNT, 5, E04, E06, E02, E09, E01, + ST_PYR, 5, P0, P3, P7, P4, N0, + ST_TET, 4, P3, P0, P1, N0, + ST_TET, 4, P1, P0, P4, N0, + ST_PYR, 5, P1, P4, E04, E09, N0, + ST_PYR, 5, E04, P4, P7, E06, N0, + ST_PYR, 5, E06, P7, P3, E02, N0, + ST_PYR, 5, E02, P3, P1, E01, N0, + ST_TET, 4, E09, E01, P1, N0, + /* case 156 */ 9, + ST_PNT, 6, E03, E08, E04, E06, E11, E01, + ST_TET, 4, P3, P7, P4, N0, + ST_PYR, 5, E03, P3, P4, E08, N0, + ST_PYR, 5, E03, E01, P2, P3, N0, + ST_TET, 4, P2, P7, P3, N0, + ST_TET, 4, P2, E01, E11, N0, + ST_PYR, 5, P2, E11, E06, P7, N0, + ST_PYR, 5, E06, E04, P4, P7, N0, + ST_TET, 4, E04, E08, P4, N0, + /* case 157 */ 9, + ST_PNT, 5, E06, E04, E00, E11, E01, + ST_PYR, 5, P3, P7, P4, P0, N0, + ST_TET, 4, P0, P2, P3, N0, + ST_TET, 4, P2, P7, P3, N0, + ST_PYR, 5, P2, E11, E06, P7, N0, + ST_PYR, 5, E06, E04, P4, P7, N0, + ST_PYR, 5, E04, E00, P0, P4, N0, + ST_PYR, 5, E00, E01, P2, P0, N0, + ST_TET, 4, E11, P2, E01, N0, + /* case 158 */ 11, + ST_PNT, 6, P3, P4, P7, P2, E09, E11, + ST_WDG, 6, E03, E00, E08, P3, P1, P4, + ST_PYR, 5, E11, P2, P1, E09, N0, + ST_TET, 4, P2, P3, P1, N0, + ST_TET, 4, P2, P7, P3, N0, + ST_PYR, 5, E06, P7, P2, E11, N0, + ST_PYR, 5, E04, P4, P7, E06, N0, + ST_TET, 4, P7, P4, P3, N0, + ST_PYR, 5, E09, E04, E06, E11, N0, + ST_TET, 4, P4, P1, P3, N0, + ST_PYR, 5, E09, P1, P4, E04, N0, + /* case 159 */ 2, + ST_HEX, 8, P4, P1, P2, P7, E04, E09, E11, E06, + ST_WDG, 6, P3, P7, P2, P0, P4, P1, + /* case 160 */ 7, + ST_PNT, 2, E10, E09, + ST_PYR, 5, P7, P5, E04, E07, N0, + ST_PYR, 5, E06, E05, P5, P7, N0, + ST_TET, 4, E05, E09, P5, N0, + ST_TET, 4, P5, E09, E04, N0, + ST_TET, 4, E06, N0, P7, E10, + ST_TET, 4, E07, E10, P7, N0, + /* case 161 */ 5, + ST_WDG, 6, E08, E04, E07, P0, P5, P7, + ST_PYR, 5, P0, E03, E10, P7, E00, + ST_PYR, 5, E06, E05, P5, P7, E09, + ST_PYR, 5, E00, P0, P5, E09, P7, + ST_PYR, 5, E00, E09, E06, E10, P7, + /* case 162 */ 8, + ST_PNT, 4, E01, E00, E10, E10, + ST_PYR, 5, P7, P5, E04, E07, N0, + ST_PYR, 5, E05, P5, P7, E06, N0, + ST_TET, 4, P7, E07, E10, N0, + ST_TET, 4, E06, P7, E10, N0, + ST_PYR, 5, E04, P5, P1, E00, N0, + ST_PYR, 5, P5, E05, E01, P1, N0, + ST_TET, 4, E01, E00, P1, N0, + /* case 163 */ 9, + ST_PNT, 5, E01, E05, E03, E10, E06, + ST_WDG, 6, E08, E04, E07, P0, P5, P7, + ST_TET, 4, P5, P0, P7, N0, + ST_PYR, 5, P0, E03, E10, P7, N0, + ST_TET, 4, P1, P0, P5, N0, + ST_PYR, 5, E05, E01, P1, P5, N0, + ST_PYR, 5, E01, E03, P0, P1, N0, + ST_PYR, 5, P7, E06, E05, P5, N0, + ST_TET, 4, E10, E06, P7, N0, + /* case 164 */ 5, + ST_WDG, 6, P5, P7, P2, E05, E06, E11, + ST_PYR, 5, P5, P2, E01, E09, E04, + ST_PYR, 5, E02, P2, P7, E10, E07, + ST_PYR, 5, E04, E07, P7, P5, P2, + ST_PYR, 5, E04, E01, E02, E07, P2, + /* case 165 */ 5, + ST_WDG, 6, P2, P0, P5, E01, E00, E09, + ST_TET, 4, P2, P0, P5, P7, + ST_WDG, 6, E06, E05, E11, P7, P5, P2, + ST_WDG, 6, E03, E10, E02, P0, P7, P2, + ST_WDG, 6, E04, E07, E08, P5, P7, P0, + /* case 166 */ 9, + ST_PNT, 5, E00, E02, E04, E07, E10, + ST_WDG, 6, E05, E11, E06, P5, P2, P7, + ST_TET, 4, P2, P5, P7, N0, + ST_PYR, 5, P5, E04, E07, P7, N0, + ST_TET, 4, P1, P5, P2, N0, + ST_PYR, 5, E02, E00, P1, P2, N0, + ST_PYR, 5, E00, E04, P5, P1, N0, + ST_PYR, 5, P7, E10, E02, P2, N0, + ST_TET, 4, E07, E10, P7, N0, + /* case 167 */ 5, + ST_WDG, 6, E07, E08, E04, P7, P0, P5, + ST_WDG, 6, P2, P7, P0, E02, E10, E03, + ST_WDG, 6, E11, E06, E05, P2, P7, P5, + ST_TET, 4, P7, P5, P0, P2, + ST_TET, 4, P2, P1, P5, P0, + /* case 168 */ 8, + ST_PNT, 4, E02, E03, E09, E09, + ST_PYR, 5, P5, E04, E07, P7, N0, + ST_PYR, 5, E06, E05, P5, P7, N0, + ST_TET, 4, P5, E09, E04, N0, + ST_TET, 4, E05, E09, P5, N0, + ST_PYR, 5, E07, E03, P3, P7, N0, + ST_PYR, 5, P7, P3, E02, E06, N0, + ST_TET, 4, E02, P3, E03, N0, + /* case 169 */ 9, + ST_PNT, 5, E02, E06, E00, E09, E05, + ST_WDG, 6, P0, P7, P5, E08, E07, E04, + ST_TET, 4, P7, P5, P0, N0, + ST_PYR, 5, P0, P5, E09, E00, N0, + ST_TET, 4, P3, P7, P0, N0, + ST_PYR, 5, E06, P7, P3, E02, N0, + ST_PYR, 5, E02, P3, P0, E00, N0, + ST_PYR, 5, P5, P7, E06, E05, N0, + ST_TET, 4, E09, P5, E05, N0, + /* case 170 */ 2, + ST_HEX, 8, E02, E01, E05, E06, P3, P1, P5, P7, + ST_HEX, 8, P3, P1, P5, P7, E03, E00, E04, E07, + /* case 171 */ 11, + ST_PNT, 6, P0, P7, P3, P1, E05, E01, + ST_WDG, 6, E08, E04, E07, P0, P5, P7, + ST_PYR, 5, E01, P1, P5, E05, N0, + ST_TET, 4, P1, P0, P5, N0, + ST_TET, 4, P1, P3, P0, N0, + ST_PYR, 5, E02, P3, P1, E01, N0, + ST_PYR, 5, E06, P7, P3, E02, N0, + ST_TET, 4, P3, P7, P0, N0, + ST_PYR, 5, E05, E06, E02, E01, N0, + ST_TET, 4, P7, P5, P0, N0, + ST_PYR, 5, E05, P5, P7, E06, N0, + /* case 172 */ 9, + ST_PNT, 5, E03, E07, E01, E09, E04, + ST_WDG, 6, E11, E06, E05, P2, P7, P5, + ST_TET, 4, P7, P2, P5, N0, + ST_PYR, 5, P2, E01, E09, P5, N0, + ST_TET, 4, P3, P2, P7, N0, + ST_PYR, 5, E07, E03, P3, P7, N0, + ST_PYR, 5, E03, E01, P2, P3, N0, + ST_PYR, 5, P5, E04, E07, P7, N0, + ST_TET, 4, E09, E04, P5, N0, + /* case 173 */ 5, + ST_WDG, 6, E09, E00, E01, P5, P0, P2, + ST_WDG, 6, P7, P5, P0, E07, E04, E08, + ST_WDG, 6, E06, E05, E11, P7, P5, P2, + ST_TET, 4, P5, P2, P0, P7, + ST_TET, 4, P7, P3, P2, P0, + /* case 174 */ 11, + ST_PNT, 6, P2, P5, P1, P3, E07, E03, + ST_WDG, 6, E11, E06, E05, P2, P7, P5, + ST_PYR, 5, E03, P3, P7, E07, N0, + ST_TET, 4, P3, P2, P7, N0, + ST_TET, 4, P3, P1, P2, N0, + ST_PYR, 5, E00, P1, P3, E03, N0, + ST_PYR, 5, E04, P5, P1, E00, N0, + ST_TET, 4, P1, P5, P2, N0, + ST_PYR, 5, E07, E04, E00, E03, N0, + ST_TET, 4, P5, P7, P2, N0, + ST_PYR, 5, E07, P7, P5, E04, N0, + /* case 175 */ 5, + ST_WDG, 6, E06, E05, E11, P7, P5, P2, + ST_WDG, 6, P7, P5, P0, E07, E04, E08, + ST_PYR, 5, P3, P0, P1, P2, P7, + ST_TET, 4, P5, P1, P0, P7, + ST_TET, 4, P5, P2, P1, P7, + /* case 176 */ 7, + ST_PNT, 5, E09, E08, E05, E06, E10, + ST_PYR, 5, E08, E10, P7, P4, N0, + ST_PYR, 5, E09, E08, P4, P5, N0, + ST_TET, 4, E05, E09, P5, N0, + ST_TET, 4, P5, P4, P7, N0, + ST_PYR, 5, E05, P5, P7, E06, N0, + ST_TET, 4, P7, E10, E06, N0, + /* case 177 */ 5, + ST_TET, 4, P4, P5, P0, P7, + ST_PYR, 5, E10, P7, P0, E03, E00, + ST_PYR, 5, E06, E05, P5, P7, E09, + ST_PYR, 5, P0, P5, E09, E00, P7, + ST_PYR, 5, E10, E00, E09, E06, P7, + /* case 178 */ 9, + ST_PNT, 6, E08, E00, E01, E05, E06, E10, + ST_TET, 4, P4, P5, P1, N0, + ST_PYR, 5, E08, P4, P1, E00, N0, + ST_PYR, 5, E08, E10, P7, P4, N0, + ST_TET, 4, P7, P5, P4, N0, + ST_TET, 4, P7, E10, E06, N0, + ST_PYR, 5, P7, E06, E05, P5, N0, + ST_PYR, 5, E05, E01, P1, P5, N0, + ST_TET, 4, E01, E00, P1, N0, + /* case 179 */ 9, + ST_PNT, 5, E05, E01, E03, E06, E10, + ST_PYR, 5, P4, P5, P1, P0, N0, + ST_TET, 4, P0, P7, P4, N0, + ST_TET, 4, P7, P5, P4, N0, + ST_PYR, 5, P7, E06, E05, P5, N0, + ST_PYR, 5, E05, E01, P1, P5, N0, + ST_PYR, 5, E01, E03, P0, P1, N0, + ST_PYR, 5, E03, E10, P7, P0, N0, + ST_TET, 4, E06, P7, E10, N0, + /* case 180 */ 9, + ST_PNT, 5, E08, E10, E09, E01, E02, + ST_WDG, 6, P5, P7, P2, E05, E06, E11, + ST_TET, 4, P7, P2, P5, N0, + ST_PYR, 5, P5, P2, E01, E09, N0, + ST_TET, 4, P4, P7, P5, N0, + ST_PYR, 5, E10, P7, P4, E08, N0, + ST_PYR, 5, E08, P4, P5, E09, N0, + ST_PYR, 5, P2, P7, E10, E02, N0, + ST_TET, 4, E01, P2, E02, N0, + /* case 181 */ 5, + ST_WDG, 6, P2, P0, P5, E01, E00, E09, + ST_WDG, 6, E10, E02, E03, P7, P2, P0, + ST_WDG, 6, P7, P2, P5, E06, E11, E05, + ST_TET, 4, P2, P0, P5, P7, + ST_TET, 4, P7, P5, P4, P0, + /* case 182 */ 11, + ST_PNT, 6, P5, P2, P1, P4, E10, E08, + ST_WDG, 6, P5, P7, P2, E05, E06, E11, + ST_PYR, 5, E08, E10, P7, P4, N0, + ST_TET, 4, P4, P7, P5, N0, + ST_TET, 4, P4, P5, P1, N0, + ST_PYR, 5, E00, E08, P4, P1, N0, + ST_PYR, 5, E02, E00, P1, P2, N0, + ST_TET, 4, P1, P5, P2, N0, + ST_PYR, 5, E10, E08, E00, E02, N0, + ST_TET, 4, P2, P5, P7, N0, + ST_PYR, 5, E10, E02, P2, P7, N0, + /* case 183 */ 5, + ST_WDG, 6, P7, P2, P5, E06, E11, E05, + ST_WDG, 6, E10, E02, E03, P7, P2, P0, + ST_PYR, 5, P4, P5, P1, P0, P7, + ST_TET, 4, P2, P0, P1, P7, + ST_TET, 4, P2, P1, P5, P7, + /* case 184 */ 9, + ST_PNT, 6, E08, E03, E02, E06, E05, E09, + ST_TET, 4, P4, P3, P7, N0, + ST_PYR, 5, E08, E03, P3, P4, N0, + ST_PYR, 5, E08, P4, P5, E09, N0, + ST_TET, 4, P5, P4, P7, N0, + ST_TET, 4, P5, E05, E09, N0, + ST_PYR, 5, P5, P7, E06, E05, N0, + ST_PYR, 5, E06, P7, P3, E02, N0, + ST_TET, 4, E02, P3, E03, N0, + /* case 185 */ 9, + ST_PNT, 5, E06, E02, E00, E05, E09, + ST_PYR, 5, P4, P0, P3, P7, N0, + ST_TET, 4, P0, P4, P5, N0, + ST_TET, 4, P5, P4, P7, N0, + ST_PYR, 5, P5, P7, E06, E05, N0, + ST_PYR, 5, E06, P7, P3, E02, N0, + ST_PYR, 5, E02, P3, P0, E00, N0, + ST_PYR, 5, E00, P0, P5, E09, N0, + ST_TET, 4, E05, E09, P5, N0, + /* case 186 */ 11, + ST_PNT, 6, P4, P3, P7, P5, E01, E05, + ST_WDG, 6, P4, P1, P3, E08, E00, E03, + ST_PYR, 5, E05, E01, P1, P5, N0, + ST_TET, 4, P5, P1, P4, N0, + ST_TET, 4, P5, P4, P7, N0, + ST_PYR, 5, E06, E05, P5, P7, N0, + ST_PYR, 5, E02, E06, P7, P3, N0, + ST_TET, 4, P7, P4, P3, N0, + ST_PYR, 5, E01, E05, E06, E02, N0, + ST_TET, 4, P3, P4, P1, N0, + ST_PYR, 5, E01, E02, P3, P1, N0, + /* case 187 */ 2, + ST_HEX, 8, E02, E01, E05, E06, P3, P1, P5, P7, + ST_WDG, 6, P0, P3, P1, P4, P7, P5, + /* case 188 */ 11, + ST_PNT, 6, P7, P5, P4, P3, E01, E03, + ST_WDG, 6, P7, P2, P5, E06, E11, E05, + ST_PYR, 5, E03, E01, P2, P3, N0, + ST_TET, 4, P3, P2, P7, N0, + ST_TET, 4, P3, P7, P4, N0, + ST_PYR, 5, E08, E03, P3, P4, N0, + ST_PYR, 5, E09, E08, P4, P5, N0, + ST_TET, 4, P4, P7, P5, N0, + ST_PYR, 5, E01, E03, E08, E09, N0, + ST_TET, 4, P5, P7, P2, N0, + ST_PYR, 5, E01, E09, P5, P2, N0, + /* case 189 */ 5, + ST_WDG, 6, P2, P5, P7, E11, E05, E06, + ST_WDG, 6, E01, E09, E00, P2, P5, P0, + ST_PYR, 5, P3, P7, P4, P0, P2, + ST_TET, 4, P5, P0, P4, P2, + ST_TET, 4, P5, P4, P7, P2, + /* case 190 */ 6, + ST_WDG, 6, E03, E00, E08, P3, P1, P4, + ST_WDG, 6, E11, E06, E05, P2, P7, P5, + ST_TET, 4, P5, P4, P7, P2, + ST_TET, 4, P2, P3, P1, P4, + ST_TET, 4, P3, P2, P7, P4, + ST_TET, 4, P2, P1, P5, P4, + /* case 191 */ 9, + ST_PNT, 7, P1, P0, P3, P2, P5, P4, P7, + ST_WDG, 6, P5, P7, P2, E05, E06, E11, + ST_TET, 4, P5, P7, P2, N0, + ST_PYR, 5, P4, P0, P3, P7, N0, + ST_TET, 4, P7, P3, P2, N0, + ST_PYR, 5, P1, P2, P3, P0, N0, + ST_TET, 4, P1, P5, P2, N0, + ST_PYR, 5, P1, P0, P4, P5, N0, + ST_TET, 4, P5, P4, P7, N0, + /* case 192 */ 1, + ST_WDG, 6, P6, E05, E11, P7, E07, E10, + /* case 193 */ 8, + ST_PNT, 4, E11, E05, E00, E00, + ST_PYR, 5, P0, P7, E07, E08, N0, + ST_PYR, 5, E10, P7, P0, E03, N0, + ST_TET, 4, P0, E08, E00, N0, + ST_TET, 4, E03, P0, E00, N0, + ST_PYR, 5, E07, P7, P6, E05, N0, + ST_PYR, 5, P7, E10, E11, P6, N0, + ST_TET, 4, E11, E05, P6, N0, + /* case 194 */ 8, + ST_PNT, 4, E10, E07, E00, E00, + ST_PYR, 5, P1, E09, E05, P6, N0, + ST_PYR, 5, E11, E01, P1, P6, N0, + ST_TET, 4, P1, E00, E09, N0, + ST_TET, 4, E01, E00, P1, N0, + ST_PYR, 5, E05, E07, P7, P6, N0, + ST_PYR, 5, P6, P7, E10, E11, N0, + ST_TET, 4, E10, P7, E07, N0, + /* case 195 */ 2, + ST_HEX, 8, E08, E07, E05, E09, P0, P7, P6, P1, + ST_HEX, 8, P0, P7, P6, P1, E03, E10, E11, E01, + /* case 196 */ 7, + ST_PNT, 5, E07, E05, E10, E02, E01, + ST_PYR, 5, E05, P6, P2, E01, N0, + ST_PYR, 5, E07, P7, P6, E05, N0, + ST_TET, 4, E10, P7, E07, N0, + ST_TET, 4, P7, P2, P6, N0, + ST_PYR, 5, E10, E02, P2, P7, N0, + ST_TET, 4, P2, E02, E01, N0, + /* case 197 */ 9, + ST_PNT, 5, E05, E01, E07, E08, E00, + ST_WDG, 6, E10, E02, E03, P7, P2, P0, + ST_TET, 4, P2, P7, P0, N0, + ST_PYR, 5, P7, E07, E08, P0, N0, + ST_TET, 4, P6, P7, P2, N0, + ST_PYR, 5, E01, E05, P6, P2, N0, + ST_PYR, 5, E05, E07, P7, P6, N0, + ST_PYR, 5, P0, E00, E01, P2, N0, + ST_TET, 4, E08, E00, P0, N0, + /* case 198 */ 9, + ST_PNT, 6, E02, E10, E07, E05, E09, E00, + ST_TET, 4, P2, P6, P7, N0, + ST_PYR, 5, E02, P2, P7, E10, N0, + ST_PYR, 5, E02, E00, P1, P2, N0, + ST_TET, 4, P1, P6, P2, N0, + ST_TET, 4, P1, E00, E09, N0, + ST_PYR, 5, P1, E09, E05, P6, N0, + ST_PYR, 5, E05, E07, P7, P6, N0, + ST_TET, 4, E07, E10, P7, N0, + /* case 199 */ 11, + ST_PNT, 6, P2, P0, P1, P6, E07, E05, + ST_WDG, 6, P2, P7, P0, E02, E10, E03, + ST_PYR, 5, E05, E07, P7, P6, N0, + ST_TET, 4, P6, P7, P2, N0, + ST_TET, 4, P6, P2, P1, N0, + ST_PYR, 5, E09, E05, P6, P1, N0, + ST_PYR, 5, E08, E09, P1, P0, N0, + ST_TET, 4, P1, P2, P0, N0, + ST_PYR, 5, E07, E05, E09, E08, N0, + ST_TET, 4, P0, P2, P7, N0, + ST_PYR, 5, E07, E08, P0, P7, N0, + /* case 200 */ 7, + ST_PNT, 5, E03, E07, E02, E11, E05, + ST_PYR, 5, E07, P7, P6, E05, N0, + ST_PYR, 5, E03, P3, P7, E07, N0, + ST_TET, 4, E02, P3, E03, N0, + ST_TET, 4, P3, P6, P7, N0, + ST_PYR, 5, E02, E11, P6, P3, N0, + ST_TET, 4, P6, E11, E05, N0, + /* case 201 */ 9, + ST_PNT, 6, E02, E11, E05, E07, E08, E00, + ST_TET, 4, P3, P6, P7, N0, + ST_PYR, 5, E02, E11, P6, P3, N0, + ST_PYR, 5, E02, P3, P0, E00, N0, + ST_TET, 4, P0, P3, P7, N0, + ST_TET, 4, P0, E08, E00, N0, + ST_PYR, 5, P0, P7, E07, E08, N0, + ST_PYR, 5, E07, P7, P6, E05, N0, + ST_TET, 4, E05, P6, E11, N0, + /* case 202 */ 9, + ST_PNT, 5, E07, E05, E03, E00, E09, + ST_WDG, 6, E02, E11, E01, P3, P6, P1, + ST_TET, 4, P6, P3, P1, N0, + ST_PYR, 5, P3, E03, E00, P1, N0, + ST_TET, 4, P7, P3, P6, N0, + ST_PYR, 5, E05, E07, P7, P6, N0, + ST_PYR, 5, E07, E03, P3, P7, N0, + ST_PYR, 5, P1, E09, E05, P6, N0, + ST_TET, 4, E00, E09, P1, N0, + /* case 203 */ 11, + ST_PNT, 6, P3, P1, P0, P7, E05, E07, + ST_WDG, 6, E02, E11, E01, P3, P6, P1, + ST_PYR, 5, E07, P7, P6, E05, N0, + ST_TET, 4, P7, P3, P6, N0, + ST_TET, 4, P7, P0, P3, N0, + ST_PYR, 5, E08, P0, P7, E07, N0, + ST_PYR, 5, E09, P1, P0, E08, N0, + ST_TET, 4, P0, P1, P3, N0, + ST_PYR, 5, E05, E09, E08, E07, N0, + ST_TET, 4, P1, P6, P3, N0, + ST_PYR, 5, E05, P6, P1, E09, N0, + /* case 204 */ 1, + ST_HEX, 8, P3, P2, P6, P7, E03, E01, E05, E07, + /* case 205 */ 9, + ST_PNT, 5, E01, E05, E07, E00, E08, + ST_PYR, 5, P3, P2, P6, P7, N0, + ST_TET, 4, P7, P0, P3, N0, + ST_TET, 4, P0, P2, P3, N0, + ST_PYR, 5, P0, E00, E01, P2, N0, + ST_PYR, 5, E01, E05, P6, P2, N0, + ST_PYR, 5, E05, E07, P7, P6, N0, + ST_PYR, 5, E07, E08, P0, P7, N0, + ST_TET, 4, E00, P0, E08, N0, + /* case 206 */ 9, + ST_PNT, 5, E05, E07, E03, E09, E00, + ST_PYR, 5, P2, P6, P7, P3, N0, + ST_TET, 4, P3, P1, P2, N0, + ST_TET, 4, P1, P6, P2, N0, + ST_PYR, 5, P1, E09, E05, P6, N0, + ST_PYR, 5, E05, E07, P7, P6, N0, + ST_PYR, 5, E07, E03, P3, P7, N0, + ST_PYR, 5, E03, E00, P1, P3, N0, + ST_TET, 4, E09, P1, E00, N0, + /* case 207 */ 2, + ST_HEX, 8, E08, E07, E05, E09, P0, P7, P6, P1, + ST_WDG, 6, P3, P0, P7, P2, P1, P6, + /* case 208 */ 7, + ST_PNT, 5, E08, E10, E04, E05, E11, + ST_PYR, 5, E10, E11, P6, P7, N0, + ST_PYR, 5, E08, E10, P7, P4, N0, + ST_TET, 4, E04, E08, P4, N0, + ST_TET, 4, P4, P7, P6, N0, + ST_PYR, 5, E04, P4, P6, E05, N0, + ST_TET, 4, P6, E11, E05, N0, + /* case 209 */ 9, + ST_PNT, 6, E04, E05, E11, E10, E03, E00, + ST_TET, 4, P4, P7, P6, N0, + ST_PYR, 5, E04, P4, P6, E05, N0, + ST_PYR, 5, E04, E00, P0, P4, N0, + ST_TET, 4, P0, P7, P4, N0, + ST_TET, 4, P0, E00, E03, N0, + ST_PYR, 5, P0, E03, E10, P7, N0, + ST_PYR, 5, E10, E11, P6, P7, N0, + ST_TET, 4, E11, E05, P6, N0, + /* case 210 */ 9, + ST_PNT, 5, E10, E11, E08, E00, E01, + ST_WDG, 6, P4, P6, P1, E04, E05, E09, + ST_TET, 4, P6, P1, P4, N0, + ST_PYR, 5, P4, P1, E00, E08, N0, + ST_TET, 4, P7, P6, P4, N0, + ST_PYR, 5, E11, P6, P7, E10, N0, + ST_PYR, 5, E10, P7, P4, E08, N0, + ST_PYR, 5, P1, P6, E11, E01, N0, + ST_TET, 4, E00, P1, E01, N0, + /* case 211 */ 11, + ST_PNT, 6, P4, P1, P0, P7, E11, E10, + ST_WDG, 6, P4, P6, P1, E04, E05, E09, + ST_PYR, 5, E10, E11, P6, P7, N0, + ST_TET, 4, P7, P6, P4, N0, + ST_TET, 4, P7, P4, P0, N0, + ST_PYR, 5, E03, E10, P7, P0, N0, + ST_PYR, 5, E01, E03, P0, P1, N0, + ST_TET, 4, P0, P4, P1, N0, + ST_PYR, 5, E11, E10, E03, E01, N0, + ST_TET, 4, P1, P4, P6, N0, + ST_PYR, 5, E11, E01, P1, P6, N0, + /* case 212 */ 9, + ST_PNT, 6, E10, E02, E01, E05, E04, E08, + ST_TET, 4, P7, P2, P6, N0, + ST_PYR, 5, E10, E02, P2, P7, N0, + ST_PYR, 5, E10, P7, P4, E08, N0, + ST_TET, 4, P4, P7, P6, N0, + ST_TET, 4, P4, E04, E08, N0, + ST_PYR, 5, P4, P6, E05, E04, N0, + ST_PYR, 5, E05, P6, P2, E01, N0, + ST_TET, 4, E01, P2, E02, N0, + /* case 213 */ 11, + ST_PNT, 6, P7, P0, P4, P6, E01, E05, + ST_WDG, 6, E10, E02, E03, P7, P2, P0, + ST_PYR, 5, E05, P6, P2, E01, N0, + ST_TET, 4, P6, P7, P2, N0, + ST_TET, 4, P6, P4, P7, N0, + ST_PYR, 5, E04, P4, P6, E05, N0, + ST_PYR, 5, E00, P0, P4, E04, N0, + ST_TET, 4, P4, P0, P7, N0, + ST_PYR, 5, E01, E00, E04, E05, N0, + ST_TET, 4, P0, P2, P7, N0, + ST_PYR, 5, E01, P2, P0, E00, N0, + /* case 214 */ 11, + ST_PNT, 6, P6, P1, P2, P7, E08, E10, + ST_WDG, 6, E05, E04, E09, P6, P4, P1, + ST_PYR, 5, E10, P7, P4, E08, N0, + ST_TET, 4, P7, P6, P4, N0, + ST_TET, 4, P7, P2, P6, N0, + ST_PYR, 5, E02, P2, P7, E10, N0, + ST_PYR, 5, E00, P1, P2, E02, N0, + ST_TET, 4, P2, P1, P6, N0, + ST_PYR, 5, E08, E00, E02, E10, N0, + ST_TET, 4, P1, P4, P6, N0, + ST_PYR, 5, E08, P4, P1, E00, N0, + /* case 215 */ 6, + ST_WDG, 6, E09, E05, E04, P1, P6, P4, + ST_WDG, 6, E02, E03, E10, P2, P0, P7, + ST_TET, 4, P7, P4, P0, P2, + ST_TET, 4, P2, P1, P6, P4, + ST_TET, 4, P1, P2, P0, P4, + ST_TET, 4, P2, P6, P7, P4, + /* case 216 */ 5, + ST_TET, 4, P7, P6, P4, P3, + ST_PYR, 5, E03, P3, P4, E08, E04, + ST_PYR, 5, E02, E11, P6, P3, E05, + ST_PYR, 5, P4, P6, E05, E04, P3, + ST_PYR, 5, E03, E04, E05, E02, P3, + /* case 217 */ 9, + ST_PNT, 5, E02, E00, E04, E11, E05, + ST_PYR, 5, P7, P4, P0, P3, N0, + ST_TET, 4, P4, P7, P6, N0, + ST_TET, 4, P6, P7, P3, N0, + ST_PYR, 5, P6, P3, E02, E11, N0, + ST_PYR, 5, E02, P3, P0, E00, N0, + ST_PYR, 5, E00, P0, P4, E04, N0, + ST_PYR, 5, E04, P4, P6, E05, N0, + ST_TET, 4, E11, E05, P6, N0, + /* case 218 */ 5, + ST_WDG, 6, P1, P4, P6, E09, E04, E05, + ST_WDG, 6, E03, E00, E08, P3, P1, P4, + ST_WDG, 6, P3, P1, P6, E02, E01, E11, + ST_TET, 4, P1, P4, P6, P3, + ST_TET, 4, P3, P6, P7, P4, + /* case 219 */ 5, + ST_WDG, 6, E09, E05, E04, P1, P6, P4, + ST_WDG, 6, P1, P6, P3, E01, E11, E02, + ST_PYR, 5, P0, P3, P7, P4, P1, + ST_TET, 4, P6, P7, P3, P1, + ST_TET, 4, P6, P4, P7, P1, + /* case 220 */ 9, + ST_PNT, 5, E05, E01, E03, E04, E08, + ST_PYR, 5, P7, P3, P2, P6, N0, + ST_TET, 4, P3, P7, P4, N0, + ST_TET, 4, P4, P7, P6, N0, + ST_PYR, 5, P4, P6, E05, E04, N0, + ST_PYR, 5, E05, P6, P2, E01, N0, + ST_PYR, 5, E01, P2, P3, E03, N0, + ST_PYR, 5, E03, P3, P4, E08, N0, + ST_TET, 4, E04, E08, P4, N0, + /* case 221 */ 2, + ST_HEX, 8, P0, P2, P6, P4, E00, E01, E05, E04, + ST_WDG, 6, P7, P4, P6, P3, P0, P2, + /* case 222 */ 5, + ST_WDG, 6, E04, E09, E05, P4, P1, P6, + ST_WDG, 6, P4, P1, P3, E08, E00, E03, + ST_PYR, 5, P7, P3, P2, P6, P4, + ST_TET, 4, P1, P2, P3, P4, + ST_TET, 4, P1, P6, P2, P4, + /* case 223 */ 9, + ST_PNT, 7, P0, P3, P7, P4, P1, P2, P6, + ST_WDG, 6, E09, E05, E04, P1, P6, P4, + ST_TET, 4, P1, P4, P6, N0, + ST_PYR, 5, P2, P6, P7, P3, N0, + ST_TET, 4, P6, P4, P7, N0, + ST_PYR, 5, P0, P3, P7, P4, N0, + ST_TET, 4, P0, P4, P1, N0, + ST_PYR, 5, P0, P1, P2, P3, N0, + ST_TET, 4, P1, P6, P2, N0, + /* case 224 */ 7, + ST_PNT, 5, E10, E11, E07, E04, E09, + ST_PYR, 5, E11, E09, P5, P6, N0, + ST_PYR, 5, E10, E11, P6, P7, N0, + ST_TET, 4, E07, E10, P7, N0, + ST_TET, 4, P7, P6, P5, N0, + ST_PYR, 5, E07, P7, P5, E04, N0, + ST_TET, 4, P5, E09, E04, N0, + /* case 225 */ 9, + ST_PNT, 5, E11, E09, E10, E03, E00, + ST_WDG, 6, P7, P5, P0, E07, E04, E08, + ST_TET, 4, P5, P0, P7, N0, + ST_PYR, 5, P7, P0, E03, E10, N0, + ST_TET, 4, P6, P5, P7, N0, + ST_PYR, 5, E09, P5, P6, E11, N0, + ST_PYR, 5, E11, P6, P7, E10, N0, + ST_PYR, 5, P0, P5, E09, E00, N0, + ST_TET, 4, E03, P0, E00, N0, + /* case 226 */ 9, + ST_PNT, 6, E04, E07, E10, E11, E01, E00, + ST_TET, 4, P5, P7, P6, N0, + ST_PYR, 5, E04, E07, P7, P5, N0, + ST_PYR, 5, E04, P5, P1, E00, N0, + ST_TET, 4, P1, P5, P6, N0, + ST_TET, 4, P1, E01, E00, N0, + ST_PYR, 5, P1, P6, E11, E01, N0, + ST_PYR, 5, E11, P6, P7, E10, N0, + ST_TET, 4, E10, P7, E07, N0, + /* case 227 */ 11, + ST_PNT, 6, P5, P0, P1, P6, E10, E11, + ST_WDG, 6, E04, E07, E08, P5, P7, P0, + ST_PYR, 5, E11, P6, P7, E10, N0, + ST_TET, 4, P6, P5, P7, N0, + ST_TET, 4, P6, P1, P5, N0, + ST_PYR, 5, E01, P1, P6, E11, N0, + ST_PYR, 5, E03, P0, P1, E01, N0, + ST_TET, 4, P1, P0, P5, N0, + ST_PYR, 5, E10, E03, E01, E11, N0, + ST_TET, 4, P0, P7, P5, N0, + ST_PYR, 5, E10, P7, P0, E03, N0, + /* case 228 */ 5, + ST_TET, 4, P6, P5, P7, P2, + ST_PYR, 5, E01, E09, P5, P2, E04, + ST_PYR, 5, E02, P2, P7, E10, E07, + ST_PYR, 5, P5, E04, E07, P7, P2, + ST_PYR, 5, E01, E02, E07, E04, P2, + /* case 229 */ 5, + ST_WDG, 6, E08, E04, E07, P0, P5, P7, + ST_WDG, 6, P2, P0, P5, E01, E00, E09, + ST_WDG, 6, E02, E03, E10, P2, P0, P7, + ST_TET, 4, P0, P7, P5, P2, + ST_TET, 4, P2, P6, P7, P5, + /* case 230 */ 9, + ST_PNT, 5, E02, E00, E04, E10, E07, + ST_PYR, 5, P6, P2, P1, P5, N0, + ST_TET, 4, P5, P7, P6, N0, + ST_TET, 4, P7, P2, P6, N0, + ST_PYR, 5, P7, E10, E02, P2, N0, + ST_PYR, 5, E02, E00, P1, P2, N0, + ST_PYR, 5, E00, E04, P5, P1, N0, + ST_PYR, 5, E04, E07, P7, P5, N0, + ST_TET, 4, E10, P7, E07, N0, + /* case 231 */ 5, + ST_WDG, 6, P0, P7, P5, E08, E07, E04, + ST_WDG, 6, E03, E10, E02, P0, P7, P2, + ST_PYR, 5, P1, P5, P6, P2, P0, + ST_TET, 4, P7, P2, P6, P0, + ST_TET, 4, P7, P6, P5, P0, + /* case 232 */ 9, + ST_PNT, 6, E07, E04, E09, E11, E02, E03, + ST_TET, 4, P7, P6, P5, N0, + ST_PYR, 5, E07, P7, P5, E04, N0, + ST_PYR, 5, E07, E03, P3, P7, N0, + ST_TET, 4, P3, P6, P7, N0, + ST_TET, 4, P3, E03, E02, N0, + ST_PYR, 5, P3, E02, E11, P6, N0, + ST_PYR, 5, E11, E09, P5, P6, N0, + ST_TET, 4, E09, E04, P5, N0, + /* case 233 */ 11, + ST_PNT, 6, P7, P0, P3, P6, E09, E11, + ST_WDG, 6, P7, P5, P0, E07, E04, E08, + ST_PYR, 5, E11, E09, P5, P6, N0, + ST_TET, 4, P6, P5, P7, N0, + ST_TET, 4, P6, P7, P3, N0, + ST_PYR, 5, E02, E11, P6, P3, N0, + ST_PYR, 5, E00, E02, P3, P0, N0, + ST_TET, 4, P3, P7, P0, N0, + ST_PYR, 5, E09, E11, E02, E00, N0, + ST_TET, 4, P0, P7, P5, N0, + ST_PYR, 5, E09, E00, P0, P5, N0, + /* case 234 */ 11, + ST_PNT, 6, P6, P1, P5, P7, E03, E07, + ST_WDG, 6, P6, P3, P1, E11, E02, E01, + ST_PYR, 5, E07, E03, P3, P7, N0, + ST_TET, 4, P7, P3, P6, N0, + ST_TET, 4, P7, P6, P5, N0, + ST_PYR, 5, E04, E07, P7, P5, N0, + ST_PYR, 5, E00, E04, P5, P1, N0, + ST_TET, 4, P5, P6, P1, N0, + ST_PYR, 5, E03, E07, E04, E00, N0, + ST_TET, 4, P1, P6, P3, N0, + ST_PYR, 5, E03, E00, P1, P3, N0, + /* case 235 */ 6, + ST_WDG, 6, P1, P6, P3, E01, E11, E02, + ST_WDG, 6, P5, P0, P7, E04, E08, E07, + ST_TET, 4, P7, P0, P3, P5, + ST_TET, 4, P5, P6, P1, P3, + ST_TET, 4, P1, P0, P5, P3, + ST_TET, 4, P5, P7, P6, P3, + /* case 236 */ 9, + ST_PNT, 5, E07, E03, E01, E04, E09, + ST_PYR, 5, P6, P7, P3, P2, N0, + ST_TET, 4, P2, P5, P6, N0, + ST_TET, 4, P5, P7, P6, N0, + ST_PYR, 5, P5, E04, E07, P7, N0, + ST_PYR, 5, E07, E03, P3, P7, N0, + ST_PYR, 5, E03, E01, P2, P3, N0, + ST_PYR, 5, E01, E09, P5, P2, N0, + ST_TET, 4, E04, P5, E09, N0, + /* case 237 */ 5, + ST_WDG, 6, P0, P5, P2, E00, E09, E01, + ST_WDG, 6, E08, E04, E07, P0, P5, P7, + ST_PYR, 5, P3, P2, P6, P7, P0, + ST_TET, 4, P5, P7, P6, P0, + ST_TET, 4, P5, P6, P2, P0, + /* case 238 */ 2, + ST_HEX, 8, E00, E03, E07, E04, P1, P3, P7, P5, + ST_WDG, 6, P2, P1, P3, P6, P5, P7, + /* case 239 */ 9, + ST_PNT, 7, P3, P2, P1, P0, P7, P6, P5, + ST_WDG, 6, P7, P5, P0, E07, E04, E08, + ST_TET, 4, P7, P5, P0, N0, + ST_PYR, 5, P6, P2, P1, P5, N0, + ST_TET, 4, P5, P1, P0, N0, + ST_PYR, 5, P3, P0, P1, P2, N0, + ST_TET, 4, P3, P7, P0, N0, + ST_PYR, 5, P3, P2, P6, P7, N0, + ST_TET, 4, P7, P6, P5, N0, + /* case 240 */ 1, + ST_HEX, 8, E08, E09, E11, E10, P4, P5, P6, P7, + /* case 241 */ 9, + ST_PNT, 5, E09, E11, E10, E00, E03, + ST_PYR, 5, P4, P7, P6, P5, N0, + ST_TET, 4, P7, P4, P0, N0, + ST_TET, 4, P0, P4, P5, N0, + ST_PYR, 5, P0, P5, E09, E00, N0, + ST_PYR, 5, E09, P5, P6, E11, N0, + ST_PYR, 5, E11, P6, P7, E10, N0, + ST_PYR, 5, E10, P7, P0, E03, N0, + ST_TET, 4, E00, E03, P0, N0, + /* case 242 */ 9, + ST_PNT, 5, E11, E10, E08, E01, E00, + ST_PYR, 5, P5, P4, P7, P6, N0, + ST_TET, 4, P4, P5, P1, N0, + ST_TET, 4, P1, P5, P6, N0, + ST_PYR, 5, P1, P6, E11, E01, N0, + ST_PYR, 5, E11, P6, P7, E10, N0, + ST_PYR, 5, E10, P7, P4, E08, N0, + ST_PYR, 5, E08, P4, P1, E00, N0, + ST_TET, 4, E01, E00, P1, N0, + /* case 243 */ 2, + ST_HEX, 8, P0, P7, P6, P1, E03, E10, E11, E01, + ST_WDG, 6, P5, P1, P6, P4, P0, P7, + /* case 244 */ 9, + ST_PNT, 5, E10, E08, E09, E02, E01, + ST_PYR, 5, P6, P5, P4, P7, N0, + ST_TET, 4, P5, P6, P2, N0, + ST_TET, 4, P2, P6, P7, N0, + ST_PYR, 5, P2, P7, E10, E02, N0, + ST_PYR, 5, E10, P7, P4, E08, N0, + ST_PYR, 5, E08, P4, P5, E09, N0, + ST_PYR, 5, E09, P5, P2, E01, N0, + ST_TET, 4, E02, E01, P2, N0, + /* case 245 */ 5, + ST_WDG, 6, E00, E01, E09, P0, P2, P5, + ST_WDG, 6, P0, P2, P7, E03, E02, E10, + ST_PYR, 5, P4, P7, P6, P5, P0, + ST_TET, 4, P2, P6, P7, P0, + ST_TET, 4, P2, P5, P6, P0, + /* case 246 */ 2, + ST_HEX, 8, P1, P4, P7, P2, E00, E08, E10, E02, + ST_WDG, 6, P6, P2, P7, P5, P1, P4, + /* case 247 */ 9, + ST_PNT, 7, P4, P5, P1, P0, P7, P6, P2, + ST_WDG, 6, E10, E02, E03, P7, P2, P0, + ST_TET, 4, P7, P0, P2, N0, + ST_PYR, 5, P6, P2, P1, P5, N0, + ST_TET, 4, P2, P0, P1, N0, + ST_PYR, 5, P4, P5, P1, P0, N0, + ST_TET, 4, P4, P0, P7, N0, + ST_PYR, 5, P4, P7, P6, P5, N0, + ST_TET, 4, P7, P2, P6, N0, + /* case 248 */ 9, + ST_PNT, 5, E11, E09, E08, E02, E03, + ST_PYR, 5, P7, P6, P5, P4, N0, + ST_TET, 4, P4, P3, P7, N0, + ST_TET, 4, P3, P6, P7, N0, + ST_PYR, 5, P3, E02, E11, P6, N0, + ST_PYR, 5, E11, E09, P5, P6, N0, + ST_PYR, 5, E09, E08, P4, P5, N0, + ST_PYR, 5, E08, E03, P3, P4, N0, + ST_TET, 4, E02, P3, E03, N0, + /* case 249 */ 2, + ST_HEX, 8, E00, E09, E11, E02, P0, P5, P6, P3, + ST_WDG, 6, P4, P0, P5, P7, P3, P6, + /* case 250 */ 5, + ST_WDG, 6, P3, P1, P6, E02, E01, E11, + ST_WDG, 6, E03, E00, E08, P3, P1, P4, + ST_PYR, 5, P7, P6, P5, P4, P3, + ST_TET, 4, P1, P4, P5, P3, + ST_TET, 4, P1, P5, P6, P3, + /* case 251 */ 9, + ST_PNT, 7, P0, P4, P7, P3, P1, P5, P6, + ST_WDG, 6, P1, P6, P3, E01, E11, E02, + ST_TET, 4, P1, P6, P3, N0, + ST_PYR, 5, P5, P4, P7, P6, N0, + ST_TET, 4, P6, P7, P3, N0, + ST_PYR, 5, P0, P3, P7, P4, N0, + ST_TET, 4, P0, P1, P3, N0, + ST_PYR, 5, P0, P4, P5, P1, N0, + ST_TET, 4, P1, P5, P6, N0, + /* case 252 */ 2, + ST_HEX, 8, P4, P3, P2, P5, E08, E03, E01, E09, + ST_WDG, 6, P6, P5, P2, P7, P4, P3, + /* case 253 */ 9, + ST_PNT, 7, P3, P7, P4, P0, P2, P6, P5, + ST_WDG, 6, E01, E09, E00, P2, P5, P0, + ST_TET, 4, P2, P0, P5, N0, + ST_PYR, 5, P6, P5, P4, P7, N0, + ST_TET, 4, P5, P0, P4, N0, + ST_PYR, 5, P3, P7, P4, P0, N0, + ST_TET, 4, P3, P0, P2, N0, + ST_PYR, 5, P3, P2, P6, P7, N0, + ST_TET, 4, P2, P5, P6, N0, + /* case 254 */ 9, + ST_PNT, 7, P7, P6, P5, P4, P3, P2, P1, + ST_WDG, 6, E03, E00, E08, P3, P1, P4, + ST_TET, 4, P3, P4, P1, N0, + ST_PYR, 5, P2, P1, P5, P6, N0, + ST_TET, 4, P1, P4, P5, N0, + ST_PYR, 5, P7, P6, P5, P4, N0, + ST_TET, 4, P7, P4, P3, N0, + ST_PYR, 5, P7, P3, P2, P6, N0, + ST_TET, 4, P3, P1, P2, N0, + /* case 255 */ 1, + ST_HEX, 8, P0, P1, P2, P3, P4, P5, P6, P7, + // vtkm::CELL_SHAPE_WEDGE + /* case 0 */ 0, + /* case 1 */ 1, + ST_TET, 4, E06, E00, E02, P0, + /* case 2 */ 1, + ST_TET, 4, E07, E01, E00, P1, + /* case 3 */ 1, + ST_WDG, 6, E02, E06, P0, E01, E07, P1, + /* case 4 */ 1, + ST_TET, 4, E08, E02, E01, P2, + /* case 5 */ 1, + ST_WDG, 6, E01, E08, P2, E00, E06, P0, + /* case 6 */ 1, + ST_WDG, 6, E00, E07, P1, E02, E08, P2, + /* case 7 */ 1, + ST_WDG, 6, P0, P1, P2, E06, E07, E08, + /* case 8 */ 1, + ST_TET, 4, E06, E05, E03, P3, + /* case 9 */ 1, + ST_WDG, 6, P0, E00, E02, P3, E03, E05, + /* case 10 */ 7, + ST_PNT, 6, E00, E01, E07, E03, E05, E06, + ST_PYR, 5, E06, E00, P1, P3, N0, + ST_PYR, 5, P3, P1, E07, E03, N0, + ST_TET, 4, P3, E03, E05, N0, + ST_TET, 4, E05, E06, P3, N0, + ST_TET, 4, P1, E01, E07, N0, + ST_TET, 4, P1, E00, E01, N0, + /* case 11 */ 7, + ST_PNT, 5, E01, E02, E05, E03, E07, + ST_TET, 4, P0, P1, P3, N0, + ST_PYR, 5, E02, E01, P1, P0, N0, + ST_PYR, 5, E02, P0, P3, E05, N0, + ST_TET, 4, E05, P3, E03, N0, + ST_PYR, 5, P3, P1, E07, E03, N0, + ST_TET, 4, P1, E01, E07, N0, + /* case 12 */ 7, + ST_PNT, 6, E05, E03, E06, E02, E01, E08, + ST_PYR, 5, E08, P2, P3, E05, N0, + ST_PYR, 5, P2, E02, E06, P3, N0, + ST_TET, 4, P2, E01, E02, N0, + ST_TET, 4, E01, P2, E08, N0, + ST_TET, 4, P3, E06, E03, N0, + ST_TET, 4, P3, E03, E05, N0, + /* case 13 */ 7, + ST_PNT, 5, E01, E00, E03, E05, E08, + ST_TET, 4, P0, P3, P2, N0, + ST_PYR, 5, E00, P0, P2, E01, N0, + ST_PYR, 5, E00, E03, P3, P0, N0, + ST_TET, 4, E03, E05, P3, N0, + ST_PYR, 5, P3, E05, E08, P2, N0, + ST_TET, 4, P2, E08, E01, N0, + /* case 14 */ 8, + ST_PNT, 7, E03, E05, E08, E07, P3, P2, P1, + ST_WDG, 6, P2, P1, P3, E02, E00, E06, + ST_PYR, 5, E05, E03, E07, E08, N0, + ST_PYR, 5, E07, P1, P2, E08, N0, + ST_TET, 4, P3, P2, P1, N0, + ST_TET, 4, P3, E03, E05, N0, + ST_PYR, 5, E03, P3, P1, E07, N0, + ST_PYR, 5, E08, P2, P3, E05, N0, + /* case 15 */ 8, + ST_PNT, 7, P1, P2, P3, E05, E03, E07, E08, + ST_TET, 4, P0, P2, P1, P3, + ST_PYR, 5, E05, E03, E07, E08, N0, + ST_PYR, 5, E08, E07, P1, P2, N0, + ST_TET, 4, P3, P2, P1, N0, + ST_TET, 4, P3, E03, E05, N0, + ST_PYR, 5, P3, P1, E07, E03, N0, + ST_PYR, 5, P2, P3, E05, E08, N0, + /* case 16 */ 1, + ST_TET, 4, E07, E03, E04, P4, + /* case 17 */ 7, + ST_PNT, 6, E03, E04, E07, E00, E02, E06, + ST_PYR, 5, E06, P0, P4, E03, N0, + ST_PYR, 5, P0, E00, E07, P4, N0, + ST_TET, 4, P0, E02, E00, N0, + ST_TET, 4, E02, P0, E06, N0, + ST_TET, 4, P4, E07, E04, N0, + ST_TET, 4, P4, E04, E03, N0, + /* case 18 */ 1, + ST_WDG, 6, P1, E01, E00, P4, E04, E03, + /* case 19 */ 7, + ST_PNT, 5, E02, E01, E04, E03, E06, + ST_TET, 4, P1, P4, P0, N0, + ST_PYR, 5, E01, P1, P0, E02, N0, + ST_PYR, 5, E01, E04, P4, P1, N0, + ST_TET, 4, E04, E03, P4, N0, + ST_PYR, 5, P4, E03, E06, P0, N0, + ST_TET, 4, P0, E06, E02, N0, + /* case 20 */ 7, + ST_PNT, 6, E01, E02, E08, E04, E03, E07, + ST_PYR, 5, E07, E01, P2, P4, N0, + ST_PYR, 5, P4, P2, E08, E04, N0, + ST_TET, 4, P4, E04, E03, N0, + ST_TET, 4, E03, E07, P4, N0, + ST_TET, 4, P2, E02, E08, N0, + ST_TET, 4, P2, E01, E02, N0, + /* case 21 */ 8, + ST_PNT, 7, E04, E03, E06, E08, P4, P0, P2, + ST_WDG, 6, P0, P2, P4, E00, E01, E07, + ST_PYR, 5, E03, E04, E08, E06, N0, + ST_PYR, 5, E08, P2, P0, E06, N0, + ST_TET, 4, P4, P0, P2, N0, + ST_TET, 4, P4, E04, E03, N0, + ST_PYR, 5, E04, P4, P2, E08, N0, + ST_PYR, 5, E06, P0, P4, E03, N0, + /* case 22 */ 7, + ST_PNT, 5, E02, E00, E03, E04, E08, + ST_TET, 4, P1, P2, P4, N0, + ST_PYR, 5, E00, E02, P2, P1, N0, + ST_PYR, 5, E00, P1, P4, E03, N0, + ST_TET, 4, E03, P4, E04, N0, + ST_PYR, 5, P4, P2, E08, E04, N0, + ST_TET, 4, P2, E02, E08, N0, + /* case 23 */ 8, + ST_PNT, 7, P2, P0, P4, E03, E04, E08, E06, + ST_TET, 4, P1, P0, P2, P4, + ST_PYR, 5, E03, E04, E08, E06, N0, + ST_PYR, 5, E06, E08, P2, P0, N0, + ST_TET, 4, P4, P0, P2, N0, + ST_TET, 4, P4, E04, E03, N0, + ST_PYR, 5, P4, P2, E08, E04, N0, + ST_PYR, 5, P0, P4, E03, E06, N0, + /* case 24 */ 1, + ST_WDG, 6, E04, E07, P4, E05, E06, P3, + /* case 25 */ 7, + ST_PNT, 5, E04, E05, E02, E00, E07, + ST_TET, 4, P3, P0, P4, N0, + ST_PYR, 5, E05, P3, P4, E04, N0, + ST_PYR, 5, E05, E02, P0, P3, N0, + ST_TET, 4, E02, E00, P0, N0, + ST_PYR, 5, P0, E00, E07, P4, N0, + ST_TET, 4, P4, E07, E04, N0, + /* case 26 */ 7, + ST_PNT, 5, E05, E04, E01, E00, E06, + ST_TET, 4, P4, P3, P1, N0, + ST_PYR, 5, E04, E05, P3, P4, N0, + ST_PYR, 5, E04, P4, P1, E01, N0, + ST_TET, 4, E01, P1, E00, N0, + ST_PYR, 5, P1, P3, E06, E00, N0, + ST_TET, 4, P3, E05, E06, N0, + /* case 27 */ 1, + ST_HEX, 8, P3, P4, E04, E05, P0, P1, E01, E02, + /* case 28 */ 8, + ST_PNT, 7, E02, E01, E07, E06, P2, P4, P3, + ST_WDG, 6, E04, E05, E08, P4, P3, P2, + ST_PYR, 5, E01, E07, E06, E02, N0, + ST_PYR, 5, E06, E07, P4, P3, N0, + ST_TET, 4, P2, P3, P4, N0, + ST_TET, 4, P2, E01, E02, N0, + ST_PYR, 5, E02, E06, P3, P2, N0, + ST_PYR, 5, E07, E01, P2, P4, N0, + /* case 29 */ 3, + ST_WDG, 6, P2, P3, P4, E08, E05, E04, + ST_TET, 4, P2, P3, P4, P0, + ST_WDG, 6, P2, P4, P0, E01, E07, E00, + /* case 30 */ 3, + ST_WDG, 6, E06, E00, E02, P3, P1, P2, + ST_TET, 4, P3, P2, P1, P4, + ST_WDG, 6, E05, E08, E04, P3, P2, P4, + /* case 31 */ 2, + ST_WDG, 6, E08, E04, E05, P2, P4, P3, + ST_PYR, 5, P0, P1, P4, P3, P2, + /* case 32 */ 1, + ST_TET, 4, E08, E04, E05, P5, + /* case 33 */ 7, + ST_PNT, 6, E02, E00, E06, E05, E04, E08, + ST_PYR, 5, E08, E02, P0, P5, N0, + ST_PYR, 5, P5, P0, E06, E05, N0, + ST_TET, 4, P5, E05, E04, N0, + ST_TET, 4, E04, E08, P5, N0, + ST_TET, 4, P0, E00, E06, N0, + ST_TET, 4, P0, E02, E00, N0, + /* case 34 */ 7, + ST_PNT, 6, E04, E05, E08, E01, E00, E07, + ST_PYR, 5, E07, P1, P5, E04, N0, + ST_PYR, 5, P1, E01, E08, P5, N0, + ST_TET, 4, P1, E00, E01, N0, + ST_TET, 4, E00, P1, E07, N0, + ST_TET, 4, P5, E08, E05, N0, + ST_TET, 4, P5, E05, E04, N0, + /* case 35 */ 8, + ST_PNT, 7, E05, E04, E07, E06, P5, P1, P0, + ST_WDG, 6, P1, P0, P5, E01, E02, E08, + ST_PYR, 5, E04, E05, E06, E07, N0, + ST_PYR, 5, E06, P0, P1, E07, N0, + ST_TET, 4, P5, P1, P0, N0, + ST_TET, 4, P5, E05, E04, N0, + ST_PYR, 5, E05, P5, P0, E06, N0, + ST_PYR, 5, E07, P1, P5, E04, N0, + /* case 36 */ 1, + ST_WDG, 6, P2, E02, E01, P5, E05, E04, + /* case 37 */ 7, + ST_PNT, 5, E00, E01, E04, E05, E06, + ST_TET, 4, P2, P0, P5, N0, + ST_PYR, 5, E01, E00, P0, P2, N0, + ST_PYR, 5, E01, P2, P5, E04, N0, + ST_TET, 4, E04, P5, E05, N0, + ST_PYR, 5, P5, P0, E06, E05, N0, + ST_TET, 4, P0, E00, E06, N0, + /* case 38 */ 7, + ST_PNT, 5, E00, E02, E05, E04, E07, + ST_TET, 4, P2, P5, P1, N0, + ST_PYR, 5, E02, P2, P1, E00, N0, + ST_PYR, 5, E02, E05, P5, P2, N0, + ST_TET, 4, E05, E04, P5, N0, + ST_PYR, 5, P5, E04, E07, P1, N0, + ST_TET, 4, P1, E07, E00, N0, + /* case 39 */ 8, + ST_PNT, 7, P0, P1, P5, E04, E05, E06, E07, + ST_TET, 4, P2, P1, P0, P5, + ST_PYR, 5, E04, E05, E06, E07, N0, + ST_PYR, 5, E07, E06, P0, P1, N0, + ST_TET, 4, P5, P1, P0, N0, + ST_TET, 4, P5, E05, E04, N0, + ST_PYR, 5, P5, P0, E06, E05, N0, + ST_PYR, 5, P1, P5, E04, E07, N0, + /* case 40 */ 1, + ST_WDG, 6, E03, E06, P3, E04, E08, P5, + /* case 41 */ 7, + ST_PNT, 5, E04, E03, E00, E02, E08, + ST_TET, 4, P3, P5, P0, N0, + ST_PYR, 5, E03, E04, P5, P3, N0, + ST_PYR, 5, E03, P3, P0, E00, N0, + ST_TET, 4, E00, P0, E02, N0, + ST_PYR, 5, P0, P5, E08, E02, N0, + ST_TET, 4, P5, E04, E08, N0, + /* case 42 */ 8, + ST_PNT, 7, E01, E00, E06, E08, P1, P3, P5, + ST_WDG, 6, E03, E04, E07, P3, P5, P1, + ST_PYR, 5, E00, E06, E08, E01, N0, + ST_PYR, 5, E08, E06, P3, P5, N0, + ST_TET, 4, P1, P5, P3, N0, + ST_TET, 4, P1, E00, E01, N0, + ST_PYR, 5, E01, E08, P5, P1, N0, + ST_PYR, 5, E06, E00, P1, P3, N0, + /* case 43 */ 3, + ST_WDG, 6, E08, E02, E01, P5, P0, P1, + ST_TET, 4, P5, P1, P0, P3, + ST_WDG, 6, E04, E07, E03, P5, P1, P3, + /* case 44 */ 7, + ST_PNT, 5, E03, E04, E01, E02, E06, + ST_TET, 4, P5, P2, P3, N0, + ST_PYR, 5, E04, P5, P3, E03, N0, + ST_PYR, 5, E04, E01, P2, P5, N0, + ST_TET, 4, E01, E02, P2, N0, + ST_PYR, 5, P2, E02, E06, P3, N0, + ST_TET, 4, P3, E06, E03, N0, + /* case 45 */ 1, + ST_HEX, 8, P5, P3, E03, E04, P2, P0, E00, E01, + /* case 46 */ 3, + ST_WDG, 6, P1, P5, P3, E07, E04, E03, + ST_TET, 4, P1, P5, P3, P2, + ST_WDG, 6, P1, P3, P2, E00, E06, E02, + /* case 47 */ 2, + ST_WDG, 6, E07, E03, E04, P1, P3, P5, + ST_PYR, 5, P2, P0, P3, P5, P1, + /* case 48 */ 1, + ST_WDG, 6, E05, E08, P5, E03, E07, P4, + /* case 49 */ 8, + ST_PNT, 7, E00, E02, E08, E07, P0, P5, P4, + ST_WDG, 6, E05, E03, E06, P5, P4, P0, + ST_PYR, 5, E02, E08, E07, E00, N0, + ST_PYR, 5, E07, E08, P5, P4, N0, + ST_TET, 4, P0, P4, P5, N0, + ST_TET, 4, P0, E02, E00, N0, + ST_PYR, 5, E00, E07, P4, P0, N0, + ST_PYR, 5, E08, E02, P0, P5, N0, + /* case 50 */ 7, + ST_PNT, 5, E05, E03, E00, E01, E08, + ST_TET, 4, P4, P1, P5, N0, + ST_PYR, 5, E03, P4, P5, E05, N0, + ST_PYR, 5, E03, E00, P1, P4, N0, + ST_TET, 4, E00, E01, P1, N0, + ST_PYR, 5, P1, E01, E08, P5, N0, + ST_TET, 4, P5, E08, E05, N0, + /* case 51 */ 3, + ST_WDG, 6, P0, P4, P5, E06, E03, E05, + ST_TET, 4, P0, P4, P5, P1, + ST_WDG, 6, P0, P5, P1, E02, E08, E01, + /* case 52 */ 7, + ST_PNT, 5, E03, E05, E02, E01, E07, + ST_TET, 4, P5, P4, P2, N0, + ST_PYR, 5, E05, E03, P4, P5, N0, + ST_PYR, 5, E05, P5, P2, E02, N0, + ST_TET, 4, E02, P2, E01, N0, + ST_PYR, 5, P2, P4, E07, E01, N0, + ST_TET, 4, P4, E03, E07, N0, + /* case 53 */ 3, + ST_WDG, 6, E07, E01, E00, P4, P2, P0, + ST_TET, 4, P4, P0, P2, P5, + ST_WDG, 6, E03, E06, E05, P4, P0, P5, + /* case 54 */ 1, + ST_HEX, 8, P4, P5, E05, E03, P1, P2, E02, E00, + /* case 55 */ 2, + ST_WDG, 6, E06, E05, E03, P0, P5, P4, + ST_PYR, 5, P1, P2, P5, P4, P0, + /* case 56 */ 1, + ST_WDG, 6, E06, E07, E08, P3, P4, P5, + /* case 57 */ 8, + ST_PNT, 7, P4, P5, P0, E02, E00, E07, E08, + ST_TET, 4, P3, P4, P5, P0, + ST_PYR, 5, E02, E08, E07, E00, N0, + ST_PYR, 5, E08, P5, P4, E07, N0, + ST_TET, 4, P0, P4, P5, N0, + ST_TET, 4, P0, E02, E00, N0, + ST_PYR, 5, P0, E00, E07, P4, N0, + ST_PYR, 5, P5, E08, E02, P0, N0, + /* case 58 */ 8, + ST_PNT, 7, P5, P3, P1, E00, E01, E08, E06, + ST_TET, 4, P4, P5, P3, P1, + ST_PYR, 5, E00, E06, E08, E01, N0, + ST_PYR, 5, E06, P3, P5, E08, N0, + ST_TET, 4, P1, P5, P3, N0, + ST_TET, 4, P1, E00, E01, N0, + ST_PYR, 5, P1, E01, E08, P5, N0, + ST_PYR, 5, P3, E06, E00, P1, N0, + /* case 59 */ 2, + ST_WDG, 6, P5, P1, P0, E08, E01, E02, + ST_PYR, 5, P3, P0, P1, P4, P5, + /* case 60 */ 8, + ST_PNT, 7, P3, P4, P2, E01, E02, E06, E07, + ST_TET, 4, P5, P3, P4, P2, + ST_PYR, 5, E01, E07, E06, E02, N0, + ST_PYR, 5, E07, P4, P3, E06, N0, + ST_TET, 4, P2, P3, P4, N0, + ST_TET, 4, P2, E01, E02, N0, + ST_PYR, 5, P2, E02, E06, P3, N0, + ST_PYR, 5, P4, E07, E01, P2, N0, + /* case 61 */ 2, + ST_WDG, 6, P4, P0, P2, E07, E00, E01, + ST_PYR, 5, P5, P2, P0, P3, P4, + /* case 62 */ 2, + ST_WDG, 6, P3, P2, P1, E06, E02, E00, + ST_PYR, 5, P4, P1, P2, P5, P3, + /* case 63 */ 1, + ST_WDG, 6, P0, P1, P2, P3, P4, P5, + // vtkm::CELL_SHAPE_PYRAMID + /* case 0 */ 0, + /* case 1 */ 1, + ST_TET, 4, P0, E00, E03, E04, + /* case 2 */ 1, + ST_TET, 4, P1, E01, E00, E05, + /* case 3 */ 1, + ST_WDG, 6, E01, E05, P1, E03, E04, P0, + /* case 4 */ 1, + ST_TET, 4, P2, E02, E01, E06, + /* case 5 */ 2, + ST_WDG, 6, P0, E00, E04, P2, E01, E06, + ST_WDG, 6, E03, P0, E04, E02, P2, E06, + /* case 6 */ 1, + ST_WDG, 6, E02, E06, P2, E00, E05, P1, + /* case 7 */ 2, + ST_WDG, 6, E04, E05, E06, P0, P1, P2, + ST_WDG, 6, P2, E02, E06, P0, E03, E04, + /* case 8 */ 1, + ST_TET, 4, P3, E03, E02, E07, + /* case 9 */ 1, + ST_WDG, 6, E00, E04, P0, E02, E07, P3, + /* case 10 */ 2, + ST_WDG, 6, P3, E03, E07, P1, E00, E05, + ST_WDG, 6, E02, P3, E07, E01, P1, E05, + /* case 11 */ 2, + ST_WDG, 6, E07, E04, E05, P3, P0, P1, + ST_WDG, 6, P1, E01, E05, P3, E02, E07, + /* case 12 */ 1, + ST_WDG, 6, E03, E07, P3, E01, E06, P2, + /* case 13 */ 2, + ST_WDG, 6, E06, E07, E04, P2, P3, P0, + ST_WDG, 6, P0, E00, E04, P2, E01, E06, + /* case 14 */ 2, + ST_WDG, 6, E05, E06, E07, P1, P2, P3, + ST_WDG, 6, P3, E03, E07, P1, E00, E05, + /* case 15 */ 1, + ST_HEX, 8, P0, P1, P2, P3, E04, E05, E06, E07, + /* case 16 */ 1, + ST_PYR, 5, E04, E05, E06, E07, P4, + /* case 17 */ 2, + ST_WDG, 6, P4, E05, E07, P0, E00, E03, + ST_TET, 4, E05, E06, E07, P4, + /* case 18 */ 2, + ST_WDG, 6, P4, E06, E04, P1, E01, E00, + ST_TET, 4, E06, E07, E04, P4, + /* case 19 */ 7, + ST_PNT, 7, E07, E06, E03, E01, P0, P1, P4, + ST_PYR, 5, E06, E07, E03, E01, N0, + ST_PYR, 5, E03, P0, P1, E01, N0, + ST_TET, 4, P0, P4, P1, N0, + ST_TET, 4, E07, E06, P4, N0, + ST_PYR, 5, E07, P4, P0, E03, N0, + ST_PYR, 5, P4, E06, E01, P1, N0, + /* case 20 */ 2, + ST_WDG, 6, P4, E07, E05, P2, E02, E01, + ST_TET, 4, E07, E04, E05, P4, + /* case 21 */ 2, + ST_WDG, 6, E00, E01, E05, P0, P2, P4, + ST_WDG, 6, E02, E03, E07, P2, P0, P4, + /* case 22 */ 7, + ST_PNT, 7, E04, E07, E00, E02, P1, P2, P4, + ST_PYR, 5, E07, E04, E00, E02, N0, + ST_PYR, 5, E00, P1, P2, E02, N0, + ST_TET, 4, P1, P4, P2, N0, + ST_TET, 4, E04, E07, P4, N0, + ST_PYR, 5, E04, P4, P1, E00, N0, + ST_PYR, 5, P4, E07, E02, P2, N0, + /* case 23 */ 2, + ST_WDG, 6, P0, P2, P4, E03, E02, E07, + ST_TET, 4, P0, P1, P2, P4, + /* case 24 */ 2, + ST_WDG, 6, P4, E04, E06, P3, E03, E02, + ST_TET, 4, E04, E05, E06, P4, + /* case 25 */ 7, + ST_PNT, 7, E06, E05, E02, E00, P3, P0, P4, + ST_PYR, 5, E05, E06, E02, E00, N0, + ST_PYR, 5, E02, P3, P0, E00, N0, + ST_TET, 4, P3, P4, P0, N0, + ST_TET, 4, E06, E05, P4, N0, + ST_PYR, 5, E06, P4, P3, E02, N0, + ST_PYR, 5, P4, E05, E00, P0, N0, + /* case 26 */ 2, + ST_WDG, 6, E03, E00, E04, P3, P1, P4, + ST_WDG, 6, E01, E02, E06, P1, P3, P4, + /* case 27 */ 2, + ST_WDG, 6, P3, P1, P4, E02, E01, E06, + ST_TET, 4, P3, P0, P1, P4, + /* case 28 */ 7, + ST_PNT, 7, E05, E04, E01, E03, P2, P3, P4, + ST_PYR, 5, E04, E05, E01, E03, N0, + ST_PYR, 5, E01, P2, P3, E03, N0, + ST_TET, 4, P2, P4, P3, N0, + ST_TET, 4, E05, E04, P4, N0, + ST_PYR, 5, E05, P4, P2, E01, N0, + ST_PYR, 5, P4, E04, E03, P3, N0, + /* case 29 */ 2, + ST_WDG, 6, P2, P0, P4, E01, E00, E05, + ST_TET, 4, P2, P3, P0, P4, + /* case 30 */ 2, + ST_WDG, 6, P1, P3, P4, E00, E03, E04, + ST_TET, 4, P1, P2, P3, P4, + /* case 31 */ 1, + ST_PYR, 5, P0, P1, P2, P3, P4, + }; + // clang-format on + return ClipTablesData[idx]; + } + + /** + * Get the the case index for the given shape and caseId. + */ + VTKM_EXEC inline static vtkm::Id GetCaseIndex(vtkm::UInt8 shape, vtkm::Id caseId) { - public: - VTKM_EXEC - vtkm::Id GetCaseIndex(vtkm::Id shape, vtkm::Id caseId) const - { - // index into ClipTablesIndices for each shape - VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Int32 CellIndexLookup[vtkm::NUMBER_OF_CELL_SHAPES] = { - -1, // 0 = vtkm::CELL_SHAPE_EMPTY_CELL - 0, // 1 = vtkm::CELL_SHAPE_VERTEX - -1, // 2 = vtkm::CELL_SHAPE_POLY_VERTEX - 2, // 3 = vtkm::CELL_SHAPE_LINE - -1, // 4 = vtkm::CELL_SHAPE_POLY_LINE - 6, // 5 = vtkm::CELL_SHAPE_TRIANGLE - -1, // 6 = vtkm::CELL_SHAPE_TRIANGLE_STRIP - -1, // 7 = vtkm::CELL_SHAPE_POLYGON - -1, // 8 = vtkm::CELL_SHAPE_PIXEL - 14, // 9 = vtkm::CELL_SHAPE_QUAD - 30, // 10 = vtkm::CELL_SHAPE_TETRA - -1, // 11 = vtkm::CELL_SHAPE_VOXEL - 46, // 12 = vtkm::CELL_SHAPE_HEXAHEDRON - 302, // 13 = vtkm::CELL_SHAPE_WEDGE - 366 // 14 = vtkm::CELL_SHAPE_PYRAMID - }; + // clang-format off + // offset into ShapeCases for each shape + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt16 ClipTablesIndices[] = { + // vtkm::CELL_SHAPE_VERTEX + 0, 1, // case 0 - 1 + // vtkm::CELL_SHAPE_LINE + 5, 6, 11, 16, // case 0 - 3 + // vtkm::CELL_SHAPE_TRIANGLE + 21, 22, 28, 34, 41, 47, 54, 61, // case 0 - 7 + // vtkm::CELL_SHAPE_QUAD + 67, 68, 74, 80, 87, 93, 106, 113, // case 0 - 7 + 125, 131, 138, 151, 163, 170, 182, 194, // case 8 - 15 + // vtkm::CELL_SHAPE_TETRA + 201, 202, 209, 216, 225, 232, 241, 250, // case 0 - 7 + 259, 266, 275, 284, 293, 302, 311, 320, // case 8 - 15 + // vtkm::CELL_SHAPE_HEXAHEDRON + 327, 328, 335, 342, 351, 358, 401, 410, // case 0 - 7 + 457, 464, 473, 516, 563, 572, 619, 666, // case 8 - 15 + 677, 684, 693, 736, 783, 796, 849, 902, // case 16 - 23 + 963, 1006, 1053, 1090, 1125, 1178, 1239, 1301, // case 24 - 31 + 1362, 1369, 1412, 1421, 1468, 1511, 1548, 1595, // case 32 - 39 + 1630, 1643, 1696, 1749, 1810, 1863, 1925, 1986, // case 40 - 47 + 2047, 2056, 2103, 2150, 2161, 2214, 2276, 2337, // case 48 - 55 + 2398, 2451, 2512, 2574, 2635, 2656, 2732, 2808, // case 56 - 63 + 2827, 2834, 2847, 2890, 2943, 2952, 3005, 3052, // case 64 - 71 + 3113, 3156, 3209, 3246, 3308, 3355, 3416, 3451, // case 72 - 79 + 3512, 3555, 3608, 3645, 3707, 3760, 3781, 3843, // case 80 - 87 + 3919, 3956, 4018, 4057, 4094, 4156, 4232, 4269, // case 88 - 95 + 4305, 4314, 4367, 4414, 4475, 4522, 4584, 4595, // case 96 - 103 + 4656, 4709, 4730, 4792, 4868, 4929, 5005, 5066, // case 104 - 111 + 5085, 5132, 5193, 5228, 5289, 5350, 5426, 5487, // case 112 - 119 + 5506, 5568, 5644, 5681, 5717, 5793, 5834, 5870, // case 120 - 127 + 5933, 5940, 5983, 5996, 6049, 6092, 6129, 6182, // case 128 - 135 + 6244, 6253, 6300, 6353, 6414, 6461, 6496, 6557, // case 136 - 143 + 6618, 6627, 6674, 6727, 6788, 6841, 6903, 6924, // case 144 - 151 + 7000, 7047, 7058, 7120, 7181, 7242, 7303, 7379, // case 152 - 159 + 7398, 7441, 7478, 7531, 7593, 7630, 7669, 7731, // case 160 - 167 + 7768, 7821, 7883, 7904, 7980, 8042, 8079, 8155, // case 168 - 175 + 8191, 8238, 8273, 8334, 8395, 8457, 8494, 8570, // case 176 - 183 + 8606, 8667, 8728, 8804, 8823, 8899, 8935, 8976, // case 184 - 191 + 9039, 9048, 9101, 9154, 9175, 9222, 9284, 9345, // case 192 - 199 + 9421, 9468, 9529, 9591, 9667, 9678, 9739, 9800, // case 200 - 207 + 9819, 9866, 9927, 9989, 10065, 10126, 10202, 10278, // case 208 - 215 + 10319, 10354, 10415, 10452, 10488, 10549, 10568, 10604, // case 216 - 223 + 10667, 10714, 10776, 10837, 10913, 10948, 10985, 11046, // case 224 - 231 + 11082, 11143, 11219, 11295, 11336, 11397, 11433, 11452, // case 232 - 239 + 11515, 11526, 11587, 11648, 11667, 11728, 11764, 11783, // case 240 - 247 + 11846, 11907, 11926, 11962, 12025, 12044, 12107, 12170, // case 248 - 255 + // vtkm::CELL_SHAPE_WEDGE + 12181, 12182, 12189, 12196, 12205, 12212, 12221, 12230, // case 0 - 7 + 12239, 12246, 12255, 12302, 12349, 12396, 12443, 12501, // case 8 - 15 + 12557, 12564, 12611, 12620, 12667, 12714, 12772, 12819, // case 16 - 23 + 12875, 12884, 12931, 12978, 12989, 13047, 13070, 13093, // case 24 - 31 + 13109, 13116, 13163, 13210, 13268, 13277, 13324, 13371, // case 32 - 39 + 13427, 13436, 13483, 13541, 13564, 13611, 13622, 13645, // case 40 - 47 + 13661, 13670, 13728, 13775, 13798, 13845, 13868, 13879, // case 48 - 55 + 13895, 13904, 13960, 14016, 14032, 14088, 14104, 14120, // case 56 - 63 + // vtkm::CELL_SHAPE_PYRAMID + 14129, 14130, 14137, 14144, 14153, 14160, 14177, 14186, // case 0 - 7 + 14203, 14210, 14219, 14236, 14253, 14262, 14279, 14296, // case 8 - 15 + 14307, 14315, 14330, 14345, 14395, 14410, 14427, 14477, // case 16 - 23 + 14492, 14507, 14557, 14574, 14589, 14639, 14654, 14669, // case 24 - 31 + }; + // clang-format on + return ClipTablesIndices[ClipTablesBase::GetCellClipTableIndex(shape) + caseId]; + } - vtkm::Id index = CellIndexLookup[shape]; - return this->ClipTablesIndicesPortal.Get(index + caseId); - } + /** + * Given the number of points and a case index, return if the cell is discarded. + */ + VTKM_EXEC inline static constexpr bool IsCellDiscarded( + vtkm::IdComponent vtkmNotUsed(numberOfPoints), + vtkm::UInt8 caseIndex) + { + return caseIndex == 0; + } - VTKM_EXEC - vtkm::UInt8 ValueAt(vtkm::Id idx) const { return this->ClipTablesDataPortal.Get(idx); } + /** + * Given the number of points and a case index, return if the cell is kept. + */ + VTKM_EXEC inline static bool IsCellKept(vtkm::IdComponent numberOfPoints, vtkm::UInt8 caseIndex) + { + return caseIndex == ClipTablesBase::GetMaxCellCase(numberOfPoints); + } - VTKM_EXEC - EdgeVec GetEdge(vtkm::Id shape, vtkm::Id edgeId) const - { - vtkm::Id index = ((shape * MAX_CELL_EDGES) + edgeId) * 2; - vtkm::IdComponent v1 = this->CellEdgesPortal.Get(index); - vtkm::IdComponent v2 = this->CellEdgesPortal.Get(index + 1); - return EdgeVec(v1, v2); - } + /** + * Get the discarded cell case index. + */ + VTKM_EXEC inline static constexpr vtkm::UInt8 GetDiscardedCellCase() { return 0; } - private: - typename vtkm::cont::ArrayHandle::ReadPortalType ClipTablesDataPortal; - typename vtkm::cont::ArrayHandle::ReadPortalType ClipTablesIndicesPortal; - typename vtkm::cont::ArrayHandle::ReadPortalType CellEdgesPortal; + /** + * Get the kept cell case index. + */ + VTKM_EXEC inline static constexpr vtkm::UInt8 GetKeptCellCase() { return 255; } +}; - friend class ClipTables; - }; +// Specialization for true +template <> +class ClipTables : public ClipTablesBase +{ +public: ClipTables() - : ClipTablesDataArray( - vtkm::cont::make_ArrayHandle(ClipTablesData, CLIP_TABLES_DATA_SIZE, vtkm::CopyFlag::Off)) - , ClipTablesIndicesArray(vtkm::cont::make_ArrayHandle(ClipTablesIndices, - CLIP_TABLES_INDICES_SIZE, - vtkm::CopyFlag::Off)) - , CellEdgesArray(vtkm::cont::make_ArrayHandle(CellEdges, CELL_EDGES_SIZE, vtkm::CopyFlag::Off)) + : ClipTablesBase() + { + } + + /** + * Get the value at the given index. + */ + VTKM_EXEC inline static vtkm::UInt8 ValueAt(vtkm::Id idx) + { + using namespace ClipTablesInformation; + // clang-format off + // table format of an example case + // number of shapes + // {{shape-type, nverts, {point1, point2...}}, ...}, \n + + // clip table for all cases of each shape + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 ClipTablesData[] = { + // vtkm::CELL_SHAPE_VERTEX + /* case 0 */ 1, + ST_VTX, 1, P0, + /* case 1 */ 0, + // vtkm::CELL_SHAPE_LINE + /* case 0 */ 1, + ST_LIN, 2, P0, P1, + /* case 1 */ 1, + ST_LIN, 2, E00, P1, + /* case 2 */ 1, + ST_LIN, 2, E00, P0, + /* case 3 */ 0, + // vtkm::CELL_SHAPE_TRIANGLE + /* case 0 */ 1, + ST_TRI, 3, P0, P1, P2, + /* case 1 */ 1, + ST_QUA, 4, P1, P2, E02, E00, + /* case 2 */ 1, + ST_QUA, 4, P2, P0, E00, E01, + /* case 3 */ 1, + ST_TRI, 3, E02, E01, P2, + /* case 4 */ 1, + ST_QUA, 4, P0, P1, E01, E02, + /* case 5 */ 1, + ST_TRI, 3, E01, E00, P1, + /* case 6 */ 1, + ST_TRI, 3, E00, E02, P0, + /* case 7 */ 0, + // vtkm::CELL_SHAPE_QUAD + /* case 0 */ 1, + ST_QUA, 4, P0, P1, P2, P3, + /* case 1 */ 2, + ST_QUA, 4, E03, E00, P1, P3, + ST_TRI, 3, P3, P1, P2, + /* case 2 */ 2, + ST_QUA, 4, E00, E01, P2, P0, + ST_TRI, 3, P0, P2, P3, + /* case 3 */ 1, + ST_QUA, 4, E03, E01, P2, P3, + /* case 4 */ 2, + ST_QUA, 4, E01, E02, P3, P1, + ST_TRI, 3, P1, P3, P0, + /* case 5 */ 2, + ST_TRI, 3, E03, E02, P3, + ST_TRI, 3, E01, E00, P1, + /* case 6 */ 1, + ST_QUA, 4, E00, E02, P3, P0, + /* case 7 */ 1, + ST_TRI, 3, E03, E02, P3, + /* case 8 */ 2, + ST_QUA, 4, E02, E03, P0, P2, + ST_TRI, 3, P2, P0, P1, + /* case 9 */ 1, + ST_QUA, 4, E02, E00, P1, P2, + /* case 10 */ 2, + ST_TRI, 3, E00, E03, P0, + ST_TRI, 3, E02, E01, P2, + /* case 11 */ 1, + ST_TRI, 3, E02, E01, P2, + /* case 12 */ 1, + ST_QUA, 4, E01, E03, P0, P1, + /* case 13 */ 1, + ST_TRI, 3, E01, E00, P1, + /* case 14 */ 1, + ST_TRI, 3, E00, E03, P0, + /* case 15 */ 0, + // vtkm::CELL_SHAPE_TETRA + /* case 0 */ 1, + ST_TET, 4, P0, P1, P2, P3, + /* case 1 */ 1, + ST_WDG, 6, E00, E03, E02, P1, P3, P2, + /* case 2 */ 1, + ST_WDG, 6, P0, P3, P2, E00, E04, E01, + /* case 3 */ 1, + ST_WDG, 6, P3, E03, E04, P2, E02, E01, + /* case 4 */ 1, + ST_WDG, 6, E02, E05, E01, P0, P3, P1, + /* case 5 */ 1, + ST_WDG, 6, P1, E00, E01, P3, E03, E05, + /* case 6 */ 1, + ST_WDG, 6, P3, E04, E05, P0, E00, E02, + /* case 7 */ 1, + ST_TET, 4, E03, E04, E05, P3, + /* case 8 */ 1, + ST_WDG, 6, P0, P2, P1, E03, E05, E04, + /* case 9 */ 1, + ST_WDG, 6, P2, E02, E05, P1, E00, E04, + /* case 10 */ 1, + ST_WDG, 6, P0, E00, E03, P2, E01, E05, + /* case 11 */ 1, + ST_TET, 4, E02, E05, E01, P2, + /* case 12 */ 1, + ST_WDG, 6, P1, E01, E04, P0, E02, E03, + /* case 13 */ 1, + ST_TET, 4, E00, E01, E04, P1, + /* case 14 */ 1, + ST_TET, 4, E00, E03, E02, P0, + /* case 15 */ 0, + // vtkm::CELL_SHAPE_HEXAHEDRON + /* case 0 */ 1, + ST_HEX, 8, P0, P1, P2, P3, P4, P5, P6, P7, + /* case 1 */ 9, + ST_PNT, 7, P1, P2, P3, P4, P5, P6, P7, + ST_WDG, 6, P1, P3, P4, E00, E03, E08, + ST_TET, 4, P1, P3, P4, N0, + ST_TET, 4, P1, P2, P3, N0, + ST_PYR, 5, P6, P7, P3, P2, N0, + ST_PYR, 5, P5, P6, P2, P1, N0, + ST_PYR, 5, P4, P7, P6, P5, N0, + ST_TET, 4, P3, P7, P4, N0, + ST_TET, 4, P4, P5, P1, N0, + /* case 2 */ 9, + ST_PNT, 7, P5, P4, P0, P2, P6, P7, P3, + ST_WDG, 6, E09, E00, E01, P5, P0, P2, + ST_TET, 4, P5, P2, P0, N0, + ST_TET, 4, P5, P0, P4, N0, + ST_PYR, 5, P7, P4, P0, P3, N0, + ST_PYR, 5, P6, P5, P4, P7, N0, + ST_PYR, 5, P2, P6, P7, P3, N0, + ST_TET, 4, P0, P2, P3, N0, + ST_TET, 4, P2, P5, P6, N0, + /* case 3 */ 2, + ST_HEX, 8, E01, P2, P3, E03, E09, P5, P4, E08, + ST_WDG, 6, P2, P6, P5, P3, P7, P4, + /* case 4 */ 9, + ST_PNT, 7, P6, P5, P1, P3, P7, P4, P0, + ST_WDG, 6, E11, E01, E02, P6, P1, P3, + ST_TET, 4, P6, P3, P1, N0, + ST_TET, 4, P6, P1, P5, N0, + ST_PYR, 5, P4, P5, P1, P0, N0, + ST_PYR, 5, P7, P6, P5, P4, N0, + ST_PYR, 5, P3, P7, P4, P0, N0, + ST_TET, 4, P1, P3, P0, N0, + ST_TET, 4, P3, P6, P7, N0, + /* case 5 */ 12, + ST_PNT, 2, E08, E11, + ST_PYR, 5, P4, P7, P6, P5, N0, + ST_TET, 4, P5, P6, P1, N0, + ST_TET, 4, P4, P5, P1, N0, + ST_TET, 4, P3, P7, P4, N0, + ST_TET, 4, P6, P7, P3, N0, + ST_PYR, 5, P6, P3, E02, E11, N0, + ST_PYR, 5, P1, P6, E11, E01, N0, + ST_TET, 4, P1, E01, E00, N0, + ST_PYR, 5, P4, P1, E00, E08, N0, + ST_PYR, 5, P4, E08, E03, P3, N0, + ST_TET, 4, P3, E03, E02, N0, + /* case 6 */ 2, + ST_HEX, 8, E02, P3, P0, E00, E11, P6, P5, E09, + ST_WDG, 6, P3, P7, P6, P0, P4, P5, + /* case 7 */ 9, + ST_PNT, 5, E08, E09, E03, E02, E11, + ST_PYR, 5, P4, P7, P6, P5, N0, + ST_TET, 4, P6, P3, N0, P7, + ST_PYR, 5, P5, P6, E11, E09, N0, + ST_PYR, 5, E08, P4, P5, E09, N0, + ST_TET, 4, P3, P7, P4, N0, + ST_PYR, 5, P3, P4, E08, E03, N0, + ST_TET, 4, P3, E03, E02, N0, + ST_PYR, 5, E11, P6, P3, E02, N0, + /* case 8 */ 9, + ST_PNT, 7, P2, P1, P0, P7, P6, P5, P4, + ST_WDG, 6, E02, E03, E10, P2, P0, P7, + ST_TET, 4, P2, P7, P0, N0, + ST_TET, 4, P2, P0, P1, N0, + ST_PYR, 5, P5, P1, P0, P4, N0, + ST_PYR, 5, P6, P2, P1, P5, N0, + ST_PYR, 5, P7, P6, P5, P4, N0, + ST_TET, 4, P0, P7, P4, N0, + ST_TET, 4, P7, P2, P6, N0, + /* case 9 */ 2, + ST_HEX, 8, E10, P7, P4, E08, E02, P2, P1, E00, + ST_WDG, 6, P7, P6, P2, P4, P5, P1, + /* case 10 */ 12, + ST_PNT, 2, E10, E09, + ST_PYR, 5, P7, P6, P5, P4, N0, + ST_TET, 4, P6, P2, P5, N0, + ST_TET, 4, P7, P2, P6, N0, + ST_TET, 4, P0, P7, P4, N0, + ST_TET, 4, P5, P0, P4, N0, + ST_PYR, 5, P5, E09, E00, P0, N0, + ST_PYR, 5, P2, E01, E09, P5, N0, + ST_TET, 4, P2, E02, E01, N0, + ST_PYR, 5, P7, E10, E02, P2, N0, + ST_PYR, 5, P7, P0, E03, E10, N0, + ST_TET, 4, P0, E00, E03, N0, + /* case 11 */ 9, + ST_PNT, 5, E09, E08, E01, E02, E10, + ST_PYR, 5, P5, P4, P7, P6, N0, + ST_TET, 4, P7, N0, P2, P6, + ST_PYR, 5, P4, E08, E10, P7, N0, + ST_PYR, 5, E09, E08, P4, P5, N0, + ST_TET, 4, P2, P5, P6, N0, + ST_PYR, 5, P2, E01, E09, P5, N0, + ST_TET, 4, P2, E02, E01, N0, + ST_PYR, 5, E10, E02, P2, P7, N0, + /* case 12 */ 2, + ST_HEX, 8, E11, P6, P7, E10, E01, P1, P0, E03, + ST_WDG, 6, P0, P4, P7, P1, P5, P6, + /* case 13 */ 9, + ST_PNT, 5, E08, E10, E00, E01, E11, + ST_PYR, 5, P4, P7, P6, P5, N0, + ST_TET, 4, P6, N0, P1, P5, + ST_PYR, 5, P7, E10, E11, P6, N0, + ST_PYR, 5, E08, E10, P7, P4, N0, + ST_TET, 4, P1, P4, P5, N0, + ST_PYR, 5, P1, E00, E08, P4, N0, + ST_TET, 4, P1, E01, E00, N0, + ST_PYR, 5, E11, E01, P1, P6, N0, + /* case 14 */ 9, + ST_PNT, 5, E10, E11, E03, E00, E09, + ST_PYR, 5, P7, P6, P5, P4, N0, + ST_TET, 4, P5, N0, P0, P4, + ST_PYR, 5, P6, E11, E09, P5, N0, + ST_PYR, 5, E10, E11, P6, P7, N0, + ST_TET, 4, P0, P7, P4, N0, + ST_PYR, 5, P0, E03, E10, P7, N0, + ST_TET, 4, P0, E00, E03, N0, + ST_PYR, 5, E09, E00, P0, P5, N0, + /* case 15 */ 1, + ST_HEX, 8, E08, E09, E11, E10, P4, P5, P6, P7, + /* case 16 */ 9, + ST_PNT, 7, P5, P1, P0, P7, P6, P2, P3, + ST_WDG, 6, P5, P0, P7, E04, E08, E07, + ST_TET, 4, P5, P0, P7, N0, + ST_TET, 4, P5, P1, P0, N0, + ST_PYR, 5, P2, P3, P0, P1, N0, + ST_PYR, 5, P6, P2, P1, P5, N0, + ST_PYR, 5, P7, P3, P2, P6, N0, + ST_TET, 4, P0, P3, P7, N0, + ST_TET, 4, P7, P6, P5, N0, + /* case 17 */ 2, + ST_HEX, 8, E04, P5, P1, E00, E07, P7, P3, E03, + ST_WDG, 6, P3, P2, P1, P7, P6, P5, + /* case 18 */ 12, + ST_PNT, 2, E07, E01, + ST_PYR, 5, P7, P3, P2, P6, N0, + ST_TET, 4, P6, P2, P5, N0, + ST_TET, 4, P7, P6, P5, N0, + ST_TET, 4, P0, P3, P7, N0, + ST_TET, 4, P2, P3, P0, N0, + ST_PYR, 5, P2, P0, E00, E01, N0, + ST_PYR, 5, P5, P2, E01, E09, N0, + ST_TET, 4, P5, E09, E04, N0, + ST_PYR, 5, P7, P5, E04, E07, N0, + ST_PYR, 5, P7, E07, E08, P0, N0, + ST_TET, 4, P0, E08, E00, N0, + /* case 19 */ 9, + ST_PNT, 5, E01, E03, E09, E04, E07, + ST_PYR, 5, P2, P6, P7, P3, N0, + ST_TET, 4, P7, P5, N0, P6, + ST_PYR, 5, P3, P7, E07, E03, N0, + ST_PYR, 5, E01, P2, P3, E03, N0, + ST_TET, 4, P5, P6, P2, N0, + ST_PYR, 5, P5, P2, E01, E09, N0, + ST_TET, 4, P5, E09, E04, N0, + ST_PYR, 5, E07, P7, P5, E04, N0, + /* case 20 */ 6, + ST_WDG, 6, E01, E02, E11, P1, P3, P6, + ST_WDG, 6, P0, P7, P5, E08, E07, E04, + ST_TET, 4, P3, P1, P6, P7, + ST_TET, 4, P5, P7, P6, P1, + ST_TET, 4, P0, P5, P1, P7, + ST_TET, 4, P3, P7, P0, P1, + /* case 21 */ 11, + ST_PNT, 4, E04, E07, E11, E11, + ST_PYR, 5, P6, P3, E02, E11, N0, + ST_TET, 4, E02, P3, E03, N0, + ST_PYR, 5, P7, E07, E03, P3, N0, + ST_TET, 4, P6, P7, P3, N0, + ST_TET, 4, P1, E01, E00, N0, + ST_TET, 4, P5, P6, P1, N0, + ST_PYR, 5, P1, P6, E11, E01, N0, + ST_TET, 4, P5, P7, P6, N0, + ST_PYR, 5, P5, E04, E07, P7, N0, + ST_PYR, 5, P5, P1, E00, E04, N0, + /* case 22 */ 11, + ST_PNT, 4, E11, E02, E07, E07, + ST_PYR, 5, P7, E07, E08, P0, N0, + ST_TET, 4, E08, E00, P0, N0, + ST_PYR, 5, P3, P0, E00, E02, N0, + ST_TET, 4, P7, P0, P3, N0, + ST_TET, 4, P5, E09, E04, N0, + ST_TET, 4, P6, P5, P7, N0, + ST_PYR, 5, P5, E04, E07, P7, N0, + ST_TET, 4, P6, P7, P3, N0, + ST_PYR, 5, P6, P3, E02, E11, N0, + ST_PYR, 5, P6, E11, E09, P5, N0, + /* case 23 */ 9, + ST_PNT, 6, E03, E02, E11, E09, E04, E07, + ST_TET, 4, P6, P5, P7, N0, + ST_PYR, 5, P6, E11, E09, P5, N0, + ST_TET, 4, P3, P6, P7, N0, + ST_PYR, 5, P3, E02, E11, P6, N0, + ST_TET, 4, E03, E02, P3, N0, + ST_PYR, 5, P3, P7, E07, E03, N0, + ST_PYR, 5, E07, P7, P5, E04, N0, + ST_TET, 4, P5, E09, E04, N0, + /* case 24 */ 12, + ST_PNT, 2, E02, E04, + ST_PYR, 5, P2, P1, P5, P6, N0, + ST_TET, 4, P6, P5, P7, N0, + ST_TET, 4, P2, P6, P7, N0, + ST_TET, 4, P0, P1, P2, N0, + ST_TET, 4, P5, P1, P0, N0, + ST_PYR, 5, P5, P0, E08, E04, N0, + ST_PYR, 5, P7, P5, E04, E07, N0, + ST_TET, 4, P7, E07, E10, N0, + ST_PYR, 5, P2, P7, E10, E02, N0, + ST_PYR, 5, P2, E02, E03, P0, N0, + ST_TET, 4, P0, E03, E08, N0, + /* case 25 */ 9, + ST_PNT, 5, E04, E00, E07, E10, E02, + ST_PYR, 5, P5, P6, P2, P1, N0, + ST_TET, 4, P2, P7, N0, P6, + ST_PYR, 5, P1, P2, E02, E00, N0, + ST_PYR, 5, E04, P5, P1, E00, N0, + ST_TET, 4, P7, P6, P5, N0, + ST_PYR, 5, P7, P5, E04, E07, N0, + ST_TET, 4, P7, E07, E10, N0, + ST_PYR, 5, E02, P2, P7, E10, N0, + /* case 26 */ 6, + ST_TET, 4, P0, E00, E03, E08, + ST_TET, 4, P5, P7, P6, P2, + ST_PYR, 5, E02, P2, P7, E10, E07, + ST_PYR, 5, E01, E09, P5, P2, E04, + ST_PYR, 5, P7, P5, E04, E07, P2, + ST_PYR, 5, E07, E04, E01, E02, P2, + /* case 27 */ 5, + ST_TET, 4, P5, P7, P6, P2, + ST_PYR, 5, E02, P2, P7, E10, E07, + ST_PYR, 5, E01, E09, P5, P2, E04, + ST_PYR, 5, P7, P5, E04, E07, P2, + ST_PYR, 5, E07, E04, E01, E02, P2, + /* case 28 */ 11, + ST_PNT, 4, E11, E01, E04, E04, + ST_PYR, 5, P5, P0, E08, E04, N0, + ST_TET, 4, E08, P0, E03, N0, + ST_PYR, 5, P1, E01, E03, P0, N0, + ST_TET, 4, P5, P1, P0, N0, + ST_TET, 4, P7, E07, E10, N0, + ST_TET, 4, P6, P5, P7, N0, + ST_PYR, 5, P7, P5, E04, E07, N0, + ST_TET, 4, P6, P1, P5, N0, + ST_PYR, 5, P6, E11, E01, P1, N0, + ST_PYR, 5, P6, P7, E10, E11, N0, + /* case 29 */ 9, + ST_PNT, 6, E00, E01, E11, E10, E07, E04, + ST_TET, 4, P6, P5, P7, N0, + ST_PYR, 5, P6, P7, E10, E11, N0, + ST_TET, 4, P1, P5, P6, N0, + ST_PYR, 5, P1, P6, E11, E01, N0, + ST_TET, 4, E00, P1, E01, N0, + ST_PYR, 5, P1, E00, E04, P5, N0, + ST_PYR, 5, E04, E07, P7, P5, N0, + ST_TET, 4, P7, E07, E10, N0, + /* case 30 */ 8, + ST_PNT, 5, E11, E09, E10, E07, E04, + ST_TET, 4, P0, E00, E03, E08, + ST_PYR, 5, P5, P6, E11, E09, N0, + ST_PYR, 5, P6, P7, E10, E11, N0, + ST_TET, 4, P7, E07, E10, N0, + ST_TET, 4, P6, P5, P7, N0, + ST_PYR, 5, P7, P5, E04, E07, N0, + ST_TET, 4, P5, E09, E04, N0, + /* case 31 */ 7, + ST_PNT, 5, E09, E11, E10, E04, E07, + ST_PYR, 5, P6, P7, E10, E11, N0, + ST_TET, 4, P7, E07, E10, N0, + ST_PYR, 5, P5, P6, E11, E09, N0, + ST_TET, 4, E04, P5, E09, N0, + ST_PYR, 5, E07, P7, P5, E04, N0, + ST_TET, 4, P7, P6, P5, N0, + /* case 32 */ 9, + ST_PNT, 7, P6, P2, P1, P4, P7, P3, P0, + ST_WDG, 6, P6, P1, P4, E05, E09, E04, + ST_TET, 4, P6, P1, P4, N0, + ST_TET, 4, P6, P2, P1, N0, + ST_PYR, 5, P3, P0, P1, P2, N0, + ST_PYR, 5, P7, P3, P2, P6, N0, + ST_PYR, 5, P4, P0, P3, P7, N0, + ST_TET, 4, P1, P0, P4, N0, + ST_TET, 4, P4, P7, P6, N0, + /* case 33 */ 12, + ST_PNT, 2, E03, E05, + ST_PYR, 5, P3, P2, P6, P7, N0, + ST_TET, 4, P2, P1, P6, N0, + ST_TET, 4, P3, P1, P2, N0, + ST_TET, 4, P4, P3, P7, N0, + ST_TET, 4, P6, P4, P7, N0, + ST_PYR, 5, P6, E05, E04, P4, N0, + ST_PYR, 5, P1, E09, E05, P6, N0, + ST_TET, 4, P1, E00, E09, N0, + ST_PYR, 5, P3, E03, E00, P1, N0, + ST_PYR, 5, P3, P4, E08, E03, N0, + ST_TET, 4, P4, E04, E08, N0, + /* case 34 */ 2, + ST_HEX, 8, E05, P6, P2, E01, E04, P4, P0, E00, + ST_WDG, 6, P0, P3, P2, P4, P7, P6, + /* case 35 */ 9, + ST_PNT, 5, E03, E01, E08, E04, E05, + ST_PYR, 5, P3, P2, P6, P7, N0, + ST_TET, 4, P6, N0, P4, P7, + ST_PYR, 5, P2, E01, E05, P6, N0, + ST_PYR, 5, E03, E01, P2, P3, N0, + ST_TET, 4, P4, P3, P7, N0, + ST_PYR, 5, P4, E08, E03, P3, N0, + ST_TET, 4, P4, E04, E08, N0, + ST_PYR, 5, E05, E04, P4, P6, N0, + /* case 36 */ 12, + ST_PNT, 2, E02, E04, + ST_PYR, 5, P3, P7, P4, P0, N0, + ST_TET, 4, P7, P6, P4, N0, + ST_TET, 4, P3, P6, P7, N0, + ST_TET, 4, P1, P3, P0, N0, + ST_TET, 4, P4, P1, P0, N0, + ST_PYR, 5, P4, E04, E09, P1, N0, + ST_PYR, 5, P6, E05, E04, P4, N0, + ST_TET, 4, P6, E11, E05, N0, + ST_PYR, 5, P3, E02, E11, P6, N0, + ST_PYR, 5, P3, P1, E01, E02, N0, + ST_TET, 4, P1, E09, E01, N0, + /* case 37 */ 6, + ST_TET, 4, P1, E00, E09, E01, + ST_TET, 4, P6, P7, P3, P4, + ST_PYR, 5, E08, E03, P3, P4, E02, + ST_PYR, 5, E04, P4, P6, E05, E11, + ST_PYR, 5, P3, E02, E11, P6, P4, + ST_PYR, 5, E02, E08, E04, E11, P4, + /* case 38 */ 9, + ST_PNT, 5, E04, E00, E05, E11, E02, + ST_PYR, 5, P4, P0, P3, P7, N0, + ST_TET, 4, P3, N0, P6, P7, + ST_PYR, 5, P0, E00, E02, P3, N0, + ST_PYR, 5, E04, E00, P0, P4, N0, + ST_TET, 4, P6, P4, P7, N0, + ST_PYR, 5, P6, E05, E04, P4, N0, + ST_TET, 4, P6, E11, E05, N0, + ST_PYR, 5, E02, E11, P6, P3, N0, + /* case 39 */ 5, + ST_TET, 4, P6, P7, P3, P4, + ST_PYR, 5, E08, E03, P3, P4, E02, + ST_PYR, 5, E04, P4, P6, E05, E11, + ST_PYR, 5, P3, E02, E11, P6, P4, + ST_PYR, 5, E02, E08, E04, E11, P4, + /* case 40 */ 6, + ST_WDG, 6, P1, P4, P6, E09, E04, E05, + ST_WDG, 6, E03, E10, E02, P0, P7, P2, + ST_TET, 4, P4, P6, P1, P7, + ST_TET, 4, P2, P6, P7, P1, + ST_TET, 4, P0, P1, P2, P7, + ST_TET, 4, P4, P0, P7, P1, + /* case 41 */ 11, + ST_PNT, 4, E02, E10, E05, E05, + ST_PYR, 5, P6, E05, E04, P4, N0, + ST_TET, 4, E04, E08, P4, N0, + ST_PYR, 5, P7, P4, E08, E10, N0, + ST_TET, 4, P6, P4, P7, N0, + ST_TET, 4, P1, E00, E09, N0, + ST_TET, 4, P2, P1, P6, N0, + ST_PYR, 5, P1, E09, E05, P6, N0, + ST_TET, 4, P2, P6, P7, N0, + ST_PYR, 5, P2, P7, E10, E02, N0, + ST_PYR, 5, P2, E02, E00, P1, N0, + /* case 42 */ 11, + ST_PNT, 4, E05, E04, E10, E10, + ST_PYR, 5, P7, P0, E03, E10, N0, + ST_TET, 4, E03, P0, E00, N0, + ST_PYR, 5, P4, E04, E00, P0, N0, + ST_TET, 4, P7, P4, P0, N0, + ST_TET, 4, P2, E02, E01, N0, + ST_TET, 4, P6, P7, P2, N0, + ST_PYR, 5, P2, P7, E10, E02, N0, + ST_TET, 4, P6, P4, P7, N0, + ST_PYR, 5, P6, E05, E04, P4, N0, + ST_PYR, 5, P6, P2, E01, E05, N0, + /* case 43 */ 9, + ST_PNT, 6, E08, E04, E05, E01, E02, E10, + ST_TET, 4, P6, P7, P2, N0, + ST_PYR, 5, P6, P2, E01, E05, N0, + ST_TET, 4, P4, P7, P6, N0, + ST_PYR, 5, P4, P6, E05, E04, N0, + ST_TET, 4, E08, P4, E04, N0, + ST_PYR, 5, P4, E08, E10, P7, N0, + ST_PYR, 5, E10, E02, P2, P7, N0, + ST_TET, 4, P2, E02, E01, N0, + /* case 44 */ 11, + ST_PNT, 4, E10, E03, E04, E04, + ST_PYR, 5, P4, E04, E09, P1, N0, + ST_TET, 4, E09, E01, P1, N0, + ST_PYR, 5, P0, P1, E01, E03, N0, + ST_TET, 4, P4, P1, P0, N0, + ST_TET, 4, P6, E11, E05, N0, + ST_TET, 4, P7, P6, P4, N0, + ST_PYR, 5, P6, E05, E04, P4, N0, + ST_TET, 4, P7, P4, P0, N0, + ST_PYR, 5, P7, P0, E03, E10, N0, + ST_PYR, 5, P7, E10, E11, P6, N0, + /* case 45 */ 8, + ST_PNT, 5, E10, E11, E08, E04, E05, + ST_TET, 4, P1, E01, E00, E09, + ST_PYR, 5, P6, P7, E10, E11, N0, + ST_PYR, 5, P7, P4, E08, E10, N0, + ST_TET, 4, P4, E04, E08, N0, + ST_TET, 4, P7, P6, P4, N0, + ST_PYR, 5, P4, P6, E05, E04, N0, + ST_TET, 4, P6, E11, E05, N0, + /* case 46 */ 9, + ST_PNT, 6, E00, E03, E10, E11, E05, E04, + ST_TET, 4, P7, P6, P4, N0, + ST_PYR, 5, P7, E10, E11, P6, N0, + ST_TET, 4, P0, P7, P4, N0, + ST_PYR, 5, P0, E03, E10, P7, N0, + ST_TET, 4, E00, E03, P0, N0, + ST_PYR, 5, P0, P4, E04, E00, N0, + ST_PYR, 5, E04, P4, P6, E05, N0, + ST_TET, 4, P6, E11, E05, N0, + /* case 47 */ 7, + ST_PNT, 5, E11, E10, E08, E05, E04, + ST_PYR, 5, P7, P4, E08, E10, N0, + ST_TET, 4, P4, E04, E08, N0, + ST_PYR, 5, P6, P7, E10, E11, N0, + ST_TET, 4, E05, P6, E11, N0, + ST_PYR, 5, E04, P4, P6, E05, N0, + ST_TET, 4, P4, P7, P6, N0, + /* case 48 */ 2, + ST_HEX, 8, E09, P1, P0, E08, E05, P6, P7, E07, + ST_WDG, 6, P1, P2, P6, P0, P3, P7, + /* case 49 */ 9, + ST_PNT, 5, E03, E07, E00, E09, E05, + ST_PYR, 5, P3, P2, P6, P7, N0, + ST_TET, 4, P6, P1, N0, P2, + ST_PYR, 5, P7, P6, E05, E07, N0, + ST_PYR, 5, E03, P3, P7, E07, N0, + ST_TET, 4, P1, P2, P3, N0, + ST_PYR, 5, P1, P3, E03, E00, N0, + ST_TET, 4, P1, E00, E09, N0, + ST_PYR, 5, E05, P6, P1, E09, N0, + /* case 50 */ 9, + ST_PNT, 5, E07, E05, E08, E00, E01, + ST_PYR, 5, P7, P3, P2, P6, N0, + ST_TET, 4, P2, P0, N0, P3, + ST_PYR, 5, P6, P2, E01, E05, N0, + ST_PYR, 5, E07, P7, P6, E05, N0, + ST_TET, 4, P0, P3, P7, N0, + ST_PYR, 5, P0, P7, E07, E08, N0, + ST_TET, 4, P0, E08, E00, N0, + ST_PYR, 5, E01, P2, P0, E00, N0, + /* case 51 */ 1, + ST_HEX, 8, P3, P2, P6, P7, E03, E01, E05, E07, + /* case 52 */ 11, + ST_PNT, 4, E07, E08, E02, E02, + ST_PYR, 5, P3, P1, E01, E02, N0, + ST_TET, 4, E01, P1, E09, N0, + ST_PYR, 5, P0, E08, E09, P1, N0, + ST_TET, 4, P3, P0, P1, N0, + ST_TET, 4, P6, E11, E05, N0, + ST_TET, 4, P7, P3, P6, N0, + ST_PYR, 5, P6, P3, E02, E11, N0, + ST_TET, 4, P7, P0, P3, N0, + ST_PYR, 5, P7, E07, E08, P0, N0, + ST_PYR, 5, P7, P6, E05, E07, N0, + /* case 53 */ 8, + ST_PNT, 5, E07, E05, E03, E02, E11, + ST_TET, 4, P1, E00, E09, E01, + ST_PYR, 5, P6, E05, E07, P7, N0, + ST_PYR, 5, P7, E07, E03, P3, N0, + ST_TET, 4, P3, E03, E02, N0, + ST_TET, 4, P7, P3, P6, N0, + ST_PYR, 5, P3, E02, E11, P6, N0, + ST_TET, 4, P6, E11, E05, N0, + /* case 54 */ 9, + ST_PNT, 6, E00, E08, E07, E05, E11, E02, + ST_TET, 4, P7, P3, P6, N0, + ST_PYR, 5, P7, P6, E05, E07, N0, + ST_TET, 4, P0, P3, P7, N0, + ST_PYR, 5, P0, P7, E07, E08, N0, + ST_TET, 4, E00, P0, E08, N0, + ST_PYR, 5, P0, E00, E02, P3, N0, + ST_PYR, 5, E02, E11, P6, P3, N0, + ST_TET, 4, P6, E11, E05, N0, + /* case 55 */ 7, + ST_PNT, 5, E05, E07, E03, E11, E02, + ST_PYR, 5, P7, E07, E03, P3, N0, + ST_TET, 4, P3, E03, E02, N0, + ST_PYR, 5, P6, E05, E07, P7, N0, + ST_TET, 4, E11, E05, P6, N0, + ST_PYR, 5, E02, E11, P6, P3, N0, + ST_TET, 4, P3, P6, P7, N0, + /* case 56 */ 11, + ST_PNT, 4, E05, E09, E02, E02, + ST_PYR, 5, P2, E02, E03, P0, N0, + ST_TET, 4, E03, E08, P0, N0, + ST_PYR, 5, P1, P0, E08, E09, N0, + ST_TET, 4, P2, P0, P1, N0, + ST_TET, 4, P7, E07, E10, N0, + ST_TET, 4, P6, P7, P2, N0, + ST_PYR, 5, P7, E10, E02, P2, N0, + ST_TET, 4, P6, P2, P1, N0, + ST_PYR, 5, P6, P1, E09, E05, N0, + ST_PYR, 5, P6, E05, E07, P7, N0, + /* case 57 */ 9, + ST_PNT, 6, E00, E09, E05, E07, E10, E02, + ST_TET, 4, P6, P7, P2, N0, + ST_PYR, 5, P6, E05, E07, P7, N0, + ST_TET, 4, P1, P6, P2, N0, + ST_PYR, 5, P1, E09, E05, P6, N0, + ST_TET, 4, E00, E09, P1, N0, + ST_PYR, 5, P1, P2, E02, E00, N0, + ST_PYR, 5, E02, P2, P7, E10, N0, + ST_TET, 4, P7, E07, E10, N0, + /* case 58 */ 8, + ST_PNT, 5, E05, E01, E07, E10, E02, + ST_TET, 4, P0, E08, E00, E03, + ST_PYR, 5, P2, E01, E05, P6, N0, + ST_PYR, 5, P6, E05, E07, P7, N0, + ST_TET, 4, P7, E07, E10, N0, + ST_TET, 4, P6, P7, P2, N0, + ST_PYR, 5, P7, E10, E02, P2, N0, + ST_TET, 4, P2, E02, E01, N0, + /* case 59 */ 7, + ST_PNT, 5, E01, E05, E07, E02, E10, + ST_PYR, 5, P6, E05, E07, P7, N0, + ST_TET, 4, P7, E07, E10, N0, + ST_PYR, 5, P2, E01, E05, P6, N0, + ST_TET, 4, E02, E01, P2, N0, + ST_PYR, 5, E10, E02, P2, P7, N0, + ST_TET, 4, P7, P2, P6, N0, + /* case 60 */ 2, + ST_WDG, 6, P1, E01, E09, P0, E03, E08, + ST_WDG, 6, P6, E05, E11, P7, E07, E10, + /* case 61 */ 2, + ST_WDG, 6, E07, P7, E10, E05, P6, E11, + ST_TET, 4, E00, P1, E01, E09, + /* case 62 */ 2, + ST_WDG, 6, E07, P7, E10, E05, P6, E11, + ST_TET, 4, E00, E03, P0, E08, + /* case 63 */ 1, + ST_WDG, 6, P7, E10, E07, P6, E11, E05, + /* case 64 */ 9, + ST_PNT, 7, P7, P4, P5, P2, P3, P0, P1, + ST_WDG, 6, E06, E05, E11, P7, P5, P2, + ST_TET, 4, P7, P2, P5, N0, + ST_TET, 4, P7, P5, P4, N0, + ST_PYR, 5, P0, P4, P5, P1, N0, + ST_PYR, 5, P3, P7, P4, P0, N0, + ST_PYR, 5, P2, P3, P0, P1, N0, + ST_TET, 4, P5, P2, P1, N0, + ST_TET, 4, P2, P7, P3, N0, + /* case 65 */ 6, + ST_WDG, 6, P5, P7, P2, E05, E06, E11, + ST_WDG, 6, E08, E03, E00, P4, P3, P1, + ST_TET, 4, P7, P2, P5, P3, + ST_TET, 4, P1, P2, P3, P5, + ST_TET, 4, P4, P5, P1, P3, + ST_TET, 4, P7, P4, P3, P5, + /* case 66 */ 12, + ST_PNT, 2, E00, E06, + ST_PYR, 5, P0, P3, P7, P4, N0, + ST_TET, 4, P4, P7, P5, N0, + ST_TET, 4, P0, P4, P5, N0, + ST_TET, 4, P2, P3, P0, N0, + ST_TET, 4, P7, P3, P2, N0, + ST_PYR, 5, P7, P2, E11, E06, N0, + ST_PYR, 5, P5, P7, E06, E05, N0, + ST_TET, 4, P5, E05, E09, N0, + ST_PYR, 5, P0, P5, E09, E00, N0, + ST_PYR, 5, P0, E00, E01, P2, N0, + ST_TET, 4, P2, E01, E11, N0, + /* case 67 */ 11, + ST_PNT, 4, E08, E03, E06, E06, + ST_PYR, 5, P7, P2, E11, E06, N0, + ST_TET, 4, E11, P2, E01, N0, + ST_PYR, 5, P3, E03, E01, P2, N0, + ST_TET, 4, P7, P3, P2, N0, + ST_TET, 4, P5, E05, E09, N0, + ST_TET, 4, P4, P7, P5, N0, + ST_PYR, 5, P5, P7, E06, E05, N0, + ST_TET, 4, P4, P3, P7, N0, + ST_PYR, 5, P4, E08, E03, P3, N0, + ST_PYR, 5, P4, P5, E09, E08, N0, + /* case 68 */ 2, + ST_HEX, 8, E06, P7, P3, E02, E05, P5, P1, E01, + ST_WDG, 6, P1, P0, P3, P5, P4, P7, + /* case 69 */ 11, + ST_PNT, 4, E06, E05, E08, E08, + ST_PYR, 5, P4, P1, E00, E08, N0, + ST_TET, 4, E00, P1, E01, N0, + ST_PYR, 5, P5, E05, E01, P1, N0, + ST_TET, 4, P4, P5, P1, N0, + ST_TET, 4, P3, E03, E02, N0, + ST_TET, 4, P7, P4, P3, N0, + ST_PYR, 5, P3, P4, E08, E03, N0, + ST_TET, 4, P7, P5, P4, N0, + ST_PYR, 5, P7, E06, E05, P5, N0, + ST_PYR, 5, P7, P3, E02, E06, N0, + /* case 70 */ 9, + ST_PNT, 5, E00, E02, E09, E05, E06, + ST_PYR, 5, P0, P3, P7, P4, N0, + ST_TET, 4, P7, N0, P5, P4, + ST_PYR, 5, P3, E02, E06, P7, N0, + ST_PYR, 5, E00, E02, P3, P0, N0, + ST_TET, 4, P5, P0, P4, N0, + ST_PYR, 5, P5, E09, E00, P0, N0, + ST_TET, 4, P5, E05, E09, N0, + ST_PYR, 5, E06, E05, P5, P7, N0, + /* case 71 */ 9, + ST_PNT, 6, E09, E05, E06, E02, E03, E08, + ST_TET, 4, P7, P4, P3, N0, + ST_PYR, 5, P7, P3, E02, E06, N0, + ST_TET, 4, P5, P4, P7, N0, + ST_PYR, 5, P5, P7, E06, E05, N0, + ST_TET, 4, E09, P5, E05, N0, + ST_PYR, 5, P5, E09, E08, P4, N0, + ST_PYR, 5, E08, E03, P3, P4, N0, + ST_TET, 4, P3, E03, E02, N0, + /* case 72 */ 12, + ST_PNT, 2, E03, E05, + ST_PYR, 5, P0, P4, P5, P1, N0, + ST_TET, 4, P1, P5, P2, N0, + ST_TET, 4, P0, P1, P2, N0, + ST_TET, 4, P7, P4, P0, N0, + ST_TET, 4, P5, P4, P7, N0, + ST_PYR, 5, P5, P7, E06, E05, N0, + ST_PYR, 5, P2, P5, E05, E11, N0, + ST_TET, 4, P2, E11, E02, N0, + ST_PYR, 5, P0, P2, E02, E03, N0, + ST_PYR, 5, P0, E03, E10, P7, N0, + ST_TET, 4, P7, E10, E06, N0, + /* case 73 */ 11, + ST_PNT, 4, E00, E08, E05, E05, + ST_PYR, 5, P5, P7, E06, E05, N0, + ST_TET, 4, E06, P7, E10, N0, + ST_PYR, 5, P4, E08, E10, P7, N0, + ST_TET, 4, P5, P4, P7, N0, + ST_TET, 4, P2, E11, E02, N0, + ST_TET, 4, P1, P5, P2, N0, + ST_PYR, 5, P2, P5, E05, E11, N0, + ST_TET, 4, P1, P4, P5, N0, + ST_PYR, 5, P1, E00, E08, P4, N0, + ST_PYR, 5, P1, P2, E02, E00, N0, + /* case 74 */ 6, + ST_TET, 4, P2, E01, E11, E02, + ST_TET, 4, P7, P4, P0, P5, + ST_PYR, 5, E09, E00, P0, P5, E03, + ST_PYR, 5, E05, P5, P7, E06, E10, + ST_PYR, 5, P0, E03, E10, P7, P5, + ST_PYR, 5, E03, E09, E05, E10, P5, + /* case 75 */ 8, + ST_PNT, 5, E08, E10, E09, E05, E06, + ST_TET, 4, P2, E02, E01, E11, + ST_PYR, 5, P7, P4, E08, E10, N0, + ST_PYR, 5, P4, P5, E09, E08, N0, + ST_TET, 4, P5, E05, E09, N0, + ST_TET, 4, P4, P7, P5, N0, + ST_PYR, 5, P5, P7, E06, E05, N0, + ST_TET, 4, P7, E10, E06, N0, + /* case 76 */ 9, + ST_PNT, 5, E03, E01, E10, E06, E05, + ST_PYR, 5, P0, P4, P5, P1, N0, + ST_TET, 4, P5, P7, N0, P4, + ST_PYR, 5, P1, P5, E05, E01, N0, + ST_PYR, 5, E03, P0, P1, E01, N0, + ST_TET, 4, P7, P4, P0, N0, + ST_PYR, 5, P7, P0, E03, E10, N0, + ST_TET, 4, P7, E10, E06, N0, + ST_PYR, 5, E05, P5, P7, E06, N0, + /* case 77 */ 9, + ST_PNT, 6, E10, E06, E05, E01, E00, E08, + ST_TET, 4, P5, P1, P4, N0, + ST_PYR, 5, P5, E05, E01, P1, N0, + ST_TET, 4, P7, P5, P4, N0, + ST_PYR, 5, P7, E06, E05, P5, N0, + ST_TET, 4, E10, E06, P7, N0, + ST_PYR, 5, P7, P4, E08, E10, N0, + ST_PYR, 5, E08, P4, P1, E00, N0, + ST_TET, 4, P1, E01, E00, N0, + /* case 78 */ 5, + ST_TET, 4, P7, P4, P0, P5, + ST_PYR, 5, E09, E00, P0, P5, E03, + ST_PYR, 5, E05, P5, P7, E06, E10, + ST_PYR, 5, P0, E03, E10, P7, P5, + ST_PYR, 5, E03, E09, E05, E10, P5, + /* case 79 */ 7, + ST_PNT, 5, E10, E08, E09, E06, E05, + ST_PYR, 5, P4, P5, E09, E08, N0, + ST_TET, 4, P5, E05, E09, N0, + ST_PYR, 5, P7, P4, E08, E10, N0, + ST_TET, 4, E06, P7, E10, N0, + ST_PYR, 5, E05, P5, P7, E06, N0, + ST_TET, 4, P5, P4, P7, N0, + /* case 80 */ 12, + ST_PNT, 2, E08, E11, + ST_PYR, 5, P0, P1, P2, P3, N0, + ST_TET, 4, P1, P5, P2, N0, + ST_TET, 4, P0, P5, P1, N0, + ST_TET, 4, P7, P0, P3, N0, + ST_TET, 4, P2, P7, P3, N0, + ST_PYR, 5, P2, E11, E06, P7, N0, + ST_PYR, 5, P5, E05, E11, P2, N0, + ST_TET, 4, P5, E04, E05, N0, + ST_PYR, 5, P0, E08, E04, P5, N0, + ST_PYR, 5, P0, P7, E07, E08, N0, + ST_TET, 4, P7, E06, E07, N0, + /* case 81 */ 11, + ST_PNT, 4, E00, E03, E11, E11, + ST_PYR, 5, P2, E11, E06, P7, N0, + ST_TET, 4, E06, E07, P7, N0, + ST_PYR, 5, P3, P7, E07, E03, N0, + ST_TET, 4, P2, P7, P3, N0, + ST_TET, 4, P5, E04, E05, N0, + ST_TET, 4, P1, P5, P2, N0, + ST_PYR, 5, P5, E05, E11, P2, N0, + ST_TET, 4, P1, P2, P3, N0, + ST_PYR, 5, P1, P3, E03, E00, N0, + ST_PYR, 5, P1, E00, E04, P5, N0, + /* case 82 */ 6, + ST_TET, 4, P5, E05, E09, E04, + ST_TET, 4, P7, P0, P3, P2, + ST_PYR, 5, E01, P2, P0, E00, E08, + ST_PYR, 5, E11, E06, P7, P2, E07, + ST_PYR, 5, P0, P7, E07, E08, P2, + ST_PYR, 5, E08, E07, E11, E01, P2, + /* case 83 */ 8, + ST_PNT, 5, E03, E07, E01, E11, E06, + ST_TET, 4, P5, E09, E04, E05, + ST_PYR, 5, P7, E07, E03, P3, N0, + ST_PYR, 5, P3, E03, E01, P2, N0, + ST_TET, 4, P2, E01, E11, N0, + ST_TET, 4, P3, P2, P7, N0, + ST_PYR, 5, P2, E11, E06, P7, N0, + ST_TET, 4, P7, E06, E07, N0, + /* case 84 */ 11, + ST_PNT, 4, E02, E01, E08, E08, + ST_PYR, 5, P0, E08, E04, P5, N0, + ST_TET, 4, E04, E05, P5, N0, + ST_PYR, 5, P1, P5, E05, E01, N0, + ST_TET, 4, P0, P5, P1, N0, + ST_TET, 4, P7, E06, E07, N0, + ST_TET, 4, P3, P7, P0, N0, + ST_PYR, 5, P7, E07, E08, P0, N0, + ST_TET, 4, P3, P0, P1, N0, + ST_PYR, 5, P3, P1, E01, E02, N0, + ST_PYR, 5, P3, E02, E06, P7, N0, + /* case 85 */ 2, + ST_WDG, 6, P7, E07, E06, P3, E03, E02, + ST_WDG, 6, P5, E05, E04, P1, E01, E00, + /* case 86 */ 8, + ST_PNT, 5, E02, E06, E00, E08, E07, + ST_TET, 4, P5, E05, E09, E04, + ST_PYR, 5, P7, P3, E02, E06, N0, + ST_PYR, 5, P3, P0, E00, E02, N0, + ST_TET, 4, P0, E08, E00, N0, + ST_TET, 4, P3, P7, P0, N0, + ST_PYR, 5, P0, P7, E07, E08, N0, + ST_TET, 4, P7, E06, E07, N0, + /* case 87 */ 2, + ST_WDG, 6, E06, P7, E07, E02, P3, E03, + ST_TET, 4, E09, E04, P5, E05, + /* case 88 */ 6, + ST_TET, 4, P7, E06, E07, E10, + ST_TET, 4, P2, P0, P1, P5, + ST_PYR, 5, E04, P5, P0, E08, E03, + ST_PYR, 5, E05, E11, P2, P5, E02, + ST_PYR, 5, P0, P2, E02, E03, P5, + ST_PYR, 5, E03, E02, E05, E04, P5, + /* case 89 */ 8, + ST_PNT, 5, E00, E02, E04, E05, E11, + ST_TET, 4, P7, E07, E10, E06, + ST_PYR, 5, P2, E02, E00, P1, N0, + ST_PYR, 5, P1, E00, E04, P5, N0, + ST_TET, 4, P5, E04, E05, N0, + ST_TET, 4, P1, P5, P2, N0, + ST_PYR, 5, P5, E05, E11, P2, N0, + ST_TET, 4, P2, E11, E02, N0, + /* case 90 */ 4, + ST_TET, 4, E07, E06, E10, P7, + ST_TET, 4, E08, E03, E00, P0, + ST_TET, 4, E04, E09, E05, P5, + ST_TET, 4, E01, E02, E11, P2, + /* case 91 */ 3, + ST_TET, 4, E04, E09, E05, P5, + ST_TET, 4, E07, E10, P7, E06, + ST_TET, 4, E01, P2, E02, E11, + /* case 92 */ 8, + ST_PNT, 5, E01, E05, E03, E08, E04, + ST_TET, 4, P7, E10, E06, E07, + ST_PYR, 5, P5, E05, E01, P1, N0, + ST_PYR, 5, P1, E01, E03, P0, N0, + ST_TET, 4, P0, E03, E08, N0, + ST_TET, 4, P1, P0, P5, N0, + ST_PYR, 5, P0, E08, E04, P5, N0, + ST_TET, 4, P5, E04, E05, N0, + /* case 93 */ 2, + ST_WDG, 6, E01, P1, E00, E05, P5, E04, + ST_TET, 4, E10, P7, E07, E06, + /* case 94 */ 3, + ST_TET, 4, E10, E07, E06, P7, + ST_TET, 4, E03, P0, E00, E08, + ST_TET, 4, E05, E09, P5, E04, + /* case 95 */ 2, + ST_TET, 4, E06, E10, E07, P7, + ST_TET, 4, E05, E04, E09, P5, + /* case 96 */ 2, + ST_HEX, 8, E11, P2, P1, E09, E06, P7, P4, E04, + ST_WDG, 6, P2, P3, P7, P1, P0, P4, + /* case 97 */ 11, + ST_PNT, 4, E06, E11, E03, E03, + ST_PYR, 5, P3, E03, E00, P1, N0, + ST_TET, 4, E00, E09, P1, N0, + ST_PYR, 5, P2, P1, E09, E11, N0, + ST_TET, 4, P3, P1, P2, N0, + ST_TET, 4, P4, E04, E08, N0, + ST_TET, 4, P7, P4, P3, N0, + ST_PYR, 5, P4, E08, E03, P3, N0, + ST_TET, 4, P7, P3, P2, N0, + ST_PYR, 5, P7, P2, E11, E06, N0, + ST_PYR, 5, P7, E06, E04, P4, N0, + /* case 98 */ 9, + ST_PNT, 5, E00, E04, E01, E11, E06, + ST_PYR, 5, P0, P3, P7, P4, N0, + ST_TET, 4, P7, P2, N0, P3, + ST_PYR, 5, P4, P7, E06, E04, N0, + ST_PYR, 5, E00, P0, P4, E04, N0, + ST_TET, 4, P2, P3, P0, N0, + ST_PYR, 5, P2, P0, E00, E01, N0, + ST_TET, 4, P2, E01, E11, N0, + ST_PYR, 5, E06, P7, P2, E11, N0, + /* case 99 */ 9, + ST_PNT, 6, E01, E11, E06, E04, E08, E03, + ST_TET, 4, P7, P4, P3, N0, + ST_PYR, 5, P7, E06, E04, P4, N0, + ST_TET, 4, P2, P7, P3, N0, + ST_PYR, 5, P2, E11, E06, P7, N0, + ST_TET, 4, E01, E11, P2, N0, + ST_PYR, 5, P2, P3, E03, E01, N0, + ST_PYR, 5, E03, P3, P4, E08, N0, + ST_TET, 4, P4, E04, E08, N0, + /* case 100 */ 9, + ST_PNT, 5, E02, E06, E01, E09, E04, + ST_PYR, 5, P3, P7, P4, P0, N0, + ST_TET, 4, P4, N0, P1, P0, + ST_PYR, 5, P7, E06, E04, P4, N0, + ST_PYR, 5, E02, E06, P7, P3, N0, + ST_TET, 4, P1, P3, P0, N0, + ST_PYR, 5, P1, E01, E02, P3, N0, + ST_TET, 4, P1, E09, E01, N0, + ST_PYR, 5, E04, E09, P1, P4, N0, + /* case 101 */ 8, + ST_PNT, 5, E06, E04, E02, E03, E08, + ST_TET, 4, P1, E09, E01, E00, + ST_PYR, 5, P4, P7, E06, E04, N0, + ST_PYR, 5, P7, P3, E02, E06, N0, + ST_TET, 4, P3, E03, E02, N0, + ST_TET, 4, P7, P4, P3, N0, + ST_PYR, 5, P3, P4, E08, E03, N0, + ST_TET, 4, P4, E04, E08, N0, + /* case 102 */ 1, + ST_HEX, 8, E00, E04, E06, E02, P0, P4, P7, P3, + /* case 103 */ 7, + ST_PNT, 5, E04, E06, E02, E08, E03, + ST_PYR, 5, P7, P3, E02, E06, N0, + ST_TET, 4, P3, E03, E02, N0, + ST_PYR, 5, P4, P7, E06, E04, N0, + ST_TET, 4, E08, P4, E04, N0, + ST_PYR, 5, E03, P3, P4, E08, N0, + ST_TET, 4, P3, P7, P4, N0, + /* case 104 */ 11, + ST_PNT, 4, E04, E09, E03, E03, + ST_PYR, 5, P0, P2, E02, E03, N0, + ST_TET, 4, E02, P2, E11, N0, + ST_PYR, 5, P1, E09, E11, P2, N0, + ST_TET, 4, P0, P1, P2, N0, + ST_TET, 4, P7, E10, E06, N0, + ST_TET, 4, P4, P0, P7, N0, + ST_PYR, 5, P7, P0, E03, E10, N0, + ST_TET, 4, P4, P1, P0, N0, + ST_PYR, 5, P4, E04, E09, P1, N0, + ST_PYR, 5, P4, P7, E06, E04, N0, + /* case 105 */ 2, + ST_WDG, 6, P4, E08, E04, P7, E10, E06, + ST_WDG, 6, P1, E09, E00, P2, E11, E02, + /* case 106 */ 8, + ST_PNT, 5, E04, E06, E00, E03, E10, + ST_TET, 4, P2, E01, E11, E02, + ST_PYR, 5, P7, E06, E04, P4, N0, + ST_PYR, 5, P4, E04, E00, P0, N0, + ST_TET, 4, P0, E00, E03, N0, + ST_TET, 4, P4, P0, P7, N0, + ST_PYR, 5, P0, E03, E10, P7, N0, + ST_TET, 4, P7, E10, E06, N0, + /* case 107 */ 2, + ST_WDG, 6, E04, P4, E08, E06, P7, E10, + ST_TET, 4, E01, P2, E02, E11, + /* case 108 */ 9, + ST_PNT, 6, E01, E09, E04, E06, E10, E03, + ST_TET, 4, P4, P0, P7, N0, + ST_PYR, 5, P4, P7, E06, E04, N0, + ST_TET, 4, P1, P0, P4, N0, + ST_PYR, 5, P1, P4, E04, E09, N0, + ST_TET, 4, E01, P1, E09, N0, + ST_PYR, 5, P1, E01, E03, P0, N0, + ST_PYR, 5, E03, E10, P7, P0, N0, + ST_TET, 4, P7, E10, E06, N0, + /* case 109 */ 2, + ST_WDG, 6, E04, P4, E08, E06, P7, E10, + ST_TET, 4, E01, E00, P1, E09, + /* case 110 */ 7, + ST_PNT, 5, E06, E04, E00, E10, E03, + ST_PYR, 5, P4, E04, E00, P0, N0, + ST_TET, 4, P0, E00, E03, N0, + ST_PYR, 5, P7, E06, E04, P4, N0, + ST_TET, 4, E10, E06, P7, N0, + ST_PYR, 5, E03, E10, P7, P0, N0, + ST_TET, 4, P0, P7, P4, N0, + /* case 111 */ 1, + ST_WDG, 6, P4, E08, E04, P7, E10, E06, + /* case 112 */ 9, + ST_PNT, 5, E08, E09, E07, E06, E11, + ST_PYR, 5, P0, P1, P2, P3, N0, + ST_TET, 4, P2, N0, P7, P3, + ST_PYR, 5, P1, E09, E11, P2, N0, + ST_PYR, 5, E08, E09, P1, P0, N0, + ST_TET, 4, P7, P0, P3, N0, + ST_PYR, 5, P7, E07, E08, P0, N0, + ST_TET, 4, P7, E06, E07, N0, + ST_PYR, 5, E11, E06, P7, P2, N0, + /* case 113 */ 9, + ST_PNT, 6, E07, E06, E11, E09, E00, E03, + ST_TET, 4, P2, P3, P1, N0, + ST_PYR, 5, P2, P1, E09, E11, N0, + ST_TET, 4, P7, P3, P2, N0, + ST_PYR, 5, P7, P2, E11, E06, N0, + ST_TET, 4, E07, P7, E06, N0, + ST_PYR, 5, P7, E07, E03, P3, N0, + ST_PYR, 5, E03, E00, P1, P3, N0, + ST_TET, 4, P1, E00, E09, N0, + /* case 114 */ 5, + ST_TET, 4, P7, P0, P3, P2, + ST_PYR, 5, E01, P2, P0, E00, E08, + ST_PYR, 5, E11, E06, P7, P2, E07, + ST_PYR, 5, P0, P7, E07, E08, P2, + ST_PYR, 5, E08, E07, E11, E01, P2, + /* case 115 */ 7, + ST_PNT, 5, E07, E03, E01, E06, E11, + ST_PYR, 5, P3, E03, E01, P2, N0, + ST_TET, 4, P2, E01, E11, N0, + ST_PYR, 5, P7, E07, E03, P3, N0, + ST_TET, 4, E06, E07, P7, N0, + ST_PYR, 5, E11, E06, P7, P2, N0, + ST_TET, 4, P2, P7, P3, N0, + /* case 116 */ 9, + ST_PNT, 6, E09, E01, E02, E06, E07, E08, + ST_TET, 4, P3, P7, P0, N0, + ST_PYR, 5, P3, E02, E06, P7, N0, + ST_TET, 4, P1, P3, P0, N0, + ST_PYR, 5, P1, E01, E02, P3, N0, + ST_TET, 4, E09, E01, P1, N0, + ST_PYR, 5, P1, P0, E08, E09, N0, + ST_PYR, 5, E08, P0, P7, E07, N0, + ST_TET, 4, P7, E06, E07, N0, + /* case 117 */ 2, + ST_WDG, 6, E06, P7, E07, E02, P3, E03, + ST_TET, 4, E09, P1, E00, E01, + /* case 118 */ 7, + ST_PNT, 5, E06, E02, E00, E07, E08, + ST_PYR, 5, P3, P0, E00, E02, N0, + ST_TET, 4, P0, E08, E00, N0, + ST_PYR, 5, P7, P3, E02, E06, N0, + ST_TET, 4, E07, P7, E06, N0, + ST_PYR, 5, E08, P0, P7, E07, N0, + ST_TET, 4, P0, P3, P7, N0, + /* case 119 */ 1, + ST_WDG, 6, P7, E07, E06, P3, E03, E02, + /* case 120 */ 8, + ST_PNT, 5, E09, E11, E08, E03, E02, + ST_TET, 4, P7, E06, E07, E10, + ST_PYR, 5, P2, P1, E09, E11, N0, + ST_PYR, 5, P1, P0, E08, E09, N0, + ST_TET, 4, P0, E03, E08, N0, + ST_TET, 4, P1, P2, P0, N0, + ST_PYR, 5, P0, P2, E02, E03, N0, + ST_TET, 4, P2, E11, E02, N0, + /* case 121 */ 2, + ST_WDG, 6, E11, P2, E02, E09, P1, E00, + ST_TET, 4, E07, E10, P7, E06, + /* case 122 */ 3, + ST_TET, 4, E07, E06, E10, P7, + ST_TET, 4, E08, E00, P0, E03, + ST_TET, 4, E11, P2, E01, E02, + /* case 123 */ 2, + ST_TET, 4, E06, E10, E07, P7, + ST_TET, 4, E11, E01, E02, P2, + /* case 124 */ 2, + ST_WDG, 6, E03, P0, E08, E01, P1, E09, + ST_TET, 4, E06, E07, P7, E10, + /* case 125 */ 2, + ST_TET, 4, E06, E10, E07, P7, + ST_TET, 4, E09, E00, E01, P1, + /* case 126 */ 2, + ST_TET, 4, E08, E03, E00, P0, + ST_TET, 4, E07, E06, E10, P7, + /* case 127 */ 1, + ST_TET, 4, E07, E06, E10, P7, + /* case 128 */ 9, + ST_PNT, 7, P6, P5, P4, P3, P2, P1, P0, + ST_WDG, 6, P6, P4, P3, E06, E07, E10, + ST_TET, 4, P6, P4, P3, N0, + ST_TET, 4, P6, P5, P4, N0, + ST_PYR, 5, P1, P0, P4, P5, N0, + ST_PYR, 5, P2, P1, P5, P6, N0, + ST_PYR, 5, P3, P0, P1, P2, N0, + ST_TET, 4, P4, P0, P3, N0, + ST_TET, 4, P3, P2, P6, N0, + /* case 129 */ 12, + ST_PNT, 2, E00, E06, + ST_PYR, 5, P1, P5, P6, P2, N0, + ST_TET, 4, P5, P4, P6, N0, + ST_TET, 4, P1, P4, P5, N0, + ST_TET, 4, P3, P1, P2, N0, + ST_TET, 4, P6, P3, P2, N0, + ST_PYR, 5, P6, E06, E10, P3, N0, + ST_PYR, 5, P4, E07, E06, P6, N0, + ST_TET, 4, P4, E08, E07, N0, + ST_PYR, 5, P1, E00, E08, P4, N0, + ST_PYR, 5, P1, P3, E03, E00, N0, + ST_TET, 4, P3, E10, E03, N0, + /* case 130 */ 6, + ST_WDG, 6, P4, P3, P6, E07, E10, E06, + ST_WDG, 6, E00, E01, E09, P0, P2, P5, + ST_TET, 4, P3, P6, P4, P2, + ST_TET, 4, P5, P6, P2, P4, + ST_TET, 4, P0, P4, P5, P2, + ST_TET, 4, P3, P0, P2, P4, + /* case 131 */ 11, + ST_PNT, 4, E09, E01, E06, E06, + ST_PYR, 5, P6, E06, E10, P3, N0, + ST_TET, 4, E10, E03, P3, N0, + ST_PYR, 5, P2, P3, E03, E01, N0, + ST_TET, 4, P6, P3, P2, N0, + ST_TET, 4, P4, E08, E07, N0, + ST_TET, 4, P5, P4, P6, N0, + ST_PYR, 5, P4, E07, E06, P6, N0, + ST_TET, 4, P5, P6, P2, N0, + ST_PYR, 5, P5, P2, E01, E09, N0, + ST_PYR, 5, P5, E09, E08, P4, N0, + /* case 132 */ 12, + ST_PNT, 2, E07, E01, + ST_PYR, 5, P4, P5, P1, P0, N0, + ST_TET, 4, P5, P6, P1, N0, + ST_TET, 4, P4, P6, P5, N0, + ST_TET, 4, P3, P4, P0, N0, + ST_TET, 4, P1, P3, P0, N0, + ST_PYR, 5, P1, E01, E02, P3, N0, + ST_PYR, 5, P6, E11, E01, P1, N0, + ST_TET, 4, P6, E06, E11, N0, + ST_PYR, 5, P4, E07, E06, P6, N0, + ST_PYR, 5, P4, P3, E10, E07, N0, + ST_TET, 4, P3, E02, E10, N0, + /* case 133 */ 6, + ST_TET, 4, P3, E03, E02, E10, + ST_TET, 4, P6, P5, P4, P1, + ST_PYR, 5, E00, E08, P4, P1, E07, + ST_PYR, 5, E01, P1, P6, E11, E06, + ST_PYR, 5, P4, E07, E06, P6, P1, + ST_PYR, 5, E07, E00, E01, E06, P1, + /* case 134 */ 11, + ST_PNT, 4, E09, E00, E07, E07, + ST_PYR, 5, P4, P3, E10, E07, N0, + ST_TET, 4, E10, P3, E02, N0, + ST_PYR, 5, P0, E00, E02, P3, N0, + ST_TET, 4, P4, P0, P3, N0, + ST_TET, 4, P6, E06, E11, N0, + ST_TET, 4, P5, P4, P6, N0, + ST_PYR, 5, P6, P4, E07, E06, N0, + ST_TET, 4, P5, P0, P4, N0, + ST_PYR, 5, P5, E09, E00, P0, N0, + ST_PYR, 5, P5, P6, E11, E09, N0, + /* case 135 */ 8, + ST_PNT, 5, E09, E11, E08, E07, E06, + ST_TET, 4, P3, E03, E02, E10, + ST_PYR, 5, P6, E11, E09, P5, N0, + ST_PYR, 5, P5, E09, E08, P4, N0, + ST_TET, 4, P4, E08, E07, N0, + ST_TET, 4, P5, P4, P6, N0, + ST_PYR, 5, P4, E07, E06, P6, N0, + ST_TET, 4, P6, E06, E11, N0, + /* case 136 */ 2, + ST_HEX, 8, E07, P4, P0, E03, E06, P6, P2, E02, + ST_WDG, 6, P4, P5, P6, P0, P1, P2, + /* case 137 */ 9, + ST_PNT, 5, E00, E02, E08, E07, E06, + ST_PYR, 5, P1, P5, P6, P2, N0, + ST_TET, 4, P6, P4, N0, P5, + ST_PYR, 5, P2, P6, E06, E02, N0, + ST_PYR, 5, E00, P1, P2, E02, N0, + ST_TET, 4, P4, P5, P1, N0, + ST_PYR, 5, P4, P1, E00, E08, N0, + ST_TET, 4, P4, E08, E07, N0, + ST_PYR, 5, E06, P6, P4, E07, N0, + /* case 138 */ 11, + ST_PNT, 4, E06, E07, E09, E09, + ST_PYR, 5, P5, E09, E00, P0, N0, + ST_TET, 4, E00, E03, P0, N0, + ST_PYR, 5, P4, P0, E03, E07, N0, + ST_TET, 4, P5, P0, P4, N0, + ST_TET, 4, P2, E02, E01, N0, + ST_TET, 4, P6, P2, P5, N0, + ST_PYR, 5, P2, E01, E09, P5, N0, + ST_TET, 4, P6, P5, P4, N0, + ST_PYR, 5, P6, P4, E07, E06, N0, + ST_PYR, 5, P6, E06, E02, P2, N0, + /* case 139 */ 9, + ST_PNT, 6, E08, E07, E06, E02, E01, E09, + ST_TET, 4, P6, P2, P5, N0, + ST_PYR, 5, P6, E06, E02, P2, N0, + ST_TET, 4, P4, P6, P5, N0, + ST_PYR, 5, P4, E07, E06, P6, N0, + ST_TET, 4, E08, E07, P4, N0, + ST_PYR, 5, P4, P5, E09, E08, N0, + ST_PYR, 5, E09, P5, P2, E01, N0, + ST_TET, 4, P2, E02, E01, N0, + /* case 140 */ 9, + ST_PNT, 5, E01, E03, E11, E06, E07, + ST_PYR, 5, P1, P0, P4, P5, N0, + ST_TET, 4, P4, N0, P6, P5, + ST_PYR, 5, P0, E03, E07, P4, N0, + ST_PYR, 5, E01, E03, P0, P1, N0, + ST_TET, 4, P6, P1, P5, N0, + ST_PYR, 5, P6, E11, E01, P1, N0, + ST_TET, 4, P6, E06, E11, N0, + ST_PYR, 5, E07, E06, P6, P4, N0, + /* case 141 */ 5, + ST_TET, 4, P6, P5, P4, P1, + ST_PYR, 5, E00, E08, P4, P1, E07, + ST_PYR, 5, E01, P1, P6, E11, E06, + ST_PYR, 5, P4, E07, E06, P6, P1, + ST_PYR, 5, E07, E00, E01, E06, P1, + /* case 142 */ 9, + ST_PNT, 6, E03, E00, E09, E11, E06, E07, + ST_TET, 4, P5, P4, P6, N0, + ST_PYR, 5, P5, P6, E11, E09, N0, + ST_TET, 4, P0, P4, P5, N0, + ST_PYR, 5, P0, P5, E09, E00, N0, + ST_TET, 4, E03, P0, E00, N0, + ST_PYR, 5, P0, E03, E07, P4, N0, + ST_PYR, 5, E07, E06, P6, P4, N0, + ST_TET, 4, P6, E06, E11, N0, + /* case 143 */ 7, + ST_PNT, 5, E11, E09, E08, E06, E07, + ST_PYR, 5, P5, E09, E08, P4, N0, + ST_TET, 4, P4, E08, E07, N0, + ST_PYR, 5, P6, E11, E09, P5, N0, + ST_TET, 4, E06, E11, P6, N0, + ST_PYR, 5, E07, E06, P6, P4, N0, + ST_TET, 4, P4, P6, P5, N0, + /* case 144 */ 2, + ST_HEX, 8, E06, P6, P5, E04, E10, P3, P0, E08, + ST_WDG, 6, P0, P1, P5, P3, P2, P6, + /* case 145 */ 9, + ST_PNT, 5, E00, E04, E03, E10, E06, + ST_PYR, 5, P1, P5, P6, P2, N0, + ST_TET, 4, P6, N0, P3, P2, + ST_PYR, 5, P5, E04, E06, P6, N0, + ST_PYR, 5, E00, E04, P5, P1, N0, + ST_TET, 4, P3, P1, P2, N0, + ST_PYR, 5, P3, E03, E00, P1, N0, + ST_TET, 4, P3, E10, E03, N0, + ST_PYR, 5, E06, E10, P3, P6, N0, + /* case 146 */ 11, + ST_PNT, 4, E06, E10, E01, E01, + ST_PYR, 5, P2, P0, E00, E01, N0, + ST_TET, 4, E00, P0, E08, N0, + ST_PYR, 5, P3, E10, E08, P0, N0, + ST_TET, 4, P2, P3, P0, N0, + ST_TET, 4, P5, E09, E04, N0, + ST_TET, 4, P6, P2, P5, N0, + ST_PYR, 5, P5, P2, E01, E09, N0, + ST_TET, 4, P6, P3, P2, N0, + ST_PYR, 5, P6, E06, E10, P3, N0, + ST_PYR, 5, P6, P5, E04, E06, N0, + /* case 147 */ 9, + ST_PNT, 6, E03, E10, E06, E04, E09, E01, + ST_TET, 4, P6, P2, P5, N0, + ST_PYR, 5, P6, P5, E04, E06, N0, + ST_TET, 4, P3, P2, P6, N0, + ST_PYR, 5, P3, P6, E06, E10, N0, + ST_TET, 4, E03, P3, E10, N0, + ST_PYR, 5, P3, E03, E01, P2, N0, + ST_PYR, 5, E01, E09, P5, P2, N0, + ST_TET, 4, P5, E09, E04, N0, + /* case 148 */ 11, + ST_PNT, 4, E04, E08, E01, E01, + ST_PYR, 5, P1, E01, E02, P3, N0, + ST_TET, 4, E02, E10, P3, N0, + ST_PYR, 5, P0, P3, E10, E08, N0, + ST_TET, 4, P1, P3, P0, N0, + ST_TET, 4, P6, E06, E11, N0, + ST_TET, 4, P5, P6, P1, N0, + ST_PYR, 5, P6, E11, E01, P1, N0, + ST_TET, 4, P5, P1, P0, N0, + ST_PYR, 5, P5, P0, E08, E04, N0, + ST_PYR, 5, P5, E04, E06, P6, N0, + /* case 149 */ 8, + ST_PNT, 5, E04, E06, E00, E01, E11, + ST_TET, 4, P3, E10, E03, E02, + ST_PYR, 5, P6, P5, E04, E06, N0, + ST_PYR, 5, P5, P1, E00, E04, N0, + ST_TET, 4, P1, E01, E00, N0, + ST_TET, 4, P5, P6, P1, N0, + ST_PYR, 5, P1, P6, E11, E01, N0, + ST_TET, 4, P6, E06, E11, N0, + /* case 150 */ 2, + ST_WDG, 6, P3, E10, E02, P0, E08, E00, + ST_WDG, 6, P6, E11, E06, P5, E09, E04, + /* case 151 */ 2, + ST_WDG, 6, E09, P5, E04, E11, P6, E06, + ST_TET, 4, E03, P3, E10, E02, + /* case 152 */ 9, + ST_PNT, 5, E02, E06, E03, E08, E04, + ST_PYR, 5, P2, P1, P5, P6, N0, + ST_TET, 4, P5, P0, N0, P1, + ST_PYR, 5, P6, P5, E04, E06, N0, + ST_PYR, 5, E02, P2, P6, E06, N0, + ST_TET, 4, P0, P1, P2, N0, + ST_PYR, 5, P0, P2, E02, E03, N0, + ST_TET, 4, P0, E03, E08, N0, + ST_PYR, 5, E04, P5, P0, E08, N0, + /* case 153 */ 1, + ST_HEX, 8, P1, P5, P6, P2, E00, E04, E06, E02, + /* case 154 */ 8, + ST_PNT, 5, E06, E04, E02, E01, E09, + ST_TET, 4, P0, E03, E08, E00, + ST_PYR, 5, P5, E04, E06, P6, N0, + ST_PYR, 5, P6, E06, E02, P2, N0, + ST_TET, 4, P2, E02, E01, N0, + ST_TET, 4, P6, P2, P5, N0, + ST_PYR, 5, P2, E01, E09, P5, N0, + ST_TET, 4, P5, E09, E04, N0, + /* case 155 */ 7, + ST_PNT, 5, E04, E06, E02, E09, E01, + ST_PYR, 5, P6, E06, E02, P2, N0, + ST_TET, 4, P2, E02, E01, N0, + ST_PYR, 5, P5, E04, E06, P6, N0, + ST_TET, 4, E09, E04, P5, N0, + ST_PYR, 5, E01, E09, P5, P2, N0, + ST_TET, 4, P2, P5, P6, N0, + /* case 156 */ 9, + ST_PNT, 6, E03, E08, E04, E06, E11, E01, + ST_TET, 4, P5, P6, P1, N0, + ST_PYR, 5, P5, E04, E06, P6, N0, + ST_TET, 4, P0, P5, P1, N0, + ST_PYR, 5, P0, E08, E04, P5, N0, + ST_TET, 4, E03, E08, P0, N0, + ST_PYR, 5, P0, P1, E01, E03, N0, + ST_PYR, 5, E01, P1, P6, E11, N0, + ST_TET, 4, P6, E06, E11, N0, + /* case 157 */ 7, + ST_PNT, 5, E06, E04, E00, E11, E01, + ST_PYR, 5, P5, P1, E00, E04, N0, + ST_TET, 4, P1, E01, E00, N0, + ST_PYR, 5, P6, P5, E04, E06, N0, + ST_TET, 4, E11, P6, E06, N0, + ST_PYR, 5, E01, P1, P6, E11, N0, + ST_TET, 4, P1, P5, P6, N0, + /* case 158 */ 2, + ST_WDG, 6, E09, P5, E04, E11, P6, E06, + ST_TET, 4, E03, E08, P0, E00, + /* case 159 */ 1, + ST_WDG, 6, P5, E04, E09, P6, E06, E11, + /* case 160 */ 12, + ST_PNT, 2, E10, E09, + ST_PYR, 5, P3, P0, P1, P2, N0, + ST_TET, 4, P2, P1, P6, N0, + ST_TET, 4, P3, P2, P6, N0, + ST_TET, 4, P4, P0, P3, N0, + ST_TET, 4, P1, P0, P4, N0, + ST_PYR, 5, P1, P4, E04, E09, N0, + ST_PYR, 5, P6, P1, E09, E05, N0, + ST_TET, 4, P6, E05, E06, N0, + ST_PYR, 5, P3, P6, E06, E10, N0, + ST_PYR, 5, P3, E10, E07, P4, N0, + ST_TET, 4, P4, E07, E04, N0, + /* case 161 */ 6, + ST_TET, 4, P4, E04, E08, E07, + ST_TET, 4, P6, P3, P2, P1, + ST_PYR, 5, E00, P1, P3, E03, E10, + ST_PYR, 5, E09, E05, P6, P1, E06, + ST_PYR, 5, P3, P6, E06, E10, P1, + ST_PYR, 5, E10, E06, E09, E00, P1, + /* case 162 */ 11, + ST_PNT, 4, E01, E00, E10, E10, + ST_PYR, 5, P3, E10, E07, P4, N0, + ST_TET, 4, E07, E04, P4, N0, + ST_PYR, 5, P0, P4, E04, E00, N0, + ST_TET, 4, P3, P4, P0, N0, + ST_TET, 4, P6, E05, E06, N0, + ST_TET, 4, P2, P6, P3, N0, + ST_PYR, 5, P6, E06, E10, P3, N0, + ST_TET, 4, P2, P3, P0, N0, + ST_PYR, 5, P2, P0, E00, E01, N0, + ST_PYR, 5, P2, E01, E05, P6, N0, + /* case 163 */ 8, + ST_PNT, 5, E01, E05, E03, E10, E06, + ST_TET, 4, P4, E04, E08, E07, + ST_PYR, 5, P6, P2, E01, E05, N0, + ST_PYR, 5, P2, P3, E03, E01, N0, + ST_TET, 4, P3, E10, E03, N0, + ST_TET, 4, P2, P6, P3, N0, + ST_PYR, 5, P3, P6, E06, E10, N0, + ST_TET, 4, P6, E05, E06, N0, + /* case 164 */ 6, + ST_TET, 4, P6, E05, E06, E11, + ST_TET, 4, P3, P0, P1, P4, + ST_PYR, 5, E04, E09, P1, P4, E01, + ST_PYR, 5, E07, P4, P3, E10, E02, + ST_PYR, 5, P1, E01, E02, P3, P4, + ST_PYR, 5, E01, E04, E07, E02, P4, + /* case 165 */ 4, + ST_TET, 4, E07, E08, E04, P4, + ST_TET, 4, E10, E02, E03, P3, + ST_TET, 4, E06, E05, E11, P6, + ST_TET, 4, E01, E09, E00, P1, + /* case 166 */ 8, + ST_PNT, 5, E00, E02, E04, E07, E10, + ST_TET, 4, P6, E11, E05, E06, + ST_PYR, 5, P3, P0, E00, E02, N0, + ST_PYR, 5, P0, P4, E04, E00, N0, + ST_TET, 4, P4, E07, E04, N0, + ST_TET, 4, P0, P3, P4, N0, + ST_PYR, 5, P4, P3, E10, E07, N0, + ST_TET, 4, P3, E02, E10, N0, + /* case 167 */ 3, + ST_TET, 4, E11, E06, E05, P6, + ST_TET, 4, E02, P3, E03, E10, + ST_TET, 4, E04, E08, P4, E07, + /* case 168 */ 11, + ST_PNT, 4, E02, E03, E09, E09, + ST_PYR, 5, P1, P4, E04, E09, N0, + ST_TET, 4, E04, P4, E07, N0, + ST_PYR, 5, P0, E03, E07, P4, N0, + ST_TET, 4, P1, P0, P4, N0, + ST_TET, 4, P6, E05, E06, N0, + ST_TET, 4, P2, P1, P6, N0, + ST_PYR, 5, P6, P1, E09, E05, N0, + ST_TET, 4, P2, P0, P1, N0, + ST_PYR, 5, P2, E02, E03, P0, N0, + ST_PYR, 5, P2, P6, E06, E02, N0, + /* case 169 */ 8, + ST_PNT, 5, E02, E06, E00, E09, E05, + ST_TET, 4, P4, E08, E07, E04, + ST_PYR, 5, P6, E06, E02, P2, N0, + ST_PYR, 5, P2, E02, E00, P1, N0, + ST_TET, 4, P1, E00, E09, N0, + ST_TET, 4, P2, P1, P6, N0, + ST_PYR, 5, P1, E09, E05, P6, N0, + ST_TET, 4, P6, E05, E06, N0, + /* case 170 */ 2, + ST_WDG, 6, P0, E03, E00, P4, E07, E04, + ST_WDG, 6, P2, E01, E02, P6, E05, E06, + /* case 171 */ 2, + ST_WDG, 6, E05, P6, E06, E01, P2, E02, + ST_TET, 4, E08, E07, P4, E04, + /* case 172 */ 8, + ST_PNT, 5, E03, E07, E01, E09, E04, + ST_TET, 4, P6, E06, E11, E05, + ST_PYR, 5, P4, P0, E03, E07, N0, + ST_PYR, 5, P0, P1, E01, E03, N0, + ST_TET, 4, P1, E09, E01, N0, + ST_TET, 4, P0, P4, P1, N0, + ST_PYR, 5, P1, P4, E04, E09, N0, + ST_TET, 4, P4, E07, E04, N0, + /* case 173 */ 3, + ST_TET, 4, E06, E05, E11, P6, + ST_TET, 4, E07, P4, E08, E04, + ST_TET, 4, E01, E00, P1, E09, + /* case 174 */ 2, + ST_WDG, 6, E07, P4, E04, E03, P0, E00, + ST_TET, 4, E11, E05, P6, E06, + /* case 175 */ 2, + ST_TET, 4, E04, E07, E08, P4, + ST_TET, 4, E05, E11, E06, P6, + /* case 176 */ 9, + ST_PNT, 5, E09, E08, E05, E06, E10, + ST_PYR, 5, P1, P2, P3, P0, N0, + ST_TET, 4, P3, P6, N0, P2, + ST_PYR, 5, P0, P3, E10, E08, N0, + ST_PYR, 5, E09, P1, P0, E08, N0, + ST_TET, 4, P6, P2, P1, N0, + ST_PYR, 5, P6, P1, E09, E05, N0, + ST_TET, 4, P6, E05, E06, N0, + ST_PYR, 5, E10, P3, P6, E06, N0, + /* case 177 */ 5, + ST_TET, 4, P6, P3, P2, P1, + ST_PYR, 5, E00, P1, P3, E03, E10, + ST_PYR, 5, E09, E05, P6, P1, E06, + ST_PYR, 5, P3, P6, E06, E10, P1, + ST_PYR, 5, E10, E06, E09, E00, P1, + /* case 178 */ 9, + ST_PNT, 6, E08, E00, E01, E05, E06, E10, + ST_TET, 4, P2, P6, P3, N0, + ST_PYR, 5, P2, E01, E05, P6, N0, + ST_TET, 4, P0, P2, P3, N0, + ST_PYR, 5, P0, E00, E01, P2, N0, + ST_TET, 4, E08, E00, P0, N0, + ST_PYR, 5, P0, P3, E10, E08, N0, + ST_PYR, 5, E10, P3, P6, E06, N0, + ST_TET, 4, P6, E05, E06, N0, + /* case 179 */ 7, + ST_PNT, 5, E05, E01, E03, E06, E10, + ST_PYR, 5, P2, P3, E03, E01, N0, + ST_TET, 4, P3, E10, E03, N0, + ST_PYR, 5, P6, P2, E01, E05, N0, + ST_TET, 4, E06, P6, E05, N0, + ST_PYR, 5, E10, P3, P6, E06, N0, + ST_TET, 4, P3, P2, P6, N0, + /* case 180 */ 8, + ST_PNT, 5, E08, E10, E09, E01, E02, + ST_TET, 4, P6, E05, E06, E11, + ST_PYR, 5, P3, E10, E08, P0, N0, + ST_PYR, 5, P0, E08, E09, P1, N0, + ST_TET, 4, P1, E09, E01, N0, + ST_TET, 4, P0, P1, P3, N0, + ST_PYR, 5, P1, E01, E02, P3, N0, + ST_TET, 4, P3, E02, E10, N0, + /* case 181 */ 3, + ST_TET, 4, E06, E05, E11, P6, + ST_TET, 4, E10, E03, P3, E02, + ST_TET, 4, E09, P1, E00, E01, + /* case 182 */ 2, + ST_WDG, 6, E08, P0, E00, E10, P3, E02, + ST_TET, 4, E05, P6, E11, E06, + /* case 183 */ 2, + ST_TET, 4, E02, E03, E10, P3, + ST_TET, 4, E11, E06, E05, P6, + /* case 184 */ 9, + ST_PNT, 6, E08, E03, E02, E06, E05, E09, + ST_TET, 4, P2, P1, P6, N0, + ST_PYR, 5, P2, P6, E06, E02, N0, + ST_TET, 4, P0, P1, P2, N0, + ST_PYR, 5, P0, P2, E02, E03, N0, + ST_TET, 4, E08, P0, E03, N0, + ST_PYR, 5, P0, E08, E09, P1, N0, + ST_PYR, 5, E09, E05, P6, P1, N0, + ST_TET, 4, P6, E05, E06, N0, + /* case 185 */ 7, + ST_PNT, 5, E06, E02, E00, E05, E09, + ST_PYR, 5, P2, E02, E00, P1, N0, + ST_TET, 4, P1, E00, E09, N0, + ST_PYR, 5, P6, E06, E02, P2, N0, + ST_TET, 4, E05, E06, P6, N0, + ST_PYR, 5, E09, E05, P6, P1, N0, + ST_TET, 4, P1, P6, P2, N0, + /* case 186 */ 2, + ST_WDG, 6, E05, P6, E06, E01, P2, E02, + ST_TET, 4, E08, P0, E03, E00, + /* case 187 */ 1, + ST_WDG, 6, P6, E06, E05, P2, E02, E01, + /* case 188 */ 2, + ST_WDG, 6, E03, P0, E08, E01, P1, E09, + ST_TET, 4, E06, P6, E05, E11, + /* case 189 */ 2, + ST_TET, 4, E09, E00, E01, P1, + ST_TET, 4, E05, E11, E06, P6, + /* case 190 */ 2, + ST_TET, 4, E00, E08, E03, P0, + ST_TET, 4, E11, E06, E05, P6, + /* case 191 */ 1, + ST_TET, 4, E05, E11, E06, P6, + /* case 192 */ 2, + ST_HEX, 8, E05, P5, P4, E07, E11, P2, P3, E10, + ST_WDG, 6, P5, P1, P2, P4, P0, P3, + /* case 193 */ 11, + ST_PNT, 4, E11, E05, E00, E00, + ST_PYR, 5, P1, E00, E08, P4, N0, + ST_TET, 4, E08, E07, P4, N0, + ST_PYR, 5, P5, P4, E07, E05, N0, + ST_TET, 4, P1, P4, P5, N0, + ST_TET, 4, P3, E10, E03, N0, + ST_TET, 4, P2, P3, P1, N0, + ST_PYR, 5, P3, E03, E00, P1, N0, + ST_TET, 4, P2, P1, P5, N0, + ST_PYR, 5, P2, P5, E05, E11, N0, + ST_PYR, 5, P2, E11, E10, P3, N0, + /* case 194 */ 11, + ST_PNT, 4, E10, E07, E00, E00, + ST_PYR, 5, P0, P5, E09, E00, N0, + ST_TET, 4, E09, P5, E05, N0, + ST_PYR, 5, P4, E07, E05, P5, N0, + ST_TET, 4, P0, P4, P5, N0, + ST_TET, 4, P2, E01, E11, N0, + ST_TET, 4, P3, P0, P2, N0, + ST_PYR, 5, P2, P0, E00, E01, N0, + ST_TET, 4, P3, P4, P0, N0, + ST_PYR, 5, P3, E10, E07, P4, N0, + ST_PYR, 5, P3, P2, E11, E10, N0, + /* case 195 */ 2, + ST_WDG, 6, P3, E03, E10, P2, E01, E11, + ST_WDG, 6, P4, E07, E08, P5, E05, E09, + /* case 196 */ 9, + ST_PNT, 5, E07, E05, E10, E02, E01, + ST_PYR, 5, P4, P5, P1, P0, N0, + ST_TET, 4, P1, N0, P3, P0, + ST_PYR, 5, P5, E05, E01, P1, N0, + ST_PYR, 5, E07, E05, P5, P4, N0, + ST_TET, 4, P3, P4, P0, N0, + ST_PYR, 5, P3, E10, E07, P4, N0, + ST_TET, 4, P3, E02, E10, N0, + ST_PYR, 5, E01, E02, P3, P1, N0, + /* case 197 */ 8, + ST_PNT, 5, E05, E01, E07, E08, E00, + ST_TET, 4, P3, E02, E10, E03, + ST_PYR, 5, P1, P5, E05, E01, N0, + ST_PYR, 5, P5, P4, E07, E05, N0, + ST_TET, 4, P4, E08, E07, N0, + ST_TET, 4, P5, P1, P4, N0, + ST_PYR, 5, P4, P1, E00, E08, N0, + ST_TET, 4, P1, E01, E00, N0, + /* case 198 */ 9, + ST_PNT, 6, E02, E10, E07, E05, E09, E00, + ST_TET, 4, P4, P5, P0, N0, + ST_PYR, 5, P4, E07, E05, P5, N0, + ST_TET, 4, P3, P4, P0, N0, + ST_PYR, 5, P3, E10, E07, P4, N0, + ST_TET, 4, E02, E10, P3, N0, + ST_PYR, 5, P3, P0, E00, E02, N0, + ST_PYR, 5, E00, P0, P5, E09, N0, + ST_TET, 4, P5, E05, E09, N0, + /* case 199 */ 2, + ST_WDG, 6, E05, P5, E09, E07, P4, E08, + ST_TET, 4, E02, P3, E03, E10, + /* case 200 */ 9, + ST_PNT, 5, E03, E07, E02, E11, E05, + ST_PYR, 5, P0, P4, P5, P1, N0, + ST_TET, 4, P5, N0, P2, P1, + ST_PYR, 5, P4, E07, E05, P5, N0, + ST_PYR, 5, E03, E07, P4, P0, N0, + ST_TET, 4, P2, P0, P1, N0, + ST_PYR, 5, P2, E02, E03, P0, N0, + ST_TET, 4, P2, E11, E02, N0, + ST_PYR, 5, E05, E11, P2, P5, N0, + /* case 201 */ 9, + ST_PNT, 6, E02, E11, E05, E07, E08, E00, + ST_TET, 4, P5, P1, P4, N0, + ST_PYR, 5, P5, P4, E07, E05, N0, + ST_TET, 4, P2, P1, P5, N0, + ST_PYR, 5, P2, P5, E05, E11, N0, + ST_TET, 4, E02, P2, E11, N0, + ST_PYR, 5, P2, E02, E00, P1, N0, + ST_PYR, 5, E00, E08, P4, P1, N0, + ST_TET, 4, P4, E08, E07, N0, + /* case 202 */ 8, + ST_PNT, 5, E07, E05, E03, E00, E09, + ST_TET, 4, P2, E11, E02, E01, + ST_PYR, 5, P5, P4, E07, E05, N0, + ST_PYR, 5, P4, P0, E03, E07, N0, + ST_TET, 4, P0, E00, E03, N0, + ST_TET, 4, P4, P5, P0, N0, + ST_PYR, 5, P0, P5, E09, E00, N0, + ST_TET, 4, P5, E05, E09, N0, + /* case 203 */ 2, + ST_WDG, 6, E05, P5, E09, E07, P4, E08, + ST_TET, 4, E02, E01, P2, E11, + /* case 204 */ 1, + ST_HEX, 8, E03, E01, E05, E07, P0, P1, P5, P4, + /* case 205 */ 7, + ST_PNT, 5, E01, E05, E07, E00, E08, + ST_PYR, 5, P5, P4, E07, E05, N0, + ST_TET, 4, P4, E08, E07, N0, + ST_PYR, 5, P1, P5, E05, E01, N0, + ST_TET, 4, E00, P1, E01, N0, + ST_PYR, 5, E08, P4, P1, E00, N0, + ST_TET, 4, P4, P5, P1, N0, + /* case 206 */ 7, + ST_PNT, 5, E05, E07, E03, E09, E00, + ST_PYR, 5, P4, P0, E03, E07, N0, + ST_TET, 4, P0, E00, E03, N0, + ST_PYR, 5, P5, P4, E07, E05, N0, + ST_TET, 4, E09, P5, E05, N0, + ST_PYR, 5, E00, P0, P5, E09, N0, + ST_TET, 4, P0, P4, P5, N0, + /* case 207 */ 1, + ST_WDG, 6, P5, E09, E05, P4, E08, E07, + /* case 208 */ 9, + ST_PNT, 5, E08, E10, E04, E05, E11, + ST_PYR, 5, P0, P1, P2, P3, N0, + ST_TET, 4, P2, P5, N0, P1, + ST_PYR, 5, P3, P2, E11, E10, N0, + ST_PYR, 5, E08, P0, P3, E10, N0, + ST_TET, 4, P5, P1, P0, N0, + ST_PYR, 5, P5, P0, E08, E04, N0, + ST_TET, 4, P5, E04, E05, N0, + ST_PYR, 5, E11, P2, P5, E05, N0, + /* case 209 */ 9, + ST_PNT, 6, E04, E05, E11, E10, E03, E00, + ST_TET, 4, P2, P3, P1, N0, + ST_PYR, 5, P2, E11, E10, P3, N0, + ST_TET, 4, P5, P2, P1, N0, + ST_PYR, 5, P5, E05, E11, P2, N0, + ST_TET, 4, E04, E05, P5, N0, + ST_PYR, 5, P5, P1, E00, E04, N0, + ST_PYR, 5, E00, P1, P3, E03, N0, + ST_TET, 4, P3, E10, E03, N0, + /* case 210 */ 8, + ST_PNT, 5, E10, E11, E08, E00, E01, + ST_TET, 4, P5, E04, E05, E09, + ST_PYR, 5, P2, E11, E10, P3, N0, + ST_PYR, 5, P3, E10, E08, P0, N0, + ST_TET, 4, P0, E08, E00, N0, + ST_TET, 4, P3, P0, P2, N0, + ST_PYR, 5, P0, E00, E01, P2, N0, + ST_TET, 4, P2, E01, E11, N0, + /* case 211 */ 2, + ST_WDG, 6, E10, P3, E03, E11, P2, E01, + ST_TET, 4, E04, P5, E09, E05, + /* case 212 */ 9, + ST_PNT, 6, E10, E02, E01, E05, E04, E08, + ST_TET, 4, P1, P0, P5, N0, + ST_PYR, 5, P1, P5, E05, E01, N0, + ST_TET, 4, P3, P0, P1, N0, + ST_PYR, 5, P3, P1, E01, E02, N0, + ST_TET, 4, E10, P3, E02, N0, + ST_PYR, 5, P3, E10, E08, P0, N0, + ST_PYR, 5, E08, E04, P5, P0, N0, + ST_TET, 4, P5, E04, E05, N0, + /* case 213 */ 2, + ST_WDG, 6, E01, P1, E00, E05, P5, E04, + ST_TET, 4, E10, E03, P3, E02, + /* case 214 */ 2, + ST_WDG, 6, E08, P0, E00, E10, P3, E02, + ST_TET, 4, E05, E09, P5, E04, + /* case 215 */ 2, + ST_TET, 4, E05, E04, E09, P5, + ST_TET, 4, E02, E03, E10, P3, + /* case 216 */ 5, + ST_TET, 4, P2, P0, P1, P5, + ST_PYR, 5, E04, P5, P0, E08, E03, + ST_PYR, 5, E05, E11, P2, P5, E02, + ST_PYR, 5, P0, P2, E02, E03, P5, + ST_PYR, 5, E03, E02, E05, E04, P5, + /* case 217 */ 7, + ST_PNT, 5, E02, E00, E04, E11, E05, + ST_PYR, 5, P1, E00, E04, P5, N0, + ST_TET, 4, P5, E04, E05, N0, + ST_PYR, 5, P2, E02, E00, P1, N0, + ST_TET, 4, E11, E02, P2, N0, + ST_PYR, 5, E05, E11, P2, P5, N0, + ST_TET, 4, P5, P2, P1, N0, + /* case 218 */ 3, + ST_TET, 4, E02, E11, E01, P2, + ST_TET, 4, E03, E08, P0, E00, + ST_TET, 4, E05, P5, E04, E09, + /* case 219 */ 2, + ST_TET, 4, E11, E01, E02, P2, + ST_TET, 4, E05, E04, E09, P5, + /* case 220 */ 7, + ST_PNT, 5, E05, E01, E03, E04, E08, + ST_PYR, 5, P1, E01, E03, P0, N0, + ST_TET, 4, P0, E03, E08, N0, + ST_PYR, 5, P5, E05, E01, P1, N0, + ST_TET, 4, E04, E05, P5, N0, + ST_PYR, 5, E08, E04, P5, P0, N0, + ST_TET, 4, P0, P5, P1, N0, + /* case 221 */ 1, + ST_WDG, 6, P1, E00, E01, P5, E04, E05, + /* case 222 */ 2, + ST_TET, 4, E00, E08, E03, P0, + ST_TET, 4, E09, E05, E04, P5, + /* case 223 */ 1, + ST_TET, 4, E09, E05, E04, P5, + /* case 224 */ 9, + ST_PNT, 5, E10, E11, E07, E04, E09, + ST_PYR, 5, P3, P0, P1, P2, N0, + ST_TET, 4, P1, P4, N0, P0, + ST_PYR, 5, P2, P1, E09, E11, N0, + ST_PYR, 5, E10, P3, P2, E11, N0, + ST_TET, 4, P4, P0, P3, N0, + ST_PYR, 5, P4, P3, E10, E07, N0, + ST_TET, 4, P4, E07, E04, N0, + ST_PYR, 5, E09, P1, P4, E04, N0, + /* case 225 */ 8, + ST_PNT, 5, E11, E09, E10, E03, E00, + ST_TET, 4, P4, E07, E04, E08, + ST_PYR, 5, P1, E09, E11, P2, N0, + ST_PYR, 5, P2, E11, E10, P3, N0, + ST_TET, 4, P3, E10, E03, N0, + ST_TET, 4, P2, P3, P1, N0, + ST_PYR, 5, P3, E03, E00, P1, N0, + ST_TET, 4, P1, E00, E09, N0, + /* case 226 */ 9, + ST_PNT, 6, E04, E07, E10, E11, E01, E00, + ST_TET, 4, P3, P0, P2, N0, + ST_PYR, 5, P3, P2, E11, E10, N0, + ST_TET, 4, P4, P0, P3, N0, + ST_PYR, 5, P4, P3, E10, E07, N0, + ST_TET, 4, E04, P4, E07, N0, + ST_PYR, 5, P4, E04, E00, P0, N0, + ST_PYR, 5, E00, E01, P2, P0, N0, + ST_TET, 4, P2, E01, E11, N0, + /* case 227 */ 2, + ST_WDG, 6, E10, P3, E03, E11, P2, E01, + ST_TET, 4, E04, E08, P4, E07, + /* case 228 */ 5, + ST_TET, 4, P3, P0, P1, P4, + ST_PYR, 5, E04, E09, P1, P4, E01, + ST_PYR, 5, E07, P4, P3, E10, E02, + ST_PYR, 5, P1, E01, E02, P3, P4, + ST_PYR, 5, E01, E04, E07, E02, P4, + /* case 229 */ 3, + ST_TET, 4, E02, E03, E10, P3, + ST_TET, 4, E01, P1, E09, E00, + ST_TET, 4, E07, E04, P4, E08, + /* case 230 */ 7, + ST_PNT, 5, E02, E00, E04, E10, E07, + ST_PYR, 5, P0, P4, E04, E00, N0, + ST_TET, 4, P4, E07, E04, N0, + ST_PYR, 5, P3, P0, E00, E02, N0, + ST_TET, 4, E10, P3, E02, N0, + ST_PYR, 5, E07, P4, P3, E10, N0, + ST_TET, 4, P4, P0, P3, N0, + /* case 231 */ 2, + ST_TET, 4, E10, E02, E03, P3, + ST_TET, 4, E07, E08, E04, P4, + /* case 232 */ 9, + ST_PNT, 6, E07, E04, E09, E11, E02, E03, + ST_TET, 4, P1, P2, P0, N0, + ST_PYR, 5, P1, E09, E11, P2, N0, + ST_TET, 4, P4, P1, P0, N0, + ST_PYR, 5, P4, E04, E09, P1, N0, + ST_TET, 4, E07, E04, P4, N0, + ST_PYR, 5, P4, P0, E03, E07, N0, + ST_PYR, 5, E03, P0, P2, E02, N0, + ST_TET, 4, P2, E11, E02, N0, + /* case 233 */ 2, + ST_WDG, 6, E11, P2, E02, E09, P1, E00, + ST_TET, 4, E07, P4, E08, E04, + /* case 234 */ 2, + ST_WDG, 6, E07, P4, E04, E03, P0, E00, + ST_TET, 4, E11, P2, E01, E02, + /* case 235 */ 2, + ST_TET, 4, E11, E01, E02, P2, + ST_TET, 4, E04, E07, E08, P4, + /* case 236 */ 7, + ST_PNT, 5, E07, E03, E01, E04, E09, + ST_PYR, 5, P0, P1, E01, E03, N0, + ST_TET, 4, P1, E09, E01, N0, + ST_PYR, 5, P4, P0, E03, E07, N0, + ST_TET, 4, E04, P4, E07, N0, + ST_PYR, 5, E09, P1, P4, E04, N0, + ST_TET, 4, P1, P0, P4, N0, + /* case 237 */ 2, + ST_TET, 4, E04, E07, E08, P4, + ST_TET, 4, E09, E00, E01, P1, + /* case 238 */ 1, + ST_WDG, 6, P4, E04, E07, P0, E00, E03, + /* case 239 */ 1, + ST_TET, 4, E07, E08, E04, P4, + /* case 240 */ 1, + ST_HEX, 8, P0, P1, P2, P3, E08, E09, E11, E10, + /* case 241 */ 7, + ST_PNT, 5, E09, E11, E10, E00, E03, + ST_PYR, 5, P2, E11, E10, P3, N0, + ST_TET, 4, P3, E10, E03, N0, + ST_PYR, 5, P1, E09, E11, P2, N0, + ST_TET, 4, E00, E09, P1, N0, + ST_PYR, 5, E03, E00, P1, P3, N0, + ST_TET, 4, P3, P1, P2, N0, + /* case 242 */ 7, + ST_PNT, 5, E11, E10, E08, E01, E00, + ST_PYR, 5, P3, E10, E08, P0, N0, + ST_TET, 4, P0, E08, E00, N0, + ST_PYR, 5, P2, E11, E10, P3, N0, + ST_TET, 4, E01, E11, P2, N0, + ST_PYR, 5, E00, E01, P2, P0, N0, + ST_TET, 4, P0, P2, P3, N0, + /* case 243 */ 1, + ST_WDG, 6, P3, E03, E10, P2, E01, E11, + /* case 244 */ 7, + ST_PNT, 5, E10, E08, E09, E02, E01, + ST_PYR, 5, P0, E08, E09, P1, N0, + ST_TET, 4, P1, E09, E01, N0, + ST_PYR, 5, P3, E10, E08, P0, N0, + ST_TET, 4, E02, E10, P3, N0, + ST_PYR, 5, E01, E02, P3, P1, N0, + ST_TET, 4, P1, P3, P0, N0, + /* case 245 */ 2, + ST_TET, 4, E02, E03, E10, P3, + ST_TET, 4, E01, E09, E00, P1, + /* case 246 */ 1, + ST_WDG, 6, P0, E00, E08, P3, E02, E10, + /* case 247 */ 1, + ST_TET, 4, E10, E02, E03, P3, + /* case 248 */ 7, + ST_PNT, 5, E11, E09, E08, E02, E03, + ST_PYR, 5, P1, P0, E08, E09, N0, + ST_TET, 4, P0, E03, E08, N0, + ST_PYR, 5, P2, P1, E09, E11, N0, + ST_TET, 4, E02, P2, E11, N0, + ST_PYR, 5, E03, P0, P2, E02, N0, + ST_TET, 4, P0, P1, P2, N0, + /* case 249 */ 1, + ST_WDG, 6, P2, E02, E11, P1, E00, E09, + /* case 250 */ 2, + ST_TET, 4, E00, E08, E03, P0, + ST_TET, 4, E01, E02, E11, P2, + /* case 251 */ 1, + ST_TET, 4, E01, E02, E11, P2, + /* case 252 */ 1, + ST_WDG, 6, P0, E08, E03, P1, E09, E01, + /* case 253 */ 1, + ST_TET, 4, E01, E09, E00, P1, + /* case 254 */ 1, + ST_TET, 4, E03, E00, E08, P0, + /* case 255 */ 0, + // vtkm::CELL_SHAPE_WEDGE + /* case 0 */ 1, + ST_WDG, 6, P0, P1, P2, P3, P4, P5, + /* case 1 */ 2, + ST_WDG, 6, E00, E02, E06, P1, P2, P3, + ST_PYR, 5, P1, P2, P5, P4, P3, + /* case 2 */ 2, + ST_WDG, 6, E01, E00, E07, P2, P0, P4, + ST_PYR, 5, P2, P0, P3, P5, P4, + /* case 3 */ 8, + ST_PNT, 7, P2, P3, P4, E01, E02, E06, E07, + ST_TET, 4, P4, P5, P3, P2, + ST_TET, 4, P2, P3, P4, N0, + ST_PYR, 5, E06, E07, P4, P3, N0, + ST_PYR, 5, E01, E07, E06, E02, N0, + ST_TET, 4, P2, E01, E02, N0, + ST_PYR, 5, P2, E02, E06, P3, N0, + ST_PYR, 5, E07, E01, P2, P4, N0, + /* case 4 */ 2, + ST_WDG, 6, E02, E01, E08, P0, P1, P5, + ST_PYR, 5, P0, P1, P4, P3, P5, + /* case 5 */ 8, + ST_PNT, 7, P1, P5, P3, E00, E01, E08, E06, + ST_TET, 4, P3, P4, P5, P1, + ST_TET, 4, P1, P5, P3, N0, + ST_PYR, 5, E08, E06, P3, P5, N0, + ST_PYR, 5, E00, E06, E08, E01, N0, + ST_TET, 4, P1, E00, E01, N0, + ST_PYR, 5, P1, E01, E08, P5, N0, + ST_PYR, 5, E06, E00, P1, P3, N0, + /* case 6 */ 8, + ST_PNT, 7, P0, P4, P5, E02, E00, E07, E08, + ST_TET, 4, P5, P3, P4, P0, + ST_TET, 4, P0, P4, P5, N0, + ST_PYR, 5, E07, E08, P5, P4, N0, + ST_PYR, 5, E02, E08, E07, E00, N0, + ST_TET, 4, P0, E02, E00, N0, + ST_PYR, 5, P0, E00, E07, P4, N0, + ST_PYR, 5, E08, E02, P0, P5, N0, + /* case 7 */ 1, + ST_WDG, 6, E06, E07, E08, P3, P4, P5, + /* case 8 */ 2, + ST_WDG, 6, P4, P5, P0, E03, E05, E06, + ST_PYR, 5, P4, P1, P2, P5, P0, + /* case 9 */ 1, + ST_HEX, 8, P1, P2, P5, P4, E00, E02, E05, E03, + /* case 10 */ 9, + ST_PNT, 6, E00, E01, E07, E03, E05, E06, + ST_PYR, 5, P5, P0, E06, E05, N0, + ST_TET, 4, P0, E00, E06, N0, + ST_PYR, 5, P0, P2, E01, E00, N0, + ST_TET, 4, P5, P2, P0, N0, + ST_PYR, 5, P4, E07, E01, P2, N0, + ST_TET, 4, P5, P4, P2, N0, + ST_PYR, 5, E05, E03, P4, P5, N0, + ST_TET, 4, E03, E07, P4, N0, + /* case 11 */ 7, + ST_PNT, 5, E01, E02, E05, E03, E07, + ST_PYR, 5, P4, P5, E05, E03, N0, + ST_TET, 4, E03, E07, P4, N0, + ST_PYR, 5, E02, E05, P5, P2, N0, + ST_PYR, 5, E01, P2, P4, E07, N0, + ST_TET, 4, P4, P2, P5, N0, + ST_TET, 4, P2, E01, E02, N0, + /* case 12 */ 9, + ST_PNT, 6, E05, E03, E06, E02, E01, E08, + ST_PYR, 5, P1, E01, E08, P5, N0, + ST_TET, 4, P5, E08, E05, N0, + ST_PYR, 5, P5, E05, E03, P4, N0, + ST_TET, 4, P1, P5, P4, N0, + ST_PYR, 5, P0, P4, E03, E06, N0, + ST_TET, 4, P1, P4, P0, N0, + ST_PYR, 5, E01, P1, P0, E02, N0, + ST_TET, 4, E02, P0, E06, N0, + /* case 13 */ 7, + ST_PNT, 5, E01, E00, E03, E05, E08, + ST_PYR, 5, P5, E05, E03, P4, N0, + ST_TET, 4, E05, P5, E08, N0, + ST_PYR, 5, E00, P1, P4, E03, N0, + ST_PYR, 5, E01, E08, P5, P1, N0, + ST_TET, 4, P5, P4, P1, N0, + ST_TET, 4, P1, E00, E01, N0, + /* case 14 */ 2, + ST_TET, 4, P0, E02, E00, E06, + ST_WDG, 6, E05, E08, P5, E03, E07, P4, + /* case 15 */ 1, + ST_WDG, 6, E03, P4, E07, E05, P5, E08, + /* case 16 */ 2, + ST_WDG, 6, P5, P3, P1, E04, E03, E07, + ST_PYR, 5, P5, P2, P0, P3, P1, + /* case 17 */ 9, + ST_PNT, 6, E03, E04, E07, E00, E02, E06, + ST_PYR, 5, P2, E02, E06, P3, N0, + ST_TET, 4, P3, E06, E03, N0, + ST_PYR, 5, P3, E03, E04, P5, N0, + ST_TET, 4, P2, P3, P5, N0, + ST_PYR, 5, P1, P5, E04, E07, N0, + ST_TET, 4, P2, P5, P1, N0, + ST_PYR, 5, E02, P2, P1, E00, N0, + ST_TET, 4, E00, P1, E07, N0, + /* case 18 */ 1, + ST_HEX, 8, P2, P0, P3, P5, E01, E00, E03, E04, + /* case 19 */ 7, + ST_PNT, 5, E02, E01, E04, E03, E06, + ST_PYR, 5, P3, E03, E04, P5, N0, + ST_TET, 4, E03, P3, E06, N0, + ST_PYR, 5, E01, P2, P5, E04, N0, + ST_PYR, 5, E02, E06, P3, P2, N0, + ST_TET, 4, P3, P5, P2, N0, + ST_TET, 4, P2, E01, E02, N0, + /* case 20 */ 9, + ST_PNT, 6, E01, E02, E08, E04, E03, E07, + ST_PYR, 5, P3, P1, E07, E03, N0, + ST_TET, 4, P1, E01, E07, N0, + ST_PYR, 5, P1, P0, E02, E01, N0, + ST_TET, 4, P3, P0, P1, N0, + ST_PYR, 5, P5, E08, E02, P0, N0, + ST_TET, 4, P3, P5, P0, N0, + ST_PYR, 5, E03, E04, P5, P3, N0, + ST_TET, 4, E04, E08, P5, N0, + /* case 21 */ 2, + ST_TET, 4, P1, E00, E01, E07, + ST_WDG, 6, E03, E06, P3, E04, E08, P5, + /* case 22 */ 7, + ST_PNT, 5, E02, E00, E03, E04, E08, + ST_PYR, 5, P5, P3, E03, E04, N0, + ST_TET, 4, E04, E08, P5, N0, + ST_PYR, 5, E00, E03, P3, P0, N0, + ST_PYR, 5, E02, P0, P5, E08, N0, + ST_TET, 4, P5, P0, P3, N0, + ST_TET, 4, P0, E02, E00, N0, + /* case 23 */ 1, + ST_WDG, 6, E04, P5, E08, E03, P3, E06, + /* case 24 */ 8, + ST_PNT, 7, P5, P0, P1, E04, E05, E06, E07, + ST_TET, 4, P1, P0, P2, P5, + ST_TET, 4, P5, P1, P0, N0, + ST_PYR, 5, E06, P0, P1, E07, N0, + ST_PYR, 5, E04, E05, E06, E07, N0, + ST_TET, 4, P5, E05, E04, N0, + ST_PYR, 5, P5, P0, E06, E05, N0, + ST_PYR, 5, E07, P1, P5, E04, N0, + /* case 25 */ 7, + ST_PNT, 5, E04, E05, E02, E00, E07, + ST_PYR, 5, P1, E00, E02, P2, N0, + ST_TET, 4, E00, P1, E07, N0, + ST_PYR, 5, E05, P5, P2, E02, N0, + ST_PYR, 5, E04, E07, P1, P5, N0, + ST_TET, 4, P1, P2, P5, N0, + ST_TET, 4, P5, E05, E04, N0, + /* case 26 */ 7, + ST_PNT, 5, E05, E04, E01, E00, E06, + ST_PYR, 5, P0, P2, E01, E00, N0, + ST_TET, 4, E00, E06, P0, N0, + ST_PYR, 5, E04, E01, P2, P5, N0, + ST_PYR, 5, E05, P5, P0, E06, N0, + ST_TET, 4, P0, P5, P2, N0, + ST_TET, 4, P5, E05, E04, N0, + /* case 27 */ 1, + ST_WDG, 6, E05, P5, E04, E02, P2, E01, + /* case 28 */ 2, + ST_TET, 4, P5, E05, E04, E08, + ST_WDG, 6, E02, E06, P0, E01, E07, P1, + /* case 29 */ 2, + ST_TET, 4, P1, E00, E01, E07, + ST_TET, 4, E05, E04, P5, E08, + /* case 30 */ 2, + ST_TET, 4, P5, E05, E04, E08, + ST_TET, 4, E00, P0, E02, E06, + /* case 31 */ 1, + ST_TET, 4, E05, E08, E04, P5, + /* case 32 */ 2, + ST_WDG, 6, P3, P4, P2, E05, E04, E08, + ST_PYR, 5, P3, P0, P1, P4, P2, + /* case 33 */ 9, + ST_PNT, 6, E02, E00, E06, E05, E04, E08, + ST_PYR, 5, P4, P2, E08, E04, N0, + ST_TET, 4, P2, E02, E08, N0, + ST_PYR, 5, P2, P1, E00, E02, N0, + ST_TET, 4, P4, P1, P2, N0, + ST_PYR, 5, P3, E06, E00, P1, N0, + ST_TET, 4, P4, P3, P1, N0, + ST_PYR, 5, E04, E05, P3, P4, N0, + ST_TET, 4, E05, E06, P3, N0, + /* case 34 */ 9, + ST_PNT, 6, E04, E05, E08, E01, E00, E07, + ST_PYR, 5, P0, E00, E07, P4, N0, + ST_TET, 4, P4, E07, E04, N0, + ST_PYR, 5, P4, E04, E05, P3, N0, + ST_TET, 4, P0, P4, P3, N0, + ST_PYR, 5, P2, P3, E05, E08, N0, + ST_TET, 4, P0, P3, P2, N0, + ST_PYR, 5, E00, P0, P2, E01, N0, + ST_TET, 4, E01, P2, E08, N0, + /* case 35 */ 2, + ST_TET, 4, P2, E01, E02, E08, + ST_WDG, 6, E04, E07, P4, E05, E06, P3, + /* case 36 */ 1, + ST_HEX, 8, P0, P1, P4, P3, E02, E01, E04, E05, + /* case 37 */ 7, + ST_PNT, 5, E00, E01, E04, E05, E06, + ST_PYR, 5, P3, P4, E04, E05, N0, + ST_TET, 4, E05, E06, P3, N0, + ST_PYR, 5, E01, E04, P4, P1, N0, + ST_PYR, 5, E00, P1, P3, E06, N0, + ST_TET, 4, P3, P1, P4, N0, + ST_TET, 4, P1, E00, E01, N0, + /* case 38 */ 7, + ST_PNT, 5, E00, E02, E05, E04, E07, + ST_PYR, 5, P4, E04, E05, P3, N0, + ST_TET, 4, E04, P4, E07, N0, + ST_PYR, 5, E02, P0, P3, E05, N0, + ST_PYR, 5, E00, E07, P4, P0, N0, + ST_TET, 4, P4, P3, P0, N0, + ST_TET, 4, P0, E02, E00, N0, + /* case 39 */ 1, + ST_WDG, 6, E05, P3, E06, E04, P4, E07, + /* case 40 */ 8, + ST_PNT, 7, P4, P2, P0, E03, E04, E08, E06, + ST_TET, 4, P0, P2, P1, P4, + ST_TET, 4, P4, P0, P2, N0, + ST_PYR, 5, E08, P2, P0, E06, N0, + ST_PYR, 5, E03, E04, E08, E06, N0, + ST_TET, 4, P4, E04, E03, N0, + ST_PYR, 5, P4, P2, E08, E04, N0, + ST_PYR, 5, E06, P0, P4, E03, N0, + /* case 41 */ 7, + ST_PNT, 5, E04, E03, E00, E02, E08, + ST_PYR, 5, P2, P1, E00, E02, N0, + ST_TET, 4, E02, E08, P2, N0, + ST_PYR, 5, E03, E00, P1, P4, N0, + ST_PYR, 5, E04, P4, P2, E08, N0, + ST_TET, 4, P2, P4, P1, N0, + ST_TET, 4, P4, E04, E03, N0, + /* case 42 */ 2, + ST_TET, 4, P4, E04, E03, E07, + ST_WDG, 6, E01, E08, P2, E00, E06, P0, + /* case 43 */ 2, + ST_TET, 4, P4, E04, E03, E07, + ST_TET, 4, E02, P2, E01, E08, + /* case 44 */ 7, + ST_PNT, 5, E03, E04, E01, E02, E06, + ST_PYR, 5, P0, E02, E01, P1, N0, + ST_TET, 4, E02, P0, E06, N0, + ST_PYR, 5, E04, P4, P1, E01, N0, + ST_PYR, 5, E03, E06, P0, P4, N0, + ST_TET, 4, P0, P1, P4, N0, + ST_TET, 4, P4, E04, E03, N0, + /* case 45 */ 1, + ST_WDG, 6, E04, P4, E03, E01, P1, E00, + /* case 46 */ 2, + ST_TET, 4, P0, E02, E00, E06, + ST_TET, 4, E04, E03, P4, E07, + /* case 47 */ 1, + ST_TET, 4, E04, E07, E03, P4, + /* case 48 */ 8, + ST_PNT, 7, P3, P1, P2, E05, E03, E07, E08, + ST_TET, 4, P2, P1, P0, P3, + ST_TET, 4, P3, P2, P1, N0, + ST_PYR, 5, E07, P1, P2, E08, N0, + ST_PYR, 5, E05, E03, E07, E08, N0, + ST_TET, 4, P3, E03, E05, N0, + ST_PYR, 5, P3, P1, E07, E03, N0, + ST_PYR, 5, E08, P2, P3, E05, N0, + /* case 49 */ 2, + ST_TET, 4, P3, E03, E05, E06, + ST_WDG, 6, E00, E07, P1, E02, E08, P2, + /* case 50 */ 7, + ST_PNT, 5, E05, E03, E00, E01, E08, + ST_PYR, 5, P2, E01, E00, P0, N0, + ST_TET, 4, E01, P2, E08, N0, + ST_PYR, 5, E03, P3, P0, E00, N0, + ST_PYR, 5, E05, E08, P2, P3, N0, + ST_TET, 4, P2, P0, P3, N0, + ST_TET, 4, P3, E03, E05, N0, + /* case 51 */ 2, + ST_TET, 4, P2, E01, E02, E08, + ST_TET, 4, E03, E05, P3, E06, + /* case 52 */ 7, + ST_PNT, 5, E03, E05, E02, E01, E07, + ST_PYR, 5, P1, P0, E02, E01, N0, + ST_TET, 4, E01, E07, P1, N0, + ST_PYR, 5, E05, E02, P0, P3, N0, + ST_PYR, 5, E03, P3, P1, E07, N0, + ST_TET, 4, P1, P3, P0, N0, + ST_TET, 4, P3, E03, E05, N0, + /* case 53 */ 2, + ST_TET, 4, P3, E03, E05, E06, + ST_TET, 4, E01, P1, E00, E07, + /* case 54 */ 1, + ST_WDG, 6, E03, P3, E05, E00, P0, E02, + /* case 55 */ 1, + ST_TET, 4, E03, E06, E05, P3, + /* case 56 */ 1, + ST_WDG, 6, P0, P1, P2, E06, E07, E08, + /* case 57 */ 1, + ST_WDG, 6, E02, P2, E08, E00, P1, E07, + /* case 58 */ 1, + ST_WDG, 6, E00, P0, E06, E01, P2, E08, + /* case 59 */ 1, + ST_TET, 4, E02, E01, E08, P2, + /* case 60 */ 1, + ST_WDG, 6, E01, P1, E07, E02, P0, E06, + /* case 61 */ 1, + ST_TET, 4, E01, E00, E07, P1, + /* case 62 */ 1, + ST_TET, 4, E00, E02, E06, P0, + /* case 63 */ 0, + // vtkm::CELL_SHAPE_PYRAMID + /* case 0 */ 1, + ST_PYR, 5, P0, P1, P2, P3, P4, + /* case 1 */ 2, + ST_WDG, 6, E00, E04, E03, P1, P4, P3, + ST_TET, 4, P1, P2, P3, P4, + /* case 2 */ 2, + ST_WDG, 6, E01, E05, E00, P2, P4, P0, + ST_TET, 4, P2, P3, P0, P4, + /* case 3 */ 7, + ST_PNT, 7, P4, E05, E04, E01, E03, P2, P3, + ST_TET, 4, E04, P4, E05, N0, + ST_PYR, 5, E01, E03, E04, E05, N0, + ST_PYR, 5, E01, E05, P4, P2, N0, + ST_TET, 4, P2, P4, P3, N0, + ST_PYR, 5, P3, P4, E04, E03, N0, + ST_PYR, 5, P2, P3, E03, E01, N0, + /* case 4 */ 2, + ST_WDG, 6, E02, E06, E01, P3, P4, P1, + ST_TET, 4, P3, P0, P1, P4, + /* case 5 */ 2, + ST_WDG, 6, E04, P4, E06, E00, P1, E01, + ST_WDG, 6, P4, E04, E06, P3, E03, E02, + /* case 6 */ 7, + ST_PNT, 7, P4, E06, E05, E02, E00, P3, P0, + ST_TET, 4, E05, P4, E06, N0, + ST_PYR, 5, E02, E00, E05, E06, N0, + ST_PYR, 5, E02, E06, P4, P3, N0, + ST_TET, 4, P3, P4, P0, N0, + ST_PYR, 5, P0, P4, E05, E00, N0, + ST_PYR, 5, P3, P0, E00, E02, N0, + /* case 7 */ 2, + ST_TET, 4, E04, E05, E06, P4, + ST_WDG, 6, E02, E03, P3, E06, E04, P4, + /* case 8 */ 2, + ST_WDG, 6, E03, E07, E02, P0, P4, P2, + ST_TET, 4, P0, P1, P2, P4, + /* case 9 */ 7, + ST_PNT, 7, P4, E04, E07, E00, E02, P1, P2, + ST_TET, 4, E07, P4, E04, N0, + ST_PYR, 5, E00, E02, E07, E04, N0, + ST_PYR, 5, E00, E04, P4, P1, N0, + ST_TET, 4, P1, P4, P2, N0, + ST_PYR, 5, P2, P4, E07, E02, N0, + ST_PYR, 5, P1, P2, E02, E00, N0, + /* case 10 */ 2, + ST_WDG, 6, E07, P4, E05, E03, P0, E00, + ST_WDG, 6, P4, E07, E05, P2, E02, E01, + /* case 11 */ 2, + ST_TET, 4, E07, E04, E05, P4, + ST_WDG, 6, E01, E02, P2, E05, E07, P4, + /* case 12 */ 7, + ST_PNT, 7, P4, E07, E06, E03, E01, P0, P1, + ST_TET, 4, E06, P4, E07, N0, + ST_PYR, 5, E03, E01, E06, E07, N0, + ST_PYR, 5, E03, E07, P4, P0, N0, + ST_TET, 4, P0, P4, P1, N0, + ST_PYR, 5, P1, P4, E06, E01, N0, + ST_PYR, 5, P0, P1, E01, E03, N0, + /* case 13 */ 2, + ST_TET, 4, E06, E07, E04, P4, + ST_WDG, 6, E00, E01, P1, E04, E06, P4, + /* case 14 */ 2, + ST_TET, 4, E05, E06, E07, P4, + ST_WDG, 6, E03, E00, P0, E07, E05, P4, + /* case 15 */ 1, + ST_PYR, 5, E04, E05, E06, E07, P4, + /* case 16 */ 1, + ST_HEX, 8, P0, P1, P2, P3, E04, E05, E06, E07, + /* case 17 */ 2, + ST_WDG, 6, E03, E07, P3, E00, E05, P1, + ST_WDG, 6, E05, E06, E07, P1, P2, P3, + /* case 18 */ 2, + ST_WDG, 6, E00, E04, P0, E01, E06, P2, + ST_WDG, 6, E06, E07, E04, P2, P3, P0, + /* case 19 */ 1, + ST_WDG, 6, E03, E07, P3, E01, E06, P2, + /* case 20 */ 2, + ST_WDG, 6, E01, E05, P1, E02, E07, P3, + ST_WDG, 6, E07, E04, E05, P3, P0, P1, + /* case 21 */ 2, + ST_TET, 4, E00, P1, E01, E05, + ST_TET, 4, P3, E03, E02, E07, + /* case 22 */ 1, + ST_WDG, 6, E00, E04, P0, E02, E07, P3, + /* case 23 */ 1, + ST_TET, 4, P3, E03, E02, E07, + /* case 24 */ 2, + ST_WDG, 6, E02, E06, P2, E03, E04, P0, + ST_WDG, 6, E04, E05, E06, P0, P1, P2, + /* case 25 */ 1, + ST_WDG, 6, E02, E06, P2, E00, E05, P1, + /* case 26 */ 2, + ST_TET, 4, E03, P0, E00, E04, + ST_TET, 4, P2, E02, E01, E06, + /* case 27 */ 1, + ST_TET, 4, P2, E02, E01, E06, + /* case 28 */ 1, + ST_WDG, 6, E01, E05, P1, E03, E04, P0, + /* case 29 */ 1, + ST_TET, 4, P1, E01, E00, E05, + /* case 30 */ 1, + ST_TET, 4, P0, E00, E03, E04, + /* case 31 */ 0, + }; + // clang-format on + return ClipTablesData[idx]; + } + + /** + * Get the the case index for the given shape and caseId. + */ + VTKM_EXEC inline static vtkm::Id GetCaseIndex(vtkm::UInt8 shape, vtkm::Id caseId) + { + // clang-format off + // Index into ClipTablesData for each shape and configuration + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt16 ClipTablesIndices[] = { + // vtkm::CELL_SHAPE_VERTEX + 0, 4, // case 0 - 1 + // vtkm::CELL_SHAPE_LINE + 5, 10, 15, 20, // case 0 - 3 + // vtkm::CELL_SHAPE_TRIANGLE + 21, 27, 34, 41, 47, 54, 60, 66, // case 0 - 7 + // vtkm::CELL_SHAPE_QUAD + 67, 74, 86, 98, 105, 117, 128, 135, // case 0 - 7 + 141, 153, 160, 171, 177, 184, 190, 196, // case 8 - 15 + // vtkm::CELL_SHAPE_TETRA + 197, 204, 213, 222, 231, 240, 249, 258, // case 0 - 7 + 265, 274, 283, 292, 299, 308, 315, 322, // case 8 - 15 + // vtkm::CELL_SHAPE_HEXAHEDRON + 323, 334, 397, 460, 479, 542, 618, 637, // case 0 - 7 + 698, 761, 780, 856, 917, 936, 997, 1058, // case 8 - 15 + 1069, 1132, 1151, 1227, 1288, 1329, 1401, 1473, // case 16 - 23 + 1534, 1610, 1671, 1712, 1747, 1819, 1880, 1933, // case 24 - 31 + 1980, 2043, 2119, 2138, 2199, 2275, 2316, 2377, // case 32 - 39 + 2412, 2453, 2525, 2597, 2658, 2730, 2783, 2844, // case 40 - 47 + 2891, 2910, 2971, 3032, 3043, 3115, 3168, 3229, // case 48 - 55 + 3276, 3348, 3409, 3462, 3509, 3526, 3541, 3556, // case 56 - 63 + 3565, 3628, 3669, 3745, 3817, 3836, 3908, 3969, // case 64 - 71 + 4030, 4106, 4178, 4219, 4272, 4333, 4394, 4429, // case 72 - 79 + 4476, 4552, 4624, 4665, 4718, 4790, 4807, 4860, // case 80 - 87 + 4875, 4916, 4969, 4994, 5013, 5066, 5081, 5100, // case 88 - 95 + 5113, 5132, 5204, 5265, 5326, 5387, 5440, 5451, // case 96 - 103 + 5498, 5570, 5587, 5640, 5655, 5716, 5731, 5778, // case 104 - 111 + 5787, 5848, 5909, 5944, 5991, 6052, 6067, 6114, // case 112 - 119 + 6123, 6176, 6191, 6210, 6223, 6238, 6251, 6264, // case 120 - 127 + 6271, 6334, 6410, 6451, 6523, 6599, 6640, 6712, // case 128 - 135 + 6765, 6784, 6845, 6917, 6978, 7039, 7074, 7135, // case 136 - 143 + 7182, 7201, 7262, 7334, 7395, 7467, 7520, 7537, // case 144 - 151 + 7552, 7613, 7624, 7677, 7724, 7785, 7832, 7847, // case 152 - 159 + 7856, 7932, 7973, 8045, 8098, 8139, 8164, 8217, // case 160 - 167 + 8236, 8308, 8361, 8378, 8393, 8446, 8465, 8480, // case 168 - 175 + 8493, 8554, 8589, 8650, 8697, 8750, 8769, 8784, // case 176 - 183 + 8797, 8858, 8905, 8920, 8929, 8944, 8957, 8970, // case 184 - 191 + 8977, 8996, 9068, 9140, 9157, 9218, 9271, 9332, // case 192 - 199 + 9347, 9408, 9469, 9522, 9537, 9548, 9595, 9642, // case 200 - 207 + 9651, 9712, 9773, 9826, 9841, 9902, 9917, 9932, // case 208 - 215 + 9945, 9980, 10027, 10046, 10059, 10106, 10115, 10128, // case 216 - 223 + 10135, 10196, 10249, 10310, 10325, 10360, 10379, 10426, // case 224 - 231 + 10439, 10500, 10515, 10530, 10543, 10590, 10603, 10612, // case 232 - 239 + 10619, 10630, 10677, 10724, 10733, 10780, 10793, 10802, // case 240 - 247 + 10809, 10856, 10865, 10878, 10885, 10894, 10901, 10908, // case 248 - 255 + // vtkm::CELL_SHAPE_WEDGE + 10909, 10918, 10934, 10950, 11006, 11022, 11078, 11134, // case 0 - 7 + 11143, 11159, 11170, 11231, 11278, 11339, 11386, 11401, // case 8 - 15 + 11410, 11426, 11487, 11498, 11545, 11606, 11621, 11668, // case 16 - 23 + 11677, 11733, 11780, 11827, 11836, 11851, 11864, 11877, // case 24 - 31 + 11884, 11900, 11961, 12022, 12037, 12048, 12095, 12142, // case 32 - 39 + 12151, 12207, 12254, 12269, 12282, 12329, 12338, 12351, // case 40 - 47 + 12358, 12414, 12429, 12476, 12489, 12536, 12549, 12558, // case 48 - 55 + 12565, 12574, 12583, 12592, 12599, 12608, 12615, 12622, // case 56 - 63 + // vtkm::CELL_SHAPE_PYRAMID + 12623, 12631, 12646, 12661, 12711, 12726, 12743, 12793, // case 0 - 7 + 12808, 12823, 12873, 12890, 12905, 12955, 12970, 12985, // case 8 - 15 + 12993, 13004, 13021, 13038, 13047, 13064, 13077, 13086, // case 16 - 23 + 13093, 13110, 13119, 13132, 13139, 13148, 13155, 13162, // case 24 - 31 + }; + // clang-format on + return ClipTablesIndices[ClipTablesBase::GetCellClipTableIndex(shape) + caseId]; + } + + /** + * Given the number of points and a case index, return if the cell is discarded. + */ + VTKM_EXEC inline static bool IsCellDiscarded(vtkm::IdComponent numberOfPoints, + vtkm::UInt8 caseIndex) { + return caseIndex == ClipTablesBase::GetMaxCellCase(numberOfPoints); } - template - DevicePortal PrepareForExecution(DeviceAdapter, vtkm::cont::Token& token) + /** + * Given the number of points and a case index, return if the cell is kept. + */ + VTKM_EXEC inline static constexpr bool IsCellKept(vtkm::IdComponent vtkmNotUsed(numberOfPoints), + vtkm::UInt8 caseIndex) { - DevicePortal portal; - portal.ClipTablesDataPortal = this->ClipTablesDataArray.PrepareForInput(DeviceAdapter(), token); - portal.ClipTablesIndicesPortal = - this->ClipTablesIndicesArray.PrepareForInput(DeviceAdapter(), token); - portal.CellEdgesPortal = this->CellEdgesArray.PrepareForInput(DeviceAdapter(), token); - return portal; + return caseIndex == 0; } -private: - vtkm::cont::ArrayHandle ClipTablesDataArray; - vtkm::cont::ArrayHandle ClipTablesIndicesArray; - vtkm::cont::ArrayHandle CellEdgesArray; + /** + * Get the discarded cell case index. + */ + VTKM_EXEC inline static constexpr vtkm::UInt8 GetDiscardedCellCase() { return 255; } + + /** + * Get the kept cell case index. + */ + VTKM_EXEC inline static constexpr vtkm::UInt8 GetKeptCellCase() { return 0; } }; } } -- GitLab From e258c5bbd04939e9c626d3d7bd4f5e66c1bd027d Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 8 Oct 2024 17:34:35 -0400 Subject: [PATCH 61/73] Add documentation for cell and point locators --- docs/users-guide/acknowledgements.rst | 4 +- docs/users-guide/examples/CMakeLists.txt | 1 + .../examples/GuideExampleCellLocator.cxx | 79 +++++- .../examples/GuideExamplePointLocator.cxx | 156 ++++++++++++ docs/users-guide/locators.rst | 231 ++++++++++++++++++ docs/users-guide/part-advanced.rst | 1 + vtkm/cont/CellLocatorBase.h | 14 +- .../CellLocatorBoundingIntervalHierarchy.h | 46 +++- vtkm/cont/CellLocatorRectilinearGrid.h | 6 + vtkm/cont/CellLocatorTwoLevel.h | 16 +- vtkm/cont/CellLocatorUniformBins.h | 16 +- vtkm/cont/CellLocatorUniformGrid.h | 8 + vtkm/cont/PointLocatorSparseGrid.h | 16 +- .../CellLocatorBoundingIntervalHierarchy.h | 28 ++- vtkm/exec/CellLocatorRectilinearGrid.h | 28 ++- vtkm/exec/CellLocatorTwoLevel.h | 13 + vtkm/exec/CellLocatorUniformBins.h | 30 ++- vtkm/exec/CellLocatorUniformGrid.h | 49 +++- vtkm/exec/PointLocatorSparseGrid.h | 14 +- 19 files changed, 685 insertions(+), 71 deletions(-) create mode 100644 docs/users-guide/examples/GuideExamplePointLocator.cxx create mode 100644 docs/users-guide/locators.rst diff --git a/docs/users-guide/acknowledgements.rst b/docs/users-guide/acknowledgements.rst index 7a054dfaf8..eabbc2175c 100644 --- a/docs/users-guide/acknowledgements.rst +++ b/docs/users-guide/acknowledgements.rst @@ -38,9 +38,7 @@ contributions to this text: .. Dave Pugmire filters: Streamlines, point transform, coordinate system transforms, add ghost cells, remove ghost cells. -**Abhishek Yenpure** and **Li-Ta Lo** for their documentation of locator structures.. - -.. (Chapter~\ref{chap:Locators}). +**Abhishek Yenpure** and **Li-Ta Lo** for their documentation of locator structures in :chapref:`locators:Locators`. .. Abhishek Yenpure: General cell locators and BoundingIntervalHierarchy .. Li-Ta Lo: General point locators and uniform grid point locator, particle density diff --git a/docs/users-guide/examples/CMakeLists.txt b/docs/users-guide/examples/CMakeLists.txt index 507a5ce0c5..f915df8868 100644 --- a/docs/users-guide/examples/CMakeLists.txt +++ b/docs/users-guide/examples/CMakeLists.txt @@ -49,6 +49,7 @@ set(examples_device GuideExampleFields.cxx GuideExampleFilterDataSetWithField.cxx GuideExampleGenerateMeshConstantShape.cxx + GuideExamplePointLocator.cxx GuideExampleSimpleAlgorithm.cxx GuideExampleSimpleHistogram.cxx GuideExampleSumOfAngles.cxx diff --git a/docs/users-guide/examples/GuideExampleCellLocator.cxx b/docs/users-guide/examples/GuideExampleCellLocator.cxx index aa40a2cd9f..3b5216876f 100644 --- a/docs/users-guide/examples/GuideExampleCellLocator.cxx +++ b/docs/users-guide/examples/GuideExampleCellLocator.cxx @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -105,12 +106,14 @@ struct DemoQueryCells //// RESUME-EXAMPLE //// //// BEGIN-EXAMPLE ConstructCellLocator + //// BEGIN-EXAMPLE CellLocatorGeneral //// vtkm::cont::CellLocatorGeneral cellLocator; cellLocator.SetCellSet(inDataSet.GetCellSet()); cellLocator.SetCoordinates(inDataSet.GetCoordinateSystem()); cellLocator.Update(); //// + //// END-EXAMPLE CellLocatorGeneral //// END-EXAMPLE ConstructCellLocator //// @@ -130,7 +133,7 @@ struct DemoQueryCells } }; -void TestCellLocator() +void TestCellLocator1() { using ValueType = vtkm::Vec3f; using ArrayType = vtkm::cont::ArrayHandle; @@ -162,9 +165,81 @@ void TestCellLocator() VTKM_TEST_ASSERT(test_equal_portals(expected.ReadPortal(), interpolated.ReadPortal())); } +//// +//// BEGIN-EXAMPLE CellLocatorChooser +//// +template +void QueryCells(const CellSetType& cellSet, + const CoordsType& coords, + const QueryPointsType& queryPoints, + const FieldType& inField, + const FieldType& interpolatedField) +{ + VTKM_IS_CELL_SET(CellSetType); + VTKM_IS_ARRAY_HANDLE(CoordsType); + VTKM_IS_ARRAY_HANDLE(QueryPointsType); + VTKM_IS_ARRAY_HANDLE(FieldType); + + vtkm::cont::CellLocatorChooser cellLocator; + cellLocator.SetCellSet(cellSet); + cellLocator.SetCoordinates(coords); + cellLocator.Update(); + + vtkm::cont::Invoker invoke; + invoke( + QueryCellsWorklet{}, queryPoints, cellLocator, cellSet, inField, interpolatedField); +} +//// +//// END-EXAMPLE CellLocatorChooser +//// + +void TestCellLocator2() +{ + using ValueType = vtkm::Vec3f; + using ArrayType = vtkm::cont::ArrayHandle; + + vtkm::cont::DataSet data = vtkm::cont::DataSetBuilderUniform::Create(DimensionSizes); + + ArrayType inField; + vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleUniformPointCoordinates( + DimensionSizes, ValueType(0.0f), ValueType(2.0f)), + inField); + + ArrayType queryPoints; + + vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleUniformPointCoordinates( + DimensionSizes - vtkm::Id3(1), ValueType(0.5f)), + queryPoints); + + ArrayType interpolated; + + QueryCells(data.GetCellSet().AsCellSet>(), + data.GetCoordinateSystem() + .GetData() + .AsArrayHandle(), + queryPoints, + inField, + interpolated); + + vtkm::cont::ArrayHandleUniformPointCoordinates expected( + DimensionSizes - vtkm::Id3(1), ValueType(1.0f), ValueType(2.0f)); + + std::cout << "Expected: "; + vtkm::cont::printSummary_ArrayHandle(expected, std::cout); + + std::cout << "Interpolated: "; + vtkm::cont::printSummary_ArrayHandle(interpolated, std::cout); + + VTKM_TEST_ASSERT(test_equal_portals(expected.ReadPortal(), interpolated.ReadPortal())); +} + void Run() { - TestCellLocator(); + TestCellLocator1(); + TestCellLocator2(); } } // anonymous namespace diff --git a/docs/users-guide/examples/GuideExamplePointLocator.cxx b/docs/users-guide/examples/GuideExamplePointLocator.cxx new file mode 100644 index 0000000000..f7e7317409 --- /dev/null +++ b/docs/users-guide/examples/GuideExamplePointLocator.cxx @@ -0,0 +1,156 @@ +//============================================================================= +// +// 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 + +namespace +{ + +constexpr vtkm::Id DimensionSize = 50; +const vtkm::Id3 DimensionSizes = vtkm::Id3(DimensionSize); + +//// +//// BEGIN-EXAMPLE UsePointLocator +//// +/// Worklet that generates for each input coordinate a unit vector that points +/// to the closest point in a locator. +struct PointToClosestWorklet : public vtkm::worklet::WorkletMapField +{ + using ControlSignature = void(FieldIn, ExecObject, WholeArrayIn, FieldOut); + using ExecutionSignature = void(_1, _2, _3, _4); + + template + VTKM_EXEC void operator()(const Point& queryPoint, + const PointLocatorExecObject& pointLocator, + const CoordinateSystemPortal& coordinateSystem, + OutType& out) const + { + // Use the point locator to find the point in the locator closest to the point + // given. + vtkm::Id pointId; + vtkm::FloatDefault distanceSquared; + pointLocator.FindNearestNeighbor(queryPoint, pointId, distanceSquared); + + // Use this information to find the nearest point and create a unit vector + // pointing to it. + if (pointId >= 0) + { + // Get nearest point coordinate. + auto point = coordinateSystem.Get(pointId); + + // Get the vector pointing to this point + out = point - queryPoint; + + // Convert to unit vector (if possible) + if (distanceSquared > vtkm::Epsilon()) + { + out = vtkm::RSqrt(distanceSquared) * out; + } + } + else + { + this->RaiseError("Locator could not find closest point."); + } + } +}; + +// +// Later in the associated Filter class... +// + +//// PAUSE-EXAMPLE +struct DemoQueryPoints +{ + vtkm::cont::Invoker Invoke; + + vtkm::cont::ArrayHandle QueryPoints; + + VTKM_CONT vtkm::cont::ArrayHandle Run( + const vtkm::cont::DataSet& inDataSet) + { + // Note: when more point locators are created, we might want to switch the + // example to a different (perhaps more general) one. + //// RESUME-EXAMPLE + //// + //// BEGIN-EXAMPLE ConstructPointLocator + //// + vtkm::cont::PointLocatorSparseGrid pointLocator; + pointLocator.SetCoordinates(inDataSet.GetCoordinateSystem()); + pointLocator.Update(); + //// + //// END-EXAMPLE ConstructPointLocator + //// + + vtkm::cont::ArrayHandle pointDirections; + + this->Invoke(PointToClosestWorklet{}, + this->QueryPoints, + &pointLocator, + pointLocator.GetCoordinates(), + pointDirections); + //// + //// END-EXAMPLE UsePointLocator + //// + + return pointDirections; + } +}; + +void TestPointLocator() +{ + using ValueType = vtkm::Vec3f; + using ArrayType = vtkm::cont::ArrayHandle; + + vtkm::cont::DataSet data = vtkm::cont::DataSetBuilderUniform::Create(DimensionSizes); + + DemoQueryPoints demo; + + vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleUniformPointCoordinates( + DimensionSizes - vtkm::Id3(1), ValueType(0.75f)), + demo.QueryPoints); + + ArrayType pointers = demo.Run(data); + + auto expected = vtkm::cont::make_ArrayHandleConstant( + vtkm::Vec3f(0.57735f), demo.QueryPoints.GetNumberOfValues()); + + std::cout << "Expected: "; + vtkm::cont::printSummary_ArrayHandle(expected, std::cout); + + std::cout << "Calculated: "; + vtkm::cont::printSummary_ArrayHandle(pointers, std::cout); + + VTKM_TEST_ASSERT(test_equal_portals(expected.ReadPortal(), pointers.ReadPortal())); +} + +void Run() +{ + TestPointLocator(); +} + +} // anonymous namespace + +int GuideExamplePointLocator(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(Run, argc, argv); +} diff --git a/docs/users-guide/locators.rst b/docs/users-guide/locators.rst new file mode 100644 index 0000000000..9e2a47425d --- /dev/null +++ b/docs/users-guide/locators.rst @@ -0,0 +1,231 @@ +============================== +Locators +============================== + +Locators are a special type of structure that allows you to take a point coordinate in space and then find a topological element that contains or is near that coordinate. +|VTKm| comes with multiple types of locators, which are categorized by the type of topological element that they find. +For example, a *cell locator* takes a coordinate in world space and finds the cell in a :class:`vtkm::cont::DataSet` that contains that cell. +Likewise, a *point locator* takes a coordinate in world space and finds a point from a :class:`vtkm::cont::CoordinateSystem` nearby. + +Different locators differ in their interface slightly, but they all follow the same basic operation. +First, they are constructed and provided with one or more elements of a :class:`vtkm::cont::DataSet`. +Then they are built with a call to an :func:`vtkm::cont::CellLocatorBase::Update` method. +The locator can then be passed to a worklet as an ``ExecObject``, which will cause the worklet to get a special execution version of the locator that can do the queries. + +.. didyouknow:: + Other visualization libraries, like |VTKm|'s big sister toolkit VTK, provide similar locator structures that allow iterative building by adding one element at a time. + |VTKm| explicitly disallows this use case. + Although iteratively adding elements to a locator is undoubtedly useful, such an operation will inevitably bottleneck a highly threaded algorithm in critical sections. + This makes iterative additions to locators too costly to support in |VTKm|. + + +------------------------------ +Cell Locators +------------------------------ + +.. index:: + double: locator; cell + +Cell Locators in |VTKm| provide a means of building spatial search structures that can later be used to find a cell containing a certain point. +This could be useful in scenarios where the application demands the cell to which a point belongs to to achieve a certain functionality. +For example, while tracing a particle's path through a vector field, after every step we lookup which cell the particle has entered to interpolate the velocity at the new location to take the next step. + +Using cell locators is a two step process. +The first step is to build the search structure. +This is done by instantiating one of the ``CellLocator`` classes, providing a cell set and coordinate system (usually from a :class:`vtkm::cont::DataSet`), and then updating the structure. +Once the cell locator is built, it can be used in the execution environment within a filter or worklet. + +Building a Cell Locator +============================== + +All cell locators in |VTKm| share the same basic interface for the required features of cell locators. +This generic interface provides methods to set the cell set (with :func:`vtkm::cont::CellLocatorBase::SetCellSet` and :func:`vtkm::cont::CellLocatorBase::GetCellSet`) and to set the coordinate system (with :func:`vtkm::cont::CellLocatorBase::SetCoordinates` and :func:`vtkm::cont::CellLocatorBase::GetCoordinates`). +Once the cell set and coordinates are provided, you may call :func:`vtkm::cont::CellLocatorBase::Update` to construct the search structures. +Although :func:`vtkm::cont::CellLocatorBase::Update` is called from the control environment, the search structure will be built on parallel devices. + +.. load-example:: ConstructCellLocator + :file: GuideExampleCellLocator.cxx + :caption: Constructing a ``CellLocator``. + +|VTKm| provides multiple implementations of cell locators. +All cell locator classes derive the abstract :class:`vtkm::cont::CellLocatorBase` class. + +.. doxygenclass:: vtkm::cont::CellLocatorBase + :members: + +The choice of which cell locator to use depends on the structure of the cells and the regularity of the distribution. + +Cell Locators for Structured Cell Sets +---------------------------------------- + +If your :class:`vtkm::cont::DataSet` has a cell set of type :class:`vtkm::cont::CellSetStructured`, this can give a locator information about the regular nature of the cells to more quickly identify cells. +The mechanism to find the cells then becomes dependent on the type of coordinates in the cell set. + +.. index:: + double: uniform grid; locator + +If the :class:`vtkm::cont::DataSet` contains a :class:`vtkm::cont::ArrayHandleUniformPointCoordinates` as the coordinate system, this is known as a uniform grid. +The cells are aligned with the world axes and have uniform spacing between them. +In this case, the :class:`vtkm::cont::CellLocatorUniformGrid` is highly optimized to find cells. + +.. doxygenclass:: vtkm::cont::CellLocatorUniformGrid + :members: + +.. index:: + double: rectilinear grid; locator + +In a related case, if the :class:`vtkm::cont::DataSet` with structured cells contains a :class:`vtkm::cont::ArrayHandleCartesianProduct` as the coordinate system, this is known as a rectilinear grid. +The cells are aligned with the world axes, but the spacing can vary between them. +In this case, the :class:`vtkm::cont::CellLocatorRectilinearGrid` is best to find cells. + +.. doxygenclass:: vtkm::cont::CellLocatorRectilinearGrid + :members: + +For a :class:`vtkm::cont::DataSet` containing any other type of cell set or coordinate types, one of the locators for irregular cell sets described below must be used. + +Cell Locators for Irregular Cell Sets +---------------------------------------- + +|VTKm| contains several locating strategies cells in irregular patterns in space. +These are typically used for cell sets with explicit connectivity or general positioning of points. +Although they will technically work on any type of data, they may be less efficient than those designed for a specific structure of data. + +A good performing locator across many distributions of cells is :class:`vtkm::cont::CellLocatorTwoLevel`. +This search structure uses a single level of indirection to adapt to an uneven distribution of cells. +This tends to lead to a good balance between the number of ids to trace while finding cells, the number of cells that need to be checked, and the space to store the structure. + +.. doxygenclass:: vtkm::cont::CellLocatorTwoLevel + :members: + +If you happen to know that the cells are evenly distributed across the bounds of the space, then the indirect reference of :class:`vtkm::cont::CellLocatorTwoLevel` is unnecessary. +Each bin in the grid will have approximately the same number of cells, and thus a single level can be used to remove some indirection in the lookup. +This is implemented with :class:`vtkm::cont::CellLocatorUniformBins`. + +.. doxygenclass:: vtkm::cont::CellLocatorUniformBins + :members: + +In contrast, a very irregular data set may have multiple orders of magnitude difference in the size of its cells. +If the cell distribution is very irregular, the :class:`vtkm::cont::CellLocatorTwoLevel` can be left with bins containing a large number of cells in a regions with very small cells. +In these cases, :class:`vtkm::cont::CellLocatorBoundingIntervalHierarchy` can be used to capture the diversity in cell distribution. +:class:`vtkm::cont::CellLocatorBoundingIntervalHierarchy` builds a search structure by recursively dividing the space of cells. +This creates a deeper structure than :class:`vtkm::cont::CellLocatorTwoLevel`, so it can take longer to find a containing bin when searching for a cell. +However, the deeper structure means that each bin is guaranteed to contain a small number of cells. + +.. doxygenclass:: vtkm::cont::CellLocatorBoundingIntervalHierarchy + :members: + +Cell Locators for Unknown Cell Sets +---------------------------------------- + +The previously described cell locators require you to know the type of cell set and coordinate system array to build a cell locator. +Often, this information is not available. +In these cases, |VTKm| provides a couple of classes to choose an appropriate locator. + +If you are developing function that is templated on the type of cell set and coordinate system, you can use the :type:`vtkm::cont::CellLocatorChooser` templated type to automatically choose a locator of an appropriate type. + +.. doxygentypedef:: vtkm::cont::CellLocatorChooser + +.. load-example:: CellLocatorChooser + :file: GuideExampleCellLocator.cxx + :caption: Using :type:`vtkm::cont::CellLocatorChooser` to determine the cell locator type. + +There are times when the type of cell locator cannot be easily determined at compile times. +In this case, the :class:`vtkm::cont::CellLocatorGeneral` can be used. +This locator will accept any type of cell set and coordinate system. +It will then choose at runtime the most appropriate cell locating structure to use. + +.. doxygenclass:: vtkm::cont::CellLocatorGeneral + :members: + +.. load-example:: CellLocatorGeneral + :file: GuideExampleCellLocator.cxx + :caption: Using :class:`vtkm::cont::CellLocatorGeneral`. + +Using Cell Locators in a Worklet +======================================== + +The :class:`vtkm::cont::CellLocatorBase` interface implements :class:`vtkm::cont::ExecutionObjectBase`. +This means that any ``CellLocator`` can be used in worklets as an ``ExecObject`` argument (as defined in the ``ControlSignature``). +See :chapref:`execution-objects:Execution Objects` for information on ``ExecObject`` arguments to worklets. + +When a ``CellLocator`` class is passed as an ``ExecObject`` argument to a worklet :class:`vtkm::cont::Invoke`, the worklet receives a different object defined in the ``vtkm::exec`` namespace. +This ``CellLocator`` object provides a ``FindCell()`` method that identifies a containing cell given a point location in space. + +.. commonerrors:: + Note that the ``CellLocator`` classes in the respective ``vtkm::cont`` and ``vtkm::exec`` namespaces are different objects with different interfaces despite the similar names. + +Below is the documentation for :class:`vtkm::exec::CellLocatorUniformGrid`, which corresponds to the execution query struct provided by :class:`vtkm::cont::CellLocatorUniformGrid`. +That said, this interface is shared among all the execution query structs provided by all locator types. + +.. doxygenclass:: vtkm::exec::CellLocatorUniformGrid + :members: + +The following example defines a simple worklet to get the value of a point field interpolated to a group of query point coordinates provided. + +.. load-example:: UseCellLocator + :file: GuideExampleCellLocator.cxx + :caption: Using a ``CellLocator`` in a worklet. + + +------------------------------ +Point Locators +------------------------------ + +.. index:: + double: locator; point + +Point Locators in |VTKm| provide a means of building spatial search structures that can later be used to find the nearest neighbor a certain point. +This could be useful in scenarios where the closest pairs of points are needed. +For example, during halo finding of particles in cosmology simulations, pairs of nearest neighbors within certain linking length are used to form clusters of particles. + +Using point locators is a two step process. +The first step is to build the search structure. +This is done by instantiating one of the ``PointLocator`` classes, providing a coordinate system (usually from a :class:`vtkm::cont::DataSet`) representing the location of points that can later be found through queries, and then updating the structure. +Once the point locator is built, it can be used in the execution environment within a filter or worklet. + +Building a Point Locator +============================== + +All point locators in |VTKm| share the same basic interface for the required features of point locators. +This generic interface provides methods to set the coordinate system (with :func:`vtkm::cont::PointLocatorBase::SetCoordinates` and :func:`vtkm::cont::PointLocatorBase::GetCoordinates`) of training points. +Once the coordinates are provided, you may call :func:`vtkm::cont::PointLocatorBase::Update` to construct the search structures. +Although :func:`vtkm::cont::PointLocatorBase::Update` is called from the control environment, the search structure will be built on parallel devices + +.. load-example:: ConstructPointLocator + :file: GuideExamplePointLocator.cxx + :caption: Constructing a ``PointLocator``. + +Point locators in |VTKm| derive the abstract :class:`vtkm::cont::PointLocatorBase` class. + +.. doxygenclass:: vtkm::cont::PointLocatorBase + :members: + +|VTKm| implements a point locator named :class:`vtkm::cont::PointLocatorSparseGrid`. + +.. doxygenclass:: vtkm::cont::PointLocatorSparseGrid + :members: + +Using Point Locators in a Worklet +======================================== + +The :class:`vtkm::cont::PointLocator::Base` interface implements :class:`vtkm::cont::ExecutionObjectBase`. +This means that any ``PointLocator`` can be used in worklets as an ``ExecObject`` argument (as defined in the ``ControlSignature``). +See :chapref:`execution-objects:Execution Objects` for information on ``ExecObject`` arguments to worklets. + +When a ``PointLocator`` class is passed as an ``ExecObject`` argument to a worklet :class:`vtkm::cont::Invoke`, the worklet receives a different object defined in the ``vtkm::exec`` namespace. +This ``PointLocator`` object provides a ``FindNearestNeighbor`` method that identifies the nearest neighbor point given a point location in space. + +.. commonerrors:: + Note that the ``PointLocator`` classes in the respective ``vtkm::cont`` and ``vtkm::exec`` namespaces are different objects with different interfaces despite the similar names. + +Below is the documentation for :class:`vtkm::exec::PointLocatorSparseGrid`, which corresponds to the execution query struct provided by :class:`vtkm::cont::PointLocatorSparseGrid`. +That said, this interface is shared among all the execution query structs provided by all locator types. + +.. doxygenclass:: vtkm::exec::PointLocatorSparseGrid + :members: + +The following example defines a simple worklet that finds points nearest to query locations. + +.. load-example:: UsePointLocator + :file: GuideExamplePointLocator.cxx + :caption: Using a ``PointLocator`` in a worklet. diff --git a/docs/users-guide/part-advanced.rst b/docs/users-guide/part-advanced.rst index 7615895aff..af1c8f6291 100644 --- a/docs/users-guide/part-advanced.rst +++ b/docs/users-guide/part-advanced.rst @@ -16,3 +16,4 @@ Advanced Development fancy-array-handles.rst globals.rst execution-objects.rst + locators.rst diff --git a/vtkm/cont/CellLocatorBase.h b/vtkm/cont/CellLocatorBase.h index bb85fcfccd..fd531563fb 100644 --- a/vtkm/cont/CellLocatorBase.h +++ b/vtkm/cont/CellLocatorBase.h @@ -12,7 +12,6 @@ #include -#include #include #include #include @@ -23,11 +22,11 @@ namespace vtkm namespace cont { -/// \brief Base class for all `CellLocator` classes. +/// @brief Base class for all `CellLocator` classes. /// -/// `CellLocatorBase` uses the curiously recurring template pattern (CRTP). Subclasses -/// must provide their own type for the template parameter. Subclasses must implement -/// `Build()` and `PrepareForExecution()` methods. +/// `CellLocatorBase` subclasses must implement the pure virtual `Build()` method. +/// They also must provide a `PrepareForExecution()` method to satisfy the +/// `ExecutionObjectBase` superclass. /// /// If a derived class changes its state in a way that invalidates its internal search /// structure, it should call the protected `SetModified()` method. This will alert the @@ -62,6 +61,11 @@ public: this->Coords = coords; this->SetModified(); } + /// @copydoc GetCoordinates + VTKM_CONT void SetCoordinates(const vtkm::cont::UnknownArrayHandle& coords) + { + this->SetCoordinates({ "coords", coords }); + } /// @brief Build the search structure used to look up cells. /// diff --git a/vtkm/cont/CellLocatorBoundingIntervalHierarchy.h b/vtkm/cont/CellLocatorBoundingIntervalHierarchy.h index 7598c2567b..7a31e15ff6 100644 --- a/vtkm/cont/CellLocatorBoundingIntervalHierarchy.h +++ b/vtkm/cont/CellLocatorBoundingIntervalHierarchy.h @@ -27,6 +27,17 @@ namespace vtkm namespace cont { +/// @brief A cell locator that performs a recursive division of space. +/// +/// `CellLocatorBoundingIntervalHierarchy` creates a search structure by recursively +/// dividing the space in which data lives. +/// It starts by choosing an axis to split and then defines a number of splitting planes +/// (set with `SetNumberOfSplittingPlanes()`). +/// These splitting planes divide the physical region into partitions, and the cells are +/// divided among these partitions. +/// The algorithm then recurses into each region and repeats the process until the regions +/// are divided to the point where the contain no more than a maximum number of cells +/// (specified with `SetMaxLeafSize()`). class VTKM_CONT_EXPORT CellLocatorBoundingIntervalHierarchy : public vtkm::cont::CellLocatorBase { public: @@ -38,6 +49,8 @@ public: using ExecObjType = vtkm::ListApply; using LastCell = typename ExecObjType::LastCell; + /// Construct a `CellLocatorBoundingIntervalHierarchy` while optionally specifying the + /// number of splitting planes and number of cells in each leaf. VTKM_CONT CellLocatorBoundingIntervalHierarchy(vtkm::IdComponent numPlanes = 4, vtkm::IdComponent maxLeafSize = 5) @@ -48,25 +61,36 @@ public: { } - VTKM_CONT - void SetNumberOfSplittingPlanes(vtkm::IdComponent numPlanes) + /// @brief Specify the number of splitting planes to use each time a region is divided. + /// + /// Larger numbers of splitting planes result in a shallower tree (which is good because + /// it means fewer memory lookups to find a cell), but too many splitting planes could lead + /// to poorly shaped regions that inefficiently partition cells. + /// + /// The default value is 4. + VTKM_CONT void SetNumberOfSplittingPlanes(vtkm::IdComponent numPlanes) { this->NumPlanes = numPlanes; this->SetModified(); } - - VTKM_CONT - vtkm::IdComponent GetNumberOfSplittingPlanes() { return this->NumPlanes; } - - VTKM_CONT - void SetMaxLeafSize(vtkm::IdComponent maxLeafSize) + /// @copydoc SetNumberOfSplittingPlanes + VTKM_CONT vtkm::IdComponent GetNumberOfSplittingPlanes() { return this->NumPlanes; } + + /// @brief Specify the number of cells in each leaf. + /// + /// Larger numbers for the maximum leaf size result in a shallower tree (which is good + /// because it means fewer memory lookups to find a cell), but it also means there will + /// be more cells to check in each leaf (which is bad as checking a cell is slower + /// than decending a tree level). + /// + /// The default value is 5. + VTKM_CONT void SetMaxLeafSize(vtkm::IdComponent maxLeafSize) { this->MaxLeafSize = maxLeafSize; this->SetModified(); } - - VTKM_CONT - vtkm::Id GetMaxLeafSize() { return this->MaxLeafSize; } + /// @copydoc SetMaxLeafSize + VTKM_CONT vtkm::Id GetMaxLeafSize() { return this->MaxLeafSize; } VTKM_CONT ExecObjType PrepareForExecution(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) const; diff --git a/vtkm/cont/CellLocatorRectilinearGrid.h b/vtkm/cont/CellLocatorRectilinearGrid.h index a0d925e212..a9988ffa2e 100644 --- a/vtkm/cont/CellLocatorRectilinearGrid.h +++ b/vtkm/cont/CellLocatorRectilinearGrid.h @@ -19,6 +19,12 @@ namespace vtkm namespace cont { +/// @brief A cell locator optimized for finding cells in a rectilinear grid. +/// +/// This locator is optimized for structured data that has nonuniform axis-aligned spacing. +/// For this cell locator to work, it has to be given a cell set of type +/// `vtkm::cont::CellSetStructured` and a coordinate system using a +/// `vtkm::cont::ArrayHandleCartesianProduct` for its data. class VTKM_CONT_EXPORT CellLocatorRectilinearGrid : public vtkm::cont::CellLocatorBase { using Structured2DType = vtkm::cont::CellSetStructured<2>; diff --git a/vtkm/cont/CellLocatorTwoLevel.h b/vtkm/cont/CellLocatorTwoLevel.h index a756a1ef15..8c0aea5402 100644 --- a/vtkm/cont/CellLocatorTwoLevel.h +++ b/vtkm/cont/CellLocatorTwoLevel.h @@ -37,8 +37,9 @@ namespace cont /// /// The algorithm used in `CellLocatorTwoLevel` is described in the following publication: /// -/// Javor Kalojanov, Markus Billeter, and Philipp Slusallek. "Two-Level Grids for Ray Tracing -/// on GPUs." _Computer Graphics Forum_, 2011, pages 307-314. DOI 10.1111/j.1467-8659.2011.01862.x +/// Javor Kalojanov, Markus Billeter, and Philipp Slusallek. +/// "Two-Level Grids for Ray Tracing on GPUs." +/// _Computer Graphics Forum_, 2011, pages 307-314. DOI 10.1111/j.1467-8659.2011.01862.x /// class VTKM_CONT_EXPORT CellLocatorTwoLevel : public vtkm::cont::CellLocatorBase { @@ -63,24 +64,31 @@ public: { } - /// Get/Set the desired approximate number of cells per level 1 bin + /// @brief Specify the desired approximate number of cells per level 1 bin. /// + /// The default value is 32. void SetDensityL1(vtkm::FloatDefault val) { this->DensityL1 = val; this->SetModified(); } + /// @copydoc SetDensityL1 vtkm::FloatDefault GetDensityL1() const { return this->DensityL1; } - /// Get/Set the desired approximate number of cells per level 1 bin + /// @brief Specify the desired approximate number of cells per level 2 bin. /// + /// This value should be relatively small as it is close to the average number + /// of cells that must be checked for each find. + /// The default value is 2. void SetDensityL2(vtkm::FloatDefault val) { this->DensityL2 = val; this->SetModified(); } + /// @copydoc SetDensityL2 vtkm::FloatDefault GetDensityL2() const { return this->DensityL2; } + /// Print a summary of the state of this locator. void PrintSummary(std::ostream& out) const; ExecObjType PrepareForExecution(vtkm::cont::DeviceAdapterId device, diff --git a/vtkm/cont/CellLocatorUniformBins.h b/vtkm/cont/CellLocatorUniformBins.h index 1f365065ef..3c23676e9c 100644 --- a/vtkm/cont/CellLocatorUniformBins.h +++ b/vtkm/cont/CellLocatorUniformBins.h @@ -34,7 +34,6 @@ namespace cont /// a uniformly dense triangle grid. In some cases the `CellLocatorUniformBins` /// produces a more efficient search structure, especially for GPUs where memory /// access patterns are critical to performance. - class VTKM_CONT_EXPORT CellLocatorUniformBins : public vtkm::cont::CellLocatorBase { template @@ -52,10 +51,19 @@ public: using ExecObjType = vtkm::ListApply; using LastCell = typename ExecObjType::LastCell; - CellLocatorUniformBins() {} - void SetDims(const vtkm::Id3& dims) { this->UniformDims = dims; } - vtkm::Id3 GetDims() const { return this->UniformDims; } + CellLocatorUniformBins() = default; + + /// @brief Specify the dimensions of the grid used to establish bins. + /// + /// This locator will establish a grid over the bounds of the input data + /// that contains the number of bins specified by these dimensions in each + /// direction. Larger dimensions will reduce the number of cells in each bin, + /// but will require more memory. `SetDims()` must be called before `Update()`. + VTKM_CONT void SetDims(const vtkm::Id3& dims) { this->UniformDims = dims; } + /// @copydoc SetDims + VTKM_CONT vtkm::Id3 GetDims() const { return this->UniformDims; } + /// Print a summary of the state of this locator. void PrintSummary(std::ostream& out) const; public: diff --git a/vtkm/cont/CellLocatorUniformGrid.h b/vtkm/cont/CellLocatorUniformGrid.h index d620a08748..00e6b34a49 100644 --- a/vtkm/cont/CellLocatorUniformGrid.h +++ b/vtkm/cont/CellLocatorUniformGrid.h @@ -19,6 +19,14 @@ namespace vtkm namespace cont { +/// @brief A cell locator optimized for finding cells in a uniform grid. +/// +/// This locator is optimized for structured data that has uniform axis-aligned spacing. +/// For this cell locator to work, it has to be given a cell set of type +/// `vtkm::cont::CellSetStructured` and a coordinate system using a +/// `vtkm::cont::ArrayHandleUniformPointCoordinates` for its coordinate system. +/// If the data set matches this structure, then this locator will be faster than +/// any others. class VTKM_CONT_EXPORT CellLocatorUniformGrid : public vtkm::cont::CellLocatorBase { public: diff --git a/vtkm/cont/PointLocatorSparseGrid.h b/vtkm/cont/PointLocatorSparseGrid.h index 00d626728f..917d55582e 100644 --- a/vtkm/cont/PointLocatorSparseGrid.h +++ b/vtkm/cont/PointLocatorSparseGrid.h @@ -37,6 +37,10 @@ class VTKM_CONT_EXPORT PointLocatorSparseGrid : public vtkm::cont::PointLocatorB public: using RangeType = vtkm::Vec; + /// @brief Specify the bounds of the space to search for points. + /// + /// If the spatial range is not set, it will be automatically defined to be + /// the space containing the points. void SetRange(const RangeType& range) { if (this->Range != range) @@ -45,7 +49,7 @@ public: this->SetModified(); } } - + /// @copydoc SetRange const RangeType& GetRange() const { return this->Range; } void SetComputeRangeFromCoordinates() @@ -57,6 +61,14 @@ public: } } + /// @brief Specify the number of bins used in the sparse grid to be searched. + /// + /// Larger dimensions result in smaller bins, which in turn means fewer points are + /// in each bin. This means comparing against fewer points. This is good when searching + /// for coincident points. However, when searching for nearest points a distance away, + /// larger dimensions require searching for more bins. + /// + /// The default number of bins is 32x32x32. void SetNumberOfBins(const vtkm::Id3& bins) { if (this->Dims != bins) @@ -65,7 +77,7 @@ public: this->SetModified(); } } - + /// @copydoc SetNumberOfBins const vtkm::Id3& GetNumberOfBins() const { return this->Dims; } VTKM_CONT diff --git a/vtkm/exec/CellLocatorBoundingIntervalHierarchy.h b/vtkm/exec/CellLocatorBoundingIntervalHierarchy.h index d60a52c3d6..74622d5191 100644 --- a/vtkm/exec/CellLocatorBoundingIntervalHierarchy.h +++ b/vtkm/exec/CellLocatorBoundingIntervalHierarchy.h @@ -60,6 +60,15 @@ struct CellLocatorBoundingIntervalHierarchyNode } }; // struct CellLocatorBoundingIntervalHierarchyNode +/// @brief Structure for locating cells. +/// +/// Use the `FindCell()` method to identify which cell contains a point in space. +/// The `FindCell()` method optionally takes a `LastCell` object, which is a +/// structure nested in this class. The `LastCell` object can help speed locating +/// cells for successive finds at nearby points. +/// +/// This class is provided by `vtkm::cont::CellLocatorBoundingIntervalHierarchy` +/// when passed to a worklet. template class VTKM_ALWAYS_EXPORT CellLocatorBoundingIntervalHierarchy { @@ -83,26 +92,27 @@ public: { } + /// @copydoc vtkm::exec::CellLocatorUniformGrid::LastCell struct LastCell { vtkm::Id CellId = -1; vtkm::Id NodeIdx = -1; }; - VTKM_EXEC - vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, - vtkm::Id& cellId, - vtkm::Vec3f& parametric) const + /// @copydoc vtkm::exec::CellLocatorUniformGrid::FindCell + VTKM_EXEC vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, + vtkm::Id& cellId, + vtkm::Vec3f& parametric) const { LastCell lastCell; return this->FindCellImpl(point, cellId, parametric, lastCell); } - VTKM_EXEC - vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, - vtkm::Id& cellId, - vtkm::Vec3f& parametric, - LastCell& lastCell) const + /// @copydoc vtkm::exec::CellLocatorUniformGrid::FindCell + VTKM_EXEC vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, + vtkm::Id& cellId, + vtkm::Vec3f& parametric, + LastCell& lastCell) const { cellId = -1; diff --git a/vtkm/exec/CellLocatorRectilinearGrid.h b/vtkm/exec/CellLocatorRectilinearGrid.h index 5129fc2eb1..9cae23ff64 100644 --- a/vtkm/exec/CellLocatorRectilinearGrid.h +++ b/vtkm/exec/CellLocatorRectilinearGrid.h @@ -28,6 +28,15 @@ namespace vtkm namespace exec { +/// @brief Structure for locating cells. +/// +/// Use the `FindCell()` method to identify which cell contains a point in space. +/// The `FindCell()` method optionally takes a `LastCell` object, which is a +/// structure nested in this class. The `LastCell` object can help speed locating +/// cells for successive finds at nearby points. +/// +/// This class is provided by `vtkm::cont::CellLocatorRectilinearGrid` +/// when passed to a worklet. class VTKM_ALWAYS_EXPORT CellLocatorRectilinearGrid { private: @@ -43,6 +52,7 @@ private: VTKM_CONT static vtkm::Id3 ToId3(vtkm::Id&& src) { return vtkm::Id3(src, 1, 1); } public: + /// @copydoc vtkm::exec::CellLocatorUniformGrid::LastCell struct LastCell { }; @@ -92,19 +102,19 @@ public: return inside; } - VTKM_EXEC - vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, - vtkm::Id& cellId, - vtkm::Vec3f& parametric, - LastCell& vtkmNotUsed(lastCell)) const + /// @copydoc vtkm::exec::CellLocatorUniformGrid::FindCell + VTKM_EXEC vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, + vtkm::Id& cellId, + vtkm::Vec3f& parametric, + LastCell& vtkmNotUsed(lastCell)) const { return this->FindCell(point, cellId, parametric); } - VTKM_EXEC - vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, - vtkm::Id& cellId, - vtkm::Vec3f& parametric) const + /// @copydoc vtkm::exec::CellLocatorUniformGrid::FindCell + VTKM_EXEC vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, + vtkm::Id& cellId, + vtkm::Vec3f& parametric) const { if (!this->IsInside(point)) { diff --git a/vtkm/exec/CellLocatorTwoLevel.h b/vtkm/exec/CellLocatorTwoLevel.h index 5d0caea8a6..2e22aabc67 100644 --- a/vtkm/exec/CellLocatorTwoLevel.h +++ b/vtkm/exec/CellLocatorTwoLevel.h @@ -88,6 +88,16 @@ namespace exec { //-------------------------------------------------------------------- + +/// @brief Structure for locating cells. +/// +/// Use the `FindCell()` method to identify which cell contains a point in space. +/// The `FindCell()` method optionally takes a `LastCell` object, which is a +/// structure nested in this class. The `LastCell` object can help speed locating +/// cells for successive finds at nearby points. +/// +/// This class is provided by `vtkm::cont::CellLocatorTwoLevel` +/// when passed to a worklet. template class VTKM_ALWAYS_EXPORT CellLocatorTwoLevel { @@ -153,12 +163,14 @@ public: { } + /// @copydoc vtkm::exec::CellLocatorUniformGrid::LastCell struct LastCell { vtkm::Id CellId = -1; vtkm::Id LeafIdx = -1; }; + /// @copydoc vtkm::exec::CellLocatorUniformGrid::FindCell VTKM_EXEC vtkm::ErrorCode FindCell(const FloatVec3& point, vtkm::Id& cellId, FloatVec3& parametric) const { @@ -166,6 +178,7 @@ public: return this->FindCellImpl(point, cellId, parametric, lastCell); } + /// @copydoc vtkm::exec::CellLocatorUniformGrid::FindCell VTKM_EXEC vtkm::ErrorCode FindCell(const FloatVec3& point, vtkm::Id& cellId, diff --git a/vtkm/exec/CellLocatorUniformBins.h b/vtkm/exec/CellLocatorUniformBins.h index cd71f661c1..b81879bded 100644 --- a/vtkm/exec/CellLocatorUniformBins.h +++ b/vtkm/exec/CellLocatorUniformBins.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -26,6 +27,16 @@ namespace exec { //-------------------------------------------------------------------- + +/// @brief Structure for locating cells. +/// +/// Use the `FindCell()` method to identify which cell contains a point in space. +/// The `FindCell()` method optionally takes a `LastCell` object, which is a +/// structure nested in this class. The `LastCell` object can help speed locating +/// cells for successive finds at nearby points. +/// +/// This class is provided by `vtkm::cont::CellLocatorBoundingIntervalHierarchy` +/// when passed to a worklet. template class VTKM_ALWAYS_EXPORT CellLocatorUniformBins { @@ -68,26 +79,27 @@ public: { } + /// @copydoc vtkm::exec::CellLocatorUniformGrid::LastCell struct LastCell { vtkm::Id CellId = -1; vtkm::Id BinIdx = -1; }; - VTKM_EXEC - vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, - vtkm::Id& cellId, - vtkm::Vec3f& parametric) const + /// @copydoc vtkm::exec::CellLocatorUniformGrid::FindCell + VTKM_EXEC vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, + vtkm::Id& cellId, + vtkm::Vec3f& parametric) const { LastCell lastCell; return this->FindCellImpl(point, cellId, parametric, lastCell); } - VTKM_EXEC - vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, - vtkm::Id& cellId, - vtkm::Vec3f& parametric, - LastCell& lastCell) const + /// @copydoc vtkm::exec::CellLocatorUniformGrid::FindCell + VTKM_EXEC vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, + vtkm::Id& cellId, + vtkm::Vec3f& parametric, + LastCell& lastCell) const { vtkm::Id binIdx = this->FindBinIdx(point); diff --git a/vtkm/exec/CellLocatorUniformGrid.h b/vtkm/exec/CellLocatorUniformGrid.h index 63293ae272..2816d1743b 100644 --- a/vtkm/exec/CellLocatorUniformGrid.h +++ b/vtkm/exec/CellLocatorUniformGrid.h @@ -27,6 +27,15 @@ namespace vtkm namespace exec { +/// @brief Structure for locating cells. +/// +/// Use the `FindCell()` method to identify which cell contains a point in space. +/// The `FindCell()` method optionally takes a `LastCell` object, which is a +/// structure nested in this class. The `LastCell` object can help speed locating +/// cells for successive finds at nearby points. +/// +/// This class is provided by `vtkm::cont::CellLocatorUniformGrid` when passed +/// to a worklet. class VTKM_ALWAYS_EXPORT CellLocatorUniformGrid { public: @@ -55,23 +64,45 @@ public: return inside; } + /// @brief Structure capturing the location of a cell in the search structure. + /// + /// An object of this type is passed to and from the `FindCell()` method. + /// If `FindCell()` is called successively with points near each other, the + /// information in this object can reduce the time to find the cell. struct LastCell { }; - VTKM_EXEC - vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, - vtkm::Id& cellId, - vtkm::Vec3f& parametric, - LastCell& vtkmNotUsed(lastCell)) const + /// @brief Locate the cell containing the provided point. + /// + /// Given the point coordinate `point`, this method determines which cell + /// contains that point. The identification of the cell is returned in + /// the `cellId` reference parameter. The method also determines the + /// cell's parametric coordinates to the point and returns that in the + /// `parametric` reference parameter. This result can be used in functions + /// like `vtkm::exec::CellInterpolate()`. + /// + /// `FindCell()` takes an optional `LastCell` parameter. This parameter + /// captures the location of the found cell and can be passed to the next + /// call of `FindCell()`. If the subsequent `FindCell()` call is for a + /// point that is in or near the same cell, the operation may go faster. + /// + /// This method will return `vtkm::ErrorCode::Success` if a cell is found. + /// If a cell is not found, `vtkm::ErrorCode::CellNotFound` is returned + /// and `cellId` is set to `-1`. + VTKM_EXEC vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, + vtkm::Id& cellId, + vtkm::Vec3f& parametric, + LastCell& lastCell) const { + (void)lastCell; return this->FindCell(point, cellId, parametric); } - VTKM_EXEC - vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, - vtkm::Id& cellId, - vtkm::Vec3f& parametric) const + /// @copydoc FindCell + VTKM_EXEC vtkm::ErrorCode FindCell(const vtkm::Vec3f& point, + vtkm::Id& cellId, + vtkm::Vec3f& parametric) const { if (!this->IsInside(point)) { diff --git a/vtkm/exec/PointLocatorSparseGrid.h b/vtkm/exec/PointLocatorSparseGrid.h index 1ad0b3a732..e5372f16d7 100644 --- a/vtkm/exec/PointLocatorSparseGrid.h +++ b/vtkm/exec/PointLocatorSparseGrid.h @@ -19,6 +19,12 @@ namespace vtkm namespace exec { +/// @brief Structure for locating point. +/// +/// Use the `FindNearestNeighbor()` method to identify which cell contains a point in space. +/// +/// This class is provided by `vtkm::cont::PointLocatorSparseGrid` when passed +/// to a worklet. class VTKM_ALWAYS_EXPORT PointLocatorSparseGrid { public: @@ -44,11 +50,11 @@ public: { } - /// \brief Nearest neighbor search using a Uniform Grid + /// @brief Nearest neighbor search using a Uniform Grid /// - /// Parallel search of nearesat neighbor for each point in the \c queryPoints in the set of - /// \c coords. Returns neareast neighbot in \c nearestNeighborIds and distances to nearest - /// neighbor in \c distances. + /// Parallel search of nearesat neighbor for each point in the `queryPoints` in the set of + /// `coords`. Returns neareast neighbot in `nearestNeighborIds` and distances to nearest + /// neighbor in `distances`. /// /// \param queryPoint Point coordinates to query for nearest neighbor. /// \param nearestNeighborId Neareast neighbor in the training dataset for each points in -- GitLab From ed82597f2b41dc57eb761ac6def817b8ca624a8d Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Thu, 19 Dec 2024 16:54:29 -0700 Subject: [PATCH 62/73] Contour Polygons with Marching Cubes Previously, the Marching Cubes contouring algorithm only had case tables for 3D polyhedra. This means that if you attempted to contour or slice surfaces or lines, you would not get any output. However, there are many valid use cases for contouring this type of data. This change adds case tables for triangles, quadrilaterals, and lines. It also adds some special cases for general polygons and poly-lines. These special cases do not use tables. Rather, there is a special routine that iterates over the points since these cells types can have any number of points. Note that to preserve the speed of the operation, contours for cells of a single dimension type are done at one time. By default, the contour filter will try 3D cells, then 2D cells, then 1D cells. It is also possible to select a particular cell dimension or to append results from all cell types together. In this latest case, the output cells will be of type `CellSetExplicit` instead of `CellSetSingleType`. --- data/baseline/filter/contour-poly.png | 3 + data/data/unstructured/mixed-cell-shapes.vtk | 3 + data/data/unstructured/poly_contour_cases.vtk | 3 + docs/changelog/contour-polys.md | 18 + docs/users-guide/provided-filters.rst | 17 +- vtkm/filter/contour/AbstractContour.h | 65 +- vtkm/filter/contour/CMakeLists.txt | 1 + vtkm/filter/contour/Contour.cxx | 1 + vtkm/filter/contour/ContourDimension.h | 42 + vtkm/filter/contour/ContourMarchingCells.cxx | 73 +- vtkm/filter/contour/ContourMarchingCells.h | 6 +- .../testing/RenderTestContourFilter.cxx | 24 + .../contour/testing/UnitTestContourFilter.cxx | 66 +- .../testing/UnitTestContourFilterNormals.cxx | 6 +- .../contour/worklet/ContourMarchingCells.h | 140 +- .../worklet/contour/MarchingCellTables.h | 1252 ++++++++++------- .../worklet/contour/MarchingCellTables.h.in | 341 ++++- .../contour/worklet/contour/MarchingCells.h | 400 +++++- 18 files changed, 1752 insertions(+), 709 deletions(-) create mode 100644 data/baseline/filter/contour-poly.png create mode 100644 data/data/unstructured/mixed-cell-shapes.vtk create mode 100644 data/data/unstructured/poly_contour_cases.vtk create mode 100644 docs/changelog/contour-polys.md create mode 100644 vtkm/filter/contour/ContourDimension.h diff --git a/data/baseline/filter/contour-poly.png b/data/baseline/filter/contour-poly.png new file mode 100644 index 0000000000..b2d1e86af7 --- /dev/null +++ b/data/baseline/filter/contour-poly.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4cc54950e4a59077715dc85afa76db9f7bc503e3458dc6a1cdaa07a0ecfa2dae +size 44574 diff --git a/data/data/unstructured/mixed-cell-shapes.vtk b/data/data/unstructured/mixed-cell-shapes.vtk new file mode 100644 index 0000000000..3da0212071 --- /dev/null +++ b/data/data/unstructured/mixed-cell-shapes.vtk @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:303a29e437be8a08cc4e33ad26128a81ff2c6aae449339ab66dbfc00939eefec +size 1126 diff --git a/data/data/unstructured/poly_contour_cases.vtk b/data/data/unstructured/poly_contour_cases.vtk new file mode 100644 index 0000000000..5876e7f421 --- /dev/null +++ b/data/data/unstructured/poly_contour_cases.vtk @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8044a2d5cad64d1e5b3b3d972eb69806c8ccb27a30dacfc4056e8ba73be8484 +size 659 diff --git a/docs/changelog/contour-polys.md b/docs/changelog/contour-polys.md new file mode 100644 index 0000000000..0a0d0b4bec --- /dev/null +++ b/docs/changelog/contour-polys.md @@ -0,0 +1,18 @@ +## Contour Polygons with Marching Cubes + +Previously, the Marching Cubes contouring algorithm only had case tables for 3D +polyhedra. This means that if you attempted to contour or slice surfaces or +lines, you would not get any output. However, there are many valid use cases for +contouring this type of data. + +This change adds case tables for triangles, quadrilaterals, and lines. It also +adds some special cases for general polygons and poly-lines. These special cases +do not use tables. Rather, there is a special routine that iterates over the +points since these cells types can have any number of points. + +Note that to preserve the speed of the operation, contours for cells of a single +dimension type are done at one time. By default, the contour filter will try 3D +cells, then 2D cells, then 1D cells. It is also possible to select a particular +cell dimension or to append results from all cell types together. In this latest +case, the output cells will be of type `CellSetExplicit` instead of +`CellSetSingleType`. diff --git a/docs/users-guide/provided-filters.rst b/docs/users-guide/provided-filters.rst index cd0c9bd221..95dad99a2e 100644 --- a/docs/users-guide/provided-filters.rst +++ b/docs/users-guide/provided-filters.rst @@ -180,29 +180,32 @@ The contouring/isosurface algorithm is implemented by :class:`vtkm::filter::cont :class:`vtkm::filter::contour::Contour` also inherits the following methods. .. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetIsoValue(vtkm::Float64) - .. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetIsoValue(vtkm::Id, vtkm::Float64) - .. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetIsoValues - .. doxygenfunction:: vtkm::filter::contour::AbstractContour::GetIsoValue .. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetGenerateNormals - .. doxygenfunction:: vtkm::filter::contour::AbstractContour::GetGenerateNormals .. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetComputeFastNormals - .. doxygenfunction:: vtkm::filter::contour::AbstractContour::GetComputeFastNormals .. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetNormalArrayName - .. doxygenfunction:: vtkm::filter::contour::AbstractContour::GetNormalArrayName -.. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetMergeDuplicatePoints +.. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetInputCellDimension +.. doxygenfunction:: vtkm::filter::contour::AbstractContour::GetInputCellDimension +.. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetInputCellDimensionToAuto +.. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetInputCellDimensionToAll +.. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetInputCellDimensionToPolyhedra +.. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetInputCellDimensionToPolygons +.. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetInputCellDimensionToLines +.. doxygenfunction:: vtkm::filter::contour::AbstractContour::SetMergeDuplicatePoints .. doxygenfunction:: vtkm::filter::contour::AbstractContour::GetMergeDuplicatePoints +.. doxygenenum:: vtkm::filter::contour::ContourDimension + .. load-example:: Contour :file: GuideExampleProvidedFilters.cxx :caption: Using :class:`vtkm::filter::contour::Contour`. diff --git a/vtkm/filter/contour/AbstractContour.h b/vtkm/filter/contour/AbstractContour.h index 5181f0ba59..a1a1bda997 100644 --- a/vtkm/filter/contour/AbstractContour.h +++ b/vtkm/filter/contour/AbstractContour.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -127,6 +128,65 @@ public: VTKM_CONT const std::string& GetNormalArrayName() const { return this->NormalArrayName; } + /// @brief Specify the dimension of cells on which to operate the contour. + /// + /// The contour filters operate on cells of a particular dimension + /// (i.e., polyhedra, polygons, or lines) and generate simplicies + /// of one less dimension (i.e., triangles, lines, or vertices). + /// The default is `vtkm::filter::contour::ContourDimension::Auto`. + VTKM_CONT void SetInputCellDimension(vtkm::filter::contour::ContourDimension dimension) + { + this->InputCellDimension = dimension; + } + + /// @copydoc SetInputCellDimension + VTKM_CONT vtkm::filter::contour::ContourDimension GetInputCellDimension() const + { + return this->InputCellDimension; + } + + /// @brief Specifies an automatic selection of the input cell dimension. + /// + /// This option first tries to contour polyhedra. If any polyhedra have the + /// contour, that is used. Otherwise, it tries to contour polygons. + /// If that fails, lines are contoured. + VTKM_CONT void SetInputCellDimensionToAuto() + { + this->SetInputCellDimension(vtkm::filter::contour::ContourDimension::Auto); + } + + /// @brief Specifies a combination of all possible contours. + /// + /// This option runs contour on all possible dimension types and then merges all contours together. + VTKM_CONT void SetInputCellDimensionToAll() + { + this->SetInputCellDimension(vtkm::filter::contour::ContourDimension::All); + } + + /// @brief Specifies running contours on polyhedra. + /// + /// This option runs contour on polyhedra, generating triangles. + VTKM_CONT void SetInputCellDimensionToPolyhedra() + { + this->SetInputCellDimension(vtkm::filter::contour::ContourDimension::Polyhedra); + } + + /// @brief Specifies running contours on polygons. + /// + /// This option runs contour on polygons, generating lines. + VTKM_CONT void SetInputCellDimensionToPolygons() + { + this->SetInputCellDimension(vtkm::filter::contour::ContourDimension::Polygons); + } + + /// @brief Specifies running contours on lines. + /// + /// This option runs contour on lines, generating vertices. + VTKM_CONT void SetInputCellDimensionToLines() + { + this->SetInputCellDimension(vtkm::filter::contour::ContourDimension::Lines); + } + /// Set whether the points generated should be unique for every triangle /// or will duplicate points be merged together. Duplicate points are identified /// by the unique edge it was generated from. @@ -144,7 +204,7 @@ public: /// Get whether the points generated should be unique for every triangle /// or will duplicate points be merged together. VTKM_CONT - bool GetMergeDuplicatePoints() { return this->MergeDuplicatedPoints; } + bool GetMergeDuplicatePoints() const { return this->MergeDuplicatedPoints; } protected: /// \brief Map a given field to the output \c DataSet , depending on its type. @@ -223,6 +283,9 @@ protected: bool GenerateNormals = true; bool ComputeFastNormals = false; + vtkm::filter::contour::ContourDimension InputCellDimension = + vtkm::filter::contour::ContourDimension::Auto; + bool AddInterpolationEdgeIds = false; bool MergeDuplicatedPoints = true; std::string NormalArrayName = "normals"; diff --git a/vtkm/filter/contour/CMakeLists.txt b/vtkm/filter/contour/CMakeLists.txt index c7a4105b88..ac885f386a 100644 --- a/vtkm/filter/contour/CMakeLists.txt +++ b/vtkm/filter/contour/CMakeLists.txt @@ -13,6 +13,7 @@ set(contour_headers ClipWithField.h ClipWithImplicitFunction.h Contour.h + ContourDimension.h ContourFlyingEdges.h ContourMarchingCells.h MIRFilter.h diff --git a/vtkm/filter/contour/Contour.cxx b/vtkm/filter/contour/Contour.cxx index 466a3657d2..e59dd72702 100644 --- a/vtkm/filter/contour/Contour.cxx +++ b/vtkm/filter/contour/Contour.cxx @@ -52,6 +52,7 @@ vtkm::cont::DataSet Contour::DoExecute(const vtkm::cont::DataSet& inDataSet) implementation->SetGenerateNormals(this->GetGenerateNormals()); implementation->SetAddInterpolationEdgeIds(this->GetAddInterpolationEdgeIds()); implementation->SetNormalArrayName(this->GetNormalArrayName()); + implementation->SetInputCellDimension(this->GetInputCellDimension()); implementation->SetActiveField(this->GetActiveFieldName()); implementation->SetFieldsToPass(this->GetFieldsToPass()); implementation->SetNumberOfIsoValues(this->GetNumberOfIsoValues()); diff --git a/vtkm/filter/contour/ContourDimension.h b/vtkm/filter/contour/ContourDimension.h new file mode 100644 index 0000000000..033cb82963 --- /dev/null +++ b/vtkm/filter/contour/ContourDimension.h @@ -0,0 +1,42 @@ +//============================================================================ +// 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. +//============================================================================ +#ifndef vtk_m_filter_contour_ContourDimension_h +#define vtk_m_filter_contour_ContourDimension_h + +namespace vtkm +{ +namespace filter +{ +namespace contour +{ + +/// @brief Identifies what type cells will be contoured. +/// +/// The `ContourDimension` enum is used by the contour filters to specify which +/// dimension of cell to contour by. +enum struct ContourDimension +{ + /// @copydoc vtkm::filter::contour::AbstractContour::SetInputCellDimensionToAuto + Auto, + /// @copydoc vtkm::filter::contour::AbstractContour::SetInputCellDimensionToAll + All, + /// @copydoc vtkm::filter::contour::AbstractContour::SetInputCellDimensionToPolyhedra + Polyhedra, + /// @copydoc vtkm::filter::contour::AbstractContour::SetInputCellDimensionToPolygons + Polygons, + /// @copydoc vtkm::filter::contour::AbstractContour::SetInputCellDimensionToLines + Lines +}; + +} +} +} // namespace vtkm::filter::contour + +#endif // vtk_m_filter_contour_ContourDimension_h diff --git a/vtkm/filter/contour/ContourMarchingCells.cxx b/vtkm/filter/contour/ContourMarchingCells.cxx index 38a3ca5005..bf6a4ffd46 100644 --- a/vtkm/filter/contour/ContourMarchingCells.cxx +++ b/vtkm/filter/contour/ContourMarchingCells.cxx @@ -13,7 +13,7 @@ #include #include - +#include namespace vtkm { @@ -23,6 +23,70 @@ namespace contour { //----------------------------------------------------------------------------- vtkm::cont::DataSet ContourMarchingCells::DoExecute(const vtkm::cont::DataSet& inDataSet) +{ + switch (this->GetInputCellDimension()) + { + case vtkm::filter::contour::ContourDimension::Auto: + { + vtkm::cont::DataSet output = this->DoExecuteDimension<3>(inDataSet); + if (output.GetNumberOfCells() > 0) + { + return output; + } + output = this->DoExecuteDimension<2>(inDataSet); + if (output.GetNumberOfCells() > 0) + { + return output; + } + output = this->DoExecuteDimension<1>(inDataSet); + return output; + } + case vtkm::filter::contour::ContourDimension::All: + { + vtkm::cont::PartitionedDataSet allData; + vtkm::cont::DataSet output = this->DoExecuteDimension<3>(inDataSet); + if (output.GetNumberOfCells() > 0) + { + allData.AppendPartition(output); + } + output = this->DoExecuteDimension<2>(inDataSet); + if (output.GetNumberOfCells() > 0) + { + allData.AppendPartition(output); + } + output = this->DoExecuteDimension<1>(inDataSet); + if (output.GetNumberOfCells() > 0) + { + allData.AppendPartition(output); + } + if (allData.GetNumberOfPartitions() > 1) + { + vtkm::filter::multi_block::MergeDataSets merge; + return merge.Execute(allData).GetPartition(0); + } + else if (allData.GetNumberOfPartitions() == 1) + { + return allData.GetPartition(0); + } + else + { + return output; + } + } + case vtkm::filter::contour::ContourDimension::Polyhedra: + return this->DoExecuteDimension<3>(inDataSet); + case vtkm::filter::contour::ContourDimension::Polygons: + return this->DoExecuteDimension<2>(inDataSet); + case vtkm::filter::contour::ContourDimension::Lines: + return this->DoExecuteDimension<1>(inDataSet); + default: + throw vtkm::cont::ErrorBadValue("Invalid value for ContourDimension."); + } +} + + +template +vtkm::cont::DataSet ContourMarchingCells::DoExecuteDimension(const vtkm::cont::DataSet& inDataSet) { vtkm::worklet::ContourMarchingCells worklet; worklet.SetMergeDuplicatePoints(this->GetMergeDuplicatePoints()); @@ -59,11 +123,12 @@ vtkm::cont::DataSet ContourMarchingCells::DoExecute(const vtkm::cont::DataSet& i if (this->GenerateNormals && !this->GetComputeFastNormals()) { - outputCells = worklet.Run(ivalues, inputCells, inputCoords, concrete, vertices, normals); + outputCells = + worklet.Run(ivalues, inputCells, inputCoords, concrete, vertices, normals); } else { - outputCells = worklet.Run(ivalues, inputCells, inputCoords, concrete, vertices); + outputCells = worklet.Run(ivalues, inputCells, inputCoords, concrete, vertices); } }; @@ -76,9 +141,9 @@ vtkm::cont::DataSet ContourMarchingCells::DoExecute(const vtkm::cont::DataSet& i this->ExecuteGenerateNormals(output, normals); this->ExecuteAddInterpolationEdgeIds(output, worklet); - return output; } + } // namespace contour } // namespace filter } // namespace vtkm diff --git a/vtkm/filter/contour/ContourMarchingCells.h b/vtkm/filter/contour/ContourMarchingCells.h index 653a1eaadf..218e4c8cd1 100644 --- a/vtkm/filter/contour/ContourMarchingCells.h +++ b/vtkm/filter/contour/ContourMarchingCells.h @@ -33,8 +33,10 @@ class VTKM_FILTER_CONTOUR_EXPORT ContourMarchingCells : public vtkm::filter::contour::AbstractContour { protected: - VTKM_CONT - vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& result) override; + VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& result) override; + + template + VTKM_CONT vtkm::cont::DataSet DoExecuteDimension(const vtkm::cont::DataSet& result); }; } // namespace contour } // namespace filter diff --git a/vtkm/filter/contour/testing/RenderTestContourFilter.cxx b/vtkm/filter/contour/testing/RenderTestContourFilter.cxx index 607abbd6f4..e813d0229d 100644 --- a/vtkm/filter/contour/testing/RenderTestContourFilter.cxx +++ b/vtkm/filter/contour/testing/RenderTestContourFilter.cxx @@ -160,12 +160,36 @@ void TestContourFilterTangle() vtkm::rendering::testing::RenderTest(result, "tangle", "filter/contour-tangle.png", testOptions); } +void TestContourFilterPoly() +{ + std::cout << "Generate Image for Contour filter on polygons" << std::endl; + + auto pathname = vtkm::cont::testing::Testing::DataPath("unstructured/poly_contour_cases.vtk"); + vtkm::io::VTKDataSetReader reader(pathname); + vtkm::cont::DataSet dataSet = reader.ReadDataSet(); + + vtkm::filter::contour::Contour contour; + contour.SetIsoValues({ -0.20, -0.12, -0.04, 0.04, 0.12, 0.20 }); + contour.SetActiveField("PerlinNoise"); + contour.SetMergeDuplicatePoints(true); + auto result = contour.Execute(dataSet); + + result.PrintSummary(std::cout); + + vtkm::rendering::testing::RenderTestOptions testOptions; + testOptions.Mapper = vtkm::rendering::testing::MapperType::Cylinder; + testOptions.Radius = 0.01f; + vtkm::rendering::testing::RenderTest( + result, "PerlinNoise", "filter/contour-poly.png", testOptions); +} + void TestContourFilter() { TestContourFilterUniform(); TestContourFilterUniformBoundaries(); TestContourFilterTangle(); TestContourFilterWedge(); + TestContourFilterPoly(); } } // namespace diff --git a/vtkm/filter/contour/testing/UnitTestContourFilter.cxx b/vtkm/filter/contour/testing/UnitTestContourFilter.cxx index 61ccdc97b0..51e9b32bee 100644 --- a/vtkm/filter/contour/testing/UnitTestContourFilter.cxx +++ b/vtkm/filter/contour/testing/UnitTestContourFilter.cxx @@ -92,9 +92,7 @@ public: } vtkm::cont::CoordinateSystem coords = result.GetCoordinateSystem(); - vtkm::cont::UnknownCellSet dcells = result.GetCellSet(); - using CellSetType = vtkm::cont::CellSetSingleType<>; - const CellSetType& cells = dcells.AsCellSet(); + vtkm::cont::UnknownCellSet cells = result.GetCellSet(); //verify that the number of points is correct (72) //verify that the number of cells is correct (160) @@ -113,10 +111,7 @@ public: "Shouldn't have less coordinates than the unmerged version"); //verify that the number of cells is correct (160) - vtkm::cont::UnknownCellSet dcells = result.GetCellSet(); - - using CellSetType = vtkm::cont::CellSetSingleType<>; - const CellSetType& cells = dcells.AsCellSet(); + vtkm::cont::UnknownCellSet cells = result.GetCellSet(); VTKM_TEST_ASSERT(cells.GetNumberOfCells() == 160, ""); } } @@ -158,9 +153,6 @@ public: vtkm::cont::DataSet dataSet = reader.ReadDataSet(); - vtkm::cont::CellSetSingleType<> cellSet; - dataSet.GetCellSet().AsCellSet(cellSet); - vtkm::cont::ArrayHandle fieldArray; dataSet.GetPointField("gyroid").GetData().AsArrayHandle(fieldArray); @@ -245,6 +237,58 @@ public: "Wrong number of cells in rectilinear contour"); } + void TestMixedShapes() const + { + auto pathname = vtkm::cont::testing::Testing::DataPath("unstructured/mixed-cell-shapes.vtk"); + vtkm::io::VTKDataSetReader reader(pathname); + vtkm::cont::DataSet input = reader.ReadDataSet(); + + // Single-cell contour + vtkm::filter::contour::Contour filter; + filter.SetActiveField("scalars"); + filter.SetMergeDuplicatePoints(true); + filter.SetIsoValues({ 5.5, 9.5, 11.5, 14.5, 17.5, 20.5, 25.5 }); + + { + filter.SetInputCellDimensionToAuto(); + vtkm::cont::DataSet output = filter.Execute(input); + VTKM_TEST_ASSERT(output.GetNumberOfPoints() == 18); + VTKM_TEST_ASSERT(output.GetNumberOfCells() == 10); + VTKM_TEST_ASSERT(output.GetCellSet().GetCellShape(0) == vtkm::CELL_SHAPE_TRIANGLE); + } + + { + filter.SetInputCellDimensionToPolyhedra(); + vtkm::cont::DataSet output = filter.Execute(input); + VTKM_TEST_ASSERT(output.GetNumberOfPoints() == 18); + VTKM_TEST_ASSERT(output.GetNumberOfCells() == 10); + VTKM_TEST_ASSERT(output.GetCellSet().GetCellShape(0) == vtkm::CELL_SHAPE_TRIANGLE); + } + + { + filter.SetInputCellDimensionToPolygons(); + vtkm::cont::DataSet output = filter.Execute(input); + VTKM_TEST_ASSERT(output.GetNumberOfPoints() == 16); + VTKM_TEST_ASSERT(output.GetNumberOfCells() == 8); + VTKM_TEST_ASSERT(output.GetCellSet().GetCellShape(0) == vtkm::CELL_SHAPE_LINE); + } + + { + filter.SetInputCellDimensionToLines(); + vtkm::cont::DataSet output = filter.Execute(input); + VTKM_TEST_ASSERT(output.GetNumberOfPoints() == 2); + VTKM_TEST_ASSERT(output.GetNumberOfCells() == 2); + VTKM_TEST_ASSERT(output.GetCellSet().GetCellShape(0) == vtkm::CELL_SHAPE_VERTEX); + } + + { + filter.SetInputCellDimensionToAll(); + vtkm::cont::DataSet output = filter.Execute(input); + VTKM_TEST_ASSERT(output.GetNumberOfPoints() == 36); + VTKM_TEST_ASSERT(output.GetNumberOfCells() == 20); + } + } + void operator()() const { this->TestContourUniformGrid(72); @@ -265,6 +309,8 @@ public: this->TestNonUniformStructured(); this->TestUnsupportedFlyingEdges(); + + this->TestMixedShapes(); } }; // class TestContourFilter diff --git a/vtkm/filter/contour/testing/UnitTestContourFilterNormals.cxx b/vtkm/filter/contour/testing/UnitTestContourFilterNormals.cxx index e149bcbea4..16fd69ea00 100644 --- a/vtkm/filter/contour/testing/UnitTestContourFilterNormals.cxx +++ b/vtkm/filter/contour/testing/UnitTestContourFilterNormals.cxx @@ -48,8 +48,7 @@ vtkm::cont::DataSet MakeNormalsTestDataSet() // Verify that the direction of the normals is consistent with the triangle winding. void CheckWinding(const vtkm::cont::DataSet& contour) { - vtkm::cont::CellSetSingleType<> cellSet; - contour.GetCellSet().AsCellSet(cellSet); + vtkm::cont::UnknownCellSet cellSet = contour.GetCellSet(); vtkm::cont::ArrayHandle coords; contour.GetCoordinateSystem().GetData().AsArrayHandle(coords); @@ -61,8 +60,9 @@ void CheckWinding(const vtkm::cont::DataSet& contour) for (vtkm::Id triId = 0; triId < cellSet.GetNumberOfCells(); ++triId) { + VTKM_TEST_ASSERT(cellSet.GetNumberOfPointsInCell(triId) == 3); vtkm::Id3 pointIds; - cellSet.GetIndices(triId, pointIds); + cellSet.GetCellPointIds(triId, &pointIds[0]); vtkm::Vec3f facetNormal = vtkm::TriangleNormal( coordsPortal.Get(pointIds[0]), coordsPortal.Get(pointIds[1]), coordsPortal.Get(pointIds[2])); diff --git a/vtkm/filter/contour/worklet/ContourMarchingCells.h b/vtkm/filter/contour/worklet/ContourMarchingCells.h index 9ecdde5692..079c9e30df 100644 --- a/vtkm/filter/contour/worklet/ContourMarchingCells.h +++ b/vtkm/filter/contour/worklet/ContourMarchingCells.h @@ -32,6 +32,7 @@ namespace worklet namespace contour { +template struct DeduceCoordType { template @@ -40,10 +41,11 @@ struct DeduceCoordType vtkm::cont::CellSetSingleType<>& result, Args&&... args) const { - result = marching_cells::execute(cells, coords, std::forward(args)...); + result = marching_cells::execute(cells, coords, std::forward(args)...); } }; +template struct DeduceCellType { template @@ -58,18 +60,20 @@ struct DeduceCellType }; // Declared outside of class, non-inline so that instantiations can be exported correctly. +template template -void DeduceCellType::operator()(const CellSetType& cells, - const vtkm::cont::CoordinateSystem& coordinateSystem, - vtkm::cont::CellSetSingleType<>& outputCells, - const std::vector& isovalues, - const vtkm::cont::ArrayHandle& input, - vtkm::cont::ArrayHandle& vertices, - vtkm::cont::ArrayHandle& normals, - vtkm::worklet::contour::CommonState& sharedState) const +void DeduceCellType::operator()( + const CellSetType& cells, + const vtkm::cont::CoordinateSystem& coordinateSystem, + vtkm::cont::CellSetSingleType<>& outputCells, + const std::vector& isovalues, + const vtkm::cont::ArrayHandle& input, + vtkm::cont::ArrayHandle& vertices, + vtkm::cont::ArrayHandle& normals, + vtkm::worklet::contour::CommonState& sharedState) const { vtkm::cont::CastAndCall(coordinateSystem, - contour::DeduceCoordType{}, + contour::DeduceCoordType{}, cells, outputCells, isovalues, @@ -125,7 +129,7 @@ public: public: // Filter called without normals generation - template + template VTKM_CONT vtkm::cont::CellSetSingleType<> Run( const std::vector& isovalues, const vtkm::cont::UnknownCellSet& cells, @@ -138,7 +142,7 @@ public: vtkm::cont::CellSetSingleType<> outputCells; vtkm::cont::CastAndCall(cells, - contour::DeduceCellType{}, + contour::DeduceCellType{}, coordinateSystem, outputCells, isovalues, @@ -150,7 +154,7 @@ public: } // Filter called with normals generation - template + template VTKM_CONT vtkm::cont::CellSetSingleType<> Run( const std::vector& isovalues, const vtkm::cont::UnknownCellSet& cells, @@ -163,7 +167,7 @@ public: vtkm::cont::CellSetSingleType<> outputCells; vtkm::cont::CastAndCall(cells, - contour::DeduceCellType{}, + contour::DeduceCellType{}, coordinateSystem, outputCells, isovalues, @@ -183,7 +187,7 @@ private: } // namespace vtkm::worklet VTKM_INSTANTIATION_BEGIN -extern template void vtkm::worklet::contour::DeduceCellType::operator()( +extern template void vtkm::worklet::contour::DeduceCellType<2>::operator()( const vtkm::cont::CellSetStructured<2>& cells, const vtkm::cont::CoordinateSystem& coordinateSystem, vtkm::cont::CellSetSingleType<>& outputCells, @@ -194,7 +198,7 @@ extern template void vtkm::worklet::contour::DeduceCellType::operator()( vtkm::worklet::contour::CommonState& sharedState) const; VTKM_INSTANTIATION_END VTKM_INSTANTIATION_BEGIN -extern template void vtkm::worklet::contour::DeduceCellType::operator()( +extern template void vtkm::worklet::contour::DeduceCellType<2>::operator()( const vtkm::cont::CellSetStructured<2>& cells, const vtkm::cont::CoordinateSystem& coordinateSystem, vtkm::cont::CellSetSingleType<>& outputCells, @@ -206,7 +210,7 @@ extern template void vtkm::worklet::contour::DeduceCellType::operator()( VTKM_INSTANTIATION_END VTKM_INSTANTIATION_BEGIN -extern template void vtkm::worklet::contour::DeduceCellType::operator()( +extern template void vtkm::worklet::contour::DeduceCellType<3>::operator()( const vtkm::cont::CellSetStructured<3>& cells, const vtkm::cont::CoordinateSystem& coordinateSystem, vtkm::cont::CellSetSingleType<>& outputCells, @@ -217,7 +221,7 @@ extern template void vtkm::worklet::contour::DeduceCellType::operator()( vtkm::worklet::contour::CommonState& sharedState) const; VTKM_INSTANTIATION_END VTKM_INSTANTIATION_BEGIN -extern template void vtkm::worklet::contour::DeduceCellType::operator()( +extern template void vtkm::worklet::contour::DeduceCellType<3>::operator()( const vtkm::cont::CellSetStructured<3>& cells, const vtkm::cont::CoordinateSystem& coordinateSystem, vtkm::cont::CellSetSingleType<>& outputCells, @@ -229,7 +233,99 @@ extern template void vtkm::worklet::contour::DeduceCellType::operator()( VTKM_INSTANTIATION_END VTKM_INSTANTIATION_BEGIN -extern template void vtkm::worklet::contour::DeduceCellType::operator()( +extern template void vtkm::worklet::contour::DeduceCellType<3>::operator()( + const vtkm::cont::CellSetExplicit<>& cells, + const vtkm::cont::CoordinateSystem& coordinateSystem, + vtkm::cont::CellSetSingleType<>& outputCells, + const std::vector& isovalues, + const vtkm::cont::ArrayHandle& input, + vtkm::cont::ArrayHandle& vertices, + vtkm::cont::ArrayHandle& normals, + vtkm::worklet::contour::CommonState& sharedState) const; +VTKM_INSTANTIATION_END +VTKM_INSTANTIATION_BEGIN +extern template void vtkm::worklet::contour::DeduceCellType<3>::operator()( + const vtkm::cont::CellSetExplicit<>& cells, + const vtkm::cont::CoordinateSystem& coordinateSystem, + vtkm::cont::CellSetSingleType<>& outputCells, + const std::vector& isovalues, + const vtkm::cont::ArrayHandle& input, + vtkm::cont::ArrayHandle& vertices, + vtkm::cont::ArrayHandle& normals, + vtkm::worklet::contour::CommonState& sharedState) const; +VTKM_INSTANTIATION_END + +VTKM_INSTANTIATION_BEGIN +extern template void vtkm::worklet::contour::DeduceCellType<3>::operator()( + const vtkm::cont::CellSetSingleType<>& cells, + const vtkm::cont::CoordinateSystem& coordinateSystem, + vtkm::cont::CellSetSingleType<>& outputCells, + const std::vector& isovalues, + const vtkm::cont::ArrayHandle& input, + vtkm::cont::ArrayHandle& vertices, + vtkm::cont::ArrayHandle& normals, + vtkm::worklet::contour::CommonState& sharedState) const; +VTKM_INSTANTIATION_END +VTKM_INSTANTIATION_BEGIN +extern template void vtkm::worklet::contour::DeduceCellType<3>::operator()( + const vtkm::cont::CellSetSingleType<>& cells, + const vtkm::cont::CoordinateSystem& coordinateSystem, + vtkm::cont::CellSetSingleType<>& outputCells, + const std::vector& isovalues, + const vtkm::cont::ArrayHandle& input, + vtkm::cont::ArrayHandle& vertices, + vtkm::cont::ArrayHandle& normals, + vtkm::worklet::contour::CommonState& sharedState) const; +VTKM_INSTANTIATION_END + +VTKM_INSTANTIATION_BEGIN +extern template void vtkm::worklet::contour::DeduceCellType<2>::operator()( + const vtkm::cont::CellSetExplicit<>& cells, + const vtkm::cont::CoordinateSystem& coordinateSystem, + vtkm::cont::CellSetSingleType<>& outputCells, + const std::vector& isovalues, + const vtkm::cont::ArrayHandle& input, + vtkm::cont::ArrayHandle& vertices, + vtkm::cont::ArrayHandle& normals, + vtkm::worklet::contour::CommonState& sharedState) const; +VTKM_INSTANTIATION_END +VTKM_INSTANTIATION_BEGIN +extern template void vtkm::worklet::contour::DeduceCellType<2>::operator()( + const vtkm::cont::CellSetExplicit<>& cells, + const vtkm::cont::CoordinateSystem& coordinateSystem, + vtkm::cont::CellSetSingleType<>& outputCells, + const std::vector& isovalues, + const vtkm::cont::ArrayHandle& input, + vtkm::cont::ArrayHandle& vertices, + vtkm::cont::ArrayHandle& normals, + vtkm::worklet::contour::CommonState& sharedState) const; +VTKM_INSTANTIATION_END + +VTKM_INSTANTIATION_BEGIN +extern template void vtkm::worklet::contour::DeduceCellType<2>::operator()( + const vtkm::cont::CellSetSingleType<>& cells, + const vtkm::cont::CoordinateSystem& coordinateSystem, + vtkm::cont::CellSetSingleType<>& outputCells, + const std::vector& isovalues, + const vtkm::cont::ArrayHandle& input, + vtkm::cont::ArrayHandle& vertices, + vtkm::cont::ArrayHandle& normals, + vtkm::worklet::contour::CommonState& sharedState) const; +VTKM_INSTANTIATION_END +VTKM_INSTANTIATION_BEGIN +extern template void vtkm::worklet::contour::DeduceCellType<2>::operator()( + const vtkm::cont::CellSetSingleType<>& cells, + const vtkm::cont::CoordinateSystem& coordinateSystem, + vtkm::cont::CellSetSingleType<>& outputCells, + const std::vector& isovalues, + const vtkm::cont::ArrayHandle& input, + vtkm::cont::ArrayHandle& vertices, + vtkm::cont::ArrayHandle& normals, + vtkm::worklet::contour::CommonState& sharedState) const; +VTKM_INSTANTIATION_END + +VTKM_INSTANTIATION_BEGIN +extern template void vtkm::worklet::contour::DeduceCellType<1>::operator()( const vtkm::cont::CellSetExplicit<>& cells, const vtkm::cont::CoordinateSystem& coordinateSystem, vtkm::cont::CellSetSingleType<>& outputCells, @@ -240,7 +336,7 @@ extern template void vtkm::worklet::contour::DeduceCellType::operator()( vtkm::worklet::contour::CommonState& sharedState) const; VTKM_INSTANTIATION_END VTKM_INSTANTIATION_BEGIN -extern template void vtkm::worklet::contour::DeduceCellType::operator()( +extern template void vtkm::worklet::contour::DeduceCellType<1>::operator()( const vtkm::cont::CellSetExplicit<>& cells, const vtkm::cont::CoordinateSystem& coordinateSystem, vtkm::cont::CellSetSingleType<>& outputCells, @@ -252,7 +348,7 @@ extern template void vtkm::worklet::contour::DeduceCellType::operator()( VTKM_INSTANTIATION_END VTKM_INSTANTIATION_BEGIN -extern template void vtkm::worklet::contour::DeduceCellType::operator()( +extern template void vtkm::worklet::contour::DeduceCellType<1>::operator()( const vtkm::cont::CellSetSingleType<>& cells, const vtkm::cont::CoordinateSystem& coordinateSystem, vtkm::cont::CellSetSingleType<>& outputCells, @@ -263,7 +359,7 @@ extern template void vtkm::worklet::contour::DeduceCellType::operator()( vtkm::worklet::contour::CommonState& sharedState) const; VTKM_INSTANTIATION_END VTKM_INSTANTIATION_BEGIN -extern template void vtkm::worklet::contour::DeduceCellType::operator()( +extern template void vtkm::worklet::contour::DeduceCellType<1>::operator()( const vtkm::cont::CellSetSingleType<>& cells, const vtkm::cont::CoordinateSystem& coordinateSystem, vtkm::cont::CellSetSingleType<>& outputCells, diff --git a/vtkm/filter/contour/worklet/contour/MarchingCellTables.h b/vtkm/filter/contour/worklet/contour/MarchingCellTables.h index 56dc9e8d9d..446b81d3cf 100644 --- a/vtkm/filter/contour/worklet/contour/MarchingCellTables.h +++ b/vtkm/filter/contour/worklet/contour/MarchingCellTables.h @@ -42,551 +42,735 @@ VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_HEXAHEDRON == 12); VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_WEDGE == 13); VTKM_STATIC_ASSERT(vtkm::CELL_SHAPE_PYRAMID == 14); -VTKM_EXEC inline vtkm::IdComponent GetNumTrianglesOffset(vtkm::UInt8 cellType) +template +class MarchingCellTables; + +template <> +class MarchingCellTables<3> { - // clang-format off - VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent offsets[] = { - 0, // CELL_SHAPE_EMPTY - 0, // CELL_SHAPE_VERTEX - 0, // CELL_SHAPE_POLY_VERTEX - 0, // CELL_SHAPE_LINE - 0, // CELL_SHAPE_POLY_LINE - 0, // CELL_SHAPE_TRIANGLE - 0, // CELL_SHAPE_TRIANGLE_STRIP - 0, // CELL_SHAPE_POLYGON - 0, // CELL_SHAPE_PIXEL - 0, // CELL_SHAPE_QUAD - 0, // CELL_SHAPE_TETRA - 16, // CELL_SHAPE_VOXEL - 16, // CELL_SHAPE_HEXAHEDRON - 272, // CELL_SHAPE_WEDGE - 336, // CELL_SHAPE_PYRAMID - 0 // dummy - }; - // clang-format on + VTKM_EXEC static inline vtkm::IdComponent GetNumOutCellsOffset(vtkm::UInt8 inCellType) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent offsets[] = { + 0, // CELL_SHAPE_EMPTY + 0, // CELL_SHAPE_VERTEX + 0, // CELL_SHAPE_POLY_VERTEX + 0, // CELL_SHAPE_LINE + 0, // CELL_SHAPE_POLY_LINE + 0, // CELL_SHAPE_TRIANGLE + 0, // CELL_SHAPE_TRIANGLE_STRIP + 0, // CELL_SHAPE_POLYGON + 0, // CELL_SHAPE_PIXEL + 0, // CELL_SHAPE_QUAD + 0, // CELL_SHAPE_TETRA + 16, // CELL_SHAPE_VOXEL + 16, // CELL_SHAPE_HEXAHEDRON + 272, // CELL_SHAPE_WEDGE + 336, // CELL_SHAPE_PYRAMID + 368 // table size + }; + // clang-format on - return offsets[cellType]; -} + return offsets[inCellType]; + } -VTKM_EXEC inline vtkm::UInt8 GetNumTriangles(vtkm::UInt8 cellType, vtkm::UInt8 caseNumber) -{ - // clang-format off - VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 numTriangles[] = { - // CELL_SHAPE_EMPTY - // CELL_SHAPE_VERTEX - // CELL_SHAPE_POLY_VERTEX - // CELL_SHAPE_LINE - // CELL_SHAPE_POLY_LINE - // CELL_SHAPE_TRIANGLE - // CELL_SHAPE_TRIANGLE_STRIP - // CELL_SHAPE_POLYGON - // CELL_SHAPE_PIXEL - // CELL_SHAPE_QUAD - // CELL_SHAPE_TETRA - 0, 1, 1, 2, 1, 2, 2, 1, 1, 2, 2, 1, 2, 1, 1, 0, // cases 0 - 15 - // CELL_SHAPE_VOXEL - // CELL_SHAPE_HEXAHEDRON - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 2, // cases 0 - 15 - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 16 - 31 - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 32 - 47 - 2, 3, 3, 2, 3, 4, 4, 3, 3, 4, 4, 3, 4, 5, 5, 2, // cases 48 - 63 - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 64 - 79 - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4, // cases 80 - 95 - 2, 3, 3, 4, 3, 4, 2, 3, 3, 4, 4, 5, 4, 5, 3, 2, // cases 96 - 111 - 3, 4, 4, 3, 4, 5, 3, 2, 4, 5, 5, 4, 5, 2, 4, 1, // cases 112 - 127 - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 128 - 143 - 2, 3, 3, 4, 3, 4, 4, 5, 3, 2, 4, 3, 4, 3, 5, 2, // cases 144 - 159 - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4, // cases 160 - 175 - 3, 4, 4, 3, 4, 5, 5, 4, 4, 3, 5, 2, 5, 4, 2, 1, // cases 176 - 191 - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 2, 3, 3, 2, // cases 192 - 207 - 3, 4, 4, 5, 4, 5, 5, 2, 4, 3, 5, 4, 3, 2, 4, 1, // cases 208 - 223 - 3, 4, 4, 5, 4, 5, 3, 4, 4, 5, 5, 2, 3, 4, 2, 1, // cases 224 - 239 - 2, 3, 3, 2, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 1, 0, // cases 240 - 255 - // CELL_SHAPE_WEDGE - 0, 1, 1, 2, 1, 2, 2, 1, 1, 2, 2, 3, 2, 3, 3, 2, // cases 0 - 15 - 1, 2, 2, 3, 2, 3, 3, 2, 2, 3, 3, 2, 3, 4, 4, 1, // cases 16 - 31 - 1, 2, 2, 3, 2, 3, 3, 2, 2, 3, 3, 4, 3, 2, 4, 1, // cases 32 - 47 - 2, 3, 3, 4, 3, 4, 2, 1, 1, 2, 2, 1, 2, 1, 1, 0, // cases 48 - 63 - // CELL_SHAPE_PYRAMID - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 2, // cases 0 - 15 - 2, 3, 3, 2, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 1, 0, // cases 16 - 31 - 0 // dummy - }; - // clang-format on + VTKM_EXEC static inline vtkm::IdComponent GetOutCellTableOffset(vtkm::UInt8 inCellType, + vtkm::UInt8 caseNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent tableOffset[] = { + // CELL_SHAPE_EMPTY + // CELL_SHAPE_VERTEX + // CELL_SHAPE_POLY_VERTEX + // CELL_SHAPE_LINE + // CELL_SHAPE_POLY_LINE + // CELL_SHAPE_TRIANGLE + // CELL_SHAPE_TRIANGLE_STRIP + // CELL_SHAPE_POLYGON + // CELL_SHAPE_PIXEL + // CELL_SHAPE_QUAD + // CELL_SHAPE_TETRA + 0, 0, 1, 2, 4, 5, 7, 9, // cases 0 - 7 + 10, 11, 13, 15, 16, 18, 19, 20, // cases 8 - 15 + // CELL_SHAPE_VOXEL + // CELL_SHAPE_HEXAHEDRON + 20, 20, 21, 22, 24, 25, 27, 29, // cases 0 - 7 + 32, 33, 35, 37, 40, 42, 45, 48, // cases 8 - 15 + 50, 51, 53, 55, 58, 60, 63, 66, // cases 16 - 23 + 70, 72, 75, 78, 82, 85, 89, 93, // cases 24 - 31 + 96, 97, 99, 101, 104, 106, 109, 112, // cases 32 - 39 + 116, 118, 121, 124, 128, 131, 135, 139, // cases 40 - 47 + 142, 144, 147, 150, 152, 155, 159, 163, // cases 48 - 55 + 166, 169, 173, 177, 180, 184, 189, 194, // cases 56 - 63 + 196, 197, 199, 201, 204, 206, 209, 212, // cases 64 - 71 + 216, 218, 221, 224, 228, 231, 235, 239, // cases 72 - 79 + 242, 244, 247, 250, 254, 257, 261, 265, // cases 80 - 87 + 270, 273, 277, 281, 286, 290, 295, 300, // cases 88 - 95 + 304, 306, 309, 312, 316, 319, 323, 325, // cases 96 - 103 + 328, 331, 335, 339, 344, 348, 353, 356, // cases 104 - 111 + 358, 361, 365, 369, 372, 376, 381, 384, // cases 112 - 119 + 386, 390, 395, 400, 404, 409, 411, 415, // cases 120 - 127 + 416, 417, 419, 421, 424, 426, 429, 432, // cases 128 - 135 + 436, 438, 441, 444, 448, 451, 455, 459, // cases 136 - 143 + 462, 464, 467, 470, 474, 477, 481, 485, // cases 144 - 151 + 490, 493, 495, 499, 502, 506, 509, 514, // cases 152 - 159 + 516, 518, 521, 524, 528, 531, 535, 539, // cases 160 - 167 + 544, 547, 551, 555, 560, 564, 569, 574, // cases 168 - 175 + 578, 581, 585, 589, 592, 596, 601, 606, // cases 176 - 183 + 610, 614, 617, 622, 624, 629, 633, 635, // cases 184 - 191 + 636, 638, 641, 644, 648, 651, 655, 659, // cases 192 - 199 + 664, 667, 671, 675, 680, 682, 685, 688, // cases 200 - 207 + 690, 693, 697, 701, 706, 710, 715, 720, // cases 208 - 215 + 722, 726, 729, 734, 738, 741, 743, 747, // cases 216 - 223 + 748, 751, 755, 759, 764, 768, 773, 776, // cases 224 - 231 + 780, 784, 789, 794, 796, 799, 803, 805, // cases 232 - 239 + 806, 808, 811, 814, 816, 819, 823, 825, // cases 240 - 247 + 826, 829, 831, 835, 836, 838, 839, 840, // cases 248 - 255 + // CELL_SHAPE_WEDGE + 840, 840, 841, 842, 844, 845, 847, 849, // cases 0 - 7 + 850, 851, 853, 855, 858, 860, 863, 866, // cases 8 - 15 + 868, 869, 871, 873, 876, 878, 881, 884, // cases 16 - 23 + 886, 888, 891, 894, 896, 899, 903, 907, // cases 24 - 31 + 908, 909, 911, 913, 916, 918, 921, 924, // cases 32 - 39 + 926, 928, 931, 934, 938, 941, 943, 947, // cases 40 - 47 + 948, 950, 953, 956, 960, 963, 967, 969, // cases 48 - 55 + 970, 971, 973, 975, 976, 978, 979, 980, // cases 56 - 63 + // CELL_SHAPE_PYRAMID + 980, 980, 981, 982, 984, 985, 987, 989, // cases 0 - 7 + 992, 993, 995, 997, 1000, 1002, 1005, 1008, // cases 8 - 15 + 1010, 1012, 1015, 1018, 1020, 1023, 1027, 1029, // cases 16 - 23 + 1030, 1033, 1035, 1039, 1040, 1042, 1043, 1044, // cases 24 - 31 + 0 // dummy + }; + // clang-format on - vtkm::IdComponent offset = GetNumTrianglesOffset(cellType); - return numTriangles[offset + caseNumber]; -} + vtkm::IdComponent offset = GetNumOutCellsOffset(inCellType); + return tableOffset[offset + caseNumber]; + } + +public: + VTKM_EXEC static inline vtkm::UInt8 GetNumOutCells(vtkm::UInt8 inCellType, vtkm::UInt8 caseNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 numOutCells[] = { + // CELL_SHAPE_EMPTY + // CELL_SHAPE_VERTEX + // CELL_SHAPE_POLY_VERTEX + // CELL_SHAPE_LINE + // CELL_SHAPE_POLY_LINE + // CELL_SHAPE_TRIANGLE + // CELL_SHAPE_TRIANGLE_STRIP + // CELL_SHAPE_POLYGON + // CELL_SHAPE_PIXEL + // CELL_SHAPE_QUAD + // CELL_SHAPE_TETRA + 0, 1, 1, 2, 1, 2, 2, 1, 1, 2, 2, 1, 2, 1, 1, 0, // cases 0 - 15 + // CELL_SHAPE_VOXEL + // CELL_SHAPE_HEXAHEDRON + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 2, // cases 0 - 15 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 16 - 31 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 32 - 47 + 2, 3, 3, 2, 3, 4, 4, 3, 3, 4, 4, 3, 4, 5, 5, 2, // cases 48 - 63 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 64 - 79 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4, // cases 80 - 95 + 2, 3, 3, 4, 3, 4, 2, 3, 3, 4, 4, 5, 4, 5, 3, 2, // cases 96 - 111 + 3, 4, 4, 3, 4, 5, 3, 2, 4, 5, 5, 4, 5, 2, 4, 1, // cases 112 - 127 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, // cases 128 - 143 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 2, 4, 3, 4, 3, 5, 2, // cases 144 - 159 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 4, // cases 160 - 175 + 3, 4, 4, 3, 4, 5, 5, 4, 4, 3, 5, 2, 5, 4, 2, 1, // cases 176 - 191 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 2, 3, 3, 2, // cases 192 - 207 + 3, 4, 4, 5, 4, 5, 5, 2, 4, 3, 5, 4, 3, 2, 4, 1, // cases 208 - 223 + 3, 4, 4, 5, 4, 5, 3, 4, 4, 5, 5, 2, 3, 4, 2, 1, // cases 224 - 239 + 2, 3, 3, 2, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 1, 0, // cases 240 - 255 + // CELL_SHAPE_WEDGE + 0, 1, 1, 2, 1, 2, 2, 1, 1, 2, 2, 3, 2, 3, 3, 2, // cases 0 - 15 + 1, 2, 2, 3, 2, 3, 3, 2, 2, 3, 3, 2, 3, 4, 4, 1, // cases 16 - 31 + 1, 2, 2, 3, 2, 3, 3, 2, 2, 3, 3, 4, 3, 2, 4, 1, // cases 32 - 47 + 2, 3, 3, 4, 3, 4, 2, 1, 1, 2, 2, 1, 2, 1, 1, 0, // cases 48 - 63 + // CELL_SHAPE_PYRAMID + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 2, // cases 0 - 15 + 2, 3, 3, 2, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 1, 0, // cases 16 - 31 + 0 // dummy + }; + // clang-format on -VTKM_EXEC inline vtkm::IdComponent GetTriTableOffset(vtkm::UInt8 cellType, vtkm::UInt8 caseNumber) + vtkm::IdComponent offset = GetNumOutCellsOffset(inCellType); + if (offset == GetNumOutCellsOffset(inCellType + 1)) + { + // This part of the table is empty. + return 0; + } + else + { + return numOutCells[offset + caseNumber]; + } + } + + VTKM_EXEC static inline const vtkm::UInt8* GetCellEdges(vtkm::UInt8 inCellType, + vtkm::UInt8 caseNumber, + vtkm::UInt8 outCellNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 triTable[][3] = { + // CELL_SHAPE_EMPTY + // CELL_SHAPE_VERTEX + // CELL_SHAPE_POLY_VERTEX + // CELL_SHAPE_LINE + // CELL_SHAPE_POLY_LINE + // CELL_SHAPE_TRIANGLE + // CELL_SHAPE_TRIANGLE_STRIP + // CELL_SHAPE_POLYGON + // CELL_SHAPE_PIXEL + // CELL_SHAPE_QUAD + // CELL_SHAPE_TETRA + // case 0 + { 0, 3, 2 }, // case 1 + { 0, 1, 4 }, // case 2 + { 1, 4, 2 }, { 2, 4, 3 }, // case 3 + { 1, 2, 5 }, // case 4 + { 0, 3, 5 }, { 0, 5, 1 }, // case 5 + { 0, 2, 5 }, { 0, 5, 4 }, // case 6 + { 5, 4, 3 }, // case 7 + { 3, 4, 5 }, // case 8 + { 4, 5, 0 }, { 5, 2, 0 }, // case 9 + { 1, 5, 0 }, { 5, 3, 0 }, // case 10 + { 5, 2, 1 }, // case 11 + { 3, 4, 2 }, { 2, 4, 1 }, // case 12 + { 4, 1, 0 }, // case 13 + { 2, 3, 0 }, // case 14 + // case 15 + // CELL_SHAPE_VOXEL + // CELL_SHAPE_HEXAHEDRON + // case 0 + { 0, 8, 3 }, // case 1 + { 0, 1, 9 }, // case 2 + { 1, 8, 3 }, { 9, 8, 1 }, // case 3 + { 1, 2, 11 }, // case 4 + { 0, 8, 3 }, { 1, 2, 11 }, // case 5 + { 9, 2, 11 }, { 0, 2, 9 }, // case 6 + { 2, 8, 3 }, { 2, 11, 8 }, { 11, 9, 8 }, // case 7 + { 3, 10, 2 }, // case 8 + { 0, 10, 2 }, { 8, 10, 0 }, // case 9 + { 1, 9, 0 }, { 2, 3, 10 }, // case 10 + { 1, 10, 2 }, { 1, 9, 10 }, { 9, 8, 10 }, // case 11 + { 3, 11, 1 }, { 10, 11, 3 }, // case 12 + { 0, 11, 1 }, { 0, 8, 11 }, { 8, 10, 11 }, // case 13 + { 3, 9, 0 }, { 3, 10, 9 }, { 10, 11, 9 }, // case 14 + { 9, 8, 11 }, { 11, 8, 10 }, // case 15 + { 4, 7, 8 }, // case 16 + { 4, 3, 0 }, { 7, 3, 4 }, // case 17 + { 0, 1, 9 }, { 8, 4, 7 }, // case 18 + { 4, 1, 9 }, { 4, 7, 1 }, { 7, 3, 1 }, // case 19 + { 1, 2, 11 }, { 8, 4, 7 }, // case 20 + { 3, 4, 7 }, { 3, 0, 4 }, { 1, 2, 11 }, // case 21 + { 9, 2, 11 }, { 9, 0, 2 }, { 8, 4, 7 }, // case 22 + { 2, 11, 9 }, { 2, 9, 7 }, { 2, 7, 3 }, { 7, 9, 4 }, // case 23 + { 8, 4, 7 }, { 3, 10, 2 }, // case 24 + { 10, 4, 7 }, { 10, 2, 4 }, { 2, 0, 4 }, // case 25 + { 9, 0, 1 }, { 8, 4, 7 }, { 2, 3, 10 }, // case 26 + { 4, 7, 10 }, { 9, 4, 10 }, { 9, 10, 2 }, { 9, 2, 1 }, // case 27 + { 3, 11, 1 }, { 3, 10, 11 }, { 7, 8, 4 }, // case 28 + { 1, 10, 11 }, { 1, 4, 10 }, { 1, 0, 4 }, { 7, 10, 4 }, // case 29 + { 4, 7, 8 }, { 9, 0, 10 }, { 9, 10, 11 }, { 10, 0, 3 }, // case 30 + { 4, 7, 10 }, { 4, 10, 9 }, { 9, 10, 11 }, // case 31 + { 9, 5, 4 }, // case 32 + { 9, 5, 4 }, { 0, 8, 3 }, // case 33 + { 0, 5, 4 }, { 1, 5, 0 }, // case 34 + { 8, 5, 4 }, { 8, 3, 5 }, { 3, 1, 5 }, // case 35 + { 1, 2, 11 }, { 9, 5, 4 }, // case 36 + { 3, 0, 8 }, { 1, 2, 11 }, { 4, 9, 5 }, // case 37 + { 5, 2, 11 }, { 5, 4, 2 }, { 4, 0, 2 }, // case 38 + { 2, 11, 5 }, { 3, 2, 5 }, { 3, 5, 4 }, { 3, 4, 8 }, // case 39 + { 9, 5, 4 }, { 2, 3, 10 }, // case 40 + { 0, 10, 2 }, { 0, 8, 10 }, { 4, 9, 5 }, // case 41 + { 0, 5, 4 }, { 0, 1, 5 }, { 2, 3, 10 }, // case 42 + { 2, 1, 5 }, { 2, 5, 8 }, { 2, 8, 10 }, { 4, 8, 5 }, // case 43 + { 11, 3, 10 }, { 11, 1, 3 }, { 9, 5, 4 }, // case 44 + { 4, 9, 5 }, { 0, 8, 1 }, { 8, 11, 1 }, { 8, 10, 11 }, // case 45 + { 5, 4, 0 }, { 5, 0, 10 }, { 5, 10, 11 }, { 10, 0, 3 }, // case 46 + { 5, 4, 8 }, { 5, 8, 11 }, { 11, 8, 10 }, // case 47 + { 9, 7, 8 }, { 5, 7, 9 }, // case 48 + { 9, 3, 0 }, { 9, 5, 3 }, { 5, 7, 3 }, // case 49 + { 0, 7, 8 }, { 0, 1, 7 }, { 1, 5, 7 }, // case 50 + { 1, 5, 3 }, { 3, 5, 7 }, // case 51 + { 9, 7, 8 }, { 9, 5, 7 }, { 11, 1, 2 }, // case 52 + { 11, 1, 2 }, { 9, 5, 0 }, { 5, 3, 0 }, { 5, 7, 3 }, // case 53 + { 8, 0, 2 }, { 8, 2, 5 }, { 8, 5, 7 }, { 11, 5, 2 }, // case 54 + { 2, 11, 5 }, { 2, 5, 3 }, { 3, 5, 7 }, // case 55 + { 7, 9, 5 }, { 7, 8, 9 }, { 3, 10, 2 }, // case 56 + { 9, 5, 7 }, { 9, 7, 2 }, { 9, 2, 0 }, { 2, 7, 10 }, // case 57 + { 2, 3, 10 }, { 0, 1, 8 }, { 1, 7, 8 }, { 1, 5, 7 }, // case 58 + { 10, 2, 1 }, { 10, 1, 7 }, { 7, 1, 5 }, // case 59 + { 9, 5, 8 }, { 8, 5, 7 }, { 11, 1, 3 }, { 11, 3, 10 }, // case 60 + { 5, 7, 0 }, { 5, 0, 9 }, { 7, 10, 0 }, { 1, 0, 11 }, { 10, 11, 0 }, // case 61 + { 10, 11, 0 }, { 10, 0, 3 }, { 11, 5, 0 }, { 8, 0, 7 }, { 5, 7, 0 }, // case 62 + { 10, 11, 5 }, { 7, 10, 5 }, // case 63 + { 11, 6, 5 }, // case 64 + { 0, 8, 3 }, { 5, 11, 6 }, // case 65 + { 9, 0, 1 }, { 5, 11, 6 }, // case 66 + { 1, 8, 3 }, { 1, 9, 8 }, { 5, 11, 6 }, // case 67 + { 1, 6, 5 }, { 2, 6, 1 }, // case 68 + { 1, 6, 5 }, { 1, 2, 6 }, { 3, 0, 8 }, // case 69 + { 9, 6, 5 }, { 9, 0, 6 }, { 0, 2, 6 }, // case 70 + { 5, 9, 8 }, { 5, 8, 2 }, { 5, 2, 6 }, { 3, 2, 8 }, // case 71 + { 2, 3, 10 }, { 11, 6, 5 }, // case 72 + { 10, 0, 8 }, { 10, 2, 0 }, { 11, 6, 5 }, // case 73 + { 0, 1, 9 }, { 2, 3, 10 }, { 5, 11, 6 }, // case 74 + { 5, 11, 6 }, { 1, 9, 2 }, { 9, 10, 2 }, { 9, 8, 10 }, // case 75 + { 6, 3, 10 }, { 6, 5, 3 }, { 5, 1, 3 }, // case 76 + { 0, 8, 10 }, { 0, 10, 5 }, { 0, 5, 1 }, { 5, 10, 6 }, // case 77 + { 3, 10, 6 }, { 0, 3, 6 }, { 0, 6, 5 }, { 0, 5, 9 }, // case 78 + { 6, 5, 9 }, { 6, 9, 10 }, { 10, 9, 8 }, // case 79 + { 5, 11, 6 }, { 4, 7, 8 }, // case 80 + { 4, 3, 0 }, { 4, 7, 3 }, { 6, 5, 11 }, // case 81 + { 1, 9, 0 }, { 5, 11, 6 }, { 8, 4, 7 }, // case 82 + { 11, 6, 5 }, { 1, 9, 7 }, { 1, 7, 3 }, { 7, 9, 4 }, // case 83 + { 6, 1, 2 }, { 6, 5, 1 }, { 4, 7, 8 }, // case 84 + { 1, 2, 5 }, { 5, 2, 6 }, { 3, 0, 4 }, { 3, 4, 7 }, // case 85 + { 8, 4, 7 }, { 9, 0, 5 }, { 0, 6, 5 }, { 0, 2, 6 }, // case 86 + { 7, 3, 9 }, { 7, 9, 4 }, { 3, 2, 9 }, { 5, 9, 6 }, { 2, 6, 9 }, // case 87 + { 3, 10, 2 }, { 7, 8, 4 }, { 11, 6, 5 }, // case 88 + { 5, 11, 6 }, { 4, 7, 2 }, { 4, 2, 0 }, { 2, 7, 10 }, // case 89 + { 0, 1, 9 }, { 4, 7, 8 }, { 2, 3, 10 }, { 5, 11, 6 }, // case 90 + { 9, 2, 1 }, { 9, 10, 2 }, { 9, 4, 10 }, { 7, 10, 4 }, { 5, 11, 6 }, // case 91 + { 8, 4, 7 }, { 3, 10, 5 }, { 3, 5, 1 }, { 5, 10, 6 }, // case 92 + { 5, 1, 10 }, { 5, 10, 6 }, { 1, 0, 10 }, { 7, 10, 4 }, { 0, 4, 10 }, // case 93 + { 0, 5, 9 }, { 0, 6, 5 }, { 0, 3, 6 }, { 10, 6, 3 }, { 8, 4, 7 }, // case 94 + { 6, 5, 9 }, { 6, 9, 10 }, { 4, 7, 9 }, { 7, 10, 9 }, // case 95 + { 11, 4, 9 }, { 6, 4, 11 }, // case 96 + { 4, 11, 6 }, { 4, 9, 11 }, { 0, 8, 3 }, // case 97 + { 11, 0, 1 }, { 11, 6, 0 }, { 6, 4, 0 }, // case 98 + { 8, 3, 1 }, { 8, 1, 6 }, { 8, 6, 4 }, { 6, 1, 11 }, // case 99 + { 1, 4, 9 }, { 1, 2, 4 }, { 2, 6, 4 }, // case 100 + { 3, 0, 8 }, { 1, 2, 9 }, { 2, 4, 9 }, { 2, 6, 4 }, // case 101 + { 0, 2, 4 }, { 4, 2, 6 }, // case 102 + { 8, 3, 2 }, { 8, 2, 4 }, { 4, 2, 6 }, // case 103 + { 11, 4, 9 }, { 11, 6, 4 }, { 10, 2, 3 }, // case 104 + { 0, 8, 2 }, { 2, 8, 10 }, { 4, 9, 11 }, { 4, 11, 6 }, // case 105 + { 3, 10, 2 }, { 0, 1, 6 }, { 0, 6, 4 }, { 6, 1, 11 }, // case 106 + { 6, 4, 1 }, { 6, 1, 11 }, { 4, 8, 1 }, { 2, 1, 10 }, { 8, 10, 1 }, // case 107 + { 9, 6, 4 }, { 9, 3, 6 }, { 9, 1, 3 }, { 10, 6, 3 }, // case 108 + { 8, 10, 1 }, { 8, 1, 0 }, { 10, 6, 1 }, { 9, 1, 4 }, { 6, 4, 1 }, // case 109 + { 3, 10, 6 }, { 3, 6, 0 }, { 0, 6, 4 }, // case 110 + { 6, 4, 8 }, { 10, 6, 8 }, // case 111 + { 7, 11, 6 }, { 7, 8, 11 }, { 8, 9, 11 }, // case 112 + { 0, 7, 3 }, { 0, 11, 7 }, { 0, 9, 11 }, { 6, 7, 11 }, // case 113 + { 11, 6, 7 }, { 1, 11, 7 }, { 1, 7, 8 }, { 1, 8, 0 }, // case 114 + { 11, 6, 7 }, { 11, 7, 1 }, { 1, 7, 3 }, // case 115 + { 1, 2, 6 }, { 1, 6, 8 }, { 1, 8, 9 }, { 8, 6, 7 }, // case 116 + { 2, 6, 9 }, { 2, 9, 1 }, { 6, 7, 9 }, { 0, 9, 3 }, { 7, 3, 9 }, // case 117 + { 7, 8, 0 }, { 7, 0, 6 }, { 6, 0, 2 }, // case 118 + { 7, 3, 2 }, { 6, 7, 2 }, // case 119 + { 2, 3, 10 }, { 11, 6, 8 }, { 11, 8, 9 }, { 8, 6, 7 }, // case 120 + { 2, 0, 7 }, { 2, 7, 10 }, { 0, 9, 7 }, { 6, 7, 11 }, { 9, 11, 7 }, // case 121 + { 1, 8, 0 }, { 1, 7, 8 }, { 1, 11, 7 }, { 6, 7, 11 }, { 2, 3, 10 }, // case 122 + { 10, 2, 1 }, { 10, 1, 7 }, { 11, 6, 1 }, { 6, 7, 1 }, // case 123 + { 8, 9, 6 }, { 8, 6, 7 }, { 9, 1, 6 }, { 10, 6, 3 }, { 1, 3, 6 }, // case 124 + { 0, 9, 1 }, { 10, 6, 7 }, // case 125 + { 7, 8, 0 }, { 7, 0, 6 }, { 3, 10, 0 }, { 10, 6, 0 }, // case 126 + { 7, 10, 6 }, // case 127 + { 7, 6, 10 }, // case 128 + { 3, 0, 8 }, { 10, 7, 6 }, // case 129 + { 0, 1, 9 }, { 10, 7, 6 }, // case 130 + { 8, 1, 9 }, { 8, 3, 1 }, { 10, 7, 6 }, // case 131 + { 11, 1, 2 }, { 6, 10, 7 }, // case 132 + { 1, 2, 11 }, { 3, 0, 8 }, { 6, 10, 7 }, // case 133 + { 2, 9, 0 }, { 2, 11, 9 }, { 6, 10, 7 }, // case 134 + { 6, 10, 7 }, { 2, 11, 3 }, { 11, 8, 3 }, { 11, 9, 8 }, // case 135 + { 7, 2, 3 }, { 6, 2, 7 }, // case 136 + { 7, 0, 8 }, { 7, 6, 0 }, { 6, 2, 0 }, // case 137 + { 2, 7, 6 }, { 2, 3, 7 }, { 0, 1, 9 }, // case 138 + { 1, 6, 2 }, { 1, 8, 6 }, { 1, 9, 8 }, { 8, 7, 6 }, // case 139 + { 11, 7, 6 }, { 11, 1, 7 }, { 1, 3, 7 }, // case 140 + { 11, 7, 6 }, { 1, 7, 11 }, { 1, 8, 7 }, { 1, 0, 8 }, // case 141 + { 0, 3, 7 }, { 0, 7, 11 }, { 0, 11, 9 }, { 6, 11, 7 }, // case 142 + { 7, 6, 11 }, { 7, 11, 8 }, { 8, 11, 9 }, // case 143 + { 6, 8, 4 }, { 10, 8, 6 }, // case 144 + { 3, 6, 10 }, { 3, 0, 6 }, { 0, 4, 6 }, // case 145 + { 8, 6, 10 }, { 8, 4, 6 }, { 9, 0, 1 }, // case 146 + { 9, 4, 6 }, { 9, 6, 3 }, { 9, 3, 1 }, { 10, 3, 6 }, // case 147 + { 6, 8, 4 }, { 6, 10, 8 }, { 2, 11, 1 }, // case 148 + { 1, 2, 11 }, { 3, 0, 10 }, { 0, 6, 10 }, { 0, 4, 6 }, // case 149 + { 4, 10, 8 }, { 4, 6, 10 }, { 0, 2, 9 }, { 2, 11, 9 }, // case 150 + { 11, 9, 3 }, { 11, 3, 2 }, { 9, 4, 3 }, { 10, 3, 6 }, { 4, 6, 3 }, // case 151 + { 8, 2, 3 }, { 8, 4, 2 }, { 4, 6, 2 }, // case 152 + { 0, 4, 2 }, { 4, 6, 2 }, // case 153 + { 1, 9, 0 }, { 2, 3, 4 }, { 2, 4, 6 }, { 4, 3, 8 }, // case 154 + { 1, 9, 4 }, { 1, 4, 2 }, { 2, 4, 6 }, // case 155 + { 8, 1, 3 }, { 8, 6, 1 }, { 8, 4, 6 }, { 6, 11, 1 }, // case 156 + { 11, 1, 0 }, { 11, 0, 6 }, { 6, 0, 4 }, // case 157 + { 4, 6, 3 }, { 4, 3, 8 }, { 6, 11, 3 }, { 0, 3, 9 }, { 11, 9, 3 }, // case 158 + { 11, 9, 4 }, { 6, 11, 4 }, // case 159 + { 4, 9, 5 }, { 7, 6, 10 }, // case 160 + { 0, 8, 3 }, { 4, 9, 5 }, { 10, 7, 6 }, // case 161 + { 5, 0, 1 }, { 5, 4, 0 }, { 7, 6, 10 }, // case 162 + { 10, 7, 6 }, { 8, 3, 4 }, { 3, 5, 4 }, { 3, 1, 5 }, // case 163 + { 9, 5, 4 }, { 11, 1, 2 }, { 7, 6, 10 }, // case 164 + { 6, 10, 7 }, { 1, 2, 11 }, { 0, 8, 3 }, { 4, 9, 5 }, // case 165 + { 7, 6, 10 }, { 5, 4, 11 }, { 4, 2, 11 }, { 4, 0, 2 }, // case 166 + { 3, 4, 8 }, { 3, 5, 4 }, { 3, 2, 5 }, { 11, 5, 2 }, { 10, 7, 6 }, // case 167 + { 7, 2, 3 }, { 7, 6, 2 }, { 5, 4, 9 }, // case 168 + { 9, 5, 4 }, { 0, 8, 6 }, { 0, 6, 2 }, { 6, 8, 7 }, // case 169 + { 3, 6, 2 }, { 3, 7, 6 }, { 1, 5, 0 }, { 5, 4, 0 }, // case 170 + { 6, 2, 8 }, { 6, 8, 7 }, { 2, 1, 8 }, { 4, 8, 5 }, { 1, 5, 8 }, // case 171 + { 9, 5, 4 }, { 11, 1, 6 }, { 1, 7, 6 }, { 1, 3, 7 }, // case 172 + { 1, 6, 11 }, { 1, 7, 6 }, { 1, 0, 7 }, { 8, 7, 0 }, { 9, 5, 4 }, // case 173 + { 4, 0, 11 }, { 4, 11, 5 }, { 0, 3, 11 }, { 6, 11, 7 }, { 3, 7, 11 }, // case 174 + { 7, 6, 11 }, { 7, 11, 8 }, { 5, 4, 11 }, { 4, 8, 11 }, // case 175 + { 6, 9, 5 }, { 6, 10, 9 }, { 10, 8, 9 }, // case 176 + { 3, 6, 10 }, { 0, 6, 3 }, { 0, 5, 6 }, { 0, 9, 5 }, // case 177 + { 0, 10, 8 }, { 0, 5, 10 }, { 0, 1, 5 }, { 5, 6, 10 }, // case 178 + { 6, 10, 3 }, { 6, 3, 5 }, { 5, 3, 1 }, // case 179 + { 1, 2, 11 }, { 9, 5, 10 }, { 9, 10, 8 }, { 10, 5, 6 }, // case 180 + { 0, 10, 3 }, { 0, 6, 10 }, { 0, 9, 6 }, { 5, 6, 9 }, { 1, 2, 11 }, // case 181 + { 10, 8, 5 }, { 10, 5, 6 }, { 8, 0, 5 }, { 11, 5, 2 }, { 0, 2, 5 }, // case 182 + { 6, 10, 3 }, { 6, 3, 5 }, { 2, 11, 3 }, { 11, 5, 3 }, // case 183 + { 5, 8, 9 }, { 5, 2, 8 }, { 5, 6, 2 }, { 3, 8, 2 }, // case 184 + { 9, 5, 6 }, { 9, 6, 0 }, { 0, 6, 2 }, // case 185 + { 1, 5, 8 }, { 1, 8, 0 }, { 5, 6, 8 }, { 3, 8, 2 }, { 6, 2, 8 }, // case 186 + { 1, 5, 6 }, { 2, 1, 6 }, // case 187 + { 1, 3, 6 }, { 1, 6, 11 }, { 3, 8, 6 }, { 5, 6, 9 }, { 8, 9, 6 }, // case 188 + { 11, 1, 0 }, { 11, 0, 6 }, { 9, 5, 0 }, { 5, 6, 0 }, // case 189 + { 0, 3, 8 }, { 5, 6, 11 }, // case 190 + { 11, 5, 6 }, // case 191 + { 10, 5, 11 }, { 7, 5, 10 }, // case 192 + { 10, 5, 11 }, { 10, 7, 5 }, { 8, 3, 0 }, // case 193 + { 5, 10, 7 }, { 5, 11, 10 }, { 1, 9, 0 }, // case 194 + { 11, 7, 5 }, { 11, 10, 7 }, { 9, 8, 1 }, { 8, 3, 1 }, // case 195 + { 10, 1, 2 }, { 10, 7, 1 }, { 7, 5, 1 }, // case 196 + { 0, 8, 3 }, { 1, 2, 7 }, { 1, 7, 5 }, { 7, 2, 10 }, // case 197 + { 9, 7, 5 }, { 9, 2, 7 }, { 9, 0, 2 }, { 2, 10, 7 }, // case 198 + { 7, 5, 2 }, { 7, 2, 10 }, { 5, 9, 2 }, { 3, 2, 8 }, { 9, 8, 2 }, // case 199 + { 2, 5, 11 }, { 2, 3, 5 }, { 3, 7, 5 }, // case 200 + { 8, 2, 0 }, { 8, 5, 2 }, { 8, 7, 5 }, { 11, 2, 5 }, // case 201 + { 9, 0, 1 }, { 5, 11, 3 }, { 5, 3, 7 }, { 3, 11, 2 }, // case 202 + { 9, 8, 2 }, { 9, 2, 1 }, { 8, 7, 2 }, { 11, 2, 5 }, { 7, 5, 2 }, // case 203 + { 1, 3, 5 }, { 3, 7, 5 }, // case 204 + { 0, 8, 7 }, { 0, 7, 1 }, { 1, 7, 5 }, // case 205 + { 9, 0, 3 }, { 9, 3, 5 }, { 5, 3, 7 }, // case 206 + { 9, 8, 7 }, { 5, 9, 7 }, // case 207 + { 5, 8, 4 }, { 5, 11, 8 }, { 11, 10, 8 }, // case 208 + { 5, 0, 4 }, { 5, 10, 0 }, { 5, 11, 10 }, { 10, 3, 0 }, // case 209 + { 0, 1, 9 }, { 8, 4, 11 }, { 8, 11, 10 }, { 11, 4, 5 }, // case 210 + { 11, 10, 4 }, { 11, 4, 5 }, { 10, 3, 4 }, { 9, 4, 1 }, { 3, 1, 4 }, // case 211 + { 2, 5, 1 }, { 2, 8, 5 }, { 2, 10, 8 }, { 4, 5, 8 }, // case 212 + { 0, 4, 10 }, { 0, 10, 3 }, { 4, 5, 10 }, { 2, 10, 1 }, { 5, 1, 10 }, // case 213 + { 0, 2, 5 }, { 0, 5, 9 }, { 2, 10, 5 }, { 4, 5, 8 }, { 10, 8, 5 }, // case 214 + { 9, 4, 5 }, { 2, 10, 3 }, // case 215 + { 2, 5, 11 }, { 3, 5, 2 }, { 3, 4, 5 }, { 3, 8, 4 }, // case 216 + { 5, 11, 2 }, { 5, 2, 4 }, { 4, 2, 0 }, // case 217 + { 3, 11, 2 }, { 3, 5, 11 }, { 3, 8, 5 }, { 4, 5, 8 }, { 0, 1, 9 }, // case 218 + { 5, 11, 2 }, { 5, 2, 4 }, { 1, 9, 2 }, { 9, 4, 2 }, // case 219 + { 8, 4, 5 }, { 8, 5, 3 }, { 3, 5, 1 }, // case 220 + { 0, 4, 5 }, { 1, 0, 5 }, // case 221 + { 8, 4, 5 }, { 8, 5, 3 }, { 9, 0, 5 }, { 0, 3, 5 }, // case 222 + { 9, 4, 5 }, // case 223 + { 4, 10, 7 }, { 4, 9, 10 }, { 9, 11, 10 }, // case 224 + { 0, 8, 3 }, { 4, 9, 7 }, { 9, 10, 7 }, { 9, 11, 10 }, // case 225 + { 1, 11, 10 }, { 1, 10, 4 }, { 1, 4, 0 }, { 7, 4, 10 }, // case 226 + { 3, 1, 4 }, { 3, 4, 8 }, { 1, 11, 4 }, { 7, 4, 10 }, { 11, 10, 4 }, // case 227 + { 4, 10, 7 }, { 9, 10, 4 }, { 9, 2, 10 }, { 9, 1, 2 }, // case 228 + { 9, 7, 4 }, { 9, 10, 7 }, { 9, 1, 10 }, { 2, 10, 1 }, { 0, 8, 3 }, // case 229 + { 10, 7, 4 }, { 10, 4, 2 }, { 2, 4, 0 }, // case 230 + { 10, 7, 4 }, { 10, 4, 2 }, { 8, 3, 4 }, { 3, 2, 4 }, // case 231 + { 2, 9, 11 }, { 2, 7, 9 }, { 2, 3, 7 }, { 7, 4, 9 }, // case 232 + { 9, 11, 7 }, { 9, 7, 4 }, { 11, 2, 7 }, { 8, 7, 0 }, { 2, 0, 7 }, // case 233 + { 3, 7, 11 }, { 3, 11, 2 }, { 7, 4, 11 }, { 1, 11, 0 }, { 4, 0, 11 }, // case 234 + { 1, 11, 2 }, { 8, 7, 4 }, // case 235 + { 4, 9, 1 }, { 4, 1, 7 }, { 7, 1, 3 }, // case 236 + { 4, 9, 1 }, { 4, 1, 7 }, { 0, 8, 1 }, { 8, 7, 1 }, // case 237 + { 4, 0, 3 }, { 7, 4, 3 }, // case 238 + { 4, 8, 7 }, // case 239 + { 9, 11, 8 }, { 11, 10, 8 }, // case 240 + { 3, 0, 9 }, { 3, 9, 10 }, { 10, 9, 11 }, // case 241 + { 0, 1, 11 }, { 0, 11, 8 }, { 8, 11, 10 }, // case 242 + { 3, 1, 11 }, { 10, 3, 11 }, // case 243 + { 1, 2, 10 }, { 1, 10, 9 }, { 9, 10, 8 }, // case 244 + { 3, 0, 9 }, { 3, 9, 10 }, { 1, 2, 9 }, { 2, 10, 9 }, // case 245 + { 0, 2, 10 }, { 8, 0, 10 }, // case 246 + { 3, 2, 10 }, // case 247 + { 2, 3, 8 }, { 2, 8, 11 }, { 11, 8, 9 }, // case 248 + { 9, 11, 2 }, { 0, 9, 2 }, // case 249 + { 2, 3, 8 }, { 2, 8, 11 }, { 0, 1, 8 }, { 1, 11, 8 }, // case 250 + { 1, 11, 2 }, // case 251 + { 1, 3, 8 }, { 9, 1, 8 }, // case 252 + { 0, 9, 1 }, // case 253 + { 0, 3, 8 }, // case 254 + // case 255 + // CELL_SHAPE_WEDGE + // case 0 + { 0, 6, 2 }, // case 1 + { 0, 1, 7 }, // case 2 + { 6, 1, 7 }, { 6, 2, 1 }, // case 3 + { 1, 2, 8 }, // case 4 + { 6, 1, 0 }, { 6, 8, 1 }, // case 5 + { 0, 2, 8 }, { 7, 0, 8 }, // case 6 + { 7, 6, 8 }, // case 7 + { 3, 5, 6 }, // case 8 + { 3, 5, 0 }, { 5, 2, 0 }, // case 9 + { 0, 1, 7 }, { 6, 3, 5 }, // case 10 + { 1, 7, 3 }, { 1, 3, 5 }, { 1, 5, 2 }, // case 11 + { 2, 8, 1 }, { 6, 3, 5 }, // case 12 + { 0, 3, 1 }, { 1, 3, 5 }, { 1, 5, 8 }, // case 13 + { 6, 3, 5 }, { 0, 8, 7 }, { 0, 2, 8 }, // case 14 + { 7, 3, 5 }, { 7, 5, 8 }, // case 15 + { 7, 4, 3 }, // case 16 + { 7, 4, 3 }, { 0, 6, 2 }, // case 17 + { 0, 1, 3 }, { 1, 4, 3 }, // case 18 + { 1, 4, 3 }, { 1, 3, 6 }, { 1, 6, 2 }, // case 19 + { 7, 4, 3 }, { 2, 8, 1 }, // case 20 + { 7, 4, 3 }, { 6, 1, 0 }, { 6, 8, 1 }, // case 21 + { 0, 4, 3 }, { 0, 8, 4 }, { 0, 2, 8 }, // case 22 + { 6, 8, 3 }, { 3, 8, 4 }, // case 23 + { 6, 7, 4 }, { 6, 4, 5 }, // case 24 + { 0, 7, 5 }, { 7, 4, 5 }, { 2, 0, 5 }, // case 25 + { 1, 6, 0 }, { 1, 5, 6 }, { 1, 4, 5 }, // case 26 + { 2, 1, 5 }, { 5, 1, 4 }, // case 27 + { 2, 8, 1 }, { 6, 7, 5 }, { 7, 4, 5 }, // case 28 + { 0, 7, 5 }, { 7, 4, 5 }, { 0, 5, 1 }, { 1, 5, 8 }, // case 29 + { 0, 2, 8 }, { 0, 8, 4 }, { 0, 4, 5 }, { 0, 5, 6 }, // case 30 + { 8, 4, 5 }, // case 31 + { 4, 8, 5 }, // case 32 + { 4, 8, 5 }, { 0, 6, 2 }, // case 33 + { 4, 8, 5 }, { 0, 1, 7 }, // case 34 + { 4, 8, 5 }, { 6, 1, 7 }, { 6, 2, 1 }, // case 35 + { 1, 5, 4 }, { 2, 5, 1 }, // case 36 + { 1, 5, 4 }, { 1, 6, 5 }, { 1, 0, 6 }, // case 37 + { 5, 4, 7 }, { 5, 7, 0 }, { 5, 0, 2 }, // case 38 + { 6, 4, 7 }, { 6, 5, 4 }, // case 39 + { 6, 3, 8 }, { 3, 4, 8 }, // case 40 + { 0, 3, 4 }, { 0, 4, 8 }, { 0, 8, 2 }, // case 41 + { 7, 0, 1 }, { 6, 3, 4 }, { 6, 4, 8 }, // case 42 + { 1, 7, 3 }, { 1, 3, 2 }, { 2, 3, 8 }, { 8, 3, 4 }, // case 43 + { 2, 6, 1 }, { 6, 3, 1 }, { 3, 4, 1 }, // case 44 + { 0, 3, 1 }, { 1, 3, 4 }, // case 45 + { 7, 0, 4 }, { 4, 0, 2 }, { 4, 2, 3 }, { 3, 2, 6 }, // case 46 + { 7, 3, 4 }, // case 47 + { 7, 8, 5 }, { 7, 5, 3 }, // case 48 + { 0, 6, 2 }, { 7, 8, 5 }, { 7, 5, 3 }, // case 49 + { 0, 1, 3 }, { 1, 5, 3 }, { 1, 8, 5 }, // case 50 + { 2, 1, 6 }, { 6, 1, 3 }, { 5, 1, 8 }, { 3, 1, 5 }, // case 51 + { 1, 3, 7 }, { 1, 5, 3 }, { 1, 2, 5 }, // case 52 + { 1, 0, 6 }, { 1, 6, 5 }, { 1, 5, 7 }, { 7, 5, 3 }, // case 53 + { 0, 2, 5 }, { 0, 5, 3 }, // case 54 + { 3, 6, 5 }, // case 55 + { 7, 8, 6 }, // case 56 + { 0, 7, 8 }, { 0, 8, 2 }, // case 57 + { 0, 1, 6 }, { 1, 8, 6 }, // case 58 + { 2, 1, 8 }, // case 59 + { 6, 7, 1 }, { 6, 1, 2 }, // case 60 + { 0, 7, 1 }, // case 61 + { 0, 2, 6 }, // case 62 + // case 63 + // CELL_SHAPE_PYRAMID + // case 0 + { 3, 4, 0 }, // case 1 + { 5, 1, 0 }, // case 2 + { 5, 1, 4 }, { 1, 3, 4 }, // case 3 + { 6, 2, 1 }, // case 4 + { 3, 4, 0 }, { 6, 2, 1 }, // case 5 + { 5, 2, 0 }, { 6, 2, 5 }, // case 6 + { 2, 3, 4 }, { 2, 4, 6 }, { 4, 5, 6 }, // case 7 + { 2, 7, 3 }, // case 8 + { 2, 7, 4 }, { 4, 0, 2 }, // case 9 + { 5, 1, 0 }, { 2, 7, 3 }, // case 10 + { 5, 7, 4 }, { 1, 7, 5 }, { 2, 7, 1 }, // case 11 + { 6, 3, 1 }, { 7, 3, 6 }, // case 12 + { 4, 6, 7 }, { 0, 6, 4 }, { 1, 6, 0 }, // case 13 + { 7, 5, 6 }, { 3, 5, 7 }, { 0, 5, 3 }, // case 14 + { 7, 4, 5 }, { 7, 5, 6 }, // case 15 + { 7, 5, 4 }, { 7, 6, 5 }, // case 16 + { 5, 0, 3 }, { 6, 5, 3 }, { 7, 6, 3 }, // case 17 + { 1, 0, 4 }, { 7, 1, 4 }, { 6, 1, 7 }, // case 18 + { 6, 1, 3 }, { 7, 6, 3 }, // case 19 + { 7, 5, 4 }, { 7, 1, 5 }, { 7, 2, 1 }, // case 20 + { 3, 7, 0 }, { 7, 5, 0 }, { 7, 2, 5 }, { 2, 1, 5 }, // case 21 + { 4, 2, 0 }, { 7, 2, 4 }, // case 22 + { 7, 2, 3 }, // case 23 + { 2, 4, 3 }, { 5, 4, 2 }, { 6, 5, 2 }, // case 24 + { 2, 5, 0 }, { 2, 6, 5 }, // case 25 + { 6, 1, 0 }, { 4, 6, 0 }, { 3, 6, 4 }, { 3, 2, 6 }, // case 26 + { 2, 6, 1 }, // case 27 + { 1, 4, 3 }, { 1, 5, 4 }, // case 28 + { 1, 5, 0 }, // case 29 + { 4, 3, 0 }, // case 30 + // case 31 + { 0, 0, 0 } // dummy + }; + // clang-format on + + vtkm::IdComponent offset = GetOutCellTableOffset(inCellType, caseNumber); + return triTable[offset + outCellNumber]; + } +}; + +template <> +class MarchingCellTables<2> { - // clang-format off - VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent triTableOffset[] = { - // CELL_SHAPE_EMPTY - // CELL_SHAPE_VERTEX - // CELL_SHAPE_POLY_VERTEX - // CELL_SHAPE_LINE - // CELL_SHAPE_POLY_LINE - // CELL_SHAPE_TRIANGLE - // CELL_SHAPE_TRIANGLE_STRIP - // CELL_SHAPE_POLYGON - // CELL_SHAPE_PIXEL - // CELL_SHAPE_QUAD - // CELL_SHAPE_TETRA - 0, 0, 1, 2, 4, 5, 7, 9, // cases 0 - 7 - 10, 11, 13, 15, 16, 18, 19, 20, // cases 8 - 15 - // CELL_SHAPE_VOXEL - // CELL_SHAPE_HEXAHEDRON - 20, 20, 21, 22, 24, 25, 27, 29, // cases 0 - 7 - 32, 33, 35, 37, 40, 42, 45, 48, // cases 8 - 15 - 50, 51, 53, 55, 58, 60, 63, 66, // cases 16 - 23 - 70, 72, 75, 78, 82, 85, 89, 93, // cases 24 - 31 - 96, 97, 99, 101, 104, 106, 109, 112, // cases 32 - 39 - 116, 118, 121, 124, 128, 131, 135, 139, // cases 40 - 47 - 142, 144, 147, 150, 152, 155, 159, 163, // cases 48 - 55 - 166, 169, 173, 177, 180, 184, 189, 194, // cases 56 - 63 - 196, 197, 199, 201, 204, 206, 209, 212, // cases 64 - 71 - 216, 218, 221, 224, 228, 231, 235, 239, // cases 72 - 79 - 242, 244, 247, 250, 254, 257, 261, 265, // cases 80 - 87 - 270, 273, 277, 281, 286, 290, 295, 300, // cases 88 - 95 - 304, 306, 309, 312, 316, 319, 323, 325, // cases 96 - 103 - 328, 331, 335, 339, 344, 348, 353, 356, // cases 104 - 111 - 358, 361, 365, 369, 372, 376, 381, 384, // cases 112 - 119 - 386, 390, 395, 400, 404, 409, 411, 415, // cases 120 - 127 - 416, 417, 419, 421, 424, 426, 429, 432, // cases 128 - 135 - 436, 438, 441, 444, 448, 451, 455, 459, // cases 136 - 143 - 462, 464, 467, 470, 474, 477, 481, 485, // cases 144 - 151 - 490, 493, 495, 499, 502, 506, 509, 514, // cases 152 - 159 - 516, 518, 521, 524, 528, 531, 535, 539, // cases 160 - 167 - 544, 547, 551, 555, 560, 564, 569, 574, // cases 168 - 175 - 578, 581, 585, 589, 592, 596, 601, 606, // cases 176 - 183 - 610, 614, 617, 622, 624, 629, 633, 635, // cases 184 - 191 - 636, 638, 641, 644, 648, 651, 655, 659, // cases 192 - 199 - 664, 667, 671, 675, 680, 682, 685, 688, // cases 200 - 207 - 690, 693, 697, 701, 706, 710, 715, 720, // cases 208 - 215 - 722, 726, 729, 734, 738, 741, 743, 747, // cases 216 - 223 - 748, 751, 755, 759, 764, 768, 773, 776, // cases 224 - 231 - 780, 784, 789, 794, 796, 799, 803, 805, // cases 232 - 239 - 806, 808, 811, 814, 816, 819, 823, 825, // cases 240 - 247 - 826, 829, 831, 835, 836, 838, 839, 840, // cases 248 - 255 - // CELL_SHAPE_WEDGE - 840, 840, 841, 842, 844, 845, 847, 849, // cases 0 - 7 - 850, 851, 853, 855, 858, 860, 863, 866, // cases 8 - 15 - 868, 869, 871, 873, 876, 878, 881, 884, // cases 16 - 23 - 886, 888, 891, 894, 896, 899, 903, 907, // cases 24 - 31 - 908, 909, 911, 913, 916, 918, 921, 924, // cases 32 - 39 - 926, 928, 931, 934, 938, 941, 943, 947, // cases 40 - 47 - 948, 950, 953, 956, 960, 963, 967, 969, // cases 48 - 55 - 970, 971, 973, 975, 976, 978, 979, 980, // cases 56 - 63 - // CELL_SHAPE_PYRAMID - 980, 980, 981, 982, 984, 985, 987, 989, // cases 0 - 7 - 992, 993, 995, 997, 1000, 1002, 1005, 1008, // cases 8 - 15 - 1010, 1012, 1015, 1018, 1020, 1023, 1027, 1029, // cases 16 - 23 - 1030, 1033, 1035, 1039, 1040, 1042, 1043, 1044, // cases 24 - 31 - 0 // dummy - }; - // clang-format on + VTKM_EXEC static inline vtkm::IdComponent GetNumOutCellsOffset(vtkm::UInt8 inCellType) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent offsets[] = { + 0, // CELL_SHAPE_EMPTY + 0, // CELL_SHAPE_VERTEX + 0, // CELL_SHAPE_POLY_VERTEX + 0, // CELL_SHAPE_LINE + 0, // CELL_SHAPE_POLY_LINE + 0, // CELL_SHAPE_TRIANGLE + 8, // CELL_SHAPE_TRIANGLE_STRIP + 8, // CELL_SHAPE_POLYGON + 8, // CELL_SHAPE_PIXEL + 8, // CELL_SHAPE_QUAD + 24, // CELL_SHAPE_TETRA + 24, // CELL_SHAPE_VOXEL + 24, // CELL_SHAPE_HEXAHEDRON + 24, // CELL_SHAPE_WEDGE + 24, // CELL_SHAPE_PYRAMID + 24 // table size + }; + // clang-format on - vtkm::IdComponent offset = GetNumTrianglesOffset(cellType); - return triTableOffset[offset + caseNumber]; -} + return offsets[inCellType]; + } -VTKM_EXEC inline const vtkm::UInt8* GetTriangleEdges(vtkm::UInt8 cellType, - vtkm::UInt8 caseNumber, - vtkm::UInt8 triangleNumber) + VTKM_EXEC static inline vtkm::IdComponent GetOutCellTableOffset(vtkm::UInt8 inCellType, + vtkm::UInt8 caseNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent tableOffset[] = { + // CELL_SHAPE_EMPTY + // CELL_SHAPE_VERTEX + // CELL_SHAPE_POLY_VERTEX + // CELL_SHAPE_LINE + // CELL_SHAPE_POLY_LINE + // CELL_SHAPE_TRIANGLE + 0, 0, 1, 2, 3, 4, 5, 6, // cases 0 - 7 + // CELL_SHAPE_TRIANGLE_STRIP + // CELL_SHAPE_POLYGON + // CELL_SHAPE_PIXEL + // CELL_SHAPE_QUAD + 6, 6, 7, 8, 9, 10, 12, 13, // cases 0 - 7 + 14, 15, 16, 18, 19, 20, 21, 22, // cases 8 - 15 + // CELL_SHAPE_TETRA + // CELL_SHAPE_VOXEL + // CELL_SHAPE_HEXAHEDRON + // CELL_SHAPE_WEDGE + // CELL_SHAPE_PYRAMID + 0 // dummy + }; + // clang-format on + + vtkm::IdComponent offset = GetNumOutCellsOffset(inCellType); + return tableOffset[offset + caseNumber]; + } + +public: + VTKM_EXEC static inline vtkm::UInt8 GetNumOutCells(vtkm::UInt8 inCellType, vtkm::UInt8 caseNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 numOutCells[] = { + // CELL_SHAPE_EMPTY + // CELL_SHAPE_VERTEX + // CELL_SHAPE_POLY_VERTEX + // CELL_SHAPE_LINE + // CELL_SHAPE_POLY_LINE + // CELL_SHAPE_TRIANGLE + 0, 1, 1, 1, 1, 1, 1, 0, // cases 0 - 7 + // CELL_SHAPE_TRIANGLE_STRIP + // CELL_SHAPE_POLYGON + // CELL_SHAPE_PIXEL + // CELL_SHAPE_QUAD + 0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0, // cases 0 - 15 + // CELL_SHAPE_TETRA + // CELL_SHAPE_VOXEL + // CELL_SHAPE_HEXAHEDRON + // CELL_SHAPE_WEDGE + // CELL_SHAPE_PYRAMID + 0 // dummy + }; + // clang-format on + + vtkm::IdComponent offset = GetNumOutCellsOffset(inCellType); + if (offset == GetNumOutCellsOffset(inCellType + 1)) + { + // This part of the table is empty. + return 0; + } + else + { + return numOutCells[offset + caseNumber]; + } + } + + VTKM_EXEC static inline const vtkm::UInt8* GetCellEdges(vtkm::UInt8 inCellType, + vtkm::UInt8 caseNumber, + vtkm::UInt8 outCellNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 lineTable[][2] = { + // CELL_SHAPE_EMPTY + // CELL_SHAPE_VERTEX + // CELL_SHAPE_POLY_VERTEX + // CELL_SHAPE_LINE + // CELL_SHAPE_POLY_LINE + // CELL_SHAPE_TRIANGLE + // case 0 + { 0, 2 }, // case 1 + { 1, 0 }, // case 2 + { 1, 2 }, // case 3 + { 2, 1 }, // case 4 + { 0, 1 }, // case 5 + { 2, 0 }, // case 6 + // case 7 + // CELL_SHAPE_TRIANGLE_STRIP + // CELL_SHAPE_POLYGON + // CELL_SHAPE_PIXEL + // CELL_SHAPE_QUAD + // case 0 + { 0, 3 }, // case 1 + { 1, 0 }, // case 2 + { 1, 3 }, // case 3 + { 2, 1 }, // case 4 + { 0, 3 }, { 2, 1 }, // case 5 + { 2, 0 }, // case 6 + { 2, 3 }, // case 7 + { 3, 2 }, // case 8 + { 0, 2 }, // case 9 + { 1, 0 }, { 3, 2 }, // case 10 + { 1, 2 }, // case 11 + { 3, 1 }, // case 12 + { 0, 1 }, // case 13 + { 3, 0 }, // case 14 + // case 15 + // CELL_SHAPE_TETRA + // CELL_SHAPE_VOXEL + // CELL_SHAPE_HEXAHEDRON + // CELL_SHAPE_WEDGE + // CELL_SHAPE_PYRAMID + { 0, 0 } // dummy + }; + // clang-format on + + vtkm::IdComponent offset = GetOutCellTableOffset(inCellType, caseNumber); + return lineTable[offset + outCellNumber]; + } +}; + +template +VTKM_EXEC inline vtkm::UInt8 GetNumOutCells(vtkm::UInt8 inCellType, vtkm::UInt8 caseNumber) { - // clang-format off - VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 triTable[][3] = { - // CELL_SHAPE_EMPTY - // CELL_SHAPE_VERTEX - // CELL_SHAPE_POLY_VERTEX - // CELL_SHAPE_LINE - // CELL_SHAPE_POLY_LINE - // CELL_SHAPE_TRIANGLE - // CELL_SHAPE_TRIANGLE_STRIP - // CELL_SHAPE_POLYGON - // CELL_SHAPE_PIXEL - // CELL_SHAPE_QUAD - // CELL_SHAPE_TETRA - // case 0 - { 0, 3, 2 }, // case 1 - { 0, 1, 4 }, // case 2 - { 1, 4, 2 }, { 2, 4, 3 }, // case 3 - { 1, 2, 5 }, // case 4 - { 0, 3, 5 }, { 0, 5, 1 }, // case 5 - { 0, 2, 5 }, { 0, 5, 4 }, // case 6 - { 5, 4, 3 }, // case 7 - { 3, 4, 5 }, // case 8 - { 4, 5, 0 }, { 5, 2, 0 }, // case 9 - { 1, 5, 0 }, { 5, 3, 0 }, // case 10 - { 5, 2, 1 }, // case 11 - { 3, 4, 2 }, { 2, 4, 1 }, // case 12 - { 4, 1, 0 }, // case 13 - { 2, 3, 0 }, // case 14 - // case 15 - // CELL_SHAPE_VOXEL - // CELL_SHAPE_HEXAHEDRON - // case 0 - { 0, 8, 3 }, // case 1 - { 0, 1, 9 }, // case 2 - { 1, 8, 3 }, { 9, 8, 1 }, // case 3 - { 1, 2, 11 }, // case 4 - { 0, 8, 3 }, { 1, 2, 11 }, // case 5 - { 9, 2, 11 }, { 0, 2, 9 }, // case 6 - { 2, 8, 3 }, { 2, 11, 8 }, { 11, 9, 8 }, // case 7 - { 3, 10, 2 }, // case 8 - { 0, 10, 2 }, { 8, 10, 0 }, // case 9 - { 1, 9, 0 }, { 2, 3, 10 }, // case 10 - { 1, 10, 2 }, { 1, 9, 10 }, { 9, 8, 10 }, // case 11 - { 3, 11, 1 }, { 10, 11, 3 }, // case 12 - { 0, 11, 1 }, { 0, 8, 11 }, { 8, 10, 11 }, // case 13 - { 3, 9, 0 }, { 3, 10, 9 }, { 10, 11, 9 }, // case 14 - { 9, 8, 11 }, { 11, 8, 10 }, // case 15 - { 4, 7, 8 }, // case 16 - { 4, 3, 0 }, { 7, 3, 4 }, // case 17 - { 0, 1, 9 }, { 8, 4, 7 }, // case 18 - { 4, 1, 9 }, { 4, 7, 1 }, { 7, 3, 1 }, // case 19 - { 1, 2, 11 }, { 8, 4, 7 }, // case 20 - { 3, 4, 7 }, { 3, 0, 4 }, { 1, 2, 11 }, // case 21 - { 9, 2, 11 }, { 9, 0, 2 }, { 8, 4, 7 }, // case 22 - { 2, 11, 9 }, { 2, 9, 7 }, { 2, 7, 3 }, { 7, 9, 4 }, // case 23 - { 8, 4, 7 }, { 3, 10, 2 }, // case 24 - { 10, 4, 7 }, { 10, 2, 4 }, { 2, 0, 4 }, // case 25 - { 9, 0, 1 }, { 8, 4, 7 }, { 2, 3, 10 }, // case 26 - { 4, 7, 10 }, { 9, 4, 10 }, { 9, 10, 2 }, { 9, 2, 1 }, // case 27 - { 3, 11, 1 }, { 3, 10, 11 }, { 7, 8, 4 }, // case 28 - { 1, 10, 11 }, { 1, 4, 10 }, { 1, 0, 4 }, { 7, 10, 4 }, // case 29 - { 4, 7, 8 }, { 9, 0, 10 }, { 9, 10, 11 }, { 10, 0, 3 }, // case 30 - { 4, 7, 10 }, { 4, 10, 9 }, { 9, 10, 11 }, // case 31 - { 9, 5, 4 }, // case 32 - { 9, 5, 4 }, { 0, 8, 3 }, // case 33 - { 0, 5, 4 }, { 1, 5, 0 }, // case 34 - { 8, 5, 4 }, { 8, 3, 5 }, { 3, 1, 5 }, // case 35 - { 1, 2, 11 }, { 9, 5, 4 }, // case 36 - { 3, 0, 8 }, { 1, 2, 11 }, { 4, 9, 5 }, // case 37 - { 5, 2, 11 }, { 5, 4, 2 }, { 4, 0, 2 }, // case 38 - { 2, 11, 5 }, { 3, 2, 5 }, { 3, 5, 4 }, { 3, 4, 8 }, // case 39 - { 9, 5, 4 }, { 2, 3, 10 }, // case 40 - { 0, 10, 2 }, { 0, 8, 10 }, { 4, 9, 5 }, // case 41 - { 0, 5, 4 }, { 0, 1, 5 }, { 2, 3, 10 }, // case 42 - { 2, 1, 5 }, { 2, 5, 8 }, { 2, 8, 10 }, { 4, 8, 5 }, // case 43 - { 11, 3, 10 }, { 11, 1, 3 }, { 9, 5, 4 }, // case 44 - { 4, 9, 5 }, { 0, 8, 1 }, { 8, 11, 1 }, { 8, 10, 11 }, // case 45 - { 5, 4, 0 }, { 5, 0, 10 }, { 5, 10, 11 }, { 10, 0, 3 }, // case 46 - { 5, 4, 8 }, { 5, 8, 11 }, { 11, 8, 10 }, // case 47 - { 9, 7, 8 }, { 5, 7, 9 }, // case 48 - { 9, 3, 0 }, { 9, 5, 3 }, { 5, 7, 3 }, // case 49 - { 0, 7, 8 }, { 0, 1, 7 }, { 1, 5, 7 }, // case 50 - { 1, 5, 3 }, { 3, 5, 7 }, // case 51 - { 9, 7, 8 }, { 9, 5, 7 }, { 11, 1, 2 }, // case 52 - { 11, 1, 2 }, { 9, 5, 0 }, { 5, 3, 0 }, { 5, 7, 3 }, // case 53 - { 8, 0, 2 }, { 8, 2, 5 }, { 8, 5, 7 }, { 11, 5, 2 }, // case 54 - { 2, 11, 5 }, { 2, 5, 3 }, { 3, 5, 7 }, // case 55 - { 7, 9, 5 }, { 7, 8, 9 }, { 3, 10, 2 }, // case 56 - { 9, 5, 7 }, { 9, 7, 2 }, { 9, 2, 0 }, { 2, 7, 10 }, // case 57 - { 2, 3, 10 }, { 0, 1, 8 }, { 1, 7, 8 }, { 1, 5, 7 }, // case 58 - { 10, 2, 1 }, { 10, 1, 7 }, { 7, 1, 5 }, // case 59 - { 9, 5, 8 }, { 8, 5, 7 }, { 11, 1, 3 }, { 11, 3, 10 }, // case 60 - { 5, 7, 0 }, { 5, 0, 9 }, { 7, 10, 0 }, { 1, 0, 11 }, { 10, 11, 0 }, // case 61 - { 10, 11, 0 }, { 10, 0, 3 }, { 11, 5, 0 }, { 8, 0, 7 }, { 5, 7, 0 }, // case 62 - { 10, 11, 5 }, { 7, 10, 5 }, // case 63 - { 11, 6, 5 }, // case 64 - { 0, 8, 3 }, { 5, 11, 6 }, // case 65 - { 9, 0, 1 }, { 5, 11, 6 }, // case 66 - { 1, 8, 3 }, { 1, 9, 8 }, { 5, 11, 6 }, // case 67 - { 1, 6, 5 }, { 2, 6, 1 }, // case 68 - { 1, 6, 5 }, { 1, 2, 6 }, { 3, 0, 8 }, // case 69 - { 9, 6, 5 }, { 9, 0, 6 }, { 0, 2, 6 }, // case 70 - { 5, 9, 8 }, { 5, 8, 2 }, { 5, 2, 6 }, { 3, 2, 8 }, // case 71 - { 2, 3, 10 }, { 11, 6, 5 }, // case 72 - { 10, 0, 8 }, { 10, 2, 0 }, { 11, 6, 5 }, // case 73 - { 0, 1, 9 }, { 2, 3, 10 }, { 5, 11, 6 }, // case 74 - { 5, 11, 6 }, { 1, 9, 2 }, { 9, 10, 2 }, { 9, 8, 10 }, // case 75 - { 6, 3, 10 }, { 6, 5, 3 }, { 5, 1, 3 }, // case 76 - { 0, 8, 10 }, { 0, 10, 5 }, { 0, 5, 1 }, { 5, 10, 6 }, // case 77 - { 3, 10, 6 }, { 0, 3, 6 }, { 0, 6, 5 }, { 0, 5, 9 }, // case 78 - { 6, 5, 9 }, { 6, 9, 10 }, { 10, 9, 8 }, // case 79 - { 5, 11, 6 }, { 4, 7, 8 }, // case 80 - { 4, 3, 0 }, { 4, 7, 3 }, { 6, 5, 11 }, // case 81 - { 1, 9, 0 }, { 5, 11, 6 }, { 8, 4, 7 }, // case 82 - { 11, 6, 5 }, { 1, 9, 7 }, { 1, 7, 3 }, { 7, 9, 4 }, // case 83 - { 6, 1, 2 }, { 6, 5, 1 }, { 4, 7, 8 }, // case 84 - { 1, 2, 5 }, { 5, 2, 6 }, { 3, 0, 4 }, { 3, 4, 7 }, // case 85 - { 8, 4, 7 }, { 9, 0, 5 }, { 0, 6, 5 }, { 0, 2, 6 }, // case 86 - { 7, 3, 9 }, { 7, 9, 4 }, { 3, 2, 9 }, { 5, 9, 6 }, { 2, 6, 9 }, // case 87 - { 3, 10, 2 }, { 7, 8, 4 }, { 11, 6, 5 }, // case 88 - { 5, 11, 6 }, { 4, 7, 2 }, { 4, 2, 0 }, { 2, 7, 10 }, // case 89 - { 0, 1, 9 }, { 4, 7, 8 }, { 2, 3, 10 }, { 5, 11, 6 }, // case 90 - { 9, 2, 1 }, { 9, 10, 2 }, { 9, 4, 10 }, { 7, 10, 4 }, { 5, 11, 6 }, // case 91 - { 8, 4, 7 }, { 3, 10, 5 }, { 3, 5, 1 }, { 5, 10, 6 }, // case 92 - { 5, 1, 10 }, { 5, 10, 6 }, { 1, 0, 10 }, { 7, 10, 4 }, { 0, 4, 10 }, // case 93 - { 0, 5, 9 }, { 0, 6, 5 }, { 0, 3, 6 }, { 10, 6, 3 }, { 8, 4, 7 }, // case 94 - { 6, 5, 9 }, { 6, 9, 10 }, { 4, 7, 9 }, { 7, 10, 9 }, // case 95 - { 11, 4, 9 }, { 6, 4, 11 }, // case 96 - { 4, 11, 6 }, { 4, 9, 11 }, { 0, 8, 3 }, // case 97 - { 11, 0, 1 }, { 11, 6, 0 }, { 6, 4, 0 }, // case 98 - { 8, 3, 1 }, { 8, 1, 6 }, { 8, 6, 4 }, { 6, 1, 11 }, // case 99 - { 1, 4, 9 }, { 1, 2, 4 }, { 2, 6, 4 }, // case 100 - { 3, 0, 8 }, { 1, 2, 9 }, { 2, 4, 9 }, { 2, 6, 4 }, // case 101 - { 0, 2, 4 }, { 4, 2, 6 }, // case 102 - { 8, 3, 2 }, { 8, 2, 4 }, { 4, 2, 6 }, // case 103 - { 11, 4, 9 }, { 11, 6, 4 }, { 10, 2, 3 }, // case 104 - { 0, 8, 2 }, { 2, 8, 10 }, { 4, 9, 11 }, { 4, 11, 6 }, // case 105 - { 3, 10, 2 }, { 0, 1, 6 }, { 0, 6, 4 }, { 6, 1, 11 }, // case 106 - { 6, 4, 1 }, { 6, 1, 11 }, { 4, 8, 1 }, { 2, 1, 10 }, { 8, 10, 1 }, // case 107 - { 9, 6, 4 }, { 9, 3, 6 }, { 9, 1, 3 }, { 10, 6, 3 }, // case 108 - { 8, 10, 1 }, { 8, 1, 0 }, { 10, 6, 1 }, { 9, 1, 4 }, { 6, 4, 1 }, // case 109 - { 3, 10, 6 }, { 3, 6, 0 }, { 0, 6, 4 }, // case 110 - { 6, 4, 8 }, { 10, 6, 8 }, // case 111 - { 7, 11, 6 }, { 7, 8, 11 }, { 8, 9, 11 }, // case 112 - { 0, 7, 3 }, { 0, 11, 7 }, { 0, 9, 11 }, { 6, 7, 11 }, // case 113 - { 11, 6, 7 }, { 1, 11, 7 }, { 1, 7, 8 }, { 1, 8, 0 }, // case 114 - { 11, 6, 7 }, { 11, 7, 1 }, { 1, 7, 3 }, // case 115 - { 1, 2, 6 }, { 1, 6, 8 }, { 1, 8, 9 }, { 8, 6, 7 }, // case 116 - { 2, 6, 9 }, { 2, 9, 1 }, { 6, 7, 9 }, { 0, 9, 3 }, { 7, 3, 9 }, // case 117 - { 7, 8, 0 }, { 7, 0, 6 }, { 6, 0, 2 }, // case 118 - { 7, 3, 2 }, { 6, 7, 2 }, // case 119 - { 2, 3, 10 }, { 11, 6, 8 }, { 11, 8, 9 }, { 8, 6, 7 }, // case 120 - { 2, 0, 7 }, { 2, 7, 10 }, { 0, 9, 7 }, { 6, 7, 11 }, { 9, 11, 7 }, // case 121 - { 1, 8, 0 }, { 1, 7, 8 }, { 1, 11, 7 }, { 6, 7, 11 }, { 2, 3, 10 }, // case 122 - { 10, 2, 1 }, { 10, 1, 7 }, { 11, 6, 1 }, { 6, 7, 1 }, // case 123 - { 8, 9, 6 }, { 8, 6, 7 }, { 9, 1, 6 }, { 10, 6, 3 }, { 1, 3, 6 }, // case 124 - { 0, 9, 1 }, { 10, 6, 7 }, // case 125 - { 7, 8, 0 }, { 7, 0, 6 }, { 3, 10, 0 }, { 10, 6, 0 }, // case 126 - { 7, 10, 6 }, // case 127 - { 7, 6, 10 }, // case 128 - { 3, 0, 8 }, { 10, 7, 6 }, // case 129 - { 0, 1, 9 }, { 10, 7, 6 }, // case 130 - { 8, 1, 9 }, { 8, 3, 1 }, { 10, 7, 6 }, // case 131 - { 11, 1, 2 }, { 6, 10, 7 }, // case 132 - { 1, 2, 11 }, { 3, 0, 8 }, { 6, 10, 7 }, // case 133 - { 2, 9, 0 }, { 2, 11, 9 }, { 6, 10, 7 }, // case 134 - { 6, 10, 7 }, { 2, 11, 3 }, { 11, 8, 3 }, { 11, 9, 8 }, // case 135 - { 7, 2, 3 }, { 6, 2, 7 }, // case 136 - { 7, 0, 8 }, { 7, 6, 0 }, { 6, 2, 0 }, // case 137 - { 2, 7, 6 }, { 2, 3, 7 }, { 0, 1, 9 }, // case 138 - { 1, 6, 2 }, { 1, 8, 6 }, { 1, 9, 8 }, { 8, 7, 6 }, // case 139 - { 11, 7, 6 }, { 11, 1, 7 }, { 1, 3, 7 }, // case 140 - { 11, 7, 6 }, { 1, 7, 11 }, { 1, 8, 7 }, { 1, 0, 8 }, // case 141 - { 0, 3, 7 }, { 0, 7, 11 }, { 0, 11, 9 }, { 6, 11, 7 }, // case 142 - { 7, 6, 11 }, { 7, 11, 8 }, { 8, 11, 9 }, // case 143 - { 6, 8, 4 }, { 10, 8, 6 }, // case 144 - { 3, 6, 10 }, { 3, 0, 6 }, { 0, 4, 6 }, // case 145 - { 8, 6, 10 }, { 8, 4, 6 }, { 9, 0, 1 }, // case 146 - { 9, 4, 6 }, { 9, 6, 3 }, { 9, 3, 1 }, { 10, 3, 6 }, // case 147 - { 6, 8, 4 }, { 6, 10, 8 }, { 2, 11, 1 }, // case 148 - { 1, 2, 11 }, { 3, 0, 10 }, { 0, 6, 10 }, { 0, 4, 6 }, // case 149 - { 4, 10, 8 }, { 4, 6, 10 }, { 0, 2, 9 }, { 2, 11, 9 }, // case 150 - { 11, 9, 3 }, { 11, 3, 2 }, { 9, 4, 3 }, { 10, 3, 6 }, { 4, 6, 3 }, // case 151 - { 8, 2, 3 }, { 8, 4, 2 }, { 4, 6, 2 }, // case 152 - { 0, 4, 2 }, { 4, 6, 2 }, // case 153 - { 1, 9, 0 }, { 2, 3, 4 }, { 2, 4, 6 }, { 4, 3, 8 }, // case 154 - { 1, 9, 4 }, { 1, 4, 2 }, { 2, 4, 6 }, // case 155 - { 8, 1, 3 }, { 8, 6, 1 }, { 8, 4, 6 }, { 6, 11, 1 }, // case 156 - { 11, 1, 0 }, { 11, 0, 6 }, { 6, 0, 4 }, // case 157 - { 4, 6, 3 }, { 4, 3, 8 }, { 6, 11, 3 }, { 0, 3, 9 }, { 11, 9, 3 }, // case 158 - { 11, 9, 4 }, { 6, 11, 4 }, // case 159 - { 4, 9, 5 }, { 7, 6, 10 }, // case 160 - { 0, 8, 3 }, { 4, 9, 5 }, { 10, 7, 6 }, // case 161 - { 5, 0, 1 }, { 5, 4, 0 }, { 7, 6, 10 }, // case 162 - { 10, 7, 6 }, { 8, 3, 4 }, { 3, 5, 4 }, { 3, 1, 5 }, // case 163 - { 9, 5, 4 }, { 11, 1, 2 }, { 7, 6, 10 }, // case 164 - { 6, 10, 7 }, { 1, 2, 11 }, { 0, 8, 3 }, { 4, 9, 5 }, // case 165 - { 7, 6, 10 }, { 5, 4, 11 }, { 4, 2, 11 }, { 4, 0, 2 }, // case 166 - { 3, 4, 8 }, { 3, 5, 4 }, { 3, 2, 5 }, { 11, 5, 2 }, { 10, 7, 6 }, // case 167 - { 7, 2, 3 }, { 7, 6, 2 }, { 5, 4, 9 }, // case 168 - { 9, 5, 4 }, { 0, 8, 6 }, { 0, 6, 2 }, { 6, 8, 7 }, // case 169 - { 3, 6, 2 }, { 3, 7, 6 }, { 1, 5, 0 }, { 5, 4, 0 }, // case 170 - { 6, 2, 8 }, { 6, 8, 7 }, { 2, 1, 8 }, { 4, 8, 5 }, { 1, 5, 8 }, // case 171 - { 9, 5, 4 }, { 11, 1, 6 }, { 1, 7, 6 }, { 1, 3, 7 }, // case 172 - { 1, 6, 11 }, { 1, 7, 6 }, { 1, 0, 7 }, { 8, 7, 0 }, { 9, 5, 4 }, // case 173 - { 4, 0, 11 }, { 4, 11, 5 }, { 0, 3, 11 }, { 6, 11, 7 }, { 3, 7, 11 }, // case 174 - { 7, 6, 11 }, { 7, 11, 8 }, { 5, 4, 11 }, { 4, 8, 11 }, // case 175 - { 6, 9, 5 }, { 6, 10, 9 }, { 10, 8, 9 }, // case 176 - { 3, 6, 10 }, { 0, 6, 3 }, { 0, 5, 6 }, { 0, 9, 5 }, // case 177 - { 0, 10, 8 }, { 0, 5, 10 }, { 0, 1, 5 }, { 5, 6, 10 }, // case 178 - { 6, 10, 3 }, { 6, 3, 5 }, { 5, 3, 1 }, // case 179 - { 1, 2, 11 }, { 9, 5, 10 }, { 9, 10, 8 }, { 10, 5, 6 }, // case 180 - { 0, 10, 3 }, { 0, 6, 10 }, { 0, 9, 6 }, { 5, 6, 9 }, { 1, 2, 11 }, // case 181 - { 10, 8, 5 }, { 10, 5, 6 }, { 8, 0, 5 }, { 11, 5, 2 }, { 0, 2, 5 }, // case 182 - { 6, 10, 3 }, { 6, 3, 5 }, { 2, 11, 3 }, { 11, 5, 3 }, // case 183 - { 5, 8, 9 }, { 5, 2, 8 }, { 5, 6, 2 }, { 3, 8, 2 }, // case 184 - { 9, 5, 6 }, { 9, 6, 0 }, { 0, 6, 2 }, // case 185 - { 1, 5, 8 }, { 1, 8, 0 }, { 5, 6, 8 }, { 3, 8, 2 }, { 6, 2, 8 }, // case 186 - { 1, 5, 6 }, { 2, 1, 6 }, // case 187 - { 1, 3, 6 }, { 1, 6, 11 }, { 3, 8, 6 }, { 5, 6, 9 }, { 8, 9, 6 }, // case 188 - { 11, 1, 0 }, { 11, 0, 6 }, { 9, 5, 0 }, { 5, 6, 0 }, // case 189 - { 0, 3, 8 }, { 5, 6, 11 }, // case 190 - { 11, 5, 6 }, // case 191 - { 10, 5, 11 }, { 7, 5, 10 }, // case 192 - { 10, 5, 11 }, { 10, 7, 5 }, { 8, 3, 0 }, // case 193 - { 5, 10, 7 }, { 5, 11, 10 }, { 1, 9, 0 }, // case 194 - { 11, 7, 5 }, { 11, 10, 7 }, { 9, 8, 1 }, { 8, 3, 1 }, // case 195 - { 10, 1, 2 }, { 10, 7, 1 }, { 7, 5, 1 }, // case 196 - { 0, 8, 3 }, { 1, 2, 7 }, { 1, 7, 5 }, { 7, 2, 10 }, // case 197 - { 9, 7, 5 }, { 9, 2, 7 }, { 9, 0, 2 }, { 2, 10, 7 }, // case 198 - { 7, 5, 2 }, { 7, 2, 10 }, { 5, 9, 2 }, { 3, 2, 8 }, { 9, 8, 2 }, // case 199 - { 2, 5, 11 }, { 2, 3, 5 }, { 3, 7, 5 }, // case 200 - { 8, 2, 0 }, { 8, 5, 2 }, { 8, 7, 5 }, { 11, 2, 5 }, // case 201 - { 9, 0, 1 }, { 5, 11, 3 }, { 5, 3, 7 }, { 3, 11, 2 }, // case 202 - { 9, 8, 2 }, { 9, 2, 1 }, { 8, 7, 2 }, { 11, 2, 5 }, { 7, 5, 2 }, // case 203 - { 1, 3, 5 }, { 3, 7, 5 }, // case 204 - { 0, 8, 7 }, { 0, 7, 1 }, { 1, 7, 5 }, // case 205 - { 9, 0, 3 }, { 9, 3, 5 }, { 5, 3, 7 }, // case 206 - { 9, 8, 7 }, { 5, 9, 7 }, // case 207 - { 5, 8, 4 }, { 5, 11, 8 }, { 11, 10, 8 }, // case 208 - { 5, 0, 4 }, { 5, 10, 0 }, { 5, 11, 10 }, { 10, 3, 0 }, // case 209 - { 0, 1, 9 }, { 8, 4, 11 }, { 8, 11, 10 }, { 11, 4, 5 }, // case 210 - { 11, 10, 4 }, { 11, 4, 5 }, { 10, 3, 4 }, { 9, 4, 1 }, { 3, 1, 4 }, // case 211 - { 2, 5, 1 }, { 2, 8, 5 }, { 2, 10, 8 }, { 4, 5, 8 }, // case 212 - { 0, 4, 10 }, { 0, 10, 3 }, { 4, 5, 10 }, { 2, 10, 1 }, { 5, 1, 10 }, // case 213 - { 0, 2, 5 }, { 0, 5, 9 }, { 2, 10, 5 }, { 4, 5, 8 }, { 10, 8, 5 }, // case 214 - { 9, 4, 5 }, { 2, 10, 3 }, // case 215 - { 2, 5, 11 }, { 3, 5, 2 }, { 3, 4, 5 }, { 3, 8, 4 }, // case 216 - { 5, 11, 2 }, { 5, 2, 4 }, { 4, 2, 0 }, // case 217 - { 3, 11, 2 }, { 3, 5, 11 }, { 3, 8, 5 }, { 4, 5, 8 }, { 0, 1, 9 }, // case 218 - { 5, 11, 2 }, { 5, 2, 4 }, { 1, 9, 2 }, { 9, 4, 2 }, // case 219 - { 8, 4, 5 }, { 8, 5, 3 }, { 3, 5, 1 }, // case 220 - { 0, 4, 5 }, { 1, 0, 5 }, // case 221 - { 8, 4, 5 }, { 8, 5, 3 }, { 9, 0, 5 }, { 0, 3, 5 }, // case 222 - { 9, 4, 5 }, // case 223 - { 4, 10, 7 }, { 4, 9, 10 }, { 9, 11, 10 }, // case 224 - { 0, 8, 3 }, { 4, 9, 7 }, { 9, 10, 7 }, { 9, 11, 10 }, // case 225 - { 1, 11, 10 }, { 1, 10, 4 }, { 1, 4, 0 }, { 7, 4, 10 }, // case 226 - { 3, 1, 4 }, { 3, 4, 8 }, { 1, 11, 4 }, { 7, 4, 10 }, { 11, 10, 4 }, // case 227 - { 4, 10, 7 }, { 9, 10, 4 }, { 9, 2, 10 }, { 9, 1, 2 }, // case 228 - { 9, 7, 4 }, { 9, 10, 7 }, { 9, 1, 10 }, { 2, 10, 1 }, { 0, 8, 3 }, // case 229 - { 10, 7, 4 }, { 10, 4, 2 }, { 2, 4, 0 }, // case 230 - { 10, 7, 4 }, { 10, 4, 2 }, { 8, 3, 4 }, { 3, 2, 4 }, // case 231 - { 2, 9, 11 }, { 2, 7, 9 }, { 2, 3, 7 }, { 7, 4, 9 }, // case 232 - { 9, 11, 7 }, { 9, 7, 4 }, { 11, 2, 7 }, { 8, 7, 0 }, { 2, 0, 7 }, // case 233 - { 3, 7, 11 }, { 3, 11, 2 }, { 7, 4, 11 }, { 1, 11, 0 }, { 4, 0, 11 }, // case 234 - { 1, 11, 2 }, { 8, 7, 4 }, // case 235 - { 4, 9, 1 }, { 4, 1, 7 }, { 7, 1, 3 }, // case 236 - { 4, 9, 1 }, { 4, 1, 7 }, { 0, 8, 1 }, { 8, 7, 1 }, // case 237 - { 4, 0, 3 }, { 7, 4, 3 }, // case 238 - { 4, 8, 7 }, // case 239 - { 9, 11, 8 }, { 11, 10, 8 }, // case 240 - { 3, 0, 9 }, { 3, 9, 10 }, { 10, 9, 11 }, // case 241 - { 0, 1, 11 }, { 0, 11, 8 }, { 8, 11, 10 }, // case 242 - { 3, 1, 11 }, { 10, 3, 11 }, // case 243 - { 1, 2, 10 }, { 1, 10, 9 }, { 9, 10, 8 }, // case 244 - { 3, 0, 9 }, { 3, 9, 10 }, { 1, 2, 9 }, { 2, 10, 9 }, // case 245 - { 0, 2, 10 }, { 8, 0, 10 }, // case 246 - { 3, 2, 10 }, // case 247 - { 2, 3, 8 }, { 2, 8, 11 }, { 11, 8, 9 }, // case 248 - { 9, 11, 2 }, { 0, 9, 2 }, // case 249 - { 2, 3, 8 }, { 2, 8, 11 }, { 0, 1, 8 }, { 1, 11, 8 }, // case 250 - { 1, 11, 2 }, // case 251 - { 1, 3, 8 }, { 9, 1, 8 }, // case 252 - { 0, 9, 1 }, // case 253 - { 0, 3, 8 }, // case 254 - // case 255 - // CELL_SHAPE_WEDGE - // case 0 - { 0, 6, 2 }, // case 1 - { 0, 1, 7 }, // case 2 - { 6, 1, 7 }, { 6, 2, 1 }, // case 3 - { 1, 2, 8 }, // case 4 - { 6, 1, 0 }, { 6, 8, 1 }, // case 5 - { 0, 2, 8 }, { 7, 0, 8 }, // case 6 - { 7, 6, 8 }, // case 7 - { 3, 5, 6 }, // case 8 - { 3, 5, 0 }, { 5, 2, 0 }, // case 9 - { 0, 1, 7 }, { 6, 3, 5 }, // case 10 - { 1, 7, 3 }, { 1, 3, 5 }, { 1, 5, 2 }, // case 11 - { 2, 8, 1 }, { 6, 3, 5 }, // case 12 - { 0, 3, 1 }, { 1, 3, 5 }, { 1, 5, 8 }, // case 13 - { 6, 3, 5 }, { 0, 8, 7 }, { 0, 2, 8 }, // case 14 - { 7, 3, 5 }, { 7, 5, 8 }, // case 15 - { 7, 4, 3 }, // case 16 - { 7, 4, 3 }, { 0, 6, 2 }, // case 17 - { 0, 1, 3 }, { 1, 4, 3 }, // case 18 - { 1, 4, 3 }, { 1, 3, 6 }, { 1, 6, 2 }, // case 19 - { 7, 4, 3 }, { 2, 8, 1 }, // case 20 - { 7, 4, 3 }, { 6, 1, 0 }, { 6, 8, 1 }, // case 21 - { 0, 4, 3 }, { 0, 8, 4 }, { 0, 2, 8 }, // case 22 - { 6, 8, 3 }, { 3, 8, 4 }, // case 23 - { 6, 7, 4 }, { 6, 4, 5 }, // case 24 - { 0, 7, 5 }, { 7, 4, 5 }, { 2, 0, 5 }, // case 25 - { 1, 6, 0 }, { 1, 5, 6 }, { 1, 4, 5 }, // case 26 - { 2, 1, 5 }, { 5, 1, 4 }, // case 27 - { 2, 8, 1 }, { 6, 7, 5 }, { 7, 4, 5 }, // case 28 - { 0, 7, 5 }, { 7, 4, 5 }, { 0, 5, 1 }, { 1, 5, 8 }, // case 29 - { 0, 2, 8 }, { 0, 8, 4 }, { 0, 4, 5 }, { 0, 5, 6 }, // case 30 - { 8, 4, 5 }, // case 31 - { 4, 8, 5 }, // case 32 - { 4, 8, 5 }, { 0, 6, 2 }, // case 33 - { 4, 8, 5 }, { 0, 1, 7 }, // case 34 - { 4, 8, 5 }, { 6, 1, 7 }, { 6, 2, 1 }, // case 35 - { 1, 5, 4 }, { 2, 5, 1 }, // case 36 - { 1, 5, 4 }, { 1, 6, 5 }, { 1, 0, 6 }, // case 37 - { 5, 4, 7 }, { 5, 7, 0 }, { 5, 0, 2 }, // case 38 - { 6, 4, 7 }, { 6, 5, 4 }, // case 39 - { 6, 3, 8 }, { 3, 4, 8 }, // case 40 - { 0, 3, 4 }, { 0, 4, 8 }, { 0, 8, 2 }, // case 41 - { 7, 0, 1 }, { 6, 3, 4 }, { 6, 4, 8 }, // case 42 - { 1, 7, 3 }, { 1, 3, 2 }, { 2, 3, 8 }, { 8, 3, 4 }, // case 43 - { 2, 6, 1 }, { 6, 3, 1 }, { 3, 4, 1 }, // case 44 - { 0, 3, 1 }, { 1, 3, 4 }, // case 45 - { 7, 0, 4 }, { 4, 0, 2 }, { 4, 2, 3 }, { 3, 2, 6 }, // case 46 - { 7, 3, 4 }, // case 47 - { 7, 8, 5 }, { 7, 5, 3 }, // case 48 - { 0, 6, 2 }, { 7, 8, 5 }, { 7, 5, 3 }, // case 49 - { 0, 1, 3 }, { 1, 5, 3 }, { 1, 8, 5 }, // case 50 - { 2, 1, 6 }, { 6, 1, 3 }, { 5, 1, 8 }, { 3, 1, 5 }, // case 51 - { 1, 3, 7 }, { 1, 5, 3 }, { 1, 2, 5 }, // case 52 - { 1, 0, 6 }, { 1, 6, 5 }, { 1, 5, 7 }, { 7, 5, 3 }, // case 53 - { 0, 2, 5 }, { 0, 5, 3 }, // case 54 - { 3, 6, 5 }, // case 55 - { 7, 8, 6 }, // case 56 - { 0, 7, 8 }, { 0, 8, 2 }, // case 57 - { 0, 1, 6 }, { 1, 8, 6 }, // case 58 - { 2, 1, 8 }, // case 59 - { 6, 7, 1 }, { 6, 1, 2 }, // case 60 - { 0, 7, 1 }, // case 61 - { 0, 2, 6 }, // case 62 - // case 63 - // CELL_SHAPE_PYRAMID - // case 0 - { 3, 4, 0 }, // case 1 - { 5, 1, 0 }, // case 2 - { 5, 1, 4 }, { 1, 3, 4 }, // case 3 - { 6, 2, 1 }, // case 4 - { 3, 4, 0 }, { 6, 2, 1 }, // case 5 - { 5, 2, 0 }, { 6, 2, 5 }, // case 6 - { 2, 3, 4 }, { 2, 4, 6 }, { 4, 5, 6 }, // case 7 - { 2, 7, 3 }, // case 8 - { 2, 7, 4 }, { 4, 0, 2 }, // case 9 - { 5, 1, 0 }, { 2, 7, 3 }, // case 10 - { 5, 7, 4 }, { 1, 7, 5 }, { 2, 7, 1 }, // case 11 - { 6, 3, 1 }, { 7, 3, 6 }, // case 12 - { 4, 6, 7 }, { 0, 6, 4 }, { 1, 6, 0 }, // case 13 - { 7, 5, 6 }, { 3, 5, 7 }, { 0, 5, 3 }, // case 14 - { 7, 4, 5 }, { 7, 5, 6 }, // case 15 - { 7, 5, 4 }, { 7, 6, 5 }, // case 16 - { 5, 0, 3 }, { 6, 5, 3 }, { 7, 6, 3 }, // case 17 - { 1, 0, 4 }, { 7, 1, 4 }, { 6, 1, 7 }, // case 18 - { 6, 1, 3 }, { 7, 6, 3 }, // case 19 - { 7, 5, 4 }, { 7, 1, 5 }, { 7, 2, 1 }, // case 20 - { 3, 7, 0 }, { 7, 5, 0 }, { 7, 2, 5 }, { 2, 1, 5 }, // case 21 - { 4, 2, 0 }, { 7, 2, 4 }, // case 22 - { 7, 2, 3 }, // case 23 - { 2, 4, 3 }, { 5, 4, 2 }, { 6, 5, 2 }, // case 24 - { 2, 5, 0 }, { 2, 6, 5 }, // case 25 - { 6, 1, 0 }, { 4, 6, 0 }, { 3, 6, 4 }, { 3, 2, 6 }, // case 26 - { 2, 6, 1 }, // case 27 - { 1, 4, 3 }, { 1, 5, 4 }, // case 28 - { 1, 5, 0 }, // case 29 - { 4, 3, 0 }, // case 30 - // case 31 - { 0, 0, 0 } // dummy - }; - // clang-format on + return MarchingCellTables::GetNumOutCells(inCellType, caseNumber); +} - vtkm::IdComponent offset = GetTriTableOffset(cellType, caseNumber); - return triTable[offset + triangleNumber]; +template +VTKM_EXEC inline const vtkm::UInt8* GetCellEdges(vtkm::UInt8 inCellType, + vtkm::UInt8 caseNumber, + vtkm::UInt8 triangleNumber) +{ + return MarchingCellTables::GetCellEdges(inCellType, caseNumber, triangleNumber); } + } } } // namespace vtkm::worklet::marching_cells diff --git a/vtkm/filter/contour/worklet/contour/MarchingCellTables.h.in b/vtkm/filter/contour/worklet/contour/MarchingCellTables.h.in index fe10790601..eb1745557f 100644 --- a/vtkm/filter/contour/worklet/contour/MarchingCellTables.h.in +++ b/vtkm/filter/contour/worklet/contour/MarchingCellTables.h.in @@ -27,7 +27,7 @@ $# Ignore the following comment. It is meant for the generated file. #ifndef vtk_m_MarchingCellTables_h #define vtk_m_MarchingCellTables_h $py( -shape_names = ( +shape_names = [ "CELL_SHAPE_EMPTY", "CELL_SHAPE_VERTEX", "CELL_SHAPE_POLY_VERTEX", @@ -43,19 +43,54 @@ shape_names = ( "CELL_SHAPE_HEXAHEDRON", "CELL_SHAPE_WEDGE", "CELL_SHAPE_PYRAMID", -) -shape_tables = { +] +shape_tables_2D = { "CELL_SHAPE_EMPTY" : [], "CELL_SHAPE_VERTEX" : [], - # TODO "CELL_SHAPE_LINE" : [], + "CELL_SHAPE_POLY_LINE" : [], + "CELL_SHAPE_TRIANGLE" : [ + [], + [(0, 2, 255)], + [(1, 0, 255)], + [(1, 2, 255)], + [(2, 1, 255)], + [(0, 1, 255)], + [(2, 0, 255)], + [] + ], # Special implementation + "CELL_SHAPE_POLYGON" : [], + "CELL_SHAPE_QUAD" : [ + [], + [(0, 3, 255)], + [(1, 0, 255)], + [(1, 3, 255)], + [(2, 1, 255)], + [(0, 3, 255), (2, 1, 255)], + [(2, 0, 255)], + [(2, 3, 255)], + [(3, 2, 255)], + [(0, 2, 255)], + [(1, 0, 255), (3, 2, 255)], + [(1, 2, 255)], + [(3, 1, 255)], + [(0, 1, 255)], + [(3, 0, 255)], + [], + ], + "CELL_SHAPE_TETRA" : [], + "CELL_SHAPE_HEXAHEDRON" : [], + "CELL_SHAPE_WEDGE" : [], + "CELL_SHAPE_PYRAMID" : [], +} +shape_tables_3D = { + "CELL_SHAPE_EMPTY" : [], + "CELL_SHAPE_VERTEX" : [], + "CELL_SHAPE_LINE" : [], "CELL_SHAPE_POLY_LINE" : [], - # TODO "CELL_SHAPE_TRIANGLE" : [], - # Special implementation "CELL_SHAPE_POLYGON" : [], - # TODO "CELL_SHAPE_QUAD" : [], "CELL_SHAPE_TETRA" : [ [], @@ -453,134 +488,304 @@ namespace marching_cells // Make sure the table indices are the same as VTK-m's shape IDs. $for(shape_index in range(len(shape_names)))\ -$if(shape_names[shape_index] in shape_tables)\ +$if(shape_names[shape_index] in shape_tables_3D)\ VTKM_STATIC_ASSERT(vtkm::$(shape_names[shape_index]) == $(shape_index)); $endif\ $endfor\ -VTKM_EXEC inline vtkm::IdComponent GetNumTrianglesOffset(vtkm::UInt8 cellType) +template +class MarchingCellTables; + +template <> +class MarchingCellTables<3> { - // clang-format off - VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent offsets[] = { + VTKM_EXEC static inline vtkm::IdComponent GetNumOutCellsOffset(vtkm::UInt8 inCellType) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent offsets[] = { $py(offset = 0)\ $for(shape_name in shape_names)\ - $(offset), // $(shape_name) + $(offset), // $(shape_name) $py( -if shape_name in shape_tables: - offset = offset + len(shape_tables[shape_name]) +if shape_name in shape_tables_3D: + offset = offset + len(shape_tables_3D[shape_name]) else: - shape_tables[shape_name] = [] + shape_tables_3D[shape_name] = [] )\ $endfor\ - 0 // dummy - }; - // clang-format on + $(offset) // table size + }; + // clang-format on - return offsets[cellType]; -} + return offsets[inCellType]; + } -VTKM_EXEC inline vtkm::UInt8 GetNumTriangles(vtkm::UInt8 cellType, vtkm::UInt8 caseNumber) -{ - // clang-format off - VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 numTriangles[] = { + VTKM_EXEC static inline vtkm::IdComponent GetOutCellTableOffset(vtkm::UInt8 inCellType, + vtkm::UInt8 caseNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent tableOffset[] = { +$py( +offset = 0 +def table_offset_list(shape_name): + global offset + result = " " + start_case = 0 + display_case = 0 + for triangle_list in shape_tables_3D[shape_name]: + result = result + str(offset) + ", " + offset += len(triangle_list) + display_case += 1 + if display_case == 8: + result = result + " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n " + start_case += display_case + display_case = 0 + if display_case == 0: + result = result[:-6] + else: + result += " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n" + return result +)\ +$extend(table_offset_list)\ +$for(shape_name in shape_names)\ + // $(shape_name) +$table_offset_list(shape_name)\ +$endfor\ + 0 // dummy + }; + // clang-format on + + vtkm::IdComponent offset = GetNumOutCellsOffset(inCellType); + return tableOffset[offset + caseNumber]; + } + +public: + VTKM_EXEC static inline vtkm::UInt8 GetNumOutCells(vtkm::UInt8 inCellType, vtkm::UInt8 caseNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 numOutCells[] = { $py( def num_triangles_list(shape_name): - result = " " + result = " " start_case = 0 display_case = 0 - for triangle_list in shape_tables[shape_name]: + for triangle_list in shape_tables_3D[shape_name]: result = result + str(len(triangle_list)) + ", " display_case += 1 if display_case == 16: - result = result + " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n " + result = result + " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n " start_case += display_case display_case = 0 if display_case == 0: - result = result[:-4] + result = result[:-6] else: result += " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n" return result )\ $extend(num_triangles_list)\ $for(shape_name in shape_names)\ - // $(shape_name) + // $(shape_name) $num_triangles_list(shape_name)\ $endfor\ - 0 // dummy - }; - // clang-format on + 0 // dummy + }; + // clang-format on - vtkm::IdComponent offset = GetNumTrianglesOffset(cellType); - return numTriangles[offset + caseNumber]; -} + vtkm::IdComponent offset = GetNumOutCellsOffset(inCellType); + if (offset == GetNumOutCellsOffset(inCellType + 1)) + { + // This part of the table is empty. + return 0; + } + else + { + return numOutCells[offset + caseNumber]; + } + } -VTKM_EXEC inline vtkm::IdComponent GetTriTableOffset(vtkm::UInt8 cellType, vtkm::UInt8 caseNumber) + VTKM_EXEC static inline const vtkm::UInt8* GetCellEdges(vtkm::UInt8 inCellType, + vtkm::UInt8 caseNumber, + vtkm::UInt8 outCellNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 triTable[][3] = { +$py( +def triangles(triangle_list): + result = "" + for tri in triangle_list: + result += "{ " + str(tri[0]) + ", " + str(tri[1]) + ", " + str(tri[2]) + " }, " + return result +)\ +$extend(triangles)\ +$for(shape_name in shape_names)\ + // $(shape_name) +$py(casenum = 0)\ +$for(triangle_list in shape_tables_3D[shape_name])\ + $triangles(triangle_list)// case $(casenum) +$py(casenum += 1)\ +$endfor\ +$endfor\ + { 0, 0, 0 } // dummy + }; + // clang-format on + + vtkm::IdComponent offset = GetOutCellTableOffset(inCellType, caseNumber); + return triTable[offset + outCellNumber]; + } +}; + +template <> +class MarchingCellTables<2> { - // clang-format off - VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent triTableOffset[] = { + VTKM_EXEC static inline vtkm::IdComponent GetNumOutCellsOffset(vtkm::UInt8 inCellType) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent offsets[] = { +$py(offset = 0)\ +$for(shape_name in shape_names)\ + $(offset), // $(shape_name) +$py( +if shape_name in shape_tables_2D: + offset = offset + len(shape_tables_2D[shape_name]) +else: + shape_tables_2D[shape_name] = [] +)\ +$endfor\ + $(offset) // table size + }; + // clang-format on + + return offsets[inCellType]; + } + + VTKM_EXEC static inline vtkm::IdComponent GetOutCellTableOffset(vtkm::UInt8 inCellType, + vtkm::UInt8 caseNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::IdComponent tableOffset[] = { $py( offset = 0 def table_offset_list(shape_name): global offset - result = " " + result = " " start_case = 0 display_case = 0 - for triangle_list in shape_tables[shape_name]: + for line_list in shape_tables_2D[shape_name]: result = result + str(offset) + ", " - offset += len(triangle_list) + offset += len(line_list) display_case += 1 if display_case == 8: - result = result + " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n " + result = result + " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n " start_case += display_case display_case = 0 if display_case == 0: - result = result[:-4] + result = result[:-6] else: result += " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n" return result )\ $extend(table_offset_list)\ $for(shape_name in shape_names)\ - // $(shape_name) + // $(shape_name) $table_offset_list(shape_name)\ $endfor\ - 0 // dummy - }; - // clang-format on + 0 // dummy + }; + // clang-format on - vtkm::IdComponent offset = GetNumTrianglesOffset(cellType); - return triTableOffset[offset + caseNumber]; -} + vtkm::IdComponent offset = GetNumOutCellsOffset(inCellType); + return tableOffset[offset + caseNumber]; + } -VTKM_EXEC inline const vtkm::UInt8* GetTriangleEdges(vtkm::UInt8 cellType, - vtkm::UInt8 caseNumber, - vtkm::UInt8 triangleNumber) -{ - // clang-format off - VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 triTable[][3] = { +public: + VTKM_EXEC static inline vtkm::UInt8 GetNumOutCells(vtkm::UInt8 inCellType, vtkm::UInt8 caseNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 numOutCells[] = { $py( -def triangles(triangle_list): +def num_triangles_list(shape_name): + result = " " + start_case = 0 + display_case = 0 + for line_list in shape_tables_2D[shape_name]: + result = result + str(len(line_list)) + ", " + display_case += 1 + if display_case == 16: + result = result + " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n " + start_case += display_case + display_case = 0 + if display_case == 0: + result = result[:-6] + else: + result += " // cases " + str(start_case) + " - " + str(start_case + display_case - 1) + "\n" + return result +)\ +$extend(num_triangles_list)\ +$for(shape_name in shape_names)\ + // $(shape_name) +$num_triangles_list(shape_name)\ +$endfor\ + 0 // dummy + }; + // clang-format on + + vtkm::IdComponent offset = GetNumOutCellsOffset(inCellType); + if (offset == GetNumOutCellsOffset(inCellType + 1)) + { + // This part of the table is empty. + return 0; + } + else + { + return numOutCells[offset + caseNumber]; + } + } + + VTKM_EXEC static inline const vtkm::UInt8* GetCellEdges(vtkm::UInt8 inCellType, + vtkm::UInt8 caseNumber, + vtkm::UInt8 outCellNumber) + { + // clang-format off + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::UInt8 lineTable[][2] = { +$py( +def lines(line_list): result = "" - for tri in triangle_list: - result += "{ " + str(tri[0]) + ", " + str(tri[1]) + ", " + str(tri[2]) + " }, " + for line in line_list: + result += "{ " + str(line[0]) + ", " + str(line[1]) + " }, " return result )\ -$extend(triangles)\ +$extend(lines)\ $for(shape_name in shape_names)\ - // $(shape_name) + // $(shape_name) $py(casenum = 0)\ -$for(triangle_list in shape_tables[shape_name])\ - $triangles(triangle_list)// case $(casenum) +$for(line_list in shape_tables_2D[shape_name])\ + $lines(line_list)// case $(casenum) $py(casenum += 1)\ $endfor\ $endfor\ - { 0, 0, 0 } // dummy - }; - // clang-format on + { 0, 0 } // dummy + }; + // clang-format on + + vtkm::IdComponent offset = GetOutCellTableOffset(inCellType, caseNumber); + return lineTable[offset + outCellNumber]; + } +}; - vtkm::IdComponent offset = GetTriTableOffset(cellType, caseNumber); - return triTable[offset + triangleNumber]; +template +VTKM_EXEC inline vtkm::UInt8 GetNumOutCells(vtkm::UInt8 inCellType, vtkm::UInt8 caseNumber) +{ + return MarchingCellTables::GetNumOutCells(inCellType, caseNumber); } + +template +VTKM_EXEC inline const vtkm::UInt8* GetCellEdges(vtkm::UInt8 inCellType, + vtkm::UInt8 caseNumber, + vtkm::UInt8 triangleNumber) +{ + return MarchingCellTables::GetCellEdges(inCellType, caseNumber, triangleNumber); +} + } } } // namespace vtkm::worklet::marching_cells diff --git a/vtkm/filter/contour/worklet/contour/MarchingCells.h b/vtkm/filter/contour/worklet/contour/MarchingCells.h index 68666b2dde..fbf4839c3c 100644 --- a/vtkm/filter/contour/worklet/contour/MarchingCells.h +++ b/vtkm/filter/contour/worklet/contour/MarchingCells.h @@ -74,20 +74,128 @@ make_ScalarField(const vtkm::cont::ArrayHandle& ah) } // --------------------------------------------------------------------------- -template +template +struct OutCellTraits; + +template <> +struct OutCellTraits<3> +{ + static constexpr vtkm::UInt8 NUM_POINTS = 3; + static constexpr vtkm::UInt8 CELL_SHAPE = vtkm::CELL_SHAPE_TRIANGLE; +}; + +template <> +struct OutCellTraits<2> +{ + static constexpr vtkm::UInt8 NUM_POINTS = 2; + static constexpr vtkm::UInt8 CELL_SHAPE = vtkm::CELL_SHAPE_LINE; +}; + +template <> +struct OutCellTraits<1> +{ + static constexpr vtkm::UInt8 NUM_POINTS = 1; + static constexpr vtkm::UInt8 CELL_SHAPE = vtkm::CELL_SHAPE_VERTEX; +}; + +template +VTKM_EXEC vtkm::IdComponent TableNumOutCells(vtkm::UInt8 shape, + FieldType isoValue, + const FieldVecType& fieldIn) +{ + const vtkm::IdComponent numPoints = fieldIn.GetNumberOfComponents(); + // Compute the Marching Cubes case number for this cell. We need to iterate + // the isovalues until the sum >= our visit index. But we need to make + // sure the caseNumber is correct before stopping + vtkm::IdComponent caseNumber = 0; + for (vtkm::IdComponent point = 0; point < numPoints; ++point) + { + caseNumber |= (fieldIn[point] > isoValue) << point; + } + + return vtkm::worklet::marching_cells::GetNumOutCells(shape, caseNumber); +} + +template +VTKM_EXEC vtkm::IdComponent NumOutCellsSpecialCases(std::integral_constant, + vtkm::UInt8 shape, + FieldType isoValue, + const FieldVecType& fieldIn) +{ + return TableNumOutCells<3>(shape, isoValue, fieldIn); +} + +template +VTKM_EXEC vtkm::IdComponent NumOutCellsSpecialCases(std::integral_constant, + vtkm::UInt8 shape, + FieldType isoValue, + const FieldVecType& fieldIn) +{ + if (shape == vtkm::CELL_SHAPE_POLYGON) + { + const vtkm::IdComponent numPoints = fieldIn.GetNumberOfComponents(); + vtkm::IdComponent numCrossings = 0; + bool lastOver = (fieldIn[numPoints - 1] > isoValue); + for (vtkm::IdComponent point = 0; point < numPoints; ++point) + { + bool nextOver = (fieldIn[point] > isoValue); + if (lastOver != nextOver) + { + ++numCrossings; + } + lastOver = nextOver; + } + VTKM_ASSERT((numCrossings % 2) == 0); + return numCrossings / 2; + } + else + { + return TableNumOutCells<2>(shape, isoValue, fieldIn); + } +} + +template +VTKM_EXEC vtkm::IdComponent NumOutCellsSpecialCases(std::integral_constant, + vtkm::UInt8 shape, + FieldType isoValue, + const FieldVecType& fieldIn) +{ + if ((shape == vtkm::CELL_SHAPE_LINE) || (shape == vtkm::CELL_SHAPE_POLY_LINE)) + { + const vtkm::IdComponent numPoints = fieldIn.GetNumberOfComponents(); + vtkm::IdComponent numCrossings = 0; + bool lastOver = (fieldIn[0] > isoValue); + for (vtkm::IdComponent point = 1; point < numPoints; ++point) + { + bool nextOver = (fieldIn[point] > isoValue); + if (lastOver != nextOver) + { + ++numCrossings; + } + lastOver = nextOver; + } + return numCrossings; + } + else + { + return 0; + } +} + +// --------------------------------------------------------------------------- +template class ClassifyCell : public vtkm::worklet::WorkletVisitCellsWithPoints { public: - using ControlSignature = void(WholeArrayIn isoValues, + using ControlSignature = void(WholeArrayIn isovalues, FieldInPoint fieldIn, CellSetIn cellSet, FieldOutCell outNumTriangles); - using ExecutionSignature = void(CellShape, PointCount, _1, _2, _4); + using ExecutionSignature = void(CellShape, _1, _2, _4); using InputDomain = _3; template VTKM_EXEC void operator()(CellShapeType shape, - vtkm::IdComponent numVertices, const IsoValuesType& isovalues, const FieldInType& fieldIn, vtkm::IdComponent& numTriangles) const @@ -97,13 +205,8 @@ public: for (vtkm::Id i = 0; i < numIsoValues; ++i) { - vtkm::IdComponent caseNumber = 0; - for (vtkm::IdComponent j = 0; j < numVertices; ++j) - { - caseNumber |= (fieldIn[j] > isovalues.Get(i)) << j; - } - - sum += vtkm::worklet::marching_cells::GetNumTriangles(shape.Id, caseNumber); + sum += NumOutCellsSpecialCases( + std::integral_constant{}, shape.Id, isovalues.Get(i), fieldIn); } numTriangles = sum; } @@ -128,20 +231,23 @@ public: ExecObject() = default; VTKM_CONT - ExecObject(vtkm::Id size, + ExecObject(vtkm::UInt8 numPointsPerOutCell, + vtkm::Id size, vtkm::cont::ArrayHandle& interpWeights, vtkm::cont::ArrayHandle& interpIds, vtkm::cont::ArrayHandle& interpCellIds, vtkm::cont::ArrayHandle& interpContourId, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) - : InterpWeightsPortal(interpWeights.PrepareForOutput(3 * size, device, token)) - , InterpIdPortal(interpIds.PrepareForOutput(3 * size, device, token)) - , InterpCellIdPortal(interpCellIds.PrepareForOutput(3 * size, device, token)) - , InterpContourPortal(interpContourId.PrepareForOutput(3 * size, device, token)) + : InterpWeightsPortal( + interpWeights.PrepareForOutput(numPointsPerOutCell * size, device, token)) + , InterpIdPortal(interpIds.PrepareForOutput(numPointsPerOutCell * size, device, token)) + , InterpCellIdPortal( + interpCellIds.PrepareForOutput(numPointsPerOutCell * size, device, token)) + , InterpContourPortal( + interpContourId.PrepareForOutput(numPointsPerOutCell * size, device, token)) { - // Interp needs to be 3 times longer than size as they are per point of the - // output triangle + // Interp needs to be scaled as they are per point of the output cell } WritePortalType InterpWeightsPortal; WritePortalType InterpIdPortal; @@ -150,12 +256,14 @@ public: }; VTKM_CONT - EdgeWeightGenerateMetaData(vtkm::Id size, + EdgeWeightGenerateMetaData(vtkm::UInt8 inCellDimension, + vtkm::Id size, vtkm::cont::ArrayHandle& interpWeights, vtkm::cont::ArrayHandle& interpIds, vtkm::cont::ArrayHandle& interpCellIds, vtkm::cont::ArrayHandle& interpContourId) - : Size(size) + : NumPointsPerOutCell(inCellDimension) + , Size(size) , InterpWeights(interpWeights) , InterpIds(interpIds) , InterpCellIds(interpCellIds) @@ -166,7 +274,8 @@ public: VTKM_CONT ExecObject PrepareForExecution(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) { - return ExecObject(this->Size, + return ExecObject(this->NumPointsPerOutCell, + this->Size, this->InterpWeights, this->InterpIds, this->InterpCellIds, @@ -176,6 +285,7 @@ public: } private: + vtkm::UInt8 NumPointsPerOutCell; vtkm::Id Size; vtkm::cont::ArrayHandle InterpWeights; vtkm::cont::ArrayHandle InterpIds; @@ -183,10 +293,153 @@ private: vtkm::cont::ArrayHandle InterpContourId; }; +// ----------------------------------------------------------------------------- +template +VTKM_EXEC const vtkm::UInt8* TableCellEdges(vtkm::UInt8 shape, + const IsoValuesType& isoValues, + const FieldVecType& fieldIn, + vtkm::IdComponent visitIndex, + vtkm::IdComponent& contourIndex) +{ + const vtkm::IdComponent numPoints = fieldIn.GetNumberOfComponents(); + // Compute the Marching Cubes case number for this cell. We need to iterate + // the isovalues until the sum >= our visit index. But we need to make + // sure the caseNumber is correct before stopping + vtkm::IdComponent caseNumber = 0; + vtkm::IdComponent sum = 0; + vtkm::IdComponent numIsoValues = static_cast(isoValues.GetNumberOfValues()); + + for (contourIndex = 0; contourIndex < numIsoValues; ++contourIndex) + { + const auto value = isoValues.Get(contourIndex); + caseNumber = 0; + for (vtkm::IdComponent point = 0; point < numPoints; ++point) + { + caseNumber |= (fieldIn[point] > value) << point; + } + + sum += vtkm::worklet::marching_cells::GetNumOutCells(shape, caseNumber); + if (sum > visitIndex) + { + break; + } + } + + VTKM_ASSERT(contourIndex < numIsoValues); + + visitIndex = sum - visitIndex - 1; + + return vtkm::worklet::marching_cells::GetCellEdges(shape, caseNumber, visitIndex); +} + +template +VTKM_EXEC const vtkm::UInt8* CellEdgesSpecialCases(std::integral_constant, + vtkm::UInt8 shape, + const IsoValuesType& isoValues, + const FieldVecType& fieldIn, + vtkm::IdComponent visitIndex, + vtkm::IdComponent& contourIndex, + vtkm::Vec2ui_8& vtkmNotUsed(edgeBuffer)) +{ + return TableCellEdges<3>(shape, isoValues, fieldIn, visitIndex, contourIndex); +} + +template +VTKM_EXEC const vtkm::UInt8* CellEdgesSpecialCases(std::integral_constant, + vtkm::UInt8 shape, + const IsoValuesType& isoValues, + const FieldVecType& fieldIn, + vtkm::IdComponent visitIndex, + vtkm::IdComponent& contourIndex, + vtkm::Vec2ui_8& edgeBuffer) +{ + if (shape == vtkm::CELL_SHAPE_POLYGON) + { + vtkm::IdComponent numCrossings = 0; + vtkm::IdComponent numIsoValues = static_cast(isoValues.GetNumberOfValues()); + const vtkm::IdComponent numPoints = fieldIn.GetNumberOfComponents(); + for (contourIndex = 0; contourIndex < numIsoValues; ++contourIndex) + { + auto isoValue = isoValues.Get(contourIndex); + bool lastOver = (fieldIn[0] > isoValue); + for (vtkm::IdComponent point = 1; point <= numPoints; ++point) + { + bool nextOver = (fieldIn[point % numPoints] > isoValue); + if (lastOver != nextOver) + { + // Check to see if we hit the target edge. + if (visitIndex == (numCrossings / 2)) + { + if ((numCrossings % 2) == 0) + { + // Record first point. + edgeBuffer[0] = point - 1; + } + else + { + // Record second (and final) point. + edgeBuffer[1] = point - 1; + return &edgeBuffer[0]; + } + } + ++numCrossings; + } + lastOver = nextOver; + } + VTKM_ASSERT((numCrossings % 2) == 0); + } + VTKM_ASSERT(0 && "Sanity check fail."); + edgeBuffer[0] = edgeBuffer[1] = 0; + return &edgeBuffer[0]; + } + else + { + return TableCellEdges<2>(shape, isoValues, fieldIn, visitIndex, contourIndex); + } +} + +template +VTKM_EXEC const vtkm::UInt8* CellEdgesSpecialCases(std::integral_constant, + vtkm::UInt8 shape, + const IsoValuesType& isoValues, + const FieldVecType& fieldIn, + vtkm::IdComponent visitIndex, + vtkm::IdComponent& contourIndex, + vtkm::Vec2ui_8& edgeBuffer) +{ + VTKM_ASSERT((shape == vtkm::CELL_SHAPE_LINE) || (shape == vtkm::CELL_SHAPE_POLY_LINE)); + (void)shape; + vtkm::IdComponent numCrossings = 0; + vtkm::IdComponent numIsoValues = static_cast(isoValues.GetNumberOfValues()); + const vtkm::IdComponent numPoints = fieldIn.GetNumberOfComponents(); + for (contourIndex = 0; contourIndex < numIsoValues; ++contourIndex) + { + auto isoValue = isoValues.Get(contourIndex); + bool lastOver = (fieldIn[0] > isoValue); + for (vtkm::IdComponent point = 1; point < numPoints; ++point) + { + bool nextOver = (fieldIn[point] > isoValue); + if (lastOver != nextOver) + { + if (visitIndex == numCrossings) + { + edgeBuffer[0] = point - 1; + return &edgeBuffer[0]; + } + ++numCrossings; + } + lastOver = nextOver; + } + } + VTKM_ASSERT(0 && "Sanity check fail."); + edgeBuffer[0] = 0; + return &edgeBuffer[0]; +} + /// \brief Compute the weights for each edge that is used to generate /// a point in the resulting iso-surface // ----------------------------------------------------------------------------- -template +template class EdgeWeightGenerate : public vtkm::worklet::WorkletVisitCellsWithPoints { public: @@ -221,43 +474,26 @@ public: vtkm::IdComponent visitIndex, const IndicesVecType& indices) const { - const vtkm::Id outputPointId = 3 * outputCellId; + const vtkm::Id outputPointId = OutCellTraits::NUM_POINTS * outputCellId; using FieldType = typename vtkm::VecTraits::ComponentType; - vtkm::IdComponent sum = 0, caseNumber = 0; - vtkm::IdComponent i = 0, - numIsoValues = static_cast(isovalues.GetNumberOfValues()); - - for (i = 0; i < numIsoValues; ++i) - { - const FieldType ivalue = isovalues.Get(i); - // Compute the Marching Cubes case number for this cell. We need to iterate - // the isovalues until the sum >= our visit index. But we need to make - // sure the caseNumber is correct before stopping - caseNumber = 0; - for (vtkm::IdComponent j = 0; j < numVertices; ++j) - { - caseNumber |= (fieldIn[j] > ivalue) << j; - } - - sum += GetNumTriangles(shape.Id, caseNumber); - if (sum > visitIndex) - { - break; - } - } - - visitIndex = sum - visitIndex - 1; - // Interpolate for vertex positions and associated scalar values - auto edges = vtkm::worklet::marching_cells::GetTriangleEdges(shape.Id, caseNumber, visitIndex); - for (vtkm::IdComponent triVertex = 0; triVertex < 3; triVertex++) + vtkm::IdComponent contourIndex; + vtkm::Vec2ui_8 edgeBuffer; + const vtkm::UInt8* edges = CellEdgesSpecialCases(std::integral_constant{}, + shape.Id, + isovalues, + fieldIn, + visitIndex, + contourIndex, + edgeBuffer); + for (vtkm::IdComponent triVertex = 0; triVertex < OutCellTraits::NUM_POINTS; triVertex++) { vtkm::IdComponent2 edgeVertices; vtkm::Vec fieldValues; for (vtkm::IdComponent edgePointId = 0; edgePointId < 2; ++edgePointId) { - vtkm::ErrorCode errorCode = vtkm::exec::CellEdgeLocalIndex( + vtkm::ErrorCode errorCode = this->CrossingLocalIndex( numVertices, edgePointId, edges[triVertex], shape, edgeVertices[edgePointId]); if (errorCode != vtkm::ErrorCode::Success) { @@ -271,20 +507,63 @@ public: // in a subsequent call, after we have merged duplicate points metaData.InterpCellIdPortal.Set(outputPointId + triVertex, inputCellId); - metaData.InterpContourPortal.Set(outputPointId + triVertex, static_cast(i)); + metaData.InterpContourPortal.Set(outputPointId + triVertex, + static_cast(contourIndex)); metaData.InterpIdPortal.Set(outputPointId + triVertex, vtkm::Id2(indices[edgeVertices[0]], indices[edgeVertices[1]])); vtkm::FloatDefault interpolant = - static_cast(isovalues.Get(i) - fieldValues[0]) / + static_cast(isovalues.Get(contourIndex) - fieldValues[0]) / static_cast(fieldValues[1] - fieldValues[0]); metaData.InterpWeightsPortal.Set(outputPointId + triVertex, interpolant); } } + + template + static inline VTKM_EXEC vtkm::ErrorCode CrossingLocalIndex(vtkm::IdComponent numPoints, + vtkm::IdComponent pointIndex, + vtkm::IdComponent edgeIndex, + CellShapeTag shape, + vtkm::IdComponent& result); }; +template <> +template +VTKM_EXEC vtkm::ErrorCode EdgeWeightGenerate<1>::CrossingLocalIndex(vtkm::IdComponent numPoints, + vtkm::IdComponent pointIndex, + vtkm::IdComponent edgeIndex, + CellShapeTag shape, + vtkm::IdComponent& result) +{ + VTKM_ASSERT((shape.Id == vtkm::CELL_SHAPE_LINE) || (shape.Id == vtkm::CELL_SHAPE_POLY_LINE)); + (void)shape; + if ((pointIndex < 0) || (pointIndex > 1)) + { + result = -1; + return vtkm::ErrorCode::InvalidPointId; + } + if ((edgeIndex < 0) || (edgeIndex >= (numPoints - 1))) + { + result = -1; + return vtkm::ErrorCode::InvalidEdgeId; + } + result = edgeIndex + pointIndex; + return vtkm::ErrorCode::Success; +} + +template +template +VTKM_EXEC vtkm::ErrorCode EdgeWeightGenerate::CrossingLocalIndex(vtkm::IdComponent numPoints, + vtkm::IdComponent pointIndex, + vtkm::IdComponent edgeIndex, + CellShapeTag shape, + vtkm::IdComponent& result) +{ + return vtkm::exec::CellEdgeLocalIndex(numPoints, pointIndex, edgeIndex, shape, result); +} + // --------------------------------------------------------------------------- struct MultiContourLess { @@ -592,7 +871,8 @@ struct GenerateNormals }; //---------------------------------------------------------------------------- -template @@ -620,7 +900,7 @@ vtkm::cont::CellSetSingleType<> execute( // for each cell, and the number of vertices to be generated vtkm::cont::ArrayHandle numOutputTrisPerCell; { - marching_cells::ClassifyCell classifyCell; + marching_cells::ClassifyCell classifyCell; invoker(classifyCell, isoValuesHandle, inputField, cells, numOutputTrisPerCell); } @@ -628,19 +908,20 @@ vtkm::cont::CellSetSingleType<> execute( vtkm::cont::ArrayHandle contourIds; vtkm::cont::ArrayHandle originalCellIdsForPoints; { - auto scatter = EdgeWeightGenerate::MakeScatter(numOutputTrisPerCell); + auto scatter = EdgeWeightGenerate::MakeScatter(numOutputTrisPerCell); // Maps output cells to input cells. Store this for cell field mapping. sharedState.CellIdMap = scatter.GetOutputToInputMap(); EdgeWeightGenerateMetaData metaData( + Dims, scatter.GetOutputRange(numOutputTrisPerCell.GetNumberOfValues()), sharedState.InterpolationWeights, sharedState.InterpolationEdgeIds, originalCellIdsForPoints, contourIds); - invoker(EdgeWeightGenerate{}, + invoker(EdgeWeightGenerate{}, scatter, cells, //cast to a scalar field if not one, as cellderivative only works on those @@ -699,7 +980,10 @@ vtkm::cont::CellSetSingleType<> execute( //assign the connectivity to the cell set vtkm::cont::CellSetSingleType<> outputCells; - outputCells.Fill(vertices.GetNumberOfValues(), vtkm::CELL_SHAPE_TRIANGLE, 3, connectivity); + outputCells.Fill(vertices.GetNumberOfValues(), + OutCellTraits::CELL_SHAPE, + OutCellTraits::NUM_POINTS, + connectivity); //now that the vertices have been generated we can generate the normals if (sharedState.GenerateNormals) -- GitLab From 602e59bb33f368e1a87b5754763e9b65fc9e9169 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 31 Jan 2025 12:46:25 -0500 Subject: [PATCH 63/73] Clip: Use batching Employ batching of points and cells to use less memory and perform less and faster computations. --- vtkm/filter/contour/worklet/Clip.h | 643 +++++++++++++++++++---------- 1 file changed, 417 insertions(+), 226 deletions(-) diff --git a/vtkm/filter/contour/worklet/Clip.h b/vtkm/filter/contour/worklet/Clip.h index 84be4428e6..4622804897 100644 --- a/vtkm/filter/contour/worklet/Clip.h +++ b/vtkm/filter/contour/worklet/Clip.h @@ -14,19 +14,23 @@ #include #include +#include +#include +#include #include +#include #include #include #include #include #include +#include +#include #include #include -#include #include -#include #if defined(THRUST_MAJOR_VERSION) && THRUST_MAJOR_VERSION == 1 && THRUST_MINOR_VERSION == 8 && \ @@ -47,7 +51,59 @@ namespace worklet class Clip { public: - struct ClipStats + using BatchesHandle = vtkm::cont::ArrayHandleGroupVecVariable< + vtkm::cont::ArrayHandleIndex, + vtkm::cont::ArrayHandleConcatenate, + vtkm::cont::ArrayHandleConstant>>; + static BatchesHandle CreateBatches(const vtkm::Id& numberOfElements, const vtkm::Id& batchSize) + { + const vtkm::Id numberOfBatches = ((numberOfElements - 1) / batchSize) + 1; + // create the offsets array + const vtkm::cont::ArrayHandleCounting offsetsExceptLast( + 0, batchSize, numberOfBatches); + const vtkm::cont::ArrayHandleConstant lastOffset(numberOfElements, 1); + const auto offsets = vtkm::cont::make_ArrayHandleConcatenate(offsetsExceptLast, lastOffset); + // create the indices array + const auto indices = vtkm::cont::ArrayHandleIndex(numberOfElements); + return vtkm::cont::make_ArrayHandleGroupVecVariable(indices, offsets); + } + + static BatchesHandle CreateBatches(const vtkm::Id& numberOfElements) + { + auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); + if (tracker.CanRunOn(vtkm::cont::DeviceAdapterTagCuda{}) || + tracker.CanRunOn(vtkm::cont::DeviceAdapterTagKokkos{})) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Creating batches with batch size 6 for GPUs."); + return CreateBatches(numberOfElements, 6); + } + else + { + const vtkm::Int32 batchSize = + vtkm::Min(1000, vtkm::Max(1, static_cast(numberOfElements / 250000))); + VTKM_LOG_F( + vtkm::cont::LogLevel::Info, "Creating batches with batch size %d for CPUs.", batchSize); + return CreateBatches(numberOfElements, batchSize); + } + } + + struct PointBatchData + { + vtkm::Id NumberOfKeptPoints = 0; + + struct SumOp + { + VTKM_EXEC_CONT + PointBatchData operator()(const PointBatchData& stat1, const PointBatchData& stat2) const + { + PointBatchData sum = stat1; + sum.NumberOfKeptPoints += stat2.NumberOfKeptPoints; + return sum; + } + }; + }; + + struct CellBatchData { vtkm::Id NumberOfCells = 0; vtkm::Id NumberOfCellIndices = 0; @@ -58,9 +114,9 @@ public: struct SumOp { VTKM_EXEC_CONT - ClipStats operator()(const ClipStats& stat1, const ClipStats& stat2) const + CellBatchData operator()(const CellBatchData& stat1, const CellBatchData& stat2) const { - ClipStats sum = stat1; + CellBatchData sum = stat1; sum.NumberOfCells += stat2.NumberOfCells; sum.NumberOfCellIndices += stat2.NumberOfCellIndices; sum.NumberOfEdges += stat2.NumberOfEdges; @@ -103,8 +159,12 @@ public: class MarkKeptPoints : public vtkm::worklet::WorkletMapField { public: - using ControlSignature = void(FieldIn scalar, FieldOut keptPointMask); - using ExecutionSignature = _2(_1); + using ControlSignature = void(FieldIn pointBatch, + FieldOut pointBatchData, + FieldOut batchWithKeptPointsMask, + WholeArrayIn scalars, + WholeArrayOut keptPointsMask); + using ExecutionSignature = void(_1, _2, _3, _4, _5); VTKM_CONT explicit MarkKeptPoints(vtkm::Float64 isoValue) @@ -112,98 +172,160 @@ public: { } - template - VTKM_EXEC vtkm::UInt8 operator()(const T& scalar) const + template + VTKM_EXEC void operator()(const BatchType& pointBatch, + PointBatchData& pointBatchData, + vtkm::UInt8& batchWithKeptPointsMask, + const PointScalars& scalars, + KeptPointsMask& keptPointsMask) const { - return Invert ? scalar < this->IsoValue : scalar >= this->IsoValue; + for (vtkm::IdComponent id = 0, size = pointBatch.GetNumberOfComponents(); id < size; ++id) + { + const vtkm::Id& pointId = pointBatch[id]; + const auto scalar = scalars.Get(pointId); + const vtkm::UInt8 kept = Invert ? scalar < this->IsoValue : scalar >= this->IsoValue; + keptPointsMask.Set(pointId, kept); + pointBatchData.NumberOfKeptPoints += kept; + } + batchWithKeptPointsMask = pointBatchData.NumberOfKeptPoints > 0; } private: vtkm::Float64 IsoValue; }; - template - class ComputeClipStats : public vtkm::worklet::WorkletVisitCellsWithPoints + class ComputePointMaps : public vtkm::worklet::WorkletMapField { public: - using ControlSignature = void(CellSetIn cellSet, - FieldInPoint pointMask, - FieldOutCell clipStat, - FieldOutCell clippedMask, - FieldOutCell keptOrClippedMask, - FieldOutCell caseIndex); - - using ExecutionSignature = void(CellShape, PointCount, _2, _3, _4, _5, _6); + using ControlSignature = void(FieldIn pointBatch, + FieldIn pointBatchDataOffsets, + WholeArrayIn keptPointsMask, + WholeArrayOut pointsInputToOutput, + WholeArrayOut pointsOutputToInput); + using ExecutionSignature = void(_1, _2, _3, _4, _5); - using CT = internal::ClipTables; + using MaskType = vtkm::worklet::MaskSelect; - template - VTKM_EXEC void operator()(const CellShapeTag& shape, - const vtkm::IdComponent pointCount, + template + VTKM_EXEC void operator()(const BatchType& pointBatch, + const PointBatchData& pointBatchDataOffsets, const KeptPointsMask& keptPointsMask, - ClipStats& clipStat, - vtkm::UInt8& clippedMask, - vtkm::UInt8& keptOrClippedMask, - vtkm::UInt8& caseIndex) const + PointsInputToOutput& pointsInputToOutput, + PointsOutputToInput& pointsOutputToInput) const { - namespace CTI = vtkm::worklet::internal::ClipTablesInformation; - // compute case index - caseIndex = 0; - for (vtkm::IdComponent ptId = pointCount - 1; ptId >= 0; --ptId) + vtkm::Id pointOffset = pointBatchDataOffsets.NumberOfKeptPoints; + for (vtkm::IdComponent id = 0, size = pointBatch.GetNumberOfComponents(); id < size; ++id) { - static constexpr auto InvertUint8 = static_cast(Invert); - caseIndex |= (InvertUint8 != keptPointsMask[ptId]) << ptId; + const vtkm::Id& pointId = pointBatch[id]; + if (keptPointsMask.Get(pointId)) + { + pointsInputToOutput.Set(pointId, pointOffset); + pointsOutputToInput.Set(pointOffset, pointId); + pointOffset++; + } } + } + }; - if (CT::IsCellDiscarded(pointCount, caseIndex)) // discarded cell - { - // we do that to determine if a cell is discarded using only the caseIndex - caseIndex = CT::GetDiscardedCellCase(); - } - else if (CT::IsCellKept(pointCount, caseIndex)) // kept cell - { - // we do that to determine if a cell is kept using only the caseIndex - caseIndex = CT::GetKeptCellCase(); - clipStat.NumberOfCells = 1; - clipStat.NumberOfCellIndices = pointCount; - } - else // clipped cell + template + class ComputeCellStats : public vtkm::worklet::WorkletMapField + { + public: + using ControlSignature = void(FieldIn cellBatch, + FieldOut cellBatchData, + FieldOut batchWithClippedCellsMask, + FieldOut batchWithKeptOrClippedCellsMask, + WholeCellSetIn<> cellSet, + WholeArrayIn keptPointsMask, + WholeArrayOut caseIndices); + using ExecutionSignature = void(_1, _2, _3, _4, _5, _6, _7); + + using CT = internal::ClipTables; + + template + VTKM_EXEC void operator()(const BatchType& cellBatch, + CellBatchData& cellBatchData, + vtkm::UInt8& batchWithClippedCellsMask, + vtkm::UInt8& batchWithKeptOrClippedCellsMask, + const CellSetType& cellSet, + const KeptPointsMask& keptPointsMask, + CaseIndices& caseIndices) const + { + namespace CTI = vtkm::worklet::internal::ClipTablesInformation; + for (vtkm::IdComponent id = 0, size = cellBatch.GetNumberOfComponents(); id < size; ++id) { - vtkm::Id index = CT::GetCaseIndex(shape.Id, caseIndex); - const vtkm::UInt8 numberOfShapes = CT::ValueAt(index++); + const vtkm::Id& cellId = cellBatch[id]; + const auto shape = cellSet.GetCellShape(cellId); + const auto points = cellSet.GetIndices(cellId); + const vtkm::IdComponent pointCount = points.GetNumberOfComponents(); + + // compute case index + vtkm::UInt8 caseIndex = 0; + for (vtkm::IdComponent ptId = pointCount - 1; ptId >= 0; --ptId) + { + static constexpr auto InvertUint8 = static_cast(Invert); + caseIndex |= (InvertUint8 != keptPointsMask.Get(points[ptId])) << ptId; + } - clipStat.NumberOfCells = numberOfShapes; - for (vtkm::IdComponent shapeId = 0; shapeId < numberOfShapes; ++shapeId) + if (CT::IsCellDiscarded(pointCount, caseIndex)) // discarded cell + { + // we do that to determine if a cell is discarded using only the caseIndex + caseIndices.Set(cellId, CT::GetDiscardedCellCase()); + } + else if (CT::IsCellKept(pointCount, caseIndex)) // kept cell { - const vtkm::UInt8 cellShape = CT::ValueAt(index++); - const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); + // we do that to determine if a cell is kept using only the caseIndex + caseIndices.Set(cellId, CT::GetKeptCellCase()); + cellBatchData.NumberOfCells += 1; + cellBatchData.NumberOfCellIndices += pointCount; + } + else // clipped cell + { + caseIndices.Set(cellId, caseIndex); - for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; pointId++, index++) - { - // Find how many points need to be calculated using edge interpolation. - const vtkm::UInt8 pointIndex = CT::ValueAt(index); - clipStat.NumberOfEdges += (pointIndex >= CTI::E00 && pointIndex <= CTI::E11); - } - if (cellShape != CTI::ST_PNT) // normal cell - { - // Collect number of indices required for storing current shape - clipStat.NumberOfCellIndices += numberOfCellIndices; - } - else // cellShape == ST_PNT + vtkm::Id index = CT::GetCaseIndex(shape.Id, caseIndex); + const vtkm::UInt8 numberOfShapes = CT::ValueAt(index++); + + cellBatchData.NumberOfCells += numberOfShapes; + for (vtkm::IdComponent shapeId = 0; shapeId < numberOfShapes; ++shapeId) { - --clipStat.NumberOfCells; // decrement since this is a centroid shape - clipStat.NumberOfCentroids++; - clipStat.NumberOfCentroidIndices += numberOfCellIndices; + const vtkm::UInt8 cellShape = CT::ValueAt(index++); + const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); + + for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; pointId++, index++) + { + // Find how many points need to be calculated using edge interpolation. + const vtkm::UInt8 pointIndex = CT::ValueAt(index); + cellBatchData.NumberOfEdges += (pointIndex >= CTI::E00 && pointIndex <= CTI::E11); + } + if (cellShape != CTI::ST_PNT) // normal cell + { + // Collect number of indices required for storing current shape + cellBatchData.NumberOfCellIndices += numberOfCellIndices; + } + else // cellShape == CTI::ST_PNT + { + --cellBatchData.NumberOfCells; // decrement since this is a centroid shape + cellBatchData.NumberOfCentroids++; + cellBatchData.NumberOfCentroidIndices += numberOfCellIndices; + } } } } - keptOrClippedMask = caseIndex != CT::GetDiscardedCellCase(); - clippedMask = keptOrClippedMask && caseIndex != CT::GetKeptCellCase(); + batchWithClippedCellsMask = cellBatchData.NumberOfCells > 0 && + (cellBatchData.NumberOfEdges > 0 || cellBatchData.NumberOfCentroids > 0); + batchWithKeptOrClippedCellsMask = cellBatchData.NumberOfCells > 0; } }; template - class ExtractEdges : public vtkm::worklet::WorkletVisitCellsWithPoints + class ExtractEdges : public vtkm::worklet::WorkletMapField { public: VTKM_CONT @@ -212,61 +334,74 @@ public: { } - using ControlSignature = void(CellSetIn cellSet, - FieldInPoint scalars, - FieldInCell clipStatOffsets, - FieldInCell caseIndex, + using ControlSignature = void(FieldIn cellBatch, + FieldIn cellBatchDataOffsets, + WholeCellSetIn<> cellSet, + WholeArrayIn scalars, + WholeArrayIn caseIndices, WholeArrayOut edges); - - using ExecutionSignature = void(CellShape, PointIndices, _2, _3, _4, _5); + using ExecutionSignature = void(_1, _2, _3, _4, _5, _6); using MaskType = vtkm::worklet::MaskSelect; using CT = internal::ClipTables; - template - VTKM_EXEC void operator()(const CellShapeTag& shape, - const PointIndicesVec& points, + VTKM_EXEC void operator()(const BatchType& cellBatch, + const CellBatchData& cellBatchDataOffsets, + const CellSetType& cellSet, const PointScalars& scalars, - const ClipStats& clipStat, - const vtkm::UInt8& caseIndex, + const CaseIndices& caseIndices, EdgesArray& edges) const { namespace CTI = vtkm::worklet::internal::ClipTablesInformation; - vtkm::Id edgeOffset = clipStat.NumberOfEdges; + vtkm::Id edgeOffset = cellBatchDataOffsets.NumberOfEdges; - // only clipped cells have edges - vtkm::Id index = CT::GetCaseIndex(shape.Id, caseIndex); - const vtkm::UInt8 numberOfShapes = CT::ValueAt(index++); - - for (vtkm::IdComponent shapeId = 0; shapeId < numberOfShapes; shapeId++) + for (vtkm::IdComponent id = 0, size = cellBatch.GetNumberOfComponents(); id < size; ++id) { - /*vtkm::UInt8 cellShape = */ CT::ValueAt(index++); - const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); + const vtkm::Id& cellId = cellBatch[id]; + const vtkm::UInt8 caseIndex = caseIndices.Get(cellId); - for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; pointId++, index++) + if (caseIndex != CT::GetDiscardedCellCase() && + caseIndex != CT::GetKeptCellCase()) // clipped cell { - // Find how many points need to be calculated using edge interpolation. - const vtkm::UInt8 pointIndex = CT::ValueAt(index); - if (pointIndex >= CTI::E00 && pointIndex <= CTI::E11) + const auto shape = cellSet.GetCellShape(cellId); + const auto points = cellSet.GetIndices(cellId); + + // only clipped cells have edges + vtkm::Id index = CT::GetCaseIndex(shape.Id, caseIndex); + const vtkm::UInt8 numberOfShapes = CT::ValueAt(index++); + + for (vtkm::IdComponent shapeId = 0; shapeId < numberOfShapes; shapeId++) { - typename CT::EdgeVec edge = CT::GetEdge(shape.Id, pointIndex - CTI::E00); - EdgeInterpolation ei; - ei.Vertex1 = points[edge[0]]; - ei.Vertex2 = points[edge[1]]; - // For consistency purposes keep the points ordered. - if (ei.Vertex1 > ei.Vertex2) + /*vtkm::UInt8 cellShape = */ CT::ValueAt(index++); + const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); + + for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; pointId++, index++) { - vtkm::Swap(ei.Vertex1, ei.Vertex2); - vtkm::Swap(edge[0], edge[1]); + // Find how many points need to be calculated using edge interpolation. + const vtkm::UInt8 pointIndex = CT::ValueAt(index); + if (pointIndex >= CTI::E00 && pointIndex <= CTI::E11) + { + typename CT::EdgeVec edge = CT::GetEdge(shape.Id, pointIndex - CTI::E00); + EdgeInterpolation ei; + ei.Vertex1 = points[edge[0]]; + ei.Vertex2 = points[edge[1]]; + // For consistency purposes keep the points ordered. + if (ei.Vertex1 > ei.Vertex2) + { + vtkm::Swap(ei.Vertex1, ei.Vertex2); + } + ei.Weight = (static_cast(scalars.Get(ei.Vertex1)) - this->IsoValue) / + static_cast(scalars.Get(ei.Vertex2) - scalars.Get(ei.Vertex1)); + // Add edge to the list of edges. + edges.Set(edgeOffset++, ei); + } } - ei.Weight = (static_cast(scalars[edge[0]]) - this->IsoValue) / - static_cast(scalars[edge[1]] - scalars[edge[0]]); - // Add edge to the list of edges. - edges.Set(edgeOffset++, ei); } } } @@ -277,7 +412,7 @@ public: }; template - class GenerateCellSet : public vtkm::worklet::WorkletVisitCellsWithPoints + class GenerateCellSet : public vtkm::worklet::WorkletMapField { public: VTKM_CONT @@ -287,9 +422,10 @@ public: { } - using ControlSignature = void(CellSetIn cellSet, - FieldInCell caseIndex, - FieldInCell clipStatOffsets, + using ControlSignature = void(FieldIn cellBatch, + FieldIn cellBatchDataOffsets, + WholeCellSetIn<> cellSet, + WholeArrayIn caseIndices, WholeArrayIn pointMapOutputToInput, WholeArrayIn edgeIndexToUnique, WholeArrayOut centroidOffsets, @@ -298,15 +434,15 @@ public: WholeArrayOut shapes, WholeArrayOut offsets, WholeArrayOut connectivity); - using ExecutionSignature = - void(InputIndex, CellShape, PointIndices, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11); + using ExecutionSignature = void(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12); using MaskType = vtkm::worklet::MaskSelect; using CT = internal::ClipTables; - template - VTKM_EXEC void operator()(vtkm::Id cellId, - const CellShapeTag& shape, - const PointVecType& points, - const vtkm::UInt8& caseIndex, - const ClipStats& clipStatOffsets, + VTKM_EXEC void operator()(const BatchType& cellBatch, + const CellBatchData& cellBatchDataOffsets, + const CellSetType& cellSet, + const CaseIndices& caseIndices, const PointMapInputToOutput pointMapInputToOutput, const EdgeIndexToUnique& edgeIndexToUnique, CentroidOffsets& centroidOffsets, @@ -330,87 +465,99 @@ public: Connectivity& connectivity) const { namespace CTI = vtkm::worklet::internal::ClipTablesInformation; - vtkm::Id cellsOffset = clipStatOffsets.NumberOfCells; - vtkm::Id cellIndicesOffset = clipStatOffsets.NumberOfCellIndices; - vtkm::Id edgeOffset = clipStatOffsets.NumberOfEdges; - vtkm::Id centroidOffset = clipStatOffsets.NumberOfCentroids; - vtkm::Id centroidIndicesOffset = clipStatOffsets.NumberOfCentroidIndices; + vtkm::Id cellsOffset = cellBatchDataOffsets.NumberOfCells; + vtkm::Id cellIndicesOffset = cellBatchDataOffsets.NumberOfCellIndices; + vtkm::Id edgeOffset = cellBatchDataOffsets.NumberOfEdges; + vtkm::Id centroidOffset = cellBatchDataOffsets.NumberOfCentroids; + vtkm::Id centroidIndicesOffset = cellBatchDataOffsets.NumberOfCentroidIndices; - if (caseIndex == CT::GetKeptCellCase()) // kept cell - { - cellMapOutputToInput.Set(cellsOffset, cellId); - shapes.Set(cellsOffset, static_cast(shape.Id)); - offsets.Set(cellsOffset, cellIndicesOffset); - for (vtkm::IdComponent pointId = 0; pointId < points.GetNumberOfComponents(); ++pointId) - { - connectivity.Set(cellIndicesOffset++, pointMapInputToOutput.Get(points[pointId])); - } - } - else // clipped cell + for (vtkm::IdComponent id = 0, size = cellBatch.GetNumberOfComponents(); id < size; ++id) { - vtkm::Id centroidIndex = 0; - - vtkm::Id index = CT::GetCaseIndex(shape.Id, caseIndex); - const vtkm::UInt8 numberOfShapes = CT::ValueAt(index++); - - for (vtkm::IdComponent shapeId = 0; shapeId < numberOfShapes; shapeId++) + const vtkm::Id& cellId = cellBatch[id]; + const vtkm::UInt8 caseIndex = caseIndices.Get(cellId); + if (caseIndex != CT::GetDiscardedCellCase()) // not discarded cell { - const vtkm::UInt8 cellShape = CT::ValueAt(index++); - const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); - - if (cellShape != CTI::ST_PNT) // normal cell + const auto shape = cellSet.GetCellShape(cellId); + const auto points = cellSet.GetIndices(cellId); + if (caseIndex == CT::GetKeptCellCase()) // kept cell { - // Store the cell data cellMapOutputToInput.Set(cellsOffset, cellId); - shapes.Set(cellsOffset, cellShape); - offsets.Set(cellsOffset++, cellIndicesOffset); - - for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; pointId++, index++) + shapes.Set(cellsOffset, static_cast(shape.Id)); + offsets.Set(cellsOffset, cellIndicesOffset); + for (vtkm::IdComponent pointId = 0; pointId < points.GetNumberOfComponents(); ++pointId) { - // Find how many points need to be calculated using edge interpolation. - const vtkm::UInt8 pointIndex = CT::ValueAt(index); - if (pointIndex <= CTI::P7) // Input Point - { - // We know pt P0 must be > P0 since we already - // assume P0 == 0. This is why we do not - // bother subtracting P0 from pt here. - connectivity.Set(cellIndicesOffset++, - pointMapInputToOutput.Get(points[pointIndex])); - } - else if (/*pointIndex >= CTI::E00 &&*/ pointIndex <= CTI::E11) // Mid-Edge Point - { - connectivity.Set(cellIndicesOffset++, - this->EdgePointsOffset + edgeIndexToUnique.Get(edgeOffset++)); - } - else // pointIndex == CTI::N0 // Centroid Point - { - connectivity.Set(cellIndicesOffset++, centroidIndex); - } + connectivity.Set(cellIndicesOffset++, pointMapInputToOutput.Get(points[pointId])); } } - else // cellShape == CTI::ST_PNT + else // clipped cell { - // Store the centroid data - centroidIndex = this->CentroidPointsOffset + centroidOffset; - centroidOffsets.Set(centroidOffset++, centroidIndicesOffset); + vtkm::Id centroidIndex = 0; - for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; pointId++, index++) + vtkm::Id index = CT::GetCaseIndex(shape.Id, caseIndex); + const vtkm::UInt8 numberOfShapes = CT::ValueAt(index++); + + for (vtkm::IdComponent shapeId = 0; shapeId < numberOfShapes; shapeId++) { - // Find how many points need to be calculated using edge interpolation. - const vtkm::UInt8 pointIndex = CT::ValueAt(index); - if (pointIndex <= CTI::P7) // Input Point + const vtkm::UInt8 cellShape = CT::ValueAt(index++); + const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); + + if (cellShape != CTI::ST_PNT) // normal cell { - // We know pt P0 must be > P0 since we already - // assume P0 == 0. This is why we do not - // bother subtracting P0 from pt here. - centroidConnectivity.Set(centroidIndicesOffset++, - pointMapInputToOutput.Get(points[pointIndex])); + // Store the cell data + cellMapOutputToInput.Set(cellsOffset, cellId); + shapes.Set(cellsOffset, cellShape); + offsets.Set(cellsOffset++, cellIndicesOffset); + + for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; + pointId++, index++) + { + // Find how many points need to be calculated using edge interpolation. + const vtkm::UInt8 pointIndex = CT::ValueAt(index); + if (pointIndex <= CTI::P7) // Input Point + { + // We know pt P0 must be > P0 since we already + // assume P0 == 0. This is why we do not + // bother subtracting P0 from pt here. + connectivity.Set(cellIndicesOffset++, + pointMapInputToOutput.Get(points[pointIndex])); + } + else if (/*pointIndex >= CTI::E00 &&*/ pointIndex <= CTI::E11) // Mid-Edge Point + { + connectivity.Set(cellIndicesOffset++, + this->EdgePointsOffset + edgeIndexToUnique.Get(edgeOffset++)); + } + else // pointIndex == CTI::N0 // Centroid Point + { + connectivity.Set(cellIndicesOffset++, centroidIndex); + } + } } - else /*pointIndex >= CTI::E00 && pointIndex <= CTI::E11*/ // Mid-Edge Point + else // cellShape == CTI::ST_PNT { - centroidConnectivity.Set(centroidIndicesOffset++, - this->EdgePointsOffset + - edgeIndexToUnique.Get(edgeOffset++)); + // Store the centroid data + centroidIndex = this->CentroidPointsOffset + centroidOffset; + centroidOffsets.Set(centroidOffset++, centroidIndicesOffset); + + for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; + pointId++, index++) + { + // Find how many points need to be calculated using edge interpolation. + const vtkm::UInt8 pointIndex = CT::ValueAt(index); + if (pointIndex <= CTI::P7) // Input Point + { + // We know pt P0 must be > P0 since we already + // assume P0 == 0. This is why we do not + // bother subtracting P0 from pt here. + centroidConnectivity.Set(centroidIndicesOffset++, + pointMapInputToOutput.Get(points[pointIndex])); + } + else /*pointIndex >= CTI::E00 && pointIndex <= CTI::E11*/ // Mid-Edge Point + { + centroidConnectivity.Set(centroidIndicesOffset++, + this->EdgePointsOffset + + edgeIndexToUnique.Get(edgeOffset++)); + } + } } } } @@ -436,59 +583,100 @@ public: // Create an invoker. vtkm::cont::Invoker invoke; + // Create batches of points to process. + auto pointBatches = CreateBatches(numberOfInputPoints); + + // Create an array to store the point batch statistics. + vtkm::cont::ArrayHandle pointBatchesData; + pointBatchesData.Allocate(pointBatches.GetNumberOfValues()); + + // Create a mask to only process the batches that have kept points. + vtkm::cont::ArrayHandle batchesWithKeptPointsMask; + batchesWithKeptPointsMask.Allocate(pointBatches.GetNumberOfValues()); + // Create an array to store the mask of kept points. vtkm::cont::ArrayHandle keptPointsMask; keptPointsMask.Allocate(numberOfInputPoints); // Mark the points that are kept. - invoke(MarkKeptPoints(value), scalars, keptPointsMask); + invoke(MarkKeptPoints(value), + pointBatches, + pointBatchesData, + batchesWithKeptPointsMask, + scalars, + keptPointsMask); + + // Compute the total of pointBatchesData, and convert pointBatchesData to offsets in-place. + const PointBatchData pointBatchTotal = vtkm::cont::Algorithm::ScanExclusive( + pointBatchesData, pointBatchesData, PointBatchData::SumOp(), PointBatchData{}); + + // Create arrays to store the point map from input to output, and output to input. + vtkm::cont::ArrayHandle pointMapInputToOutput; + pointMapInputToOutput.Allocate(numberOfInputPoints); + this->PointMapOutputToInput.Allocate(pointBatchTotal.NumberOfKeptPoints); + + // Compute the point map from input to output, and output to input. (see Scatter Counting) + invoke(ComputePointMaps(), + vtkm::worklet::MaskSelect(batchesWithKeptPointsMask), + pointBatches, + pointBatchesData, // pointBatchesDataOffsets + keptPointsMask, + pointMapInputToOutput, + this->PointMapOutputToInput); + // Release pointBatches related arrays since they are no longer needed. + pointBatches.ReleaseResources(); + pointBatchesData.ReleaseResources(); + batchesWithKeptPointsMask.ReleaseResources(); - // Create an array to save the caseIndex for each cell. - vtkm::cont::ArrayHandle caseIndices; - caseIndices.Allocate(numberOfInputCells); + // Create batches of cells to process. + auto cellBatches = CreateBatches(numberOfInputCells); - // Create an array to store the statistics of the clip operation. - vtkm::cont::ArrayHandle clipStats; - clipStats.Allocate(numberOfInputCells); + // Create an array to store the cell batch statistics. + vtkm::cont::ArrayHandle cellBatchesData; + cellBatchesData.Allocate(cellBatches.GetNumberOfValues()); - // Create a mask to only process the cells that are clipped, to extract the edges. - vtkm::cont::ArrayHandle clippedMask; + // Create a mask to only process the batches that have clipped cells, to extract the edges. + vtkm::cont::ArrayHandle batchesWithClippedCellsMask; + batchesWithClippedCellsMask.Allocate(cellBatches.GetNumberOfValues()); - // Create a mask to only process the kept or clipped cells. - vtkm::cont::ArrayHandle keptOrClippedMask; + // Create a mask to only process the batches that have kept or clipped cells. + vtkm::cont::ArrayHandle batchesWithKeptOrClippedCellsMask; + batchesWithKeptOrClippedCellsMask.Allocate(cellBatches.GetNumberOfValues()); - // Compute the statistics of the clip operation. - invoke(ComputeClipStats(), + // Create an array to save the caseIndex for each cell. + vtkm::cont::ArrayHandle caseIndices; + caseIndices.Allocate(numberOfInputCells); + + // Compute the cell statistics of the clip operation. + invoke(ComputeCellStats(), + cellBatches, + cellBatchesData, + batchesWithClippedCellsMask, + batchesWithKeptOrClippedCellsMask, cellSet, keptPointsMask, - clipStats, - clippedMask, - keptOrClippedMask, caseIndices); - - // Create ScatterCounting on the keptPointsMask. - vtkm::worklet::ScatterCounting scatterCullDiscardedPoints(keptPointsMask, true); - auto pointMapInputToOutput = scatterCullDiscardedPoints.GetInputToOutputMap(); - this->PointMapOutputToInput = scatterCullDiscardedPoints.GetOutputToInputMap(); keptPointsMask.ReleaseResources(); // Release keptPointsMask since it's no longer needed. - // Compute the total of clipStats, and convert clipStats to offsets in-place. - const ClipStats total = - vtkm::cont::Algorithm::ScanExclusive(clipStats, clipStats, ClipStats::SumOp(), ClipStats{}); + // Compute the total of cellBatchesData, and convert cellBatchesData to offsets in-place. + const CellBatchData cellBatchTotal = vtkm::cont::Algorithm::ScanExclusive( + cellBatchesData, cellBatchesData, CellBatchData::SumOp(), CellBatchData{}); // Create an array to store the edge interpolations. vtkm::cont::ArrayHandle edgeInterpolation; - edgeInterpolation.Allocate(total.NumberOfEdges); + edgeInterpolation.Allocate(cellBatchTotal.NumberOfEdges); // Extract the edges. invoke(ExtractEdges(value), - vtkm::worklet::MaskSelect(clippedMask), + vtkm::worklet::MaskSelect(batchesWithClippedCellsMask), + cellBatches, + cellBatchesData, // cellBatchesDataOffsets cellSet, scalars, - clipStats, // clipStatOffsets caseIndices, edgeInterpolation); - clippedMask.ReleaseResources(); // Release clippedMask since it's no longer needed. + // Release batchesWithClippedCellsMask since it's no longer needed. + batchesWithClippedCellsMask.ReleaseResources(); // Sort the edge interpolations. vtkm::cont::Algorithm::Sort(edgeInterpolation, EdgeInterpolation::LessThanOp()); @@ -507,7 +695,7 @@ public: // Get the number of kept points, unique edge points, centroids, and output points. const vtkm::Id numberOfKeptPoints = this->PointMapOutputToInput.GetNumberOfValues(); const vtkm::Id numberOfUniqueEdgePoints = this->EdgePointsInterpolation.GetNumberOfValues(); - const vtkm::Id numberOfCentroids = total.NumberOfCentroids; + const vtkm::Id numberOfCentroids = cellBatchTotal.NumberOfCentroids; const vtkm::Id numberOfOutputPoints = numberOfKeptPoints + numberOfUniqueEdgePoints + numberOfCentroids; // Create the offsets to write the point indices. @@ -518,27 +706,28 @@ public: vtkm::cont::ArrayHandle centroidOffsets; centroidOffsets.Allocate(numberOfCentroids + 1); vtkm::cont::ArrayHandle centroidConnectivity; - centroidConnectivity.Allocate(total.NumberOfCentroidIndices); + centroidConnectivity.Allocate(cellBatchTotal.NumberOfCentroidIndices); this->CentroidPointsInterpolation = vtkm::cont::make_ArrayHandleGroupVecVariable(centroidConnectivity, centroidOffsets); // Allocate the output cell set. vtkm::cont::ArrayHandle shapes; - shapes.Allocate(total.NumberOfCells); + shapes.Allocate(cellBatchTotal.NumberOfCells); vtkm::cont::ArrayHandle offsets; - offsets.Allocate(total.NumberOfCells + 1); + offsets.Allocate(cellBatchTotal.NumberOfCells + 1); vtkm::cont::ArrayHandle connectivity; - connectivity.Allocate(total.NumberOfCellIndices); + connectivity.Allocate(cellBatchTotal.NumberOfCellIndices); // Allocate Cell Map output to Input. - this->CellMapOutputToInput.Allocate(total.NumberOfCells); + this->CellMapOutputToInput.Allocate(cellBatchTotal.NumberOfCells); // Generate the output cell set. invoke(GenerateCellSet(this->EdgePointsOffset, this->CentroidPointsOffset), - vtkm::worklet::MaskSelect(keptOrClippedMask), + vtkm::worklet::MaskSelect(batchesWithKeptOrClippedCellsMask), + cellBatches, + cellBatchesData, // cellBatchesDataOffsets cellSet, caseIndices, - clipStats, // clipStatOffsets pointMapInputToOutput, edgeInterpolationIndexToUnique, centroidOffsets, @@ -550,8 +739,10 @@ public: // All no longer needed arrays will be released at the end of this function. // Set the last offset to the size of the connectivity. - vtkm::cont::ArraySetValue(total.NumberOfCells, total.NumberOfCellIndices, offsets); - vtkm::cont::ArraySetValue(numberOfCentroids, total.NumberOfCentroidIndices, centroidOffsets); + vtkm::cont::ArraySetValue( + cellBatchTotal.NumberOfCells, cellBatchTotal.NumberOfCellIndices, offsets); + vtkm::cont::ArraySetValue( + numberOfCentroids, cellBatchTotal.NumberOfCentroidIndices, centroidOffsets); vtkm::cont::CellSetExplicit<> output; output.Fill(numberOfOutputPoints, shapes, connectivity, offsets); @@ -742,7 +933,7 @@ namespace detail // causes a different code path which does not have the bug template <> -struct is_integral : public true_type +struct is_integral : public true_type { }; } -- GitLab From 91e3ba6e035d1dc2a9ed4ca3537506ff2279c387 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 31 Jan 2025 12:46:33 -0500 Subject: [PATCH 64/73] Clip tests: Fix typos --- vtkm/filter/contour/testing/UnitTestClipWithFieldFilter.cxx | 2 +- .../testing/UnitTestClipWithImplicitFunctionFilter.cxx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vtkm/filter/contour/testing/UnitTestClipWithFieldFilter.cxx b/vtkm/filter/contour/testing/UnitTestClipWithFieldFilter.cxx index 92a42c8e2c..eccce126a5 100644 --- a/vtkm/filter/contour/testing/UnitTestClipWithFieldFilter.cxx +++ b/vtkm/filter/contour/testing/UnitTestClipWithFieldFilter.cxx @@ -74,7 +74,7 @@ void TestClipExplicit() for (int i = 0; i < 6; ++i) { VTKM_TEST_ASSERT(test_equal(resultArrayHandle.ReadPortal().Get(i), expected[i]), - "Wrong result for Clip fliter on triangle explicit data"); + "Wrong result for Clip filter on triangle explicit data"); } } diff --git a/vtkm/filter/contour/testing/UnitTestClipWithImplicitFunctionFilter.cxx b/vtkm/filter/contour/testing/UnitTestClipWithImplicitFunctionFilter.cxx index 055865ae04..79954bc82a 100644 --- a/vtkm/filter/contour/testing/UnitTestClipWithImplicitFunctionFilter.cxx +++ b/vtkm/filter/contour/testing/UnitTestClipWithImplicitFunctionFilter.cxx @@ -96,7 +96,7 @@ void TestClipStructuredSphere(vtkm::Float64 offset) for (int i = 0; i < 12; ++i) { VTKM_TEST_ASSERT(test_equal(resultArrayHandle.ReadPortal().Get(i), expected[i]), - "Wrong result for ClipWithImplicitFunction fliter on sturctured quads data"); + "Wrong result for ClipWithImplicitFunction filter on structured quads data"); } } @@ -134,7 +134,7 @@ void TestClipStructuredInvertedSphere() for (int i = 0; i < 5; ++i) { VTKM_TEST_ASSERT(test_equal(resultArrayHandle.ReadPortal().Get(i), expected[i]), - "Wrong result for ClipWithImplicitFunction fliter on sturctured quads data"); + "Wrong result for ClipWithImplicitFunction filter on sturctured quads data"); } } @@ -169,7 +169,7 @@ void TestClipStructuredInvertedMultiPlane() for (int i = 0; i < 4; ++i) { VTKM_TEST_ASSERT(test_equal(resultArrayHandle.ReadPortal().Get(i), expected[i]), - "Wrong result for ClipWithImplicitFunction fliter on sturctured data in " + "Wrong result for ClipWithImplicitFunction filter on sturctured data in " "TestClipStructuredInvertedMultiPlane"); } } -- GitLab From 43ef91528b42138593d578ca9ede64423fe10a4e Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Thu, 6 Feb 2025 13:42:27 -0500 Subject: [PATCH 65/73] Add changelog --- ...rove-performance-and-memory-consumption.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 docs/changelog/clip-improve-performance-and-memory-consumption.md diff --git a/docs/changelog/clip-improve-performance-and-memory-consumption.md b/docs/changelog/clip-improve-performance-and-memory-consumption.md new file mode 100644 index 0000000000..bc34ff5659 --- /dev/null +++ b/docs/changelog/clip-improve-performance-and-memory-consumption.md @@ -0,0 +1,38 @@ +## Clip: Improve performance and memory consumption + +The following set of improvements have been implemented for the Clip algorithm: + +1. Input points that are kept are determined by comparing their scalar value with the isovalue, instead of checking the + output cells' connectivity. +2. Output arrays are written only once, and they are not transformed. Due to that, no auxiliary arrays are needed to + perform the transformations. +3. A fast path for discarded and kept cells has been added, which are the most common cell cases. +4. ClipTables are now more descriptive, and the non-inverted case tables have been imported from VTK, such that both VTK + and VTK-m produce the same results. +5. Employ batching of points and cells to use less memory and perform less and faster computations. + +The new `Clip` algorithm: + +On the CPU: + +1. Batch size = min(1000, max(1, numberOfElements / 250000)). +2. Memory-footprint (the bigger the dataset the greater the benefit): + 1. For almost nothing is kept: 10.22x to 99.67x less memory footprint + 2. For almost half is kept: 2.62x to 4.30x less memory footprint + 3. For almost everything is kept: 2.38x to 3.21x less memory footprint +3. Performance (the bigger the dataset the greater the benefit): + 1. For almost nothing is kept: 1.63x to 7.79x faster + 2. For almost half is kept: 1.75x to 5.28x faster + 3. For almost everything is kept: 1.71x to 5.35x faster + +On the GPU: + +1. Batch size = 6. +2. Memory-footprint (the bigger the dataset the greater the benefit): + 1. For almost nothing is kept: 1.71x to 7.75x less memory footprint + 2. For almost half is kept: 1.11x to 1.36x less memory footprint + 3. For almost everything is kept: 1.09x to 1.31x less memory footprint +3. Performance (the bigger the dataset the greater the benefit): + 1. For almost nothing is kept: 1.54x to 9.67x faster + 2. For almost half is kept: 1.38x to 4.68x faster + 3. For almost everything is kept: 1.21x to 4.46x faster -- GitLab From 617eb9adf05cd63e305b537dec297ba6a1420c64 Mon Sep 17 00:00:00 2001 From: Vicente Adolfo Bolea Sanchez Date: Mon, 17 Mar 2025 15:51:03 -0400 Subject: [PATCH 66/73] cmake: bump min cmake version to 3.15 --- CMakeLists.txt | 3 +-- README.md | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46d1cfc37c..cfa85247d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,7 @@ ## PURPOSE. See the above copyright notice for more information. ##============================================================================ -# If you want CUDA support, you will need to have CMake 3.13 on Linux/OSX. -cmake_minimum_required(VERSION 3.12...3.15 FATAL_ERROR) +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) project (VTKm) # We only allow c++14 diff --git a/README.md b/README.md index 9b794aeac5..3aff451a8e 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,7 @@ VTK-m Requires: + MSVC 2015+ + Intel 17.0.4+ + [CMake](http://www.cmake.org/download/) - + CMake 3.12+ - + CMake 3.13+ (for CUDA support) + + CMake 3.15+ + CMake 3.24+ (for ROCM+THRUST support) Optional dependencies are: -- GitLab From 51a3ad8bdc295a67cab218cf2e7820ada1e830e7 Mon Sep 17 00:00:00 2001 From: Vicente Adolfo Bolea Sanchez Date: Mon, 17 Mar 2025 15:51:04 -0400 Subject: [PATCH 67/73] ci: bump min tested cmake ver to 3.15.5 --- .gitlab-ci.yml | 2 +- .gitlab/ci/config/cmake.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bad5564337..380c83ddc0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -126,7 +126,7 @@ stages: .use_minimum_supported_cmake: variables: - CMAKE_VERSION: "3.13.5" + CMAKE_VERSION: "3.15.5" .warning_policy: allow_failure: diff --git a/.gitlab/ci/config/cmake.sh b/.gitlab/ci/config/cmake.sh index 7cfc5a5292..07d4ac8374 100755 --- a/.gitlab/ci/config/cmake.sh +++ b/.gitlab/ci/config/cmake.sh @@ -21,7 +21,7 @@ case "$( uname -s )" in Linux) readonly -A sumsByVersion=( # We require CMake >= 3.13 in the CI to support CUDA builds - ['3.13.5']='e2fd0080a6f0fc1ec84647acdcd8e0b4019770f48d83509e6a5b0b6ea27e5864' + ['3.15.5']='03cfd669d0f990040ec89bb63a3ae7f6d61fd17c1c4d5e7ec3d1a35fe1f043f0' ['3.30.2']='cdd7fb352605cee3ae53b0e18b5929b642900e33d6b0173e19f6d4f2067ebf16' ) shatool="sha256sum" -- GitLab From cf5b3134f83465c36d3c9e0450cda9031bb36db2 Mon Sep 17 00:00:00 2001 From: Vicente Adolfo Bolea Sanchez Date: Sat, 15 Mar 2025 19:08:22 -0400 Subject: [PATCH 68/73] bump diy2 --- vtkm/thirdparty/diy/update.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtkm/thirdparty/diy/update.sh b/vtkm/thirdparty/diy/update.sh index a6fdcf8b3d..97cc5ad57f 100755 --- a/vtkm/thirdparty/diy/update.sh +++ b/vtkm/thirdparty/diy/update.sh @@ -8,7 +8,7 @@ readonly name="diy" readonly ownership="Diy Upstream " readonly subtree="vtkm/thirdparty/$name/vtkm$name" readonly repo="https://gitlab.kitware.com/third-party/diy2.git" -readonly tag="for/vtk-m-20230616-g40ea01f9" +readonly tag="for/vtk-m-20250321-g40ea01f9" readonly paths=" cmake include -- GitLab From 86f8b9ff3b78c56501c151528d55782eeac6432b Mon Sep 17 00:00:00 2001 From: Diy Upstream Date: Wed, 19 Mar 2025 21:50:20 -0400 Subject: [PATCH 69/73] diy 2025-03-19 (bd468e35) Code extracted from: https://gitlab.kitware.com/third-party/diy2.git at commit bd468e35508e0d1e1528e8030031f942778cbfed (for/vtk-m-20250321-g40ea01f9). --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 344398ea9d..748372e5ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ #============================================================================= project (DIY) -cmake_minimum_required (VERSION 3.9) +cmake_minimum_required (VERSION 3.15) list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) -- GitLab From 14f09d44dada57de84941a95e4495c65f4a2a090 Mon Sep 17 00:00:00 2001 From: Mingzhe Li Date: Wed, 2 Apr 2025 14:43:44 -0400 Subject: [PATCH 70/73] Distribute Isosurface extraction from branch decomposition Given the distributed hierarchical contour tree and its branch decomposition, we compute the volume of each branch and select the branches with high volumes (i.e., Top K). Then, we extract the meshes on the contours nearby the saddles of these top-volume branches. --- .../ContourTreeApp.cxx | 259 +++- .../ContourTreeAppDataIO.h | 17 +- vtkm/filter/scalar_topology/CMakeLists.txt | 12 +- .../ContourTreeUniformDistributed.h | 2 +- .../DistributedBranchDecompositionFilter.cxx | 38 +- .../DistributedBranchDecompositionFilter.h | 3 + .../ExtractTopVolumeContoursFilter.cxx | 184 +++ ...ter.h => ExtractTopVolumeContoursFilter.h} | 35 +- .../SelectTopVolumeBranchesFilter.cxx | 373 ++++++ .../SelectTopVolumeBranchesFilter.h | 100 ++ .../SelectTopVolumeContoursFilter.cxx | 205 --- .../scalar_topology/internal/CMakeLists.txt | 6 +- ...eDistributedBranchDecompositionFunctor.cxx | 16 +- ...uteDistributedBranchDecompositionFunctor.h | 7 + .../internal/ExchangeBranchEndsFunctor.cxx | 20 +- .../internal/ExchangeBranchEndsFunctor.h | 8 + .../ExtractTopVolumeContoursBlock.cxx | 843 +++++++++++++ .../internal/ExtractTopVolumeContoursBlock.h | 106 ++ .../internal/ParentBranchExtremaFunctor.cxx | 306 +++++ .../internal/SelectTopVolumeBranchesBlock.cxx | 376 ++++++ ...Block.h => SelectTopVolumeBranchesBlock.h} | 45 +- .../SelectTopVolumeBranchesFunctor.cxx | 426 +++++++ .../internal/SelectTopVolumeBranchesFunctor.h | 96 ++ .../internal/SelectTopVolumeContoursBlock.cxx | 229 ---- .../SelectTopVolumeContoursFunctor.cxx | 448 ------- .../internal/UpdateParentBranchFunctor.cxx | 391 ++++++ ...sFunctor.h => UpdateParentBranchFunctor.h} | 21 +- ...stingContourTreeUniformDistributedFilter.h | 110 +- .../scalar_topology/worklet/CMakeLists.txt | 3 +- .../HierarchicalVolumetricBranchDecomposer.h | 9 + .../BranchEndComparator.h | 24 +- .../GetOuterEndWorklet.h | 2 +- .../HierarchicalAugmenter.h | 27 +- .../HierarchicalAugmenterFunctor.h | 9 + .../HierarchicalContourTree.h | 9 + .../HierarchicalHyperSweeper.h | 4 +- .../hierarchical_augmenter/CMakeLists.txt | 2 +- ...nWorklet.h => FillEmptyIterationWorklet.h} | 49 +- .../FindSuperArcForUnknownNode.h | 10 + .../CMakeLists.txt | 9 +- .../CopyConstArraysWorklet.h | 1110 +++++++++++++++++ .../GetCellCasesWorklet.h | 204 +++ .../GetEdgesInCellWorklet.h | 492 ++++++++ .../GetSuperarcByIsoValueWorklet.h | 140 +++ .../extract_top_volume_contours/Types.h | 76 ++ .../AboveThresholdWorklet.h | 108 ++ .../AssignValueWorklet.h | 160 +++ .../BinarySearchWorklet.h | 125 ++ .../BranchDecompositionTreeMaker.h | 539 ++++++++ .../BranchParentComparator.h | 236 ++++ .../BranchSaddleIsKnownWorklet.h | 131 ++ .../BranchVolumeComparator.h | 24 +- .../select_top_volume_branches/CMakeLists.txt | 31 + .../ClarifyBranchEndSupernodeTypeWorklet.h | 9 +- .../CollectOuterSaddleWorklet.h | 123 ++ .../GetBranchHierarchyWorklet.h | 61 + .../GetBranchVolumeWorklet.h | 10 +- .../GetParentBranchWorklet.h | 214 ++++ .../select_top_volume_branches/Predicates.h | 94 ++ .../TopVolumeBranchData.h | 152 +++ .../UpdateInfoByBranchDirectionWorklet.h | 8 +- .../UpdateOuterSaddleWorklet.h | 141 +++ 62 files changed, 7955 insertions(+), 1072 deletions(-) create mode 100644 vtkm/filter/scalar_topology/ExtractTopVolumeContoursFilter.cxx rename vtkm/filter/scalar_topology/{SelectTopVolumeContoursFilter.h => ExtractTopVolumeContoursFilter.h} (73%) create mode 100644 vtkm/filter/scalar_topology/SelectTopVolumeBranchesFilter.cxx create mode 100644 vtkm/filter/scalar_topology/SelectTopVolumeBranchesFilter.h delete mode 100644 vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx create mode 100644 vtkm/filter/scalar_topology/internal/ExtractTopVolumeContoursBlock.cxx create mode 100644 vtkm/filter/scalar_topology/internal/ExtractTopVolumeContoursBlock.h create mode 100644 vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.cxx create mode 100644 vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesBlock.cxx rename vtkm/filter/scalar_topology/internal/{SelectTopVolumeContoursBlock.h => SelectTopVolumeBranchesBlock.h} (71%) create mode 100644 vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesFunctor.cxx create mode 100644 vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesFunctor.h delete mode 100644 vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.cxx delete mode 100644 vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx create mode 100644 vtkm/filter/scalar_topology/internal/UpdateParentBranchFunctor.cxx rename vtkm/filter/scalar_topology/internal/{SelectTopVolumeContoursFunctor.h => UpdateParentBranchFunctor.h} (83%) rename vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/{CreateSuperarcsUpdateFirstSupernodePerIterationWorklet.h => FillEmptyIterationWorklet.h} (68%) rename vtkm/filter/scalar_topology/worklet/{select_top_volume_contours => extract_top_volume_contours}/CMakeLists.txt (81%) create mode 100644 vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/CopyConstArraysWorklet.h create mode 100644 vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetCellCasesWorklet.h create mode 100644 vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetEdgesInCellWorklet.h create mode 100644 vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetSuperarcByIsoValueWorklet.h create mode 100644 vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/Types.h create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/AboveThresholdWorklet.h create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/AssignValueWorklet.h create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BinarySearchWorklet.h create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchDecompositionTreeMaker.h create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchParentComparator.h create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchSaddleIsKnownWorklet.h rename vtkm/filter/scalar_topology/worklet/{select_top_volume_contours => select_top_volume_branches}/BranchVolumeComparator.h (88%) create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/CMakeLists.txt rename vtkm/filter/scalar_topology/worklet/{select_top_volume_contours => select_top_volume_branches}/ClarifyBranchEndSupernodeTypeWorklet.h (93%) create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/CollectOuterSaddleWorklet.h create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/GetBranchHierarchyWorklet.h rename vtkm/filter/scalar_topology/worklet/{select_top_volume_contours => select_top_volume_branches}/GetBranchVolumeWorklet.h (94%) create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/GetParentBranchWorklet.h create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/Predicates.h create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/TopVolumeBranchData.h rename vtkm/filter/scalar_topology/worklet/{select_top_volume_contours => select_top_volume_branches}/UpdateInfoByBranchDirectionWorklet.h (94%) create mode 100644 vtkm/filter/scalar_topology/worklet/select_top_volume_branches/UpdateOuterSaddleWorklet.h diff --git a/examples/contour_tree_distributed/ContourTreeApp.cxx b/examples/contour_tree_distributed/ContourTreeApp.cxx index ed9ca59d27..fabee4386a 100644 --- a/examples/contour_tree_distributed/ContourTreeApp.cxx +++ b/examples/contour_tree_distributed/ContourTreeApp.cxx @@ -74,7 +74,8 @@ #include #include -#include +#include +#include #include #include #include @@ -272,6 +273,11 @@ int main(int argc, char* argv[]) } } + bool computeIsosurface = false; + // When true, we extract the isosurface at the isovalue STRICTLY above/below the saddle. + // i.e., we ignore the simulation of simplicity when extracting isosurfaces. + // WARNING: this may return incorrect isosurfaces when using presimplification. + bool shiftIsovalueByEpsilon = false; vtkm::Id numBranches = 0; if (parser.hasOption("--numBranches")) { @@ -291,11 +297,45 @@ int main(int argc, char* argv[]) augmentHierarchicalTree = true; } } - - ValueType eps = 0.00001f; - - if (parser.hasOption("--eps")) - eps = std::stof(parser.getOption("--eps")); + if (parser.hasOption("--computeIsosurface")) + { + computeIsosurface = true; + } + if (numBranches == 0 && computeIsosurface) + { + std::cerr << "Error: --computeIsosurface cannot proceed without specifying --numBranches!" + << std::endl; + MPI_Finalize(); + return EXIT_FAILURE; + } + if (parser.hasOption("--shiftIsovalueByEpsilon")) + { + shiftIsovalueByEpsilon = true; + if (presimplifyThreshold > 0) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Warning: when presimplifyThreshold > 0, --shiftIsovalueByEpsilon may return " + "incorrect isosurfaces!"); + } + } + if (!computeIsosurface && shiftIsovalueByEpsilon) + { + if (numBranches > 0) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Warning: --shiftIsovalueByEpsilon requires --computeIsosurface " + "and a nonzero --numBranches! Enabling --computeIsosurface option."); + computeIsosurface = true; + } + else + { + std::cerr + << "Error: --shiftIsovalueByEpsilon cannot proceed without specifying --numBranches!" + << std::endl; + MPI_Finalize(); + return EXIT_FAILURE; + } + } #ifdef ENABLE_HDFIO std::string dataset_name = "data"; @@ -372,8 +412,15 @@ int main(int argc, char* argv[]) std::cout << "--numBranches Number of top volume branches to select." << std::endl; std::cout << " Requires --computeVolumeBranchDecomposition." << std::endl; std::cout - << "--eps= Floating point offset awary from the critical point. (default=0.00001)" - << std::endl; + << "--computeIsosurface Compute isosurfaces near the saddle end of top volume branches." + << std::endl + << " Requires specifying --numBranches." << std::endl; + std::cout << "--shiftIsovalueByEpsilon If set, the contour isovalues will be strictly " + "above/below the contour," + << " which will ignore the simulation of simplicity when " + "extracting contours. " + << std::endl + << " Requires specifying --numBranches." << std::endl; std::cout << "--presimplifyThreshold Integer volume threshold for presimplifying the tree" << std::endl; std::cout << " Default value is 0, indicating no presimplification" @@ -421,12 +468,14 @@ int main(int argc, char* argv[]) << " augmentHierarchicalTree=" << augmentHierarchicalTree << std::endl << " computeVolumetricBranchDecomposition=" << computeHierarchicalVolumetricBranchDecomposition << std::endl + << " numBranches=" << numBranches << std::endl + << " computeIsosurface=" << computeIsosurface << std::endl + << " shiftIsovalueByEpsilon=" << shiftIsovalueByEpsilon << std::endl << " presimplifyThreshold=" << presimplifyThreshold << std::endl << " saveOutputData=" << saveOutputData << std::endl << " forwardSummary=" << forwardSummary << std::endl << " nblocks=" << numBlocks << std::endl << " nbranches=" << numBranches << std::endl - << " eps=" << eps << std::endl #ifdef ENABLE_HDFIO << " dataset=" << dataset_name << " (HDF5 only)" << std::endl << " blocksPerDim=" << blocksPerDimIn[0] << "," << blocksPerDimIn[1] << "," @@ -490,6 +539,12 @@ int main(int argc, char* argv[]) << " forwardSummary=" << forwardSummary << std::endl << " numBlocks=" << numBlocks << std::endl << " augmentHierarchicalTree=" << augmentHierarchicalTree << std::endl + << " computeVolumetricBranchDecomposition=" + << computeHierarchicalVolumetricBranchDecomposition << std::endl + << " numBranches=" << numBranches << std::endl + << " computeIsosurface=" << computeIsosurface << std::endl + << " shiftIsovalueByEpsilon=" << shiftIsovalueByEpsilon << std::endl + << " presimplifyThreshold=" << presimplifyThreshold << std::endl << " numRanks=" << size << std::endl << " rank=" << rank << std::endl #ifdef ENABLE_HDFIO @@ -668,6 +723,7 @@ int main(int argc, char* argv[]) // Execute the contour tree analysis auto result = filter.Execute(useDataSet); + const vtkm::Id nPartitions = result.GetNumberOfPartitions(); currTime = totalTime.GetElapsedTime(); vtkm::Float64 computeContourTreeTime = currTime - prevTime; @@ -685,6 +741,10 @@ int main(int argc, char* argv[]) { vtkm::filter::scalar_topology::DistributedBranchDecompositionFilter bd_filter; bd_result = bd_filter.Execute(result); + + // We carried over all fields in result to bd_result. + // To save memory, we destroy result by replacing it with an empty PartitionedDataset. + result = vtkm::cont::PartitionedDataSet{}; } currTime = totalTime.GetElapsedTime(); vtkm::Float64 branchDecompTime = currTime - prevTime; @@ -692,13 +752,43 @@ int main(int argc, char* argv[]) // Compute SelectTopVolumeContours if needed vtkm::cont::PartitionedDataSet tp_result; + vtkm::cont::PartitionedDataSet iso_result; + bool isTopVolumeBranchValid = true; if (numBranches > 0) { - vtkm::filter::scalar_topology::SelectTopVolumeContoursFilter tp_filter; + vtkm::filter::scalar_topology::SelectTopVolumeBranchesFilter tp_filter; tp_filter.SetSavedBranches(numBranches); + tp_filter.SetPresimplifyThreshold(presimplifyThreshold); tp_result = tp_filter.Execute(bd_result); + + // We carried over all fields in bd_result to tp_result. + // To save memory, we destroy bd_result by replacing it with an empty PartitionedDataset. + bd_result = vtkm::cont::PartitionedDataSet{}; + + // detecting whether the dataset is empty + // it is relatively safe to assume that the result has at least one partition + if (tp_result.GetPartition(0).GetNumberOfFields() < 1) + { + isTopVolumeBranchValid = false; + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + std::endl + << "Warning: SelectTopVolumeBranchesFilter did not return any valid branches!" + << std::endl + << "Skipping isosurface extraction." << std::endl); + } + if (isTopVolumeBranchValid && computeIsosurface) + { + vtkm::filter::scalar_topology::ExtractTopVolumeContoursFilter iso_filter; + iso_filter.SetMarchingCubes(useMarchingCubes); + iso_filter.SetShiftIsovalueByEpsilon(shiftIsovalueByEpsilon); + iso_result = iso_filter.Execute(tp_result); + } } + currTime = totalTime.GetElapsedTime(); + vtkm::Float64 topVolBranchTime = currTime - prevTime; + prevTime = currTime; + // Save output if (saveOutputData) { @@ -706,9 +796,10 @@ int main(int argc, char* argv[]) { if (computeHierarchicalVolumetricBranchDecomposition) { - for (vtkm::Id ds_no = 0; ds_no < result.GetNumberOfPartitions(); ++ds_no) + vtkm::cont::PartitionedDataSet& output_result = numBranches > 0 ? tp_result : bd_result; + for (vtkm::Id ds_no = 0; ds_no < nPartitions; ++ds_no) { - auto ds = bd_result.GetPartition(ds_no); + auto ds = output_result.GetPartition(ds_no); std::string branchDecompositionIntermediateFileName = std::string("BranchDecompositionIntermediate_Rank_") + std::to_string(static_cast(rank)) + std::string("_Block_") + @@ -742,27 +833,32 @@ int main(int argc, char* argv[]) } } - if (numBranches > 0) + if (numBranches > 0 && isTopVolumeBranchValid) { #ifndef DEBUG_PRINT bool print_to_files = (rank == 0); vtkm::Id max_blocks_to_print = 1; #else bool print_to_files = true; - vtkm::Id max_blocks_to_print = result.GetNumberOfPartitions(); + vtkm::Id max_blocks_to_print = nPartitions; #endif for (vtkm::Id ds_no = 0; print_to_files && ds_no < max_blocks_to_print; ++ds_no) { - auto ds = tp_result.GetPartition(ds_no); + auto ds = output_result.GetPartition(ds_no); + std::string topVolumeBranchFileName = std::string("TopVolumeBranch_Rank_") + std::to_string(static_cast(rank)) + std::string("_Block_") + std::to_string(static_cast(ds_no)) + std::string(".txt"); std::ofstream topVolumeBranchStream(topVolumeBranchFileName.c_str()); - auto topVolBranchGRId = ds.GetField("TopVolumeBranchGlobalRegularIds") - .GetData() - .AsArrayHandle>() - .ReadPortal(); + auto topVolBranchUpperEnd = ds.GetField("TopVolumeBranchUpperEnd") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto topVolBranchLowerEnd = ds.GetField("TopVolumeBranchLowerEnd") + .GetData() + .AsArrayHandle>() + .ReadPortal(); auto topVolBranchVolume = ds.GetField("TopVolumeBranchVolume") .GetData() .AsArrayHandle>() @@ -776,10 +872,11 @@ int main(int argc, char* argv[]) .AsArrayHandle>() .ReadPortal(); - vtkm::Id nSelectedBranches = topVolBranchGRId.GetNumberOfValues(); + vtkm::Id nSelectedBranches = topVolBranchUpperEnd.GetNumberOfValues(); for (vtkm::Id branch = 0; branch < nSelectedBranches; ++branch) { - topVolumeBranchStream << std::setw(12) << topVolBranchGRId.Get(branch) + topVolumeBranchStream << std::setw(12) << topVolBranchUpperEnd.Get(branch) + << std::setw(12) << topVolBranchLowerEnd.Get(branch) << std::setw(14) << topVolBranchVolume.Get(branch) << std::setw(5) << topVolBranchSaddleEpsilon.Get(branch) << std::setw(14) << topVolBranchSaddleIsoValue.Get(branch) @@ -793,18 +890,125 @@ int main(int argc, char* argv[]) for (vtkm::Id branch = 0; branch < nSelectedBranches; ++branch) { - isoValuesStream << (topVolBranchSaddleIsoValue.Get(branch) + - (eps * topVolBranchSaddleEpsilon.Get(branch))) - << " "; + isoValuesStream << topVolBranchSaddleIsoValue.Get(branch) << " "; } isoValuesStream << std::endl; } + + for (vtkm::Id ds_no = 0; computeIsosurface && ds_no < nPartitions; ++ds_no) + { + auto ds = iso_result.GetPartition(ds_no); + + std::string isosurfaceFileName = std::string("Isosurface_Rank_") + + std::to_string(static_cast(rank)) + std::string("_Block_") + + std::to_string(static_cast(ds_no)) + std::string(".txt"); + std::ofstream isosurfaceStream(isosurfaceFileName.c_str()); + + auto isosurfaceEdgesFrom = ds.GetField("IsosurfaceEdgesFrom") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesTo = ds.GetField("IsosurfaceEdgesTo") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesLabels = ds.GetField("IsosurfaceEdgesLabels") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesOrders = ds.GetField("IsosurfaceEdgesOrders") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesOffset = ds.GetField("IsosurfaceEdgesOffset") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceIsoValue = ds.GetField("IsosurfaceIsoValue") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + vtkm::Id nIsosurfaceEdges = isosurfaceEdgesFrom.GetNumberOfValues(); + vtkm::Id isoSurfaceCount = 0; + + if (nDims == 2) + for (vtkm::Id edge = 0; edge < nIsosurfaceEdges; ++edge) + { + while (isoSurfaceCount < isosurfaceEdgesLabels.GetNumberOfValues() && + edge == isosurfaceEdgesOffset.Get(isoSurfaceCount)) + { + isosurfaceStream << "Isosurface Info:" << std::setw(5) + << isosurfaceEdgesLabels.Get(isoSurfaceCount) << std::setw(10) + << isosurfaceEdgesOrders.Get(isoSurfaceCount) << std::setw(10) + << isosurfaceIsoValue.Get(isoSurfaceCount) << std::endl; + isoSurfaceCount++; + } + isosurfaceStream << std::setw(6) << isosurfaceEdgesFrom.Get(edge) << std::setw(6) + << isosurfaceEdgesTo.Get(edge) << std::endl; + } + else if (nDims == 3) + { + VTKM_ASSERT(nIsosurfaceEdges % 3 == 0); + for (vtkm::Id edge = 0; edge < nIsosurfaceEdges; edge += 3) + { + while (isoSurfaceCount < isosurfaceEdgesLabels.GetNumberOfValues() && + edge == isosurfaceEdgesOffset.Get(isoSurfaceCount)) + { + isosurfaceStream << "Isosurface Info:" << std::setw(5) + << isosurfaceEdgesLabels.Get(isoSurfaceCount) << std::setw(10) + << isosurfaceEdgesOrders.Get(isoSurfaceCount) << std::setw(10) + << isosurfaceIsoValue.Get(isoSurfaceCount) << std::endl; + isoSurfaceCount++; + } + isosurfaceStream << std::setw(6) << isosurfaceEdgesFrom.Get(edge) << std::setw(6) + << isosurfaceEdgesTo.Get(edge) << std::setw(6) + << isosurfaceEdgesTo.Get(edge + 1) << std::endl; + } + } + } + + for (vtkm::Id ds_no = 0; ds_no < nPartitions; ++ds_no) + { + auto ds = output_result.GetPartition(ds_no); + + std::string branchDecompositionVolumeFileName = + std::string("BranchDecompositionVolume_Rank_") + + std::to_string(static_cast(rank)) + std::string("_Block_") + + std::to_string(static_cast(ds_no)) + std::string(".txt"); + std::ofstream bdVolStream(branchDecompositionVolumeFileName.c_str()); + + auto upperEndGRId = ds.GetField("UpperEndGlobalRegularIds") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto lowerEndGRId = ds.GetField("LowerEndGlobalRegularIds") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto branchSaddleEpsilon = ds.GetField("BranchSaddleEpsilon") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto branchVolume = ds.GetField("BranchVolume") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + vtkm::Id nBranches = upperEndGRId.GetNumberOfValues(); + + for (vtkm::Id branch = 0; branch < nBranches; ++branch) + { + bdVolStream << std::setw(12) << upperEndGRId.Get(branch) << std::setw(12) + << lowerEndGRId.Get(branch) << std::setw(3) + << branchSaddleEpsilon.Get(branch) << std::setw(18) + << branchVolume.Get(branch) << std::endl; + } + } } } else { - for (vtkm::Id ds_no = 0; ds_no < result.GetNumberOfPartitions(); ++ds_no) + for (vtkm::Id ds_no = 0; ds_no < nPartitions; ++ds_no) { auto ds = result.GetPartition(ds_no); vtkm::worklet::contourtree_augmented::IdArrayType supernodes; @@ -838,7 +1042,7 @@ int main(int argc, char* argv[]) } else { - for (vtkm::Id ds_no = 0; ds_no < result.GetNumberOfPartitions(); ++ds_no) + for (vtkm::Id ds_no = 0; ds_no < nPartitions; ++ds_no) { vtkm::worklet::contourtree_distributed::TreeCompiler treeCompiler; treeCompiler.AddHierarchicalTree(result.GetPartition(ds_no)); @@ -855,7 +1059,6 @@ int main(int argc, char* argv[]) } } - currTime = totalTime.GetElapsedTime(); vtkm::Float64 saveOutputDataTime = currTime - prevTime; prevTime = currTime; @@ -907,6 +1110,8 @@ int main(int argc, char* argv[]) << ": " << postFilterSyncTime << " seconds" << std::endl << std::setw(42) << std::left << " Branch Decomposition" << ": " << branchDecompTime << " seconds" << std::endl + << std::setw(42) << std::left << " Top Volume Branch Extraction" + << ": " << topVolBranchTime << " seconds" << std::endl << std::setw(42) << std::left << " Save Tree Compiler Data" << ": " << saveOutputDataTime << " seconds" << std::endl << std::setw(42) << std::left << " Total Time" diff --git a/examples/contour_tree_distributed/ContourTreeAppDataIO.h b/examples/contour_tree_distributed/ContourTreeAppDataIO.h index acbf32b1c1..847e43add4 100644 --- a/examples/contour_tree_distributed/ContourTreeAppDataIO.h +++ b/examples/contour_tree_distributed/ContourTreeAppDataIO.h @@ -87,7 +87,7 @@ /// Convert a 3D index of a cube to rank index -vtkm::Id to1DIndex(vtkm::Id3 idx, vtkm::Id3 dims) +inline vtkm::Id to1DIndex(vtkm::Id3 idx, vtkm::Id3 dims) { // return (idx[2] * dims[0] * dims[1]) + (idx[1] * dims[0]) + idx[0]; // Swap first and second dimension @@ -95,7 +95,7 @@ vtkm::Id to1DIndex(vtkm::Id3 idx, vtkm::Id3 dims) } /// Convert the rank index to the index of the cube -vtkm::Id3 to3DIndex(vtkm::Id idx, vtkm::Id3 dims) +inline vtkm::Id3 to3DIndex(vtkm::Id idx, vtkm::Id3 dims) { vtkm::Id3 res; res[2] = idx / (dims[0] * dims[1]); @@ -233,6 +233,19 @@ bool read3DHDF5File(const int& mpi_rank, { count[2] = globalSize[2] - offset[2]; } + // Check that we cover the end of the dataset + if (blockIndex[0] == blocksPerDim[0] - 1 && (vtkm::Id(offset[0] + count[0]) != globalSize[0])) + { + count[0] = globalSize[0] - offset[0]; + } + if (blockIndex[1] == blocksPerDim[1] - 1 && (vtkm::Id(offset[1] + count[1]) != globalSize[1])) + { + count[1] = globalSize[1] - offset[1]; + } + if (blockIndex[2] == blocksPerDim[2] - 1 && (vtkm::Id(offset[2] + count[2]) != globalSize[2])) + { + count[2] = globalSize[2] - offset[2]; + } blockSize = vtkm::Id3{ static_cast(count[0]), static_cast(count[1]), static_cast(count[2]) }; diff --git a/vtkm/filter/scalar_topology/CMakeLists.txt b/vtkm/filter/scalar_topology/CMakeLists.txt index 5603ebaa15..aeb7e2f468 100644 --- a/vtkm/filter/scalar_topology/CMakeLists.txt +++ b/vtkm/filter/scalar_topology/CMakeLists.txt @@ -12,21 +12,25 @@ set(scalar_topology_headers ContourTreeUniformAugmented.h ContourTreeUniformDistributed.h DistributedBranchDecompositionFilter.h - SelectTopVolumeContoursFilter.h + SelectTopVolumeBranchesFilter.h + ExtractTopVolumeContoursFilter.h ) set(scalar_topology_sources internal/BranchDecompositionBlock.cxx - internal/SelectTopVolumeContoursBlock.cxx + internal/SelectTopVolumeBranchesBlock.cxx + internal/ExtractTopVolumeContoursBlock.cxx internal/ComputeBlockIndices.cxx internal/ComputeDistributedBranchDecompositionFunctor.cxx - internal/SelectTopVolumeContoursFunctor.cxx + internal/SelectTopVolumeBranchesFunctor.cxx + internal/UpdateParentBranchFunctor.cxx internal/ExchangeBranchEndsFunctor.cxx ContourTreeUniform.cxx ContourTreeUniformAugmented.cxx ContourTreeUniformDistributed.cxx DistributedBranchDecompositionFilter.cxx - SelectTopVolumeContoursFilter.cxx + SelectTopVolumeBranchesFilter.cxx + ExtractTopVolumeContoursFilter.cxx ) vtkm_library( diff --git a/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.h b/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.h index 525fa48148..6093cd55f1 100644 --- a/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.h +++ b/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.h @@ -199,7 +199,7 @@ private: bool AugmentHierarchicalTree; /// Threshold to use for volume pre-simplification - vtkm::Id PresimplifyThreshold; + vtkm::Id PresimplifyThreshold = 0; /// Save dot files for all tree computations bool SaveDotFiles; diff --git a/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.cxx b/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.cxx index 0f522424bc..316d07b921 100644 --- a/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.cxx +++ b/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.cxx @@ -80,6 +80,7 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D timingsStream << " " << std::setw(60) << std::left << "Create DIY Master and Assigner (Branch Decomposition)" << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); // Compute global ids (gids) for our local blocks @@ -116,6 +117,7 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D timingsStream << " " << std::setw(60) << std::left << "Get DIY Information (Branch Decomposition)" << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); @@ -161,11 +163,6 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D ghosts, diyDivisions); - // TODO/FIXME: Check what happened here and possibly eliminate! - for (vtkm::Id bi = 0; bi < input.GetNumberOfPartitions(); bi++) - { - } - timingsStream << " " << std::setw(60) << std::left << "Create DIY Decomposer and Assigner (Branch Decomposition)" << ": " << timer.GetElapsedTime() << " seconds" << std::endl; @@ -237,7 +234,8 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D branch_decomposition_master, assigner, partners, - vtkm::filter::scalar_topology::internal::ComputeDistributedBranchDecompositionFunctor{}); + vtkm::filter::scalar_topology::internal::ComputeDistributedBranchDecompositionFunctor( + this->TimingsLogLevel)); timingsStream << " " << std::setw(60) << std::left << "Exchanging best up/down supernode and volume" @@ -314,12 +312,19 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D b->VolumetricBranchDecomposer.CollectBranches(ds, b->BranchRoots); }); + timingsStream << " " << std::setw(38) << std::left << "CollectBranchEnds" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + // Now we have collected the branches, we do a global reduction to exchance branch end information // across all compute ranks - vtkmdiy::reduce(branch_decomposition_master, - assigner, - partners, - vtkm::filter::scalar_topology::internal::ExchangeBranchEndsFunctor{}); + auto exchangeBranchEndsFunctor = + vtkm::filter::scalar_topology::internal::ExchangeBranchEndsFunctor(this->TimingsLogLevel); + vtkmdiy::reduce(branch_decomposition_master, assigner, partners, exchangeBranchEndsFunctor); + + timingsStream << " " << std::setw(38) << std::left << "ExchangeBranchEnds" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); std::vector outputDataSets(input.GetNumberOfPartitions()); // Copy input data set to output @@ -347,6 +352,15 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D b->VolumetricBranchDecomposer.LowerEndGRId); outputDataSets[b->LocalBlockNo].AddField(LowerEndGRIdField); + vtkm::cont::Field UpperEndLocalIdField("UpperEndLocalIds", + vtkm::cont::Field::Association::WholeDataSet, + b->VolumetricBranchDecomposer.UpperEndLocalId); + outputDataSets[b->LocalBlockNo].AddField(UpperEndLocalIdField); + vtkm::cont::Field LowerEndLocalIdField("LowerEndLocalIds", + vtkm::cont::Field::Association::WholeDataSet, + b->VolumetricBranchDecomposer.LowerEndLocalId); + outputDataSets[b->LocalBlockNo].AddField(LowerEndLocalIdField); + vtkm::cont::Field UpperEndIntrinsicVolume( "UpperEndIntrinsicVolume", vtkm::cont::Field::Association::WholeDataSet, @@ -383,7 +397,7 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D vtkm::cont::Field::Association::WholeDataSet, b->VolumetricBranchDecomposer.UpperEndValue); outputDataSets[b->LocalBlockNo].AddField(UpperEndValue); - vtkm::cont::Field BranchRoot("BranchRoot", + vtkm::cont::Field BranchRoot("BranchRootByBranch", vtkm::cont::Field::Association::WholeDataSet, b->VolumetricBranchDecomposer.BranchRoot); outputDataSets[b->LocalBlockNo].AddField(BranchRoot); @@ -397,7 +411,7 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D << "Creating Branch Decomposition Output Data" << ": " << timer.GetElapsedTime() << " seconds" << std::endl; - VTKM_LOG_S(vtkm::cont::LogLevel::Perf, + VTKM_LOG_S(this->TimingsLogLevel, std::endl << "----------- DoExecutePartitions Timings ------------" << std::endl << timingsStream.str()); diff --git a/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.h b/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.h index be1b677a0e..e2ecd7f516 100644 --- a/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.h +++ b/vtkm/filter/scalar_topology/DistributedBranchDecompositionFilter.h @@ -68,6 +68,9 @@ private: VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet&) override; VTKM_CONT vtkm::cont::PartitionedDataSet DoExecutePartitions( const vtkm::cont::PartitionedDataSet& inData) override; + + /// Log level to be used for outputting timing information. Default is vtkm::cont::LogLevel::Perf + vtkm::cont::LogLevel TimingsLogLevel = vtkm::cont::LogLevel::Perf; }; } // namespace scalar_topology diff --git a/vtkm/filter/scalar_topology/ExtractTopVolumeContoursFilter.cxx b/vtkm/filter/scalar_topology/ExtractTopVolumeContoursFilter.cxx new file mode 100644 index 0000000000..f7b4e487fd --- /dev/null +++ b/vtkm/filter/scalar_topology/ExtractTopVolumeContoursFilter.cxx @@ -0,0 +1,184 @@ +//============================================================================ +// 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 + +// vtkm includes +#include + +// DIY includes +// clang-format off +VTKM_THIRDPARTY_PRE_INCLUDE +#include +#include +VTKM_THIRDPARTY_POST_INCLUDE +// clang-format on + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ + +VTKM_CONT vtkm::cont::DataSet ExtractTopVolumeContoursFilter::DoExecute(const vtkm::cont::DataSet&) +{ + throw vtkm::cont::ErrorFilterExecution( + "ExtractTopVolumeContoursFilter expects PartitionedDataSet as input."); +} + +VTKM_CONT vtkm::cont::PartitionedDataSet ExtractTopVolumeContoursFilter::DoExecutePartitions( + const vtkm::cont::PartitionedDataSet& input) +{ + vtkm::cont::Timer timer; + timer.Start(); + std::stringstream timingsStream; + + auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); + int rank = comm.rank(); + int size = comm.size(); + + + using ExtractTopVolumeContoursBlock = + vtkm::filter::scalar_topology::internal::ExtractTopVolumeContoursBlock; + vtkmdiy::Master branch_top_volume_master(comm, + 1, // Use 1 thread, VTK-M will do the treading + -1, // All blocks in memory + 0, // No create function + ExtractTopVolumeContoursBlock::Destroy); + + timingsStream << " " << std::setw(60) << std::left + << "Create DIY Master and Assigner (Contour Extraction)" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + auto firstDS = input.GetPartition(0); + vtkm::Id3 firstPointDimensions, firstGlobalPointDimensions, firstGlobalPointIndexStart; + firstDS.GetCellSet().CastAndCallForTypes( + vtkm::worklet::contourtree_augmented::GetLocalAndGlobalPointDimensions(), + firstPointDimensions, + firstGlobalPointDimensions, + firstGlobalPointIndexStart); + int numDims = firstGlobalPointDimensions[2] > 1 ? 3 : 2; + auto vtkmBlocksPerDimensionRP = input.GetPartition(0) + .GetField("vtkmBlocksPerDimension") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + + // ... compute division vector for global domain + int globalNumberOfBlocks = 1; + + for (vtkm::IdComponent d = 0; d < static_cast(numDims); ++d) + { + globalNumberOfBlocks *= static_cast(vtkmBlocksPerDimensionRP.Get(d)); + } + + // Record time to compute the local block ids + timingsStream << " " << std::setw(60) << std::left + << "Get DIY Information (Contour Extraction)" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + vtkmdiy::DynamicAssigner assigner(comm, size, globalNumberOfBlocks); + for (vtkm::Id localBlockIndex = 0; localBlockIndex < input.GetNumberOfPartitions(); + ++localBlockIndex) + { + const vtkm::cont::DataSet& ds = input.GetPartition(localBlockIndex); + int globalBlockId = static_cast( + vtkm::cont::ArrayGetValue(0, + ds.GetField("vtkmGlobalBlockId") + .GetData() + .AsArrayHandle>())); + + ExtractTopVolumeContoursBlock* b = + new ExtractTopVolumeContoursBlock(localBlockIndex, globalBlockId); + + branch_top_volume_master.add(globalBlockId, b, new vtkmdiy::Link()); + assigner.set_rank(rank, globalBlockId); + } + + // Log time to copy the data to the block data objects + timingsStream << " " << std::setw(60) << std::left << "Initialize Contour Extraction Data" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + timingsStream << " " << std::setw(60) << std::left + << "Create DIY Assigner (Contour Extraction)" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + // Fix the vtkmdiy links. + vtkmdiy::fix_links(branch_top_volume_master, assigner); + + timingsStream << " " << std::setw(60) << std::left << "Fix DIY Links (Contour Extraction)" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + // We compute everything we need for contour extraction and put them in the output dataset. + branch_top_volume_master.foreach ([&](ExtractTopVolumeContoursBlock* b, + const vtkmdiy::Master::ProxyWithLink&) { + const vtkm::cont::DataSet& ds = input.GetPartition(b->LocalBlockNo); + b->ExtractIsosurfaceOnSelectedBranch( + ds, this->GetMarchingCubes(), this->GetShiftIsovalueByEpsilon(), this->GetTimingsLogLevel()); + }); + + timingsStream << " " << std::setw(60) << std::left << "Draw Contours By Branches" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + std::vector outputDataSets(input.GetNumberOfPartitions()); + // we need to send everything that contour extraction needs to the output dataset + branch_top_volume_master.foreach ([&](ExtractTopVolumeContoursBlock* b, + const vtkmdiy::Master::ProxyWithLink&) { + vtkm::cont::Field IsosurfaceEdgeFromField( + "IsosurfaceEdgesFrom", vtkm::cont::Field::Association::WholeDataSet, b->IsosurfaceEdgesFrom); + outputDataSets[b->LocalBlockNo].AddField(IsosurfaceEdgeFromField); + vtkm::cont::Field IsosurfaceEdgeToField( + "IsosurfaceEdgesTo", vtkm::cont::Field::Association::WholeDataSet, b->IsosurfaceEdgesTo); + outputDataSets[b->LocalBlockNo].AddField(IsosurfaceEdgeToField); + vtkm::cont::Field IsosurfaceEdgeLabelField("IsosurfaceEdgesLabels", + vtkm::cont::Field::Association::WholeDataSet, + b->IsosurfaceEdgesLabels); + outputDataSets[b->LocalBlockNo].AddField(IsosurfaceEdgeLabelField); + + vtkm::cont::Field IsosurfaceEdgeOffsetField("IsosurfaceEdgesOffset", + vtkm::cont::Field::Association::WholeDataSet, + b->IsosurfaceEdgesOffset); + outputDataSets[b->LocalBlockNo].AddField(IsosurfaceEdgeOffsetField); + + vtkm::cont::Field IsosurfaceEdgeOrderField("IsosurfaceEdgesOrders", + vtkm::cont::Field::Association::WholeDataSet, + b->IsosurfaceEdgesOrders); + outputDataSets[b->LocalBlockNo].AddField(IsosurfaceEdgeOrderField); + vtkm::cont::Field IsosurfaceIsoValueField( + "IsosurfaceIsoValue", vtkm::cont::Field::Association::WholeDataSet, b->IsosurfaceIsoValue); + outputDataSets[b->LocalBlockNo].AddField(IsosurfaceIsoValueField); + }); + + timingsStream << " " << std::setw(38) << std::left << "Creating Contour Extraction Output Data" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + + VTKM_LOG_S(this->TimingsLogLevel, + std::endl + << "----------- DoExecutePartitions Timings ------------" << std::endl + << timingsStream.str()); + + return vtkm::cont::PartitionedDataSet{ outputDataSets }; +} + +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm diff --git a/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.h b/vtkm/filter/scalar_topology/ExtractTopVolumeContoursFilter.h similarity index 73% rename from vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.h rename to vtkm/filter/scalar_topology/ExtractTopVolumeContoursFilter.h index cdc8c2f3ff..8be2168ea5 100644 --- a/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.h +++ b/vtkm/filter/scalar_topology/ExtractTopVolumeContoursFilter.h @@ -39,12 +39,11 @@ // //============================================================================= -#ifndef vtk_m_filter_scalar_topology_SelectTopVolumeContoursFilter_h -#define vtk_m_filter_scalar_topology_SelectTopVolumeContoursFilter_h +#ifndef vtk_m_filter_scalar_topology_ExtractTopVolumeContoursFilter_h +#define vtk_m_filter_scalar_topology_ExtractTopVolumeContoursFilter_h #include #include -#include namespace vtkm { @@ -52,28 +51,38 @@ namespace filter { namespace scalar_topology { -/// \brief Compute branch decompostion from distributed contour tree -class VTKM_FILTER_SCALAR_TOPOLOGY_EXPORT SelectTopVolumeContoursFilter : public vtkm::filter::Filter +/// \brief Compute branch decompostion from distributed contour tree +class VTKM_FILTER_SCALAR_TOPOLOGY_EXPORT ExtractTopVolumeContoursFilter + : public vtkm::filter::Filter { public: - VTKM_CONT SelectTopVolumeContoursFilter() = default; + VTKM_CONT ExtractTopVolumeContoursFilter() = default; - VTKM_CONT void SetSavedBranches(const vtkm::Id& numBranches) + VTKM_CONT void SetMarchingCubes(const bool& marchingCubes) { - this->nSavedBranches = numBranches; + this->IsMarchingCubes = marchingCubes; } + VTKM_CONT void SetShiftIsovalueByEpsilon(const bool& shiftIsovalueByEps) + { + this->IsShiftIsovalueByEpsilon = shiftIsovalueByEps; + } + + VTKM_CONT bool GetMarchingCubes() { return this->IsMarchingCubes; } + VTKM_CONT bool GetShiftIsovalueByEpsilon() { return this->IsShiftIsovalueByEpsilon; } + VTKM_CONT vtkm::cont::LogLevel GetTimingsLogLevel() { return this->TimingsLogLevel; } + private: VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet&) override; VTKM_CONT vtkm::cont::PartitionedDataSet DoExecutePartitions( const vtkm::cont::PartitionedDataSet& inData) override; - vtkm::Id nSavedBranches; - vtkm::worklet::contourtree_augmented::IdArrayType BranchVolume; - vtkm::worklet::contourtree_augmented::IdArrayType BranchSaddleEpsilon; - vtkm::worklet::contourtree_augmented::IdArrayType SortedBranchByVolume; - vtkm::cont::UnknownArrayHandle BranchSaddleIsoValue; + bool IsMarchingCubes = false; + bool IsShiftIsovalueByEpsilon = false; + + /// Log level to be used for outputting timing information. Default is vtkm::cont::LogLevel::Perf + vtkm::cont::LogLevel TimingsLogLevel = vtkm::cont::LogLevel::Perf; }; } // namespace scalar_topology diff --git a/vtkm/filter/scalar_topology/SelectTopVolumeBranchesFilter.cxx b/vtkm/filter/scalar_topology/SelectTopVolumeBranchesFilter.cxx new file mode 100644 index 0000000000..ad7522a8ba --- /dev/null +++ b/vtkm/filter/scalar_topology/SelectTopVolumeBranchesFilter.cxx @@ -0,0 +1,373 @@ +//============================================================================ +// 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 + +// vtkm includes +#include + +// DIY includes +// clang-format off +VTKM_THIRDPARTY_PRE_INCLUDE +#include +#include +VTKM_THIRDPARTY_POST_INCLUDE +// clang-format on + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ + +VTKM_CONT vtkm::cont::DataSet SelectTopVolumeBranchesFilter::DoExecute(const vtkm::cont::DataSet&) +{ + throw vtkm::cont::ErrorFilterExecution( + "SelectTopVolumeBranchesFilter expects PartitionedDataSet as input."); +} + +VTKM_CONT vtkm::cont::PartitionedDataSet SelectTopVolumeBranchesFilter::DoExecutePartitions( + const vtkm::cont::PartitionedDataSet& input) +{ + vtkm::cont::Timer timer; + timer.Start(); + std::stringstream timingsStream; + + auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); + int rank = comm.rank(); + int size = comm.size(); + + + using SelectTopVolumeBranchesBlock = + vtkm::filter::scalar_topology::internal::SelectTopVolumeBranchesBlock; + vtkmdiy::Master branch_top_volume_master(comm, + 1, // Use 1 thread, VTK-M will do the treading + -1, // All blocks in memory + 0, // No create function + SelectTopVolumeBranchesBlock::Destroy); + + timingsStream << " " << std::setw(60) << std::left + << "Create DIY Master and Assigner (Branch Selection)" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + auto firstDS = input.GetPartition(0); + vtkm::Id3 firstPointDimensions, firstGlobalPointDimensions, firstGlobalPointIndexStart; + firstDS.GetCellSet().CastAndCallForTypes( + vtkm::worklet::contourtree_augmented::GetLocalAndGlobalPointDimensions(), + firstPointDimensions, + firstGlobalPointDimensions, + firstGlobalPointIndexStart); + int numDims = firstGlobalPointDimensions[2] > 1 ? 3 : 2; + auto vtkmBlocksPerDimensionRP = input.GetPartition(0) + .GetField("vtkmBlocksPerDimension") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + + // ... compute division vector for global domain + using RegularDecomposer = vtkmdiy::RegularDecomposer; + RegularDecomposer::DivisionsVector diyDivisions(numDims); + vtkmdiy::DiscreteBounds diyBounds(numDims); + int globalNumberOfBlocks = 1; + + for (vtkm::IdComponent d = 0; d < static_cast(numDims); ++d) + { + diyDivisions[d] = static_cast(vtkmBlocksPerDimensionRP.Get(d)); + globalNumberOfBlocks *= static_cast(vtkmBlocksPerDimensionRP.Get(d)); + diyBounds.min[d] = 0; + diyBounds.max[d] = static_cast(firstGlobalPointDimensions[d]); + } + + // Record time to compute the local block ids + + timingsStream << " " << std::setw(60) << std::left << "Get DIY Information (Branch Selection)" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + vtkmdiy::DynamicAssigner assigner(comm, size, globalNumberOfBlocks); + for (vtkm::Id localBlockIndex = 0; localBlockIndex < input.GetNumberOfPartitions(); + ++localBlockIndex) + { + const vtkm::cont::DataSet& ds = input.GetPartition(localBlockIndex); + int globalBlockId = static_cast( + vtkm::cont::ArrayGetValue(0, + ds.GetField("vtkmGlobalBlockId") + .GetData() + .AsArrayHandle>())); + + SelectTopVolumeBranchesBlock* b = + new SelectTopVolumeBranchesBlock(localBlockIndex, globalBlockId); + + branch_top_volume_master.add(globalBlockId, b, new vtkmdiy::Link()); + assigner.set_rank(rank, globalBlockId); + } + + // Log time to copy the data to the block data objects + timingsStream << " " << std::setw(60) << std::left << "Initialize Branch Selection Data" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + // Set up DIY for binary reduction + RegularDecomposer::BoolVector shareFace(3, true); + RegularDecomposer::BoolVector wrap(3, false); + RegularDecomposer::CoordinateVector ghosts(3, 1); + RegularDecomposer decomposer(numDims, + diyBounds, + static_cast(globalNumberOfBlocks), + shareFace, + wrap, + ghosts, + diyDivisions); + + timingsStream << " " << std::setw(60) << std::left + << "Create DIY Decomposer and Assigner (Branch Decomposition)" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + // Fix the vtkmdiy links. + vtkmdiy::fix_links(branch_top_volume_master, assigner); + + timingsStream << " " << std::setw(60) << std::left << "Fix DIY Links (Branch Selection)" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + // partners for merge over regular block grid + vtkmdiy::RegularSwapPartners partners( + decomposer, // domain decomposition + 2, // radix of k-ary reduction. + true // contiguous: true=distance doubling, false=distance halving + ); + + timingsStream << " " << std::setw(60) << std::left + << "Create DIY Swap Partners (Branch Selection)" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + // compute the branch volume, and select the top branch by volume locally + branch_top_volume_master.foreach ( + [&](SelectTopVolumeBranchesBlock* b, const vtkmdiy::Master::ProxyWithLink&) { + using vtkm::worklet::contourtree_augmented::IdArrayType; + const auto& globalSize = firstGlobalPointDimensions; + vtkm::Id totalVolume = globalSize[0] * globalSize[1] * globalSize[2]; + const vtkm::cont::DataSet& ds = input.GetPartition(b->LocalBlockNo); + + // compute the volume of branches + b->SortBranchByVolume(ds, totalVolume); + // select the top branch by volume + b->SelectLocalTopVolumeBranches(ds, this->GetSavedBranches()); + }); + + timingsStream << " " << std::setw(60) << std::left << "SelectBranchByVolume" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + // We apply block reduction to collect the top NumSavedBranches branches by volume + vtkmdiy::reduce(branch_top_volume_master, + assigner, + partners, + vtkm::filter::scalar_topology::internal::SelectTopVolumeBranchesFunctor( + this->NumSavedBranches, this->TimingsLogLevel)); + + timingsStream << " " << std::setw(60) << std::left << "SelectGlobalTopVolumeBranches" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + // before computing the hierarchy of selected branches, we exclude selected branches + // with volume <= presimplifyThreshold + branch_top_volume_master.foreach ( + [&](SelectTopVolumeBranchesBlock* b, const vtkmdiy::Master::ProxyWithLink&) { + this->SetSavedBranches(b->ExcludeTopVolumeBranchByThreshold(this->GetPresimplifyThreshold())); + }); + + // if we do not have any saved branches, + // case 1. didn't specify nBranches correctly, and/or + // case 2. over pre-simplified, + // then we terminate the function prematurely. + if (this->NumSavedBranches <= 0) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "No branch is remaining!\n" + "Check the presimplification level or the number of branches to save."); + std::vector emptyDataSets(input.GetNumberOfPartitions()); + return vtkm::cont::PartitionedDataSet{ emptyDataSets }; + } + + // we compute the hierarchy of selected branches adding the root branch for each block + branch_top_volume_master.foreach ( + [&](SelectTopVolumeBranchesBlock* b, const vtkmdiy::Master::ProxyWithLink&) { + const vtkm::cont::DataSet& ds = input.GetPartition(b->LocalBlockNo); + b->ComputeTopVolumeBranchHierarchy(ds); + }); + + timingsStream << " " << std::setw(60) << std::left << "ComputeTopVolumeBranchHierarchy" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + // We apply block reduction to update + // 1. the global branch hierarchy + // 2. the outer-most saddle isovalue on all parent branches + vtkmdiy::reduce( + branch_top_volume_master, + assigner, + partners, + vtkm::filter::scalar_topology::internal::UpdateParentBranchFunctor(this->TimingsLogLevel)); + + timingsStream << " " << std::setw(60) << std::left << "Update Parent Branch Information" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + timer.Start(); + + // The next step is to extract contours. + // However, we use a separate filter to do it. + // This is because we want to utilize the existing Contour filter in VTK-m, + // but the work is not trivial and need more discussion (e.g., implicit mesh triangulation) + + // Create output dataset + std::vector outputDataSets(input.GetNumberOfPartitions()); + // Copy input data set to output + // This will make the output dataset pretty large. + // Unfortunately, this step seems to be inevitable, + // because searching for the superarc of cells requires information of the contour tree + for (vtkm::Id ds_no = 0; ds_no < input.GetNumberOfPartitions(); ++ds_no) + { + outputDataSets[ds_no] = input.GetPartition(ds_no); + } + + // we need to send everything that contour extraction needs to the output dataset + branch_top_volume_master.foreach ([&](SelectTopVolumeBranchesBlock* b, + const vtkmdiy::Master::ProxyWithLink&) { + vtkm::cont::Field BranchVolumeField( + "BranchVolume", vtkm::cont::Field::Association::WholeDataSet, b->TopVolumeData.BranchVolume); + outputDataSets[b->LocalBlockNo].AddField(BranchVolumeField); + vtkm::cont::Field BranchSaddleEpsilonField("BranchSaddleEpsilon", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.BranchSaddleEpsilon); + outputDataSets[b->LocalBlockNo].AddField(BranchSaddleEpsilonField); + vtkm::cont::Field TopVolBranchUpperEndField("TopVolumeBranchUpperEnd", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.TopVolumeBranchUpperEndGRId); + outputDataSets[b->LocalBlockNo].AddField(TopVolBranchUpperEndField); + vtkm::cont::Field TopVolBranchLowerEndField("TopVolumeBranchLowerEnd", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.TopVolumeBranchLowerEndGRId); + outputDataSets[b->LocalBlockNo].AddField(TopVolBranchLowerEndField); + vtkm::cont::Field TopVolumeBranchGRIdsField("TopVolumeBranchGlobalRegularIds", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.TopVolumeBranchRootGRId); + outputDataSets[b->LocalBlockNo].AddField(TopVolumeBranchGRIdsField); + vtkm::cont::Field TopVolBranchVolumeField("TopVolumeBranchVolume", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.TopVolumeBranchVolume); + outputDataSets[b->LocalBlockNo].AddField(TopVolBranchVolumeField); + vtkm::cont::Field TopVolBranchSaddleEpsilonField("TopVolumeBranchSaddleEpsilon", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.TopVolumeBranchSaddleEpsilon); + outputDataSets[b->LocalBlockNo].AddField(TopVolBranchSaddleEpsilonField); + vtkm::cont::Field TopVolBranchSaddleIsoValueField( + "TopVolumeBranchSaddleIsoValue", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.TopVolumeBranchSaddleIsoValue); + outputDataSets[b->LocalBlockNo].AddField(TopVolBranchSaddleIsoValueField); + + // additional data for isosurface extraction. + // Most of them are intermediate arrays and should not be parts of the actual output. + // this->TopVolumeData.TopVolBranchKnownByBlockStencil + vtkm::cont::Field TopVolBranchKnownByBlockStencilField( + "TopVolumeBranchKnownByBlockStencil", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.TopVolBranchKnownByBlockStencil); + outputDataSets[b->LocalBlockNo].AddField(TopVolBranchKnownByBlockStencilField); + // this->TopVolumeData.TopVolBranchInfoActualIndex + vtkm::cont::Field TopVolBranchInfoActualIndexField( + "TopVolumeBranchInformationIndex", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.TopVolBranchInfoActualIndex); + outputDataSets[b->LocalBlockNo].AddField(TopVolBranchInfoActualIndexField); + // this->TopVolumeData.IsParentBranch + vtkm::cont::Field IsParentBranchField("IsParentBranch", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.IsParentBranch); + outputDataSets[b->LocalBlockNo].AddField(IsParentBranchField); + // this->TopVolumeData.ExtraMaximaBranchLowerEnd + vtkm::cont::Field ExtraMaximaBranchLowerEndField("ExtraMaximaBranchLowerEnd", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.ExtraMaximaBranchLowerEnd); + outputDataSets[b->LocalBlockNo].AddField(ExtraMaximaBranchLowerEndField); + // this->TopVolumeData.ExtraMaximaBranchUpperEnd + vtkm::cont::Field ExtraMaximaBranchUpperEndField("ExtraMaximaBranchUpperEnd", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.ExtraMaximaBranchUpperEnd); + outputDataSets[b->LocalBlockNo].AddField(ExtraMaximaBranchUpperEndField); + // this->TopVolumeData.ExtraMaximaBranchOrder + vtkm::cont::Field ExtraMaximaBranchOrderField("ExtraMaximaBranchOrder", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.ExtraMaximaBranchOrder); + outputDataSets[b->LocalBlockNo].AddField(ExtraMaximaBranchOrderField); + // this->TopVolumeData.ExtraMaximaBranchSaddleGRId + vtkm::cont::Field ExtraMaximaBranchSaddleGRIdField( + "ExtraMaximaBranchSaddleGRId", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.ExtraMaximaBranchSaddleGRId); + outputDataSets[b->LocalBlockNo].AddField(ExtraMaximaBranchSaddleGRIdField); + // this->TopVolumeData.ExtraMaximaBranchIsoValue + vtkm::cont::Field ExtraMaximaBranchIsoValueField("ExtraMaximaBranchIsoValue", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.ExtraMaximaBranchIsoValue); + outputDataSets[b->LocalBlockNo].AddField(ExtraMaximaBranchIsoValueField); + // this->TopVolumeData.ExtraMinimaBranchLowerEnd + vtkm::cont::Field ExtraMinimaBranchLowerEndField("ExtraMinimaBranchLowerEnd", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.ExtraMinimaBranchLowerEnd); + outputDataSets[b->LocalBlockNo].AddField(ExtraMinimaBranchLowerEndField); + // this->TopVolumeData.ExtraMinimaBranchUpperEnd + vtkm::cont::Field ExtraMinimaBranchUpperEndField("ExtraMinimaBranchUpperEnd", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.ExtraMinimaBranchUpperEnd); + outputDataSets[b->LocalBlockNo].AddField(ExtraMinimaBranchUpperEndField); + // this->TopVolumeData.ExtraMinimaBranchOrder + vtkm::cont::Field ExtraMinimaBranchOrderField("ExtraMinimaBranchOrder", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.ExtraMinimaBranchOrder); + outputDataSets[b->LocalBlockNo].AddField(ExtraMinimaBranchOrderField); + // this->TopVolumeData.ExtraMinimaBranchSaddleGRId + vtkm::cont::Field ExtraMinimaBranchSaddleGRIdField( + "ExtraMinimaBranchSaddleGRId", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.ExtraMinimaBranchSaddleGRId); + outputDataSets[b->LocalBlockNo].AddField(ExtraMinimaBranchSaddleGRIdField); + // this->TopVolumeData.ExtraMinimaBranchIsoValue + vtkm::cont::Field ExtraMinimaBranchIsoValueField("ExtraMinimaBranchIsoValue", + vtkm::cont::Field::Association::WholeDataSet, + b->TopVolumeData.ExtraMinimaBranchIsoValue); + outputDataSets[b->LocalBlockNo].AddField(ExtraMinimaBranchIsoValueField); + }); + + timingsStream << " " << std::setw(38) << std::left << "Creating Branch Selection Output Data" + << ": " << timer.GetElapsedTime() << " seconds" << std::endl; + + VTKM_LOG_S(this->TimingsLogLevel, + std::endl + << "----------- DoExecutePartitions Timings ------------" << std::endl + << timingsStream.str()); + + return vtkm::cont::PartitionedDataSet{ outputDataSets }; +} + +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm diff --git a/vtkm/filter/scalar_topology/SelectTopVolumeBranchesFilter.h b/vtkm/filter/scalar_topology/SelectTopVolumeBranchesFilter.h new file mode 100644 index 0000000000..e0be7a21ee --- /dev/null +++ b/vtkm/filter/scalar_topology/SelectTopVolumeBranchesFilter.h @@ -0,0 +1,100 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= + +#ifndef vtk_m_filter_scalar_topology_SelectTopVolumeBranchesFilter_h +#define vtk_m_filter_scalar_topology_SelectTopVolumeBranchesFilter_h + +#include +#include + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ + +/// \brief Compute branch decompostion from distributed contour tree +class VTKM_FILTER_SCALAR_TOPOLOGY_EXPORT SelectTopVolumeBranchesFilter : public vtkm::filter::Filter +{ +public: + VTKM_CONT SelectTopVolumeBranchesFilter() = default; + + VTKM_CONT void SetSavedBranches(const vtkm::Id& numBranches) + { + this->NumSavedBranches = numBranches; + } + + VTKM_CONT void SetPresimplifyThreshold(const vtkm::Id& presimpThres) + { + this->PresimplifyThreshold = presimpThres; + } + + VTKM_CONT vtkm::Id GetSavedBranches() { return this->NumSavedBranches; } + VTKM_CONT vtkm::Id GetPresimplifyThreshold() { return this->PresimplifyThreshold; } + VTKM_CONT vtkm::cont::LogLevel GetTimingsLogLevel() { return this->TimingsLogLevel; } + +private: + VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet&) override; + VTKM_CONT vtkm::cont::PartitionedDataSet DoExecutePartitions( + const vtkm::cont::PartitionedDataSet& inData) override; + + vtkm::Id NumSavedBranches = 1; + vtkm::Id PresimplifyThreshold = 0; + vtkm::cont::ArrayHandle BranchVolume; + vtkm::cont::ArrayHandle BranchSaddleEpsilon; + vtkm::cont::ArrayHandle SortedBranchByVolume; + vtkm::cont::UnknownArrayHandle BranchSaddleIsoValue; + + // the parent branch for top volume branches + // we only care about the parent branch for top volume branches at the moment + // as a result, the index stored in this array follows the descending order of branch volumes + vtkm::cont::ArrayHandle TopVolBranchParent; + + /// Log level to be used for outputting timing information. Default is vtkm::cont::LogLevel::Perf + vtkm::cont::LogLevel TimingsLogLevel = vtkm::cont::LogLevel::Perf; +}; + +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx b/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx deleted file mode 100644 index f14a09e255..0000000000 --- a/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx +++ /dev/null @@ -1,205 +0,0 @@ -//============================================================================ -// 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 - - -// vtkm includes -#include - -// DIY includes -// clang-format off -VTKM_THIRDPARTY_PRE_INCLUDE -#include -#include -VTKM_THIRDPARTY_POST_INCLUDE -// clang-format on - -namespace vtkm -{ -namespace filter -{ -namespace scalar_topology -{ - -VTKM_CONT vtkm::cont::DataSet SelectTopVolumeContoursFilter::DoExecute(const vtkm::cont::DataSet&) -{ - throw vtkm::cont::ErrorFilterExecution( - "SelectTopVolumeContoursFilter expects PartitionedDataSet as input."); -} - -VTKM_CONT vtkm::cont::PartitionedDataSet SelectTopVolumeContoursFilter::DoExecutePartitions( - const vtkm::cont::PartitionedDataSet& input) -{ - auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator(); - int rank = comm.rank(); - int size = comm.size(); - - using SelectTopVolumeContoursBlock = - vtkm::filter::scalar_topology::internal::SelectTopVolumeContoursBlock; - vtkmdiy::Master branch_top_volume_master(comm, - 1, // Use 1 thread, VTK-M will do the treading - -1, // All blocks in memory - 0, // No create function - SelectTopVolumeContoursBlock::Destroy); - - auto firstDS = input.GetPartition(0); - vtkm::Id3 firstPointDimensions, firstGlobalPointDimensions, firstGlobalPointIndexStart; - firstDS.GetCellSet().CastAndCallForTypes( - vtkm::worklet::contourtree_augmented::GetLocalAndGlobalPointDimensions(), - firstPointDimensions, - firstGlobalPointDimensions, - firstGlobalPointIndexStart); - int numDims = firstGlobalPointDimensions[2] > 1 ? 3 : 2; - auto vtkmBlocksPerDimensionRP = input.GetPartition(0) - .GetField("vtkmBlocksPerDimension") - .GetData() - .AsArrayHandle>() - .ReadPortal(); - - int globalNumberOfBlocks = 1; - - for (vtkm::IdComponent d = 0; d < static_cast(numDims); ++d) - { - globalNumberOfBlocks *= static_cast(vtkmBlocksPerDimensionRP.Get(d)); - } - - vtkmdiy::DynamicAssigner assigner(comm, size, globalNumberOfBlocks); - for (vtkm::Id localBlockIndex = 0; localBlockIndex < input.GetNumberOfPartitions(); - ++localBlockIndex) - { - const vtkm::cont::DataSet& ds = input.GetPartition(localBlockIndex); - int globalBlockId = static_cast( - vtkm::cont::ArrayGetValue(0, - ds.GetField("vtkmGlobalBlockId") - .GetData() - .AsArrayHandle>())); - - SelectTopVolumeContoursBlock* b = - new SelectTopVolumeContoursBlock(localBlockIndex, globalBlockId); - - branch_top_volume_master.add(globalBlockId, b, new vtkmdiy::Link()); - assigner.set_rank(rank, globalBlockId); - } - - vtkmdiy::fix_links(branch_top_volume_master, assigner); - - branch_top_volume_master.foreach ( - [&](SelectTopVolumeContoursBlock* b, const vtkmdiy::Master::ProxyWithLink&) { - const auto& globalSize = firstGlobalPointDimensions; - vtkm::Id totalVolume = globalSize[0] * globalSize[1] * globalSize[2]; - const vtkm::cont::DataSet& ds = input.GetPartition(b->LocalBlockNo); - - b->SortBranchByVolume(ds, totalVolume); - - // copy the top volume branches into a smaller array - // we skip index 0 because it must be the main branch (which has the highest volume) - vtkm::Id nActualSavedBranches = - std::min(this->nSavedBranches, b->SortedBranchByVolume.GetNumberOfValues() - 1); - - vtkm::worklet::contourtree_augmented::IdArrayType topVolumeBranch; - vtkm::cont::Algorithm::CopySubRange( - b->SortedBranchByVolume, 1, nActualSavedBranches, topVolumeBranch); - - auto branchRootGRId = - ds.GetField("BranchRootGRId").GetData().AsArrayHandle>(); - - auto upperEndGRId = ds.GetField("UpperEndGlobalRegularIds") - .GetData() - .AsArrayHandle>(); - - auto lowerEndGRId = ds.GetField("LowerEndGlobalRegularIds") - .GetData() - .AsArrayHandle>(); - - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - branchRootGRId, topVolumeBranch, b->TopVolumeBranchRootGRId); - - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - branchRootGRId, topVolumeBranch, b->TopVolumeBranchRootGRId); - - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - b->BranchVolume, topVolumeBranch, b->TopVolumeBranchVolume); - - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - b->BranchSaddleEpsilon, topVolumeBranch, b->TopVolumeBranchSaddleEpsilon); - - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - upperEndGRId, topVolumeBranch, b->TopVolumeBranchUpperEndGRId); - - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - lowerEndGRId, topVolumeBranch, b->TopVolumeBranchLowerEndGRId); - - auto resolveArray = [&](const auto& inArray) { - using InArrayHandleType = std::decay_t; - InArrayHandleType topVolBranchSaddleIsoValue; - vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( - inArray, topVolumeBranch, topVolBranchSaddleIsoValue); - b->TopVolumeBranchSaddleIsoValue = topVolBranchSaddleIsoValue; - }; - - b->BranchSaddleIsoValue - .CastAndCallForTypes(resolveArray); - }); - - // We apply all-to-all broadcast to collect the top nSavedBranches branches by volume - vtkmdiy::all_to_all( - branch_top_volume_master, - assigner, - vtkm::filter::scalar_topology::internal::SelectTopVolumeContoursFunctor(this->nSavedBranches)); - - // For each block, we compute the get the extracted isosurface for every selected branch - // storing format: key (branch ID) - Value (list of meshes in the isosurface) - - std::vector outputDataSets(input.GetNumberOfPartitions()); - - branch_top_volume_master.foreach ( - [&](SelectTopVolumeContoursBlock* b, const vtkmdiy::Master::ProxyWithLink&) { - vtkm::cont::Field TopVolBranchUpperEndField("TopVolumeBranchUpperEnd", - vtkm::cont::Field::Association::WholeDataSet, - b->TopVolumeBranchUpperEndGRId); - outputDataSets[b->LocalBlockNo].AddField(TopVolBranchUpperEndField); - vtkm::cont::Field TopVolBranchLowerEndField("TopVolumeBranchLowerEnd", - vtkm::cont::Field::Association::WholeDataSet, - b->TopVolumeBranchLowerEndGRId); - outputDataSets[b->LocalBlockNo].AddField(TopVolBranchLowerEndField); - vtkm::cont::Field TopVolBranchGRIdField("TopVolumeBranchGlobalRegularIds", - vtkm::cont::Field::Association::WholeDataSet, - b->TopVolumeBranchRootGRId); - outputDataSets[b->LocalBlockNo].AddField(TopVolBranchGRIdField); - vtkm::cont::Field TopVolBranchVolumeField("TopVolumeBranchVolume", - vtkm::cont::Field::Association::WholeDataSet, - b->TopVolumeBranchVolume); - outputDataSets[b->LocalBlockNo].AddField(TopVolBranchVolumeField); - vtkm::cont::Field TopVolBranchSaddleEpsilonField("TopVolumeBranchSaddleEpsilon", - vtkm::cont::Field::Association::WholeDataSet, - b->TopVolumeBranchSaddleEpsilon); - outputDataSets[b->LocalBlockNo].AddField(TopVolBranchSaddleEpsilonField); - - auto resolveArray = [&](const auto& inArray) { - vtkm::cont::Field TopVolBranchSaddleIsoValueField( - "TopVolumeBranchSaddleIsoValue", vtkm::cont::Field::Association::WholeDataSet, inArray); - outputDataSets[b->LocalBlockNo].AddField(TopVolBranchSaddleIsoValueField); - }; - b->TopVolumeBranchSaddleIsoValue - .CastAndCallForTypes(resolveArray); - }); - - return vtkm::cont::PartitionedDataSet{ outputDataSets }; -} - -} // namespace scalar_topology -} // namespace filter -} // namespace vtkm diff --git a/vtkm/filter/scalar_topology/internal/CMakeLists.txt b/vtkm/filter/scalar_topology/internal/CMakeLists.txt index 3280d72b8a..7cc8d3e720 100644 --- a/vtkm/filter/scalar_topology/internal/CMakeLists.txt +++ b/vtkm/filter/scalar_topology/internal/CMakeLists.txt @@ -10,10 +10,12 @@ set(headers BranchDecompositionBlock.h - SelectTopVolumeContoursBlock.h + SelectTopVolumeBranchesBlock.h + ExtractTopVolumeContoursBlock.h ComputeBlockIndices.h ComputeDistributedBranchDecompositionFunctor.h - SelectTopVolumeContoursFunctor.h + UpdateParentBranchFunctor.h + SelectTopVolumeBranchesFunctor.h ExchangeBranchEndsFunctor.h ) #----------------------------------------------------------------------------- diff --git a/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.cxx b/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.cxx index 3223beb397..45a2779979 100644 --- a/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.cxx +++ b/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.cxx @@ -75,7 +75,7 @@ void ComputeDistributedBranchDecompositionFunctor::operator()( ) const { // Get our rank and DIY id - //const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank(); + const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank(); const auto selfid = rp.gid(); // Aliases to reduce verbosity @@ -110,6 +110,20 @@ void ComputeDistributedBranchDecompositionFunctor::operator()( vtkm::cont::ArrayHandle incomingBestDownSupernode; rp.dequeue(ingid, incomingBestDownSupernode); + std::stringstream dataSizeStream; + // Log the amount of exchanged data + dataSizeStream << " " << std::setw(38) << std::left << "Incoming data size" + << ": " << incomingBestUpSupernode.GetNumberOfValues() << std::endl; + + VTKM_LOG_S(this->TimingsLogLevel, + std::endl + << " ---------------- Compute Branch Decomposition Step ---------------------" + << std::endl + << " Rank : " << rank << std::endl + << " DIY Id : " << selfid << std::endl + << " Inc Id : " << ingid << std::endl + << dataSizeStream.str()); + vtkm::Id prefixLength = b->FirstSupernodePerIteration.ReadPortal().Get(rp.round() - 1)[0]; #ifdef DEBUG_PRINT diff --git a/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.h b/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.h index c9a51ea1d5..6061b1b1a7 100644 --- a/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.h +++ b/vtkm/filter/scalar_topology/internal/ComputeDistributedBranchDecompositionFunctor.h @@ -73,10 +73,17 @@ namespace internal struct ComputeDistributedBranchDecompositionFunctor { + ComputeDistributedBranchDecompositionFunctor(const vtkm::cont::LogLevel& timingsLogLevel) + : TimingsLogLevel(timingsLogLevel) + { + } + void operator()(BranchDecompositionBlock* b, const vtkmdiy::ReduceProxy& rp, // communication proxy const vtkmdiy::RegularSwapPartners& // partners of the current block (unused) ) const; + + const vtkm::cont::LogLevel TimingsLogLevel; }; } // namespace internal diff --git a/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.cxx b/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.cxx index d0b95f1092..2a5b409ce4 100644 --- a/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.cxx +++ b/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.cxx @@ -54,6 +54,7 @@ #include #include +#include #ifdef DEBUG_PRINT #define DEBUG_PRINT_COMBINED_BLOCK_IDS @@ -75,6 +76,7 @@ void ExchangeBranchEndsFunctor::operator()( ) const { // Get our rank and DIY id + const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank(); const auto selfid = rp.gid(); // Aliases to reduce verbosity @@ -93,6 +95,7 @@ void ExchangeBranchEndsFunctor::operator()( // Otherwise, we may need to process more than one incoming block if (ingid != selfid) { + #ifdef DEBUG_PRINT_COMBINED_BLOCK_IDS int incomingGlobalBlockId; rp.dequeue(ingid, incomingGlobalBlockId); @@ -126,6 +129,20 @@ void ExchangeBranchEndsFunctor::operator()( IdArrayType incomingLowerEndDependentVolume; rp.dequeue(ingid, incomingLowerEndDependentVolume); + std::stringstream dataSizeStream; + // Log the amount of exchanged data + dataSizeStream << " " << std::setw(38) << std::left << "Incoming branch size" + << ": " << incomingBranchRootGRId.GetNumberOfValues() << std::endl; + + VTKM_LOG_S(this->TimingsLogLevel, + std::endl + << " ---------------- Exchange Branch Ends Step ---------------------" + << std::endl + << " Rank : " << rank << std::endl + << " DIY Id : " << selfid << std::endl + << " Inc Id : " << ingid << std::endl + << dataSizeStream.str()); + /// Superarc and Branch IDs are given based on the hierarchical level /// Shared branches should lie on the smaller ID side of the branch array consecutively /// We filter out shared branches first @@ -307,7 +324,8 @@ void ExchangeBranchEndsFunctor::operator()( { #ifdef DEBUG_PRINT_COMBINED_BLOCK_IDS rp.enqueue(target, b->GlobalBlockId); -#endif +#endif // DEBUG_PRINT_COMBINED_BLOCK_IDS + rp.enqueue(target, branchDecomposer.BranchRootGRId); rp.enqueue(target, branchDecomposer.UpperEndGRId); rp.enqueue(target, branchDecomposer.LowerEndGRId); diff --git a/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.h b/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.h index 9eace991af..f5165dc50f 100644 --- a/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.h +++ b/vtkm/filter/scalar_topology/internal/ExchangeBranchEndsFunctor.h @@ -73,11 +73,19 @@ namespace internal struct ExchangeBranchEndsFunctor { + ExchangeBranchEndsFunctor(vtkm::cont::LogLevel timingsLogLevel = vtkm::cont::LogLevel::Perf) + : TimingsLogLevel(timingsLogLevel) + { + } + VTKM_CONT void operator()( BranchDecompositionBlock* b, const vtkmdiy::ReduceProxy& rp, // communication proxy const vtkmdiy::RegularSwapPartners& // partners of the current block (unused) ) const; + +private: + vtkm::cont::LogLevel TimingsLogLevel; }; } // namespace internal diff --git a/vtkm/filter/scalar_topology/internal/ExtractTopVolumeContoursBlock.cxx b/vtkm/filter/scalar_topology/internal/ExtractTopVolumeContoursBlock.cxx new file mode 100644 index 0000000000..caa0f96ea5 --- /dev/null +++ b/vtkm/filter/scalar_topology/internal/ExtractTopVolumeContoursBlock.cxx @@ -0,0 +1,843 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG_PRINT +#include +#endif + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ +namespace internal +{ + +ExtractTopVolumeContoursBlock::ExtractTopVolumeContoursBlock(vtkm::Id localBlockNo, + int globalBlockId) + : LocalBlockNo(localBlockNo) + , GlobalBlockId(globalBlockId) +{ +} + +void ExtractTopVolumeContoursBlock::ExtractIsosurfaceOnSelectedBranch( + const vtkm::cont::DataSet& dataSet, + const bool isMarchingCubes, + const bool shiftIsovalueByEpsilon, + const vtkm::cont::LogLevel timingsLogLevel) +{ + using vtkm::worklet::contourtree_augmented::IdArrayType; + + this->TopVolumeData.TopVolumeBranchRootGRId = + dataSet.GetField("TopVolumeBranchGlobalRegularIds").GetData().AsArrayHandle(); + // if no branch to extract from + if (this->TopVolumeData.TopVolumeBranchRootGRId.GetNumberOfValues() < 1) + return; + + // branch root global regular ID + // size: nBranches + // usage: identifier of the branch + vtkm::cont::Algorithm::Copy( + dataSet.GetField("BranchRootGRId").GetData().AsArrayHandle(), + this->TopVolumeData.BranchRootGRId); + + // branch local upper end and lower end + // size: nBranches + // usage: search for the superarc of an arbitrary point (not necessarily on grid) + auto upperEndLocalIds = + dataSet.GetField("UpperEndLocalIds").GetData().AsArrayHandle(); + auto lowerEndLocalIds = + dataSet.GetField("LowerEndLocalIds").GetData().AsArrayHandle(); + + // global regular ids + auto globalRegularIds = + dataSet.GetField("RegularNodeGlobalIds").GetData().AsArrayHandle(); + + // Extracting the mesh id information. + // Because most data arrays include nodes in other blocks, + // we need reference to the mesh id of nodes that are actually inside the block. + vtkm::Id3 globalPointDimensions; + vtkm::Id3 pointDimensions, globalPointIndexStart; + + dataSet.GetCellSet().CastAndCallForTypes( + vtkm::worklet::contourtree_augmented::GetLocalAndGlobalPointDimensions(), + pointDimensions, + globalPointDimensions, + globalPointIndexStart); + +#ifdef DEBUG_PRINT + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Block size info"); + { + std::stringstream rs; + rs << "globalPointDimensions: " << globalPointDimensions << std::endl; + rs << "pointDimensions: " << pointDimensions << std::endl; + rs << "globalPointIndexStart: " << globalPointIndexStart << std::endl; + rs << "globalRegularIDs: " << globalRegularIds.GetNumberOfValues() << std::endl; + // ds.PrintSummary(rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + + // Tool to relabel local mesh ids to global ids + auto localToGlobalIdRelabeler = vtkm::worklet::contourtree_augmented::mesh_dem::IdRelabeler( + globalPointIndexStart, pointDimensions, globalPointDimensions); + IdArrayType globalIdsByMesh; + + // Note: the cell set is different from the mesh structure. + // Here, we assume that the cell set is structured grid. + // A more general way to do this is to use CellSet().GetCellPointIds(i) + // to extract all the local ids and keep unique ones + + // local ids in the mesh + IdArrayType localIdsByMesh; + vtkm::cont::Algorithm::Copy( + vtkm::cont::ArrayHandleIndex(pointDimensions[0] * pointDimensions[1] * pointDimensions[2]), + localIdsByMesh); + // then, we transform the local ids to global ids + auto localTransformToGlobalId = + vtkm::cont::make_ArrayHandleTransform(localIdsByMesh, localToGlobalIdRelabeler); + vtkm::cont::ArrayCopyDevice(localTransformToGlobalId, globalIdsByMesh); + + // detect whether the element in globalRegularIds are in the block + // globalIdsDiscard is just a filler for the worklet format. We do not use it. + // The last slot for the worklet is useful in a later step. + // Here we just reuse the worklet + IdArrayType globalIdsWithinBlockStencil; + vtkm::cont::ArrayHandleDiscard globalIdsDiscard; + + vtkm::cont::Invoker invoke; + // stencil is 1 if the global regular id is within the block, 0 otherwise + // TODO/FIXME: A way to do binary search using built-in algorithms is LowerBound+UpperBound -> Check if identical + // Not sure if that is faster. + auto binarySearchWorklet = + vtkm::worklet::scalar_topology::select_top_volume_branches::BinarySearchWorklet(); + invoke(binarySearchWorklet, + globalRegularIds, + globalIdsByMesh, + globalIdsWithinBlockStencil, + globalIdsDiscard); + + this->TopVolumeData.TopVolumeBranchSaddleIsoValue = + dataSet.GetField("TopVolumeBranchSaddleIsoValue").GetData(); + + auto resolveArray = [&](const auto& inArray) { + using InArrayHandleType = std::decay_t; + using ValueType = typename InArrayHandleType::ValueType; + + // we need to sort all values based on the global ids + // and remove values of points that do not belong to the local block + auto dataValues = dataSet.GetField("DataValues").GetData().AsArrayHandle(); + + IdArrayType globalIdsWithinBlock; + IdArrayType localIdsWithinBlock; + InArrayHandleType dataValuesWithinBlock; + + // filter global regular ids, array ids, and data values + vtkm::cont::Algorithm::CopyIf( + globalRegularIds, globalIdsWithinBlockStencil, globalIdsWithinBlock); + vtkm::cont::Algorithm::CopyIf( + vtkm::cont::ArrayHandleIndex(globalRegularIds.GetNumberOfValues()), + globalIdsWithinBlockStencil, + localIdsWithinBlock); + vtkm::cont::Algorithm::CopyIf(dataValues, globalIdsWithinBlockStencil, dataValuesWithinBlock); + + // sorted index based on global regular ids + IdArrayType sortedGlobalIds; + vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleIndex(globalIdsByMesh.GetNumberOfValues()), + sortedGlobalIds); + vtkm::cont::Algorithm::SortByKey(globalIdsWithinBlock, sortedGlobalIds); + + // globalIdsWithinBlock (sorted) and globalIdsByMesh should be identical. + // computing globalIdsWithinBlock ensures the input data is correct + bool identical = + globalIdsWithinBlock.GetNumberOfValues() == globalIdsByMesh.GetNumberOfValues(); + if (identical) + { + vtkm::cont::ArrayHandle globalIdsIdentical; + vtkm::cont::Algorithm::Transform( + globalIdsWithinBlock, globalIdsByMesh, globalIdsIdentical, vtkm::Equal()); + identical = vtkm::cont::Algorithm::Reduce(globalIdsIdentical, true, vtkm::LogicalAnd()); + } + if (!identical) + { + vtkm::worklet::contourtree_augmented::PrintHeader(globalIdsByMesh.GetNumberOfValues()); + vtkm::worklet::contourtree_augmented::PrintIndices("globalIdsByMesh", globalIdsByMesh); + vtkm::worklet::contourtree_augmented::PrintHeader(globalIdsWithinBlock.GetNumberOfValues()); + vtkm::worklet::contourtree_augmented::PrintIndices("globalIdsWithinBlock", + globalIdsWithinBlock); + } + VTKM_ASSERT(identical); + + // filtered and sorted local node info ids + // i.e. index of global regular ids, data values, and superparents + // Note: This is not local mesh id! Make sure to distinguish them + IdArrayType sortedLocalNodeInfoIdsWithinBlock; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + localIdsWithinBlock, sortedGlobalIds, sortedLocalNodeInfoIdsWithinBlock); + + // sorted data values + // for simulation of simplicity, we also need sorted global regular IDs in globalIdsWithinBlock + InArrayHandleType sortedDataValuesWithinBlock; + vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( + dataValuesWithinBlock, sortedGlobalIds, sortedDataValuesWithinBlock); + + // create an execution object to find the superarc for an arbitrary point within the mesh + // all information below are required to initialize the execution object + auto superparents = dataSet.GetField("Superparents").GetData().AsArrayHandle(); + auto supernodes = dataSet.GetField("Supernodes").GetData().AsArrayHandle(); + auto superarcs = dataSet.GetField("Superarcs").GetData().AsArrayHandle(); + auto superchildren = dataSet.GetField("Superchildren").GetData().AsArrayHandle(); + auto whichRound = dataSet.GetField("WhichRound").GetData().AsArrayHandle(); + auto whichIteration = dataSet.GetField("WhichIteration").GetData().AsArrayHandle(); + auto hyperparents = dataSet.GetField("Hyperparents").GetData().AsArrayHandle(); + auto hypernodes = dataSet.GetField("Hypernodes").GetData().AsArrayHandle(); + auto hyperarcs = dataSet.GetField("Hyperarcs").GetData().AsArrayHandle(); + + // filtered + sorted superparents of nodes + IdArrayType superparentsWithinBlock; + vtkm::cont::Algorithm::CopyIf( + superparents, globalIdsWithinBlockStencil, superparentsWithinBlock); + IdArrayType sortedSuperparentsWithinBlock; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + superparentsWithinBlock, sortedGlobalIds, sortedSuperparentsWithinBlock); + + // initialize the exec object + // note: terms should include the contour tree as much as possible + // should pass the full arrays to the object instead of the filtered ones + auto findSuperarcForNode = + vtkm::worklet::contourtree_distributed::FindSuperArcForUnknownNode( + superparents, + supernodes, + superarcs, + superchildren, + whichRound, + whichIteration, + hyperparents, + hypernodes, + hyperarcs, + globalRegularIds, + dataValues); + + // let's check which branches are known by the block + // We check the branchGRId of top volume branches to see whether there are matches within the block + vtkm::Id nIsoValues = inArray.GetNumberOfValues(); + vtkm::Id totalNumPoints = + globalPointDimensions[0] * globalPointDimensions[1] * globalPointDimensions[2]; + + // dropping out top-volume branches that are not known by the block + this->TopVolumeData.TopVolBranchKnownByBlockStencil = + dataSet.GetField("TopVolumeBranchKnownByBlockStencil").GetData().AsArrayHandle(); + // index of top-volume branches within the block among all top-volume branches + IdArrayType topVolBranchWithinBlockId; + vtkm::cont::Algorithm::CopyIf(vtkm::cont::ArrayHandleIndex(nIsoValues), + this->TopVolumeData.TopVolBranchKnownByBlockStencil, + topVolBranchWithinBlockId); + auto topVolBranchWithinBlockIdPortal = topVolBranchWithinBlockId.ReadPortal(); + + vtkm::Id nTopVolBranchWithinBlock = topVolBranchWithinBlockId.GetNumberOfValues(); + + // filtered branch saddle values + InArrayHandleType isoValues; + vtkm::cont::Algorithm::CopyIf( + inArray, this->TopVolumeData.TopVolBranchKnownByBlockStencil, isoValues); + auto isoValuePortal = isoValues.ReadPortal(); + + this->TopVolumeData.TopVolumeBranchSaddleEpsilon = + dataSet.GetField("TopVolumeBranchSaddleEpsilon").GetData().AsArrayHandle(); + // filtered branch saddle epsilons + IdArrayType topVolBranchSaddleEpsilons; + vtkm::cont::Algorithm::CopyIf(this->TopVolumeData.TopVolumeBranchSaddleEpsilon, + this->TopVolumeData.TopVolBranchKnownByBlockStencil, + topVolBranchSaddleEpsilons); + auto topVolBranchSaddleEpsilonPortal = topVolBranchSaddleEpsilons.ReadPortal(); + + this->TopVolumeData.TopVolBranchInfoActualIndex = + dataSet.GetField("TopVolumeBranchInformationIndex").GetData().AsArrayHandle(); + this->TopVolumeData.IsParentBranch = + dataSet.GetField("IsParentBranch").GetData().AsArrayHandle>(); + // for each top-vol branch in the block + // we get their upper end and lower end local ids + IdArrayType topVolLocalBranchUpperEnd; + IdArrayType topVolLocalBranchLowerEnd; + vtkm::cont::ArrayHandle topVolIsParent; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + upperEndLocalIds, this->TopVolumeData.TopVolBranchInfoActualIndex, topVolLocalBranchUpperEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + lowerEndLocalIds, this->TopVolumeData.TopVolBranchInfoActualIndex, topVolLocalBranchLowerEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex>( + this->TopVolumeData.IsParentBranch, + this->TopVolumeData.TopVolBranchInfoActualIndex, + topVolIsParent); + auto topVolIsParentPortal = topVolIsParent.ReadPortal(); + + // We compute the global regular IDs of top-vol branch saddles. + // We extract the contour right above/below the saddle. + IdArrayType topVolLocalBranchSaddleGRId; + { + IdArrayType topVolLocalBranchSaddle; + vtkm::cont::Algorithm::Copy(topVolLocalBranchUpperEnd, topVolLocalBranchSaddle); + invoke(vtkm::worklet::scalar_topology::select_top_volume_branches::AssignValueByPositivity{}, + topVolBranchSaddleEpsilons, + topVolLocalBranchLowerEnd, + topVolLocalBranchSaddle); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + globalRegularIds, topVolLocalBranchSaddle, topVolLocalBranchSaddleGRId); + } + + // We compute the superarc of the branch within the block + // around the given isovalue + IdArrayType branchIsoSuperarcs; + branchIsoSuperarcs.Allocate(nTopVolBranchWithinBlock); + + vtkm::worklet::scalar_topology::extract_top_volume_contours::GetSuperarcByIsoValueWorklet + branchIsoSuperarcWorklet(totalNumPoints, shiftIsovalueByEpsilon); + invoke(branchIsoSuperarcWorklet, + topVolLocalBranchUpperEnd, + topVolLocalBranchLowerEnd, + isoValues, + topVolLocalBranchSaddleGRId, + topVolBranchSaddleEpsilons, + branchIsoSuperarcs, + findSuperarcForNode); + auto branchIsoSuperarcsPortal = branchIsoSuperarcs.ReadPortal(); + +#ifdef DEBUG_PRINT + std::stringstream branchStream; + + branchStream << "Debug for branch info, Block " << this->LocalBlockNo << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(sortedBranchGRId.GetNumberOfValues(), + branchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Raw Branch GR", this->TopVolumeData.BranchRootGRId, -1, branchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Raw Upper End", upperLocalEndIds, -1, branchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Raw Lower End", lowerLocalEndIds, -1, branchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Sorted Branch GR", sortedBranchGRId, -1, branchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Sorted Branch Id", sortedBranchOrder, -1, branchStream); + + vtkm::worklet::contourtree_augmented::PrintHeader(nIsoValues, branchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Top Branch GR", this->TopVolumeData.TopVolumeBranchRootGRId, -1, branchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Top Branch Stencil", topVolBranchWithinBlockStencil, -1, branchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Top Branch Idx", topVolBranchInfoIdx, -1, branchStream); + + vtkm::worklet::contourtree_augmented::PrintHeader(nTopVolBranchKnownByBlock, branchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Sorted Upper End", topVolBranchUpperLocalEnd, -1, branchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Sorted Lower End", topVolBranchLowerLocalEnd, -1, branchStream); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, branchStream.str()); +#endif + this->TopVolumeData.ExtraMaximaBranchLowerEnd = + dataSet.GetField("ExtraMaximaBranchLowerEnd").GetData().AsArrayHandle(); + this->TopVolumeData.ExtraMinimaBranchLowerEnd = + dataSet.GetField("ExtraMinimaBranchLowerEnd").GetData().AsArrayHandle(); + this->TopVolumeData.ExtraMaximaBranchUpperEnd = + dataSet.GetField("ExtraMaximaBranchUpperEnd").GetData().AsArrayHandle(); + this->TopVolumeData.ExtraMinimaBranchUpperEnd = + dataSet.GetField("ExtraMinimaBranchUpperEnd").GetData().AsArrayHandle(); + this->TopVolumeData.ExtraMaximaBranchOrder = + dataSet.GetField("ExtraMaximaBranchOrder").GetData().AsArrayHandle(); + this->TopVolumeData.ExtraMinimaBranchOrder = + dataSet.GetField("ExtraMinimaBranchOrder").GetData().AsArrayHandle(); + + const vtkm::Id nExtraMaximaBranch = + this->TopVolumeData.ExtraMaximaBranchLowerEnd.GetNumberOfValues(); + const vtkm::Id nExtraMinimaBranch = + this->TopVolumeData.ExtraMinimaBranchLowerEnd.GetNumberOfValues(); + InArrayHandleType extraMaximaBranchIsoValue; + InArrayHandleType extraMinimaBranchIsoValue; + + IdArrayType extraMaximaBranchSuperarcs; + IdArrayType extraMinimaBranchSuperarcs; + extraMaximaBranchSuperarcs.Allocate(nExtraMaximaBranch); + extraMinimaBranchSuperarcs.Allocate(nExtraMinimaBranch); + + if (nExtraMaximaBranch) + { + extraMaximaBranchIsoValue = + dataSet.GetField("ExtraMaximaBranchIsoValue").GetData().AsArrayHandle(); + this->TopVolumeData.ExtraMaximaBranchSaddleGRId = + dataSet.GetField("ExtraMaximaBranchSaddleGRId").GetData().AsArrayHandle(); + + invoke(branchIsoSuperarcWorklet, + this->TopVolumeData.ExtraMaximaBranchUpperEnd, + this->TopVolumeData.ExtraMaximaBranchLowerEnd, + extraMaximaBranchIsoValue, + this->TopVolumeData.ExtraMaximaBranchSaddleGRId, + vtkm::cont::ArrayHandleConstant(1, nExtraMaximaBranch), + extraMaximaBranchSuperarcs, + findSuperarcForNode); + +#ifdef DEBUG_PRINT + std::stringstream extraMaxStream; + extraMaxStream << "Debug for Extra Maxima Branch, Block " << this->LocalBlockNo << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(nExtraMaximaBranch, extraMaxStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Max Branch Upper End", this->TopVolumeData.ExtraMaximaBranchUpperEnd, -1, extraMaxStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Max Branch Lower End", this->TopVolumeData.ExtraMaximaBranchLowerEnd, -1, extraMaxStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "Max Branch IsoValue", extraMaximaBranchIsoValue, -1, extraMaxStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Max Branch Superarc", extraMaximaBranchSuperarcs, -1, extraMaxStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Max Branch Order", this->TopVolumeData.ExtraMaximaBranchOrder, -1, extraMaxStream); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, extraMaxStream.str()); +#endif // DEBUG_PRINT + } + + if (nExtraMinimaBranch) + { + extraMinimaBranchIsoValue = + dataSet.GetField("ExtraMinimaBranchIsoValue").GetData().AsArrayHandle(); + this->TopVolumeData.ExtraMinimaBranchSaddleGRId = + dataSet.GetField("ExtraMinimaBranchSaddleGRId").GetData().AsArrayHandle(); + + invoke(branchIsoSuperarcWorklet, + this->TopVolumeData.ExtraMinimaBranchUpperEnd, + this->TopVolumeData.ExtraMinimaBranchLowerEnd, + extraMinimaBranchIsoValue, + this->TopVolumeData.ExtraMinimaBranchSaddleGRId, + vtkm::cont::ArrayHandleConstant(-1, nExtraMinimaBranch), + extraMinimaBranchSuperarcs, + findSuperarcForNode); + +#ifdef DEBUG_PRINT + std::stringstream extraMinStream; + extraMaxStream << "Debug for Extra Maxima Branch, Block " << this->LocalBlockNo << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(nExtraMinimaBranch, extraMinStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Max Branch Upper End", this->TopVolumeData.ExtraMinimaBranchUpperEnd, -1, extraMinStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Max Branch Lower End", this->TopVolumeData.ExtraMinimaBranchLowerEnd, -1, extraMinStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "Max Branch IsoValue", extraMinimaBranchIsoValue, -1, extraMinStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Max Branch Superarc", extraMinimaBranchSuperarcs, -1, extraMinStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Max Branch Order", this->TopVolumeData.ExtraMinimaBranchOrder, -1, extraMinStream); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, extraMinStream.str()); +#endif // DEBUG_PRINT + } + + auto extraMaximaBranchSuperarcPortal = extraMaximaBranchSuperarcs.ReadPortal(); + auto extraMinimaBranchSuperarcPortal = extraMinimaBranchSuperarcs.ReadPortal(); + auto extraMaximaBranchIsoValuePortal = extraMaximaBranchIsoValue.ReadPortal(); + auto extraMinimaBranchIsoValuePortal = extraMinimaBranchIsoValue.ReadPortal(); + auto extraMaximaBranchOrderPortal = this->TopVolumeData.ExtraMaximaBranchOrder.ReadPortal(); + auto extraMinimaBranchOrderPortal = this->TopVolumeData.ExtraMinimaBranchOrder.ReadPortal(); + + // Update 01/09/2025 + // Adding the branch saddle global regular ID portals for simulation of simplicity when + // computing the cell polarity cases and draw contour lines. + auto topVolLocalBranchSaddleGRIdPortal = topVolLocalBranchSaddleGRId.ReadPortal(); + auto extraMaximaBranchSaddleGRIdPortal = + this->TopVolumeData.ExtraMaximaBranchSaddleGRId.ReadPortal(); + auto extraMinimaBranchSaddleGRIdPortal = + this->TopVolumeData.ExtraMinimaBranchSaddleGRId.ReadPortal(); + + const vtkm::Id nContours = nTopVolBranchWithinBlock + nExtraMaximaBranch + nExtraMinimaBranch; + this->IsosurfaceEdgesOffset.AllocateAndFill(nContours, 0); + this->IsosurfaceEdgesLabels.AllocateAndFill(nContours, 0); + this->IsosurfaceEdgesOrders.AllocateAndFill(nContours, 0); + this->IsosurfaceGRIds.AllocateAndFill(nContours, 0); + InArrayHandleType isosurfaceIsoValue; + isosurfaceIsoValue.AllocateAndFill(nContours, static_cast(0)); + auto edgeOffsetWritePortal = this->IsosurfaceEdgesOffset.WritePortal(); + auto edgeLabelWritePortal = this->IsosurfaceEdgesLabels.WritePortal(); + auto edgeOrderWritePortal = this->IsosurfaceEdgesOrders.WritePortal(); + auto globalRegularIdsWritePortal = this->IsosurfaceGRIds.WritePortal(); + auto isosurfaceValuePortal = isosurfaceIsoValue.WritePortal(); + + vtkm::Id nContourCandidateMeshes = 0; + // NOTE: nContours denotes the number of isosurfaces for visualization. + // The number is usually small, so linear loop is not too costly. + // NOTE update 06/16/2024: We always need the isovalue of the contour. + // As a result, we iterate through nContours (=O(k)). + // This may be parallelizable in future work + for (vtkm::Id branchIdx = 0; branchIdx < nContours; branchIdx++) + { + ValueType isoValue; + vtkm::Id currBranchSaddleEpsilon, branchSuperarc, branchOrder, branchSaddleGRId, + branchLabel = 0; + + using vtkm::worklet::scalar_topology::extract_top_volume_contours::BRANCH_SADDLE; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::BRANCH_COVER; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::MAXIMA_CONTOUR; + + if (branchIdx < nTopVolBranchWithinBlock) + { + isoValue = isoValuePortal.Get(branchIdx); + currBranchSaddleEpsilon = topVolBranchSaddleEpsilonPortal.Get(branchIdx); + branchSuperarc = branchIsoSuperarcsPortal.Get(branchIdx); + branchSaddleGRId = topVolLocalBranchSaddleGRIdPortal.Get(branchIdx); + branchOrder = topVolBranchWithinBlockIdPortal.Get(branchIdx) + 1; + branchLabel |= BRANCH_SADDLE; + branchLabel |= topVolIsParentPortal.Get(branchIdx) ? BRANCH_COVER : 0; + branchLabel |= currBranchSaddleEpsilon > 0 ? MAXIMA_CONTOUR : 0; + } + else if (branchIdx < nTopVolBranchWithinBlock + nExtraMaximaBranch) + { + const vtkm::Id idx = branchIdx - nTopVolBranchWithinBlock; + isoValue = extraMaximaBranchIsoValuePortal.Get(idx); + currBranchSaddleEpsilon = 1; + branchSuperarc = extraMaximaBranchSuperarcPortal.Get(idx); + branchSaddleGRId = extraMaximaBranchSaddleGRIdPortal.Get(idx); + branchOrder = extraMaximaBranchOrderPortal.Get(idx); + branchLabel |= MAXIMA_CONTOUR; + } + else + { + const vtkm::Id idx = branchIdx - nTopVolBranchWithinBlock - nExtraMaximaBranch; + VTKM_ASSERT(idx < nExtraMinimaBranch); + isoValue = extraMinimaBranchIsoValuePortal.Get(idx); + currBranchSaddleEpsilon = -1; + branchSuperarc = extraMinimaBranchSuperarcPortal.Get(idx); + branchSaddleGRId = extraMinimaBranchSaddleGRIdPortal.Get(idx); + branchOrder = extraMinimaBranchOrderPortal.Get(idx); + } + + edgeOffsetWritePortal.Set(branchIdx, this->IsosurfaceEdgesFrom.GetNumberOfValues()); + edgeLabelWritePortal.Set(branchIdx, branchLabel); + edgeOrderWritePortal.Set(branchIdx, branchOrder); + globalRegularIdsWritePortal.Set(branchIdx, branchSaddleGRId); + isosurfaceValuePortal.Set(branchIdx, isoValue); + + if (vtkm::worklet::contourtree_augmented::NoSuchElement(branchSuperarc)) + { + continue; + } + + // Note: by concept, there is no 3D cell if pointDimensions[2] <= 1 + const bool isData2D = globalPointDimensions[2] <= 1; + vtkm::Id nCells = isData2D + ? (pointDimensions[0] - 1) * (pointDimensions[1] - 1) + : (pointDimensions[0] - 1) * (pointDimensions[1] - 1) * (pointDimensions[2] - 1); + + // we get the polarity cases of cells + // we use lookup tables to for fast processing + using vtkm::worklet::scalar_topology::extract_top_volume_contours:: + CopyConstArraysForMarchingCubesDataTablesWorklet; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::COPY_VERTEXOFFSET; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::COPY_EDGETABLE; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::COPY_NUMBOUNDTABLE; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::COPY_BOUNDARYTABLE; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::COPY_LABELEDGETABLE; + + using vtkm::worklet::scalar_topology::extract_top_volume_contours::nVertices2d; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::nVertices3d; + + IdArrayType vertexOffSetTable; + const vtkm::Id vertexOffsetSize = isData2D ? nVertices2d * 2 : nVertices3d * 3; + + CopyConstArraysForMarchingCubesDataTablesWorklet copyVertexOffset( + isData2D, isMarchingCubes, COPY_VERTEXOFFSET); + invoke(copyVertexOffset, vtkm::cont::ArrayHandleIndex(vertexOffsetSize), vertexOffSetTable); + + using vtkm::worklet::scalar_topology::extract_top_volume_contours::nEdges2d; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::nEdgesMC3d; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::nEdgesLT3d; + + IdArrayType edgeTable; + const vtkm::Id edgeTableSize = + isData2D ? nEdges2d * 2 : (isMarchingCubes ? nEdgesMC3d * 2 : nEdgesLT3d * 2); + CopyConstArraysForMarchingCubesDataTablesWorklet copyEdgeTable( + isData2D, isMarchingCubes, COPY_EDGETABLE); + invoke(copyEdgeTable, vtkm::cont::ArrayHandleIndex(edgeTableSize), edgeTable); + + using vtkm::worklet::scalar_topology::extract_top_volume_contours::nCases2d; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::nCasesMC3d; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::nCasesLT3d; + + IdArrayType numBoundTable; + const vtkm::Id numBoundTableSize = + isData2D ? (nCases2d) : (isMarchingCubes ? (nCasesMC3d) : (nCasesLT3d)); + CopyConstArraysForMarchingCubesDataTablesWorklet copyNumBoundTable( + isData2D, isMarchingCubes, COPY_NUMBOUNDTABLE); + invoke(copyNumBoundTable, vtkm::cont::ArrayHandleIndex(numBoundTableSize), numBoundTable); + + using vtkm::worklet::scalar_topology::extract_top_volume_contours::nLineTableElemSize2d; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::nTriTableMC3dElemSize; + using vtkm::worklet::scalar_topology::extract_top_volume_contours::nTriTableLT3dElemSize; + + IdArrayType boundaryTable; + const vtkm::Id boundaryTableSize = isData2D + ? (nCases2d * nLineTableElemSize2d) + : (isMarchingCubes ? (nCasesMC3d * nTriTableMC3dElemSize) + : (nCasesLT3d * nTriTableLT3dElemSize)); + CopyConstArraysForMarchingCubesDataTablesWorklet copyBoundaryTable( + isData2D, isMarchingCubes, COPY_BOUNDARYTABLE); + invoke(copyBoundaryTable, vtkm::cont::ArrayHandleIndex(boundaryTableSize), boundaryTable); + + using vtkm::worklet::scalar_topology::extract_top_volume_contours:: + nLabelEdgeTableMC3dElemSize; + using vtkm::worklet::scalar_topology::extract_top_volume_contours:: + nLabelEdgeTableLT3dElemSize; + + IdArrayType labelEdgeTable; + const vtkm::Id labelEdgeTableSize = isData2D + ? 0 + : (isMarchingCubes ? nCasesMC3d * nLabelEdgeTableMC3dElemSize + : nCasesLT3d * nLabelEdgeTableLT3dElemSize); + labelEdgeTable.Allocate(labelEdgeTableSize); + if (!isData2D) + { + CopyConstArraysForMarchingCubesDataTablesWorklet copyLabelEdgeTable( + false, isMarchingCubes, COPY_LABELEDGETABLE); + invoke( + copyLabelEdgeTable, vtkm::cont::ArrayHandleIndex(labelEdgeTableSize), labelEdgeTable); + } + + //IdArrayType vertexOffset = globalPointDimensions[2] <= 1 + // ? vtkm::worklet::scalar_topology::extract_top_volume_contours::vertexOffset2d + // : vtkm::worklet::scalar_topology::extract_top_volume_contours::vertexOffset3d; + //IdArrayType edgeTable = globalPointDimensions[2] <= 1 + // ? vtkm::worklet::scalar_topology::extract_top_volume_contours::edgeTable2d + // : (isMarchingCubes + // ? vtkm::worklet::scalar_topology::extract_top_volume_contours::edgeTableMC3d + // : vtkm::worklet::scalar_topology::extract_top_volume_contours::edgeTableLT3d); + //IdArrayType numBoundTable = globalPointDimensions[2] <= 1 + // ? vtkm::worklet::scalar_topology::extract_top_volume_contours::numLinesTable2d + // : (isMarchingCubes + // ? vtkm::worklet::scalar_topology::extract_top_volume_contours::numTrianglesTableMC3d + // : vtkm::worklet::scalar_topology::extract_top_volume_contours::numTrianglesTableLT3d); + //IdArrayType boundaryTable = globalPointDimensions[2] <= 1 + // ? vtkm::worklet::scalar_topology::extract_top_volume_contours::lineTable2d + // : (isMarchingCubes + // ? vtkm::worklet::scalar_topology::extract_top_volume_contours::triTableMC3d + // : vtkm::worklet::scalar_topology::extract_top_volume_contours::triTableLT3d); + //IdArrayType labelEdgeTable = isMarchingCubes + // ? vtkm::worklet::scalar_topology::extract_top_volume_contours::labelEdgeTableMC3d + // : vtkm::worklet::scalar_topology::extract_top_volume_contours::labelEdgeTableLT3d; + + IdArrayType caseCells; + + caseCells.Allocate(nCells); + IdArrayType numEdgesInCell; + numEdgesInCell.Allocate(nCells); + auto caseCellsWorklet = + vtkm::worklet::scalar_topology::extract_top_volume_contours::GetCellCasesWorklet( + pointDimensions, + currBranchSaddleEpsilon, + isoValue, + shiftIsovalueByEpsilon, + branchSaddleGRId); + + invoke(caseCellsWorklet, + vtkm::cont::ArrayHandleIndex(nCells), + sortedDataValuesWithinBlock, + globalIdsWithinBlock, + vertexOffSetTable, + caseCells); + + // we compute the number of edges for each cell + // to initialize the array size of edges + IdArrayType numBoundariesInCell; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + numBoundTable, caseCells, numBoundariesInCell); + + vtkm::cont::Algorithm::Transform(numBoundariesInCell, + globalPointDimensions[2] <= 1 + ? vtkm::cont::ArrayHandleConstant(1, nCells) + : vtkm::cont::ArrayHandleConstant(3, nCells), + numEdgesInCell, + vtkm::Multiply()); + + // use prefix sum to get the offset of the starting edge in each cell/cube + vtkm::Id nEdges = + vtkm::cont::Algorithm::Reduce(numEdgesInCell, vtkm::Id(0)); + nContourCandidateMeshes += globalPointDimensions[2] <= 1 ? nEdges : nEdges / 3; + IdArrayType edgesOffset; + vtkm::cont::Algorithm::ScanExclusive(numEdgesInCell, edgesOffset); + + vtkm::cont::ArrayHandle isosurfaceEdgesFrom; + vtkm::cont::ArrayHandle isosurfaceEdgesTo; + IdArrayType isValidEdges; + isosurfaceEdgesFrom.Allocate(nEdges); + isosurfaceEdgesTo.Allocate(nEdges); + isValidEdges.Allocate(nEdges); + + // draw isosurface + auto getEdgesInCellWorklet = + vtkm::worklet::scalar_topology::extract_top_volume_contours::GetEdgesInCellWorklet< + ValueType>(pointDimensions, + globalPointIndexStart, + isoValue, + branchSaddleGRId, + branchSuperarc, + currBranchSaddleEpsilon, + totalNumPoints, + isMarchingCubes, + shiftIsovalueByEpsilon); + + invoke(getEdgesInCellWorklet, + edgesOffset, + caseCells, + sortedLocalNodeInfoIdsWithinBlock, + sortedDataValuesWithinBlock, + globalIdsWithinBlock, + vertexOffSetTable, + edgeTable, + numBoundTable, + boundaryTable, + labelEdgeTable, + isosurfaceEdgesFrom, + isosurfaceEdgesTo, + isValidEdges, + findSuperarcForNode); + + // isValidEdges: stencil about whether the edge is on the desired superarc + vtkm::cont::ArrayHandle validEdgesFrom; + vtkm::cont::ArrayHandle validEdgesTo; + + // we remove edges that are not on the desired superarc + vtkm::cont::Algorithm::CopyIf(isosurfaceEdgesFrom, isValidEdges, validEdgesFrom); + vtkm::cont::Algorithm::CopyIf(isosurfaceEdgesTo, isValidEdges, validEdgesTo); + + // append edges into the result array + vtkm::Id nValidEdges = validEdgesFrom.GetNumberOfValues(); + vtkm::Id nExistEdges = branchIdx == 0 ? 0 : this->IsosurfaceEdgesFrom.GetNumberOfValues(); + + this->IsosurfaceEdgesFrom.Allocate(nValidEdges + nExistEdges, vtkm::CopyFlag::On); + this->IsosurfaceEdgesTo.Allocate(nValidEdges + nExistEdges, vtkm::CopyFlag::On); + +#ifdef DEBUG_PRINT + std::stringstream edgeInfoStream; + contourStream << "Debug for Contour Info, Block " << this->LocalBlockNo << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(nContours, edgeInfoStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "edgeOffset", this->IsosurfaceEdgesOffset, -1, edgeInfoStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "edgeOrders", this->IsosurfaceEdgesOrders, -1, edgeInfoStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "edgeLabels", this->IsosurfaceEdgesLabels, -1, edgeInfoStream); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, edgeInfoStream.str()); +#endif // DEBUG_PRINT + + vtkm::cont::Algorithm::CopySubRange( + validEdgesFrom, 0, nValidEdges, this->IsosurfaceEdgesFrom, nExistEdges); + vtkm::cont::Algorithm::CopySubRange( + validEdgesTo, 0, nValidEdges, this->IsosurfaceEdgesTo, nExistEdges); + +#ifdef DEBUG_PRINT + std::stringstream contourStream; + contourStream << "Debug for Contour, Block " << this->LocalBlockNo << std::endl; + contourStream << "Branch Superarc = " << branchSuperarc << ", isoValue = " << isoValue + << std::endl; + + vtkm::worklet::contourtree_augmented::PrintHeader(nCells, contourStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Case of Cells", caseCells, -1, contourStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "# of Edges", numEdgesInCell, -1, contourStream); + + vtkm::worklet::contourtree_augmented::PrintHeader(nEdges, contourStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Edge Stencil", isValidEdges, -1, contourStream); + + vtkm::worklet::contourtree_augmented::PrintHeader(nValidEdges, contourStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "EdgesFrom", validEdgesFrom, -1, contourStream); + vtkm::worklet::contourtree_augmented::PrintValues( + "EdgesTo", validEdgesTo, -1, contourStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "EdgesLabels", validEdgesLabels, -1, contourStream); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, contourStream.str()); +#endif // DEBUG_PRINT + } + this->IsosurfaceIsoValue = isosurfaceIsoValue; + + const vtkm::Id nMeshesOnBranches = globalPointDimensions[2] <= 1 + ? this->IsosurfaceEdgesFrom.GetNumberOfValues() + : this->IsosurfaceEdgesFrom.GetNumberOfValues() / 3; + VTKM_LOG_S(timingsLogLevel, + std::endl + << "----------- Draw Isosurface (block=" << this->LocalBlockNo << ")------------" + << std::endl + << " " << std::setw(60) << std::left << "Number of Contours: " << nContours + << std::endl + << " " << std::setw(60) << std::left + << "Number of Isosurface Meshes: " << nContourCandidateMeshes << std::endl + << " " << std::setw(60) << std::left + << "Number of Meshes On Branches: " << nMeshesOnBranches << std::endl); + }; + this->TopVolumeData.TopVolumeBranchSaddleIsoValue + .CastAndCallForTypes(resolveArray); +} + +} // namespace internal +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm diff --git a/vtkm/filter/scalar_topology/internal/ExtractTopVolumeContoursBlock.h b/vtkm/filter/scalar_topology/internal/ExtractTopVolumeContoursBlock.h new file mode 100644 index 0000000000..58589899d4 --- /dev/null +++ b/vtkm/filter/scalar_topology/internal/ExtractTopVolumeContoursBlock.h @@ -0,0 +1,106 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_internal_ExtractTopVolumeContoursBlock_h +#define vtk_m_filter_scalar_topology_internal_ExtractTopVolumeContoursBlock_h + +#include +#include +#include + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ +namespace internal +{ + +struct ExtractTopVolumeContoursBlock +{ + ExtractTopVolumeContoursBlock(vtkm::Id localBlockNo, int globalBlockId); + + // Block metadata + vtkm::Id LocalBlockNo; + int GlobalBlockId; + + // The data class for branch arrays (e.g., branch root global regular IDs, branch volume, etc.) + // we reuse the class TopVolumeBranchData that was used in SelectTopVolumeBranchesFilter, + // because we need more than half of the arrays in this class for contour extraction + TopVolumeBranchData TopVolumeData; + + // Isosurface output + vtkm::cont::ArrayHandle IsosurfaceEdgesFrom; + vtkm::cont::ArrayHandle IsosurfaceEdgesTo; + vtkm::worklet::contourtree_augmented::IdArrayType IsosurfaceEdgesOffset; + vtkm::worklet::contourtree_augmented::IdArrayType IsosurfaceEdgesLabels; + vtkm::worklet::contourtree_augmented::IdArrayType IsosurfaceEdgesOrders; + vtkm::cont::UnknownArrayHandle IsosurfaceIsoValue; + // this is a tricky one - it is a part of the isovalue but solely used for simulation of simplicity + vtkm::worklet::contourtree_augmented::IdArrayType IsosurfaceGRIds; + + // Destroy function allowing DIY to own blocks and clean them up after use + static void Destroy(void* b) { delete static_cast(b); } + + // extract isosurfaces on top branches by volume + void ExtractIsosurfaceOnSelectedBranch(const vtkm::cont::DataSet& bdDataSet, + const bool isMarchingCubes, + const bool shiftIsovalueByEpsilon, + const vtkm::cont::LogLevel timingsLogLevel); +}; + +} // namespace internal +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm +#endif diff --git a/vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.cxx b/vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.cxx new file mode 100644 index 0000000000..6ae6aeb93a --- /dev/null +++ b/vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.cxx @@ -0,0 +1,306 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== +#include +#include +#include +#include + +#include + +#include + +#ifdef DEBUG_PRINT +#define DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE +#include +#endif + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ +namespace internal +{ + +void ParentBranchIsoValueFunctor::operator()( + SelectTopVolumeBranchesBlock* b, + const vtkmdiy::ReduceProxy& rp, // communication proxy + const vtkmdiy::RegularSwapPartners& // partners of the current block (unused) +) const +{ + // Get our rank and DIY id + const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank(); + const auto selfid = rp.gid(); + + // Aliases to reduce verbosity + using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; + + vtkm::cont::Invoker invoke; + + std::vector incoming; + rp.incoming(incoming); + for (const int ingid : incoming) + { + // NOTE/IMPORTANT: In each round we should have only one swap partner (despite for-loop here). + // If that assumption does not hold, it will break things. + // NOTE/IMPORTANT: This assumption only holds if the number of blocks is a power of two. + // Otherwise, we may need to process more than one incoming block + if (ingid != selfid) + { + + // copy incoming to the block +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + int incomingGlobalBlockId; + rp.dequeue(ingid, incomingGlobalBlockId); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Combining local block " << b->GlobalBlockId << " with incoming block " + << incomingGlobalBlockId); +#endif + vtkm::Id nSelfMaxBranch = b->tData.ExtraMaximaBranchOrder.GetNumberOfValues(); + vtkm::Id nSelfMinBranch = b->tData.ExtraMinimaBranchOrder.GetNumberOfValues(); + + // dequeue the data from other blocks. + // nExtraMaximaBranches (incoming) + // array of incoming maxima branch order + // array of incoming maxima branch isovalue + // nExtraMinimaBranches (incoming) + // array of incoming minima branch order + // array of incoming minima branch isovalue + + // the dequeue'd nIncomingMaxBranch is an array + // because vtkmdiy has bugs on communicating single variables + IdArrayType nIncomingMaxBranchWrapper; + rp.dequeue(ingid, nIncomingMaxBranchWrapper); + vtkm::Id nIncomingMaxBranch = vtkm::cont::ArrayGetValue(0, nIncomingMaxBranchWrapper); + + IdArrayType incomingMaxBranchOrder; + vtkm::cont::UnknownArrayHandle incomingMaxBranchIsoValue; + + auto resolveMaxArray = [&](auto& inArray) { + using InArrayHandleType = std::decay_t; +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + { + using ValueType = typename InArrayHandleType::ValueType; + std::stringstream rs; + vtkm::worklet::contourtree_augmented::PrintHeader(nIncomingMaxBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "incomingMaxBranchOrder", incomingMaxBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "incomingMaxBranchVal", incomingMaxBranchIsoValue, -1, rs); + + vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMaxBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfMaxBranchOrder", b->tData.ExtraMaximaBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "selfMaxBranchVal", inArray, -1, rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + InArrayHandleType incomingMaxBranchIsoValueCast = + incomingMaxBranchIsoValue.AsArrayHandle(); + vtkm::cont::Algorithm::SortByKey(incomingMaxBranchOrder, incomingMaxBranchIsoValueCast); + vtkm::worklet::scalar_topology::select_top_volume_branches::UpdateOuterSaddle + updateValueOnMaxBranch; + invoke(updateValueOnMaxBranch, + b->tData.ExtraMaximaBranchOrder, + inArray, + incomingMaxBranchOrder, + incomingMaxBranchIsoValueCast); + +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + { + std::stringstream rs; + rs << "After update, block " << b->LocalBlockNo << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMaxBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfMaxBranchOrder", b->tData.ExtraMaximaBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "selfMaxBranchVal", inArray, -1, rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + }; + + if (nIncomingMaxBranch > 0) + { + rp.dequeue(ingid, incomingMaxBranchOrder); + rp.dequeue(ingid, incomingMaxBranchIsoValue); + if (nSelfMaxBranch > 0) + b->tData.ExtraMaximaBranchIsoValue + .CastAndCallForTypes( + resolveMaxArray); + } + + // Apply the same pipeline for branches with minima to extract + // the dequeue'd nIncomingMinBranch is an array + // because vtkmdiy has bugs on communicating single variables + IdArrayType nIncomingMinBranchWrapper; + rp.dequeue(ingid, nIncomingMinBranchWrapper); + vtkm::Id nIncomingMinBranch = vtkm::cont::ArrayGetValue(0, nIncomingMinBranchWrapper); + + IdArrayType incomingMinBranchOrder; + vtkm::cont::UnknownArrayHandle incomingMinBranchIsoValue; + + auto resolveMinArray = [&](auto& inArray) { + using InArrayHandleType = std::decay_t; +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + { + using ValueType = typename InArrayHandleType::ValueType; + std::stringstream rs; + vtkm::worklet::contourtree_augmented::PrintHeader(nIncomingMinBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "incomingMinBranchOrder", incomingMinBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "incomingMinBranchVal", incomingMinBranchIsoValue, -1, rs); + + vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMinBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfMinBranchOrder", b->tData.ExtraMinimaBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "selfMinBranchVal", inArray, -1, rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + InArrayHandleType incomingMinBranchIsoValueCast = + incomingMinBranchIsoValue.AsArrayHandle(); + vtkm::cont::Algorithm::SortByKey(incomingMinBranchOrder, incomingMinBranchIsoValueCast); + vtkm::worklet::scalar_topology::select_top_volume_branches::UpdateOuterSaddle + updateValueOnMinBranch; + invoke(updateValueOnMinBranch, + b->tData.ExtraMinimaBranchOrder, + inArray, + incomingMinBranchOrder, + incomingMinBranchIsoValueCast); + +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + { + std::stringstream rs; + rs << "After update, block " << b->LocalBlockNo << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMinBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfMinBranchOrder", b->tData.ExtraMinimaBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "selfMinBranchVal", inArray, -1, rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + }; + if (nIncomingMinBranch > 0) + { + rp.dequeue(ingid, incomingMinBranchOrder); + rp.dequeue(ingid, incomingMinBranchIsoValue); + if (nSelfMinBranch > 0) + b->tData.ExtraMinimaBranchIsoValue + .CastAndCallForTypes( + resolveMinArray); + } + + // The logging is commented because the size of exchange is limited by K, + // the number of top-volume branches, which is usually small + std::stringstream dataSizeStream; + // Log the amount of exchanged data + dataSizeStream << " " << std::setw(38) << std::left << "Incoming branch size" + << ": " << nIncomingMaxBranch + nIncomingMinBranch << std::endl; + + VTKM_LOG_S(this->TimingsLogLevel, + std::endl + << " ---------------- Exchange Parent Branch Step ---------------------" + << std::endl + << " Rank : " << rank << std::endl + << " DIY Id : " << selfid << std::endl + << " Inc Id : " << ingid << std::endl + << dataSizeStream.str()); + } + } + + for (int cc = 0; cc < rp.out_link().size(); ++cc) + { + auto target = rp.out_link().target(cc); + if (target.gid != selfid) + { +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + rp.enqueue(target, b->GlobalBlockId); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Block " << b->GlobalBlockId << " enqueue to Block " << target.gid); +#endif + // We enqueue the array of nExtraMaxBranches instead of the variable itself + // Because there is a bug when nExtraMaxBranches=0. The dequeue'd value is not 0, but a random number + // vtkmdiy seems to perform better on enqueue/dequeue with containers than single variables + vtkm::Id nExtraMaxBranches = b->tData.ExtraMaximaBranchOrder.GetNumberOfValues(); + rp.enqueue(target, vtkm::cont::make_ArrayHandle({ nExtraMaxBranches })); + + if (nExtraMaxBranches) + { + rp.enqueue(target, b->tData.ExtraMaximaBranchOrder); + rp.enqueue(target, b->tData.ExtraMaximaBranchIsoValue); + } + + // We enqueue the array of nExtraMinBranches instead of the variable itself + // Because there is a bug when nExtraMinBranches=0. The dequeue'd value is not 0, but a random number + // vtkmdiy seems to perform better on enqueue/dequeue with containers than single variables + vtkm::Id nExtraMinBranches = b->tData.ExtraMinimaBranchOrder.GetNumberOfValues(); + rp.enqueue(target, vtkm::cont::make_ArrayHandle({ nExtraMinBranches })); + + if (nExtraMinBranches) + { + rp.enqueue(target, b->tData.ExtraMinimaBranchOrder); + rp.enqueue(target, b->tData.ExtraMinimaBranchIsoValue); + } + } + } +} + +} // namespace internal +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesBlock.cxx b/vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesBlock.cxx new file mode 100644 index 0000000000..0840560a78 --- /dev/null +++ b/vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesBlock.cxx @@ -0,0 +1,376 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG_PRINT +#include +#endif + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ +namespace internal +{ + +SelectTopVolumeBranchesBlock::SelectTopVolumeBranchesBlock(vtkm::Id localBlockNo, int globalBlockId) + : LocalBlockNo(localBlockNo) + , GlobalBlockId(globalBlockId) +{ +} + +void SelectTopVolumeBranchesBlock::SortBranchByVolume(const vtkm::cont::DataSet& bdDataSet, + const vtkm::Id totalVolume) +{ + /// Pipeline to compute the branch volume + /// 1. check both ends of the branch. If both leaves, then main branch, volume = totalVolume + /// 2. for other branches, check the direction of the inner superarc + /// branch volume = (inner superarc points to the senior-most node) ? + /// dependentVolume[innerSuperarc] : + /// reverseVolume[innerSuperarc] + /// NOTE: reverseVolume = totalVolume - dependentVolume + intrinsicVolume + + // Generally, if ending superarc has intrinsicVol == dependentVol, then it is a leaf node + vtkm::cont::ArrayHandle isLowerLeaf; + vtkm::cont::ArrayHandle isUpperLeaf; + + auto upperEndIntrinsicVolume = bdDataSet.GetField("UpperEndIntrinsicVolume") + .GetData() + .AsArrayHandle>(); + auto upperEndDependentVolume = bdDataSet.GetField("UpperEndDependentVolume") + .GetData() + .AsArrayHandle>(); + auto lowerEndIntrinsicVolume = bdDataSet.GetField("LowerEndIntrinsicVolume") + .GetData() + .AsArrayHandle>(); + auto lowerEndDependentVolume = bdDataSet.GetField("LowerEndDependentVolume") + .GetData() + .AsArrayHandle>(); + + auto lowerEndSuperarcId = bdDataSet.GetField("LowerEndSuperarcId") + .GetData() + .AsArrayHandle>(); + auto upperEndSuperarcId = bdDataSet.GetField("UpperEndSuperarcId") + .GetData() + .AsArrayHandle>(); + auto branchRoot = bdDataSet.GetField("BranchRootByBranch") + .GetData() + .AsArrayHandle>(); + + vtkm::cont::Algorithm::Transform( + upperEndIntrinsicVolume, upperEndDependentVolume, isUpperLeaf, vtkm::Equal()); + vtkm::cont::Algorithm::Transform( + lowerEndIntrinsicVolume, lowerEndDependentVolume, isLowerLeaf, vtkm::Equal()); + + // NOTE: special cases (one-superarc branches) exist + // if the upper end superarc == lower end superarc == branch root superarc + // then it's probably not a leaf-leaf branch (Both equalities have to be satisfied!) + // exception: the entire domain has only one superarc (intrinsic == dependent == total - 1) + // then it is a leaf-leaf branch + vtkm::cont::Invoker invoke; + + vtkm::worklet::scalar_topology::select_top_volume_branches::ClarifyBranchEndSupernodeTypeWorklet + clarifyNodeTypeWorklet(totalVolume); + + invoke(clarifyNodeTypeWorklet, + lowerEndSuperarcId, + lowerEndIntrinsicVolume, + upperEndSuperarcId, + upperEndIntrinsicVolume, + branchRoot, + isLowerLeaf, + isUpperLeaf); + + vtkm::cont::UnknownArrayHandle upperEndValue = bdDataSet.GetField("UpperEndValue").GetData(); + + // Based on the direction info of the branch, store epsilon direction and isovalue of the saddle + auto resolveArray = [&](const auto& inArray) { + using InArrayHandleType = std::decay_t; + using ValueType = typename InArrayHandleType::ValueType; + + vtkm::cont::ArrayHandle branchSaddleIsoValue; + branchSaddleIsoValue.Allocate(isLowerLeaf.GetNumberOfValues()); + this->TopVolumeData.BranchSaddleEpsilon.Allocate(isLowerLeaf.GetNumberOfValues()); + + vtkm::worklet::scalar_topology::select_top_volume_branches::UpdateInfoByBranchDirectionWorklet< + ValueType> + updateInfoWorklet; + auto lowerEndValue = bdDataSet.GetField("LowerEndValue") + .GetData() + .AsArrayHandle>(); + + invoke(updateInfoWorklet, + isLowerLeaf, + isUpperLeaf, + inArray, + lowerEndValue, + this->TopVolumeData.BranchSaddleEpsilon, + branchSaddleIsoValue); + this->TopVolumeData.BranchSaddleIsoValue = branchSaddleIsoValue; + }; + + upperEndValue.CastAndCallForTypes( + resolveArray); + + // Compute the branch volume based on the upper/lower end superarc volumes + vtkm::worklet::contourtree_augmented::IdArrayType branchVolume; + vtkm::worklet::scalar_topology::select_top_volume_branches::GetBranchVolumeWorklet + getBranchVolumeWorklet(totalVolume); + + invoke(getBranchVolumeWorklet, // worklet + lowerEndSuperarcId, // input + lowerEndIntrinsicVolume, // input + lowerEndDependentVolume, // input + upperEndSuperarcId, // input + upperEndIntrinsicVolume, // input + upperEndDependentVolume, // input + isLowerLeaf, + isUpperLeaf, + branchVolume); // output + +#ifdef DEBUG_PRINT + std::stringstream resultStream; + resultStream << "Branch Volume In The Block" << std::endl; + const vtkm::Id nVolume = branchVolume.GetNumberOfValues(); + vtkm::worklet::contourtree_augmented::PrintHeader(nVolume, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "BranchVolume", branchVolume, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices("isLowerLeaf", isLowerLeaf, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices("isUpperLeaf", isUpperLeaf, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "LowerEndIntrinsicVol", lowerEndIntrinsicVolume, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "LowerEndDependentVol", lowerEndDependentVolume, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "UpperEndIntrinsicVol", upperEndIntrinsicVolume, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "UpperEndDependentVol", upperEndDependentVolume, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "LowerEndSuperarc", lowerEndSuperarcId, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "UpperEndSuperarc", upperEndSuperarcId, -1, resultStream); + vtkm::worklet::contourtree_augmented::PrintIndices("BranchRoot", branchRoot, -1, resultStream); + resultStream << std::endl; + VTKM_LOG_S(vtkm::cont::LogLevel::Info, resultStream.str()); +#endif + + vtkm::cont::Algorithm::Copy(branchVolume, this->TopVolumeData.BranchVolume); + + const vtkm::Id nBranches = lowerEndSuperarcId.GetNumberOfValues(); + vtkm::cont::ArrayHandleIndex branchesIdx(nBranches); + vtkm::worklet::contourtree_augmented::IdArrayType sortedBranches; + vtkm::cont::Algorithm::Copy(branchesIdx, sortedBranches); + + // sort the branch volume + vtkm::cont::Algorithm::SortByKey(branchVolume, sortedBranches, vtkm::SortGreater()); + vtkm::cont::Algorithm::Copy(sortedBranches, this->TopVolumeData.SortedBranchByVolume); +} + +// Select the local top K branches by volume +void SelectTopVolumeBranchesBlock::SelectLocalTopVolumeBranches( + const vtkm::cont::DataSet& bdDataset, + const vtkm::Id nSavedBranches) +{ + using vtkm::worklet::contourtree_augmented::IdArrayType; + // copy the top volume branches into a smaller array + // we skip index 0 because it must be the main branch (which has the highest volume) + vtkm::Id nActualSavedBranches = + std::min(nSavedBranches, this->TopVolumeData.SortedBranchByVolume.GetNumberOfValues() - 1); + + vtkm::worklet::contourtree_augmented::IdArrayType topVolumeBranch; + vtkm::cont::Algorithm::CopySubRange( + this->TopVolumeData.SortedBranchByVolume, 1, nActualSavedBranches, topVolumeBranch); + + auto branchRootByBranch = bdDataset.GetField("BranchRootByBranch") + .GetData() + .AsArrayHandle>(); + + const vtkm::Id nBranches = branchRootByBranch.GetNumberOfValues(); + + auto branchRootGRId = bdDataset.GetField("BranchRootGRId") + .GetData() + .AsArrayHandle>(); + + auto upperEndGRId = bdDataset.GetField("UpperEndGlobalRegularIds") + .GetData() + .AsArrayHandle>(); + + auto lowerEndGRId = bdDataset.GetField("LowerEndGlobalRegularIds") + .GetData() + .AsArrayHandle>(); + + vtkm::cont::Algorithm::Copy(branchRootByBranch, this->TopVolumeData.BranchRootByBranch); + vtkm::cont::Algorithm::Copy(branchRootGRId, this->TopVolumeData.BranchRootGRId); + + // This seems weird, but we temporarily put the initialization of computing the branch decomposition tree here + this->TopVolumeData.IsParentBranch.AllocateAndFill(nBranches, false); + + // we permute all branch information to align with the order by volume + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + branchRootGRId, topVolumeBranch, this->TopVolumeData.TopVolumeBranchRootGRId); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + upperEndGRId, topVolumeBranch, this->TopVolumeData.TopVolumeBranchUpperEndGRId); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + lowerEndGRId, topVolumeBranch, this->TopVolumeData.TopVolumeBranchLowerEndGRId); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + this->TopVolumeData.BranchVolume, topVolumeBranch, this->TopVolumeData.TopVolumeBranchVolume); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + this->TopVolumeData.BranchSaddleEpsilon, + topVolumeBranch, + this->TopVolumeData.TopVolumeBranchSaddleEpsilon); + + auto resolveArray = [&](const auto& inArray) { + using InArrayHandleType = std::decay_t; + InArrayHandleType topVolBranchSaddleIsoValue; + vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( + inArray, topVolumeBranch, topVolBranchSaddleIsoValue); + this->TopVolumeData.TopVolumeBranchSaddleIsoValue = topVolBranchSaddleIsoValue; + }; + + this->TopVolumeData.BranchSaddleIsoValue + .CastAndCallForTypes(resolveArray); +} + +void SelectTopVolumeBranchesBlock::ComputeTopVolumeBranchHierarchy( + const vtkm::cont::DataSet& bdDataSet) +{ + this->BDTMaker.ComputeTopVolumeBranchHierarchy(bdDataSet, this->TopVolumeData); +} + +vtkm::Id SelectTopVolumeBranchesBlock::ExcludeTopVolumeBranchByThreshold( + const vtkm::Id presimplifyThreshold) +{ + using vtkm::worklet::contourtree_augmented::IdArrayType; + + // the stencil for top-volume branches of whether passing the threshold + vtkm::cont::ArrayHandle topVolumeAboveThreshold; + topVolumeAboveThreshold.AllocateAndFill( + this->TopVolumeData.TopVolumeBranchVolume.GetNumberOfValues(), true); + + vtkm::cont::Invoker invoke; + vtkm::worklet::scalar_topology::select_top_volume_branches::AboveThresholdWorklet + aboveThresholdWorklet(presimplifyThreshold); + invoke(aboveThresholdWorklet, this->TopVolumeData.TopVolumeBranchVolume, topVolumeAboveThreshold); + + // using the stencil to filter the top-volume branch information + IdArrayType filteredTopVolumeBranchRootGRId; + vtkm::cont::Algorithm::CopyIf(this->TopVolumeData.TopVolumeBranchRootGRId, + topVolumeAboveThreshold, + filteredTopVolumeBranchRootGRId); + vtkm::cont::Algorithm::Copy(filteredTopVolumeBranchRootGRId, + this->TopVolumeData.TopVolumeBranchRootGRId); + + IdArrayType filteredTopVolumeBranchVolume; + vtkm::cont::Algorithm::CopyIf(this->TopVolumeData.TopVolumeBranchVolume, + topVolumeAboveThreshold, + filteredTopVolumeBranchVolume); + vtkm::cont::Algorithm::Copy(filteredTopVolumeBranchVolume, + this->TopVolumeData.TopVolumeBranchVolume); + + auto resolveArray = [&](auto& inArray) { + using InArrayHandleType = std::decay_t; + InArrayHandleType filteredTopVolumeBranchSaddleIsoValue; + vtkm::cont::Algorithm::CopyIf( + inArray, topVolumeAboveThreshold, filteredTopVolumeBranchSaddleIsoValue); + + inArray.Allocate(filteredTopVolumeBranchVolume.GetNumberOfValues()); + vtkm::cont::Algorithm::Copy(filteredTopVolumeBranchSaddleIsoValue, inArray); + }; + this->TopVolumeData.TopVolumeBranchSaddleIsoValue + .CastAndCallForTypes(resolveArray); + + IdArrayType filteredTopVolumeBranchSaddleEpsilon; + vtkm::cont::Algorithm::CopyIf(this->TopVolumeData.TopVolumeBranchSaddleEpsilon, + topVolumeAboveThreshold, + filteredTopVolumeBranchSaddleEpsilon); + vtkm::cont::Algorithm::Copy(filteredTopVolumeBranchSaddleEpsilon, + this->TopVolumeData.TopVolumeBranchSaddleEpsilon); + + IdArrayType filteredTopVolumeBranchUpperEndGRId; + vtkm::cont::Algorithm::CopyIf(this->TopVolumeData.TopVolumeBranchUpperEndGRId, + topVolumeAboveThreshold, + filteredTopVolumeBranchUpperEndGRId); + vtkm::cont::Algorithm::Copy(filteredTopVolumeBranchUpperEndGRId, + this->TopVolumeData.TopVolumeBranchUpperEndGRId); + + IdArrayType filteredTopVolumeBranchLowerEndGRId; + vtkm::cont::Algorithm::CopyIf(this->TopVolumeData.TopVolumeBranchLowerEndGRId, + topVolumeAboveThreshold, + filteredTopVolumeBranchLowerEndGRId); + vtkm::cont::Algorithm::Copy(filteredTopVolumeBranchLowerEndGRId, + this->TopVolumeData.TopVolumeBranchLowerEndGRId); + + return filteredTopVolumeBranchVolume.GetNumberOfValues(); +} + +} // namespace internal +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h b/vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesBlock.h similarity index 71% rename from vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h rename to vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesBlock.h index 2d1ca6d677..1d123fbb06 100644 --- a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h +++ b/vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesBlock.h @@ -50,11 +50,13 @@ // Oliver Ruebel (LBNL) //============================================================================== -#ifndef vtk_m_filter_scalar_topology_internal_SelectTopVolumeContoursBlock_h -#define vtk_m_filter_scalar_topology_internal_SelectTopVolumeContoursBlock_h +#ifndef vtk_m_filter_scalar_topology_internal_SelectTopVolumeBranchesBlock_h +#define vtk_m_filter_scalar_topology_internal_SelectTopVolumeBranchesBlock_h #include #include +#include +#include namespace vtkm { @@ -65,32 +67,35 @@ namespace scalar_topology namespace internal { -struct SelectTopVolumeContoursBlock +struct SelectTopVolumeBranchesBlock { - SelectTopVolumeContoursBlock(vtkm::Id localBlockNo, int globalBlockId); - - void SortBranchByVolume(const vtkm::cont::DataSet& hierarchicalTreeDataSet, - const vtkm::Id totalVolume); + SelectTopVolumeBranchesBlock(vtkm::Id localBlockNo, int globalBlockId); // Block metadata vtkm::Id LocalBlockNo; - int GlobalBlockId; // TODO/FIXME: Check whether really needed. Possibly only during debugging + int GlobalBlockId; - vtkm::worklet::contourtree_augmented::IdArrayType BranchVolume; - vtkm::worklet::contourtree_augmented::IdArrayType BranchSaddleEpsilon; - vtkm::worklet::contourtree_augmented::IdArrayType SortedBranchByVolume; - vtkm::cont::UnknownArrayHandle BranchSaddleIsoValue; + // the data class for branch arrays (e.g., branch root global regular IDs, branch volume, etc.) + TopVolumeBranchData TopVolumeData; - // Output Datasets. - vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchRootGRId; - vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchVolume; - vtkm::cont::UnknownArrayHandle TopVolumeBranchSaddleIsoValue; - vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchSaddleEpsilon; - vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchUpperEndGRId; - vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchLowerEndGRId; + // the factory class to compute the relation of top-volume branches + BranchDecompositionTreeMaker BDTMaker; // Destroy function allowing DIY to own blocks and clean them up after use - static void Destroy(void* b) { delete static_cast(b); } + static void Destroy(void* b) { delete static_cast(b); } + + // compute the volume of local branches, and sort them by volume + void SortBranchByVolume(const vtkm::cont::DataSet& bdDataSet, const vtkm::Id totalVolume); + + // choose the top branches by volume + void SelectLocalTopVolumeBranches(const vtkm::cont::DataSet& bdDataSet, + const vtkm::Id nSavedBranches); + + // compute the branch decomposition tree (implicitly) for top branches + void ComputeTopVolumeBranchHierarchy(const vtkm::cont::DataSet& bdDataSet); + + // exclude branches whose volume <= presimplifyThreshold + vtkm::Id ExcludeTopVolumeBranchByThreshold(const vtkm::Id presimplifyThreshold); }; } // namespace internal diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesFunctor.cxx b/vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesFunctor.cxx new file mode 100644 index 0000000000..85e49f2b80 --- /dev/null +++ b/vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesFunctor.cxx @@ -0,0 +1,426 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== +#include +#include +#include +#include +#include + +#include + +#include + +#ifdef DEBUG_PRINT +#define DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH +#include +#endif + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ +namespace internal +{ + +void SelectTopVolumeBranchesFunctor::operator()( + SelectTopVolumeBranchesBlock* b, + const vtkmdiy::ReduceProxy& rp, // communication proxy + const vtkmdiy::RegularSwapPartners& // partners of the current block (unused) +) const +{ + // Get our rank and DIY id + const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank(); + const auto selfid = rp.gid(); + + // Aliases to reduce verbosity + using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; + + vtkm::cont::Invoker invoke; + + std::vector incoming; + rp.incoming(incoming); + for (const int ingid : incoming) + { + // NOTE/IMPORTANT: In each round we should have only one swap partner (despite for-loop here). + // If that assumption does not hold, it will break things. + // NOTE/IMPORTANT: This assumption only holds if the number of blocks is a power of two. + // Otherwise, we may need to process more than one incoming block + if (ingid != selfid) + { + // copy incoming to the block +#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH + int incomingGlobalBlockId; + rp.dequeue(ingid, incomingGlobalBlockId); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Combining local block " << b->GlobalBlockId << " with incoming block " + << incomingGlobalBlockId); +#endif + + // dequeue the data from other blocks. + // nIncomingBranches + // array of incoming branch global regular ID + // array of incoming branch volume + // array of branch epsilon direction + // array of branch upper end global regular ID + // array of branch lower end global regular ID + // array of branch saddle end value + vtkm::Id nIncoming; + rp.dequeue(ingid, nIncoming); + + IdArrayType incomingTopVolBranchGRId; + rp.dequeue(ingid, incomingTopVolBranchGRId); + + IdArrayType incomingTopVolBranchVolume; + rp.dequeue(ingid, incomingTopVolBranchVolume); + + IdArrayType incomingTopVolBranchSaddleEpsilon; + rp.dequeue(ingid, incomingTopVolBranchSaddleEpsilon); + + IdArrayType incomingTopVolBranchUpperEnd; + rp.dequeue(ingid, incomingTopVolBranchUpperEnd); + + IdArrayType incomingTopVolBranchLowerEnd; + rp.dequeue(ingid, incomingTopVolBranchLowerEnd); + + std::stringstream dataSizeStream; + // Log the amount of exchanged data + dataSizeStream << " " << std::setw(38) << std::left << "Incoming top volume branch size" + << ": " << nIncoming << std::endl; + + VTKM_LOG_S(this->TimingsLogLevel, + std::endl + << " ---------------- Select Top Volume Branches Step ---------------------" + << std::endl + << " Rank : " << rank << std::endl + << " DIY Id : " << selfid << std::endl + << " Inc Id : " << ingid << std::endl + << dataSizeStream.str()); + + vtkm::Id nSelf = b->TopVolumeData.TopVolumeBranchRootGRId.GetNumberOfValues(); +#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "nIncoming = " << nIncoming << ", nSelf = " << nSelf); + { + std::stringstream rs; + vtkm::worklet::contourtree_augmented::PrintHeader(nIncoming, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "incomingTopBranchId", incomingTopVolBranchGRId, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "incomingTopBranchVol", incomingTopVolBranchVolume, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "incomingSaddleVal", incomingTopVolBranchSaddleIsoValue, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "incomingUpperEnd", incomingTopVolBranchUpperEnd, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "incomingLowerEnd", incomingTopVolBranchLowerEnd, -1, rs); + + vtkm::worklet::contourtree_augmented::PrintHeader(nSelf, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfTopBranchId", b->TopVolumeData.TopVolumeBranchRootGRId, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfTopBranchVol", b->TopVolumeData.TopVolumeBranchVolume, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "selfTopSaddleVal", inArray, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfTopBranchUpperEnd", b->TopVolumeData.TopVolumeBranchUpperEndGRId, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfTopBranchLowerEnd", b->TopVolumeData.TopVolumeBranchLowerEndGRId, -1, rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + // merge incoming branches with self branches + IdArrayType mergedTopVolBranchGRId; + IdArrayType mergedTopVolBranchVolume; + IdArrayType mergedTopVolBranchSaddleEpsilon; + IdArrayType mergedTopVolBranchUpperEnd; + IdArrayType mergedTopVolBranchLowerEnd; + mergedTopVolBranchGRId.Allocate(nIncoming + nSelf); + mergedTopVolBranchVolume.Allocate(nIncoming + nSelf); + mergedTopVolBranchSaddleEpsilon.Allocate(nIncoming + nSelf); + mergedTopVolBranchUpperEnd.Allocate(nIncoming + nSelf); + mergedTopVolBranchLowerEnd.Allocate(nIncoming + nSelf); + + vtkm::cont::Algorithm::CopySubRange( + incomingTopVolBranchGRId, 0, nIncoming, mergedTopVolBranchGRId, 0); + vtkm::cont::Algorithm::CopySubRange( + incomingTopVolBranchVolume, 0, nIncoming, mergedTopVolBranchVolume, 0); + vtkm::cont::Algorithm::CopySubRange( + incomingTopVolBranchSaddleEpsilon, 0, nIncoming, mergedTopVolBranchSaddleEpsilon, 0); + vtkm::cont::Algorithm::CopySubRange( + incomingTopVolBranchUpperEnd, 0, nIncoming, mergedTopVolBranchUpperEnd, 0); + vtkm::cont::Algorithm::CopySubRange( + incomingTopVolBranchLowerEnd, 0, nIncoming, mergedTopVolBranchLowerEnd, 0); + + vtkm::cont::Algorithm::CopySubRange( + b->TopVolumeData.TopVolumeBranchRootGRId, 0, nSelf, mergedTopVolBranchGRId, nIncoming); + vtkm::cont::Algorithm::CopySubRange( + b->TopVolumeData.TopVolumeBranchVolume, 0, nSelf, mergedTopVolBranchVolume, nIncoming); + vtkm::cont::Algorithm::CopySubRange(b->TopVolumeData.TopVolumeBranchSaddleEpsilon, + 0, + nSelf, + mergedTopVolBranchSaddleEpsilon, + nIncoming); + vtkm::cont::Algorithm::CopySubRange(b->TopVolumeData.TopVolumeBranchUpperEndGRId, + 0, + nSelf, + mergedTopVolBranchUpperEnd, + nIncoming); + vtkm::cont::Algorithm::CopySubRange(b->TopVolumeData.TopVolumeBranchLowerEndGRId, + 0, + nSelf, + mergedTopVolBranchLowerEnd, + nIncoming); + + // Sort all branches (incoming + self) based on volume + // sorting key: (volume, branch global regular ID) + // the highest volume comes first, the lowest branch GR ID comes first + vtkm::cont::ArrayHandleIndex mergedBranchId(nIncoming + nSelf); + IdArrayType sortedBranchId; + vtkm::cont::Algorithm::Copy(mergedBranchId, sortedBranchId); + vtkm::worklet::scalar_topology::select_top_volume_branches::BranchVolumeComparator + branchVolumeComparator(mergedTopVolBranchGRId, mergedTopVolBranchVolume); + vtkm::cont::Algorithm::Sort(sortedBranchId, branchVolumeComparator); + + // permute the branch information based on sorting + IdArrayType permutedTopVolBranchGRId; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + mergedTopVolBranchGRId, sortedBranchId, permutedTopVolBranchGRId); + IdArrayType permutedTopVolBranchVolume; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + mergedTopVolBranchVolume, sortedBranchId, permutedTopVolBranchVolume); + IdArrayType permutedTopVolBranchSaddleEpsilon; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + mergedTopVolBranchSaddleEpsilon, sortedBranchId, permutedTopVolBranchSaddleEpsilon); + IdArrayType permutedTopVolBranchUpperEnd; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + mergedTopVolBranchUpperEnd, sortedBranchId, permutedTopVolBranchUpperEnd); + IdArrayType permutedTopVolBranchLowerEnd; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + mergedTopVolBranchLowerEnd, sortedBranchId, permutedTopVolBranchLowerEnd); + +#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH + { + std::stringstream rs; + vtkm::worklet::contourtree_augmented::PrintHeader(nIncoming + nSelf, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "permutedTopBranchId", permutedTopVolBranchGRId, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "permutedTopBranchVol", permutedTopVolBranchVolume, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "permutedTopBranchUpperEnd", permutedTopVolBranchUpperEnd, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "permutedTopBranchLowerEnd", permutedTopVolBranchLowerEnd, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "permutedTopSaddleVal", permutedTopVolBranchSaddleIsoValue, -1, rs); + } +#endif + + // there may be duplicate branches. We remove duplicate branches based on global regular IDs + // We can reuse the filter from removing duplicate branches in the process of collecting branches + IdArrayType oneIfUniqueBranch; + oneIfUniqueBranch.Allocate(nIncoming + nSelf); + vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer:: + OneIfBranchEndWorklet oneIfUniqueWorklet; + invoke(oneIfUniqueWorklet, mergedBranchId, permutedTopVolBranchGRId, oneIfUniqueBranch); + + // Remove duplicate + IdArrayType mergedUniqueBranchGRId; + IdArrayType mergedUniqueBranchVolume; + IdArrayType mergedUniqueBranchSaddleEpsilon; + IdArrayType mergedUniqueBranchUpperEnd; + IdArrayType mergedUniqueBranchLowerEnd; + + vtkm::cont::Algorithm::CopyIf( + permutedTopVolBranchGRId, oneIfUniqueBranch, mergedUniqueBranchGRId); + vtkm::cont::Algorithm::CopyIf( + permutedTopVolBranchVolume, oneIfUniqueBranch, mergedUniqueBranchVolume); + vtkm::cont::Algorithm::CopyIf( + permutedTopVolBranchSaddleEpsilon, oneIfUniqueBranch, mergedUniqueBranchSaddleEpsilon); + vtkm::cont::Algorithm::CopyIf( + permutedTopVolBranchUpperEnd, oneIfUniqueBranch, mergedUniqueBranchUpperEnd); + vtkm::cont::Algorithm::CopyIf( + permutedTopVolBranchLowerEnd, oneIfUniqueBranch, mergedUniqueBranchLowerEnd); + + vtkm::Id nMergedUnique = mergedUniqueBranchGRId.GetNumberOfValues(); + + // We move all processing to the TopVolumeBranchSaddleIsoValue here + // to reduce the size of the [&] function + // if the code inside the [&] function is too long, compilation for ubuntu1604+gcc5 may break + vtkm::cont::UnknownArrayHandle tempIncomingTopVolBranchSaddleIsoValue; + rp.dequeue(ingid, tempIncomingTopVolBranchSaddleIsoValue); + + auto resolveMerging = [&](auto& inArray) { + using InArrayHandleType = std::decay_t; + using ValueType = typename InArrayHandleType::ValueType; + + InArrayHandleType incomingTopVolBranchSaddleIsoValue = + tempIncomingTopVolBranchSaddleIsoValue.AsArrayHandle(); + InArrayHandleType mergedTopVolBranchSaddleIsoValue; + mergedTopVolBranchSaddleIsoValue.Allocate(nIncoming + nSelf); + + vtkm::cont::Algorithm::CopySubRange( + incomingTopVolBranchSaddleIsoValue, 0, nIncoming, mergedTopVolBranchSaddleIsoValue, 0); + + vtkm::cont::Algorithm::CopySubRange( + inArray, 0, nSelf, mergedTopVolBranchSaddleIsoValue, nIncoming); + + InArrayHandleType permutedTopVolBranchSaddleIsoValue; + vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( + mergedTopVolBranchSaddleIsoValue, sortedBranchId, permutedTopVolBranchSaddleIsoValue); + + InArrayHandleType mergedUniqueBranchSaddleIsoValue; + vtkm::cont::Algorithm::CopyIf( + permutedTopVolBranchSaddleIsoValue, oneIfUniqueBranch, mergedUniqueBranchSaddleIsoValue); + + if (nMergedUnique > this->NumSavedBranches) + { + inArray.Allocate(this->NumSavedBranches); + vtkm::cont::Algorithm::CopySubRange( + mergedUniqueBranchSaddleIsoValue, 0, this->NumSavedBranches, inArray); + } + else + { + inArray.Allocate(nMergedUnique); + vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleIsoValue, inArray); + } + }; + b->TopVolumeData.TopVolumeBranchSaddleIsoValue + .CastAndCallForTypes(resolveMerging); + +#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH + { + std::stringstream rs; + vtkm::worklet::contourtree_augmented::PrintHeader(nMergedUnique, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "mergedUniqueBranchId", mergedUniqueBranchGRId, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "mergedUniqueBranchVol", mergedUniqueBranchVolume, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "mergedUniqueBranchUpperEnd", mergedUniqueBranchUpperEnd, -1, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "mergedUniqueBranchLowerEnd", mergedUniqueBranchLowerEnd, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "mergedUniqueSaddleVal", mergedUniqueBranchSaddleIsoValue, -1, rs); + } +#endif + + // After removing duplicate, if there are more branches than we need + // We only save the top NumSavedBranches branches + if (nMergedUnique > this->NumSavedBranches) + { + vtkm::cont::Algorithm::CopySubRange(mergedUniqueBranchGRId, + 0, + this->NumSavedBranches, + b->TopVolumeData.TopVolumeBranchRootGRId); + vtkm::cont::Algorithm::CopySubRange(mergedUniqueBranchVolume, + 0, + this->NumSavedBranches, + b->TopVolumeData.TopVolumeBranchVolume); + vtkm::cont::Algorithm::CopySubRange(mergedUniqueBranchSaddleEpsilon, + 0, + this->NumSavedBranches, + b->TopVolumeData.TopVolumeBranchSaddleEpsilon); + vtkm::cont::Algorithm::CopySubRange(mergedUniqueBranchUpperEnd, + 0, + this->NumSavedBranches, + b->TopVolumeData.TopVolumeBranchUpperEndGRId); + vtkm::cont::Algorithm::CopySubRange(mergedUniqueBranchLowerEnd, + 0, + this->NumSavedBranches, + b->TopVolumeData.TopVolumeBranchLowerEndGRId); + } + else + { + vtkm::cont::Algorithm::Copy(mergedUniqueBranchGRId, + b->TopVolumeData.TopVolumeBranchRootGRId); + vtkm::cont::Algorithm::Copy(mergedUniqueBranchVolume, + b->TopVolumeData.TopVolumeBranchVolume); + vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleEpsilon, + b->TopVolumeData.TopVolumeBranchSaddleEpsilon); + vtkm::cont::Algorithm::Copy(mergedUniqueBranchUpperEnd, + b->TopVolumeData.TopVolumeBranchUpperEndGRId); + vtkm::cont::Algorithm::Copy(mergedUniqueBranchLowerEnd, + b->TopVolumeData.TopVolumeBranchLowerEndGRId); + } + } + } + + for (int cc = 0; cc < rp.out_link().size(); ++cc) + { + + auto target = rp.out_link().target(cc); + if (target.gid != selfid) + { +#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH + rp.enqueue(target, b->GlobalBlockId); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Block " << b->GlobalBlockId << " enqueue to Block " << target.gid); +#endif + + vtkm::Id nBranches = b->TopVolumeData.TopVolumeBranchRootGRId.GetNumberOfValues(); + + rp.enqueue(target, nBranches); + rp.enqueue(target, b->TopVolumeData.TopVolumeBranchRootGRId); + rp.enqueue(target, b->TopVolumeData.TopVolumeBranchVolume); + rp.enqueue(target, b->TopVolumeData.TopVolumeBranchSaddleEpsilon); + rp.enqueue(target, b->TopVolumeData.TopVolumeBranchUpperEndGRId); + rp.enqueue(target, b->TopVolumeData.TopVolumeBranchLowerEndGRId); + rp.enqueue(target, b->TopVolumeData.TopVolumeBranchSaddleIsoValue); + } + } +} + +} // namespace internal +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesFunctor.h b/vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesFunctor.h new file mode 100644 index 0000000000..9a731cbd3b --- /dev/null +++ b/vtkm/filter/scalar_topology/internal/SelectTopVolumeBranchesFunctor.h @@ -0,0 +1,96 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_internal_SelectTopVolumeBranchesFunctor_h +#define vtk_m_filter_scalar_topology_internal_SelectTopVolumeBranchesFunctor_h + +#include + +// clang-format off +VTKM_THIRDPARTY_PRE_INCLUDE +#include +VTKM_THIRDPARTY_POST_INCLUDE +// clang-format on + + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ +namespace internal +{ + +struct SelectTopVolumeBranchesFunctor +{ + SelectTopVolumeBranchesFunctor(const vtkm::Id& nSB, const vtkm::cont::LogLevel& timingsLogLevel) + : NumSavedBranches(nSB) + , TimingsLogLevel(timingsLogLevel) + { + } + + void operator()(SelectTopVolumeBranchesBlock* b, + const vtkmdiy::ReduceProxy& rp, // communication proxy + const vtkmdiy::RegularSwapPartners& // partners of the current block (unused) + ) const; + + const vtkm::Id NumSavedBranches; + const vtkm::cont::LogLevel TimingsLogLevel; +}; + +} // namespace internal +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.cxx b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.cxx deleted file mode 100644 index 9a3e9d2ca5..0000000000 --- a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.cxx +++ /dev/null @@ -1,229 +0,0 @@ -//============================================================================ -// 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 (c) 2018, The Regents of the University of California, through -// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals -// from the U.S. Dept. of Energy). All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// (1) Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// (2) Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// (3) Neither the name of the University of California, Lawrence Berkeley National -// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -// OF THE POSSIBILITY OF SUCH DAMAGE. -// -//============================================================================= -// -// This code is an extension of the algorithm presented in the paper: -// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. -// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. -// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization -// (LDAV), October 2016, Baltimore, Maryland. -// -// The PPP2 algorithm and software were jointly developed by -// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and -// Oliver Ruebel (LBNL) -//============================================================================== - -#include -#include -#include -#include -#include - -#ifdef DEBUG_PRINT -#include -#endif - -namespace vtkm -{ -namespace filter -{ -namespace scalar_topology -{ -namespace internal -{ - -SelectTopVolumeContoursBlock::SelectTopVolumeContoursBlock(vtkm::Id localBlockNo, int globalBlockId) - : LocalBlockNo(localBlockNo) - , GlobalBlockId(globalBlockId) -{ -} - -void SelectTopVolumeContoursBlock::SortBranchByVolume( - const vtkm::cont::DataSet& hierarchicalTreeDataSet, - const vtkm::Id totalVolume) -{ - /// Pipeline to compute the branch volume - /// 1. check both ends of the branch. If both leaves, then main branch, volume = totalVolume - /// 2. for other branches, check the direction of the inner superarc - /// branch volume = (inner superarc points to the senior-most node) ? - /// dependentVolume[innerSuperarc] : - /// reverseVolume[innerSuperarc] - /// NOTE: reverseVolume = totalVolume - dependentVolume + intrinsicVolume - - // Generally, if ending superarc has intrinsicVol == dependentVol, then it is a leaf node - vtkm::cont::ArrayHandle isLowerLeaf; - vtkm::cont::ArrayHandle isUpperLeaf; - - auto upperEndIntrinsicVolume = hierarchicalTreeDataSet.GetField("UpperEndIntrinsicVolume") - .GetData() - .AsArrayHandle>(); - auto upperEndDependentVolume = hierarchicalTreeDataSet.GetField("UpperEndDependentVolume") - .GetData() - .AsArrayHandle>(); - auto lowerEndIntrinsicVolume = hierarchicalTreeDataSet.GetField("LowerEndIntrinsicVolume") - .GetData() - .AsArrayHandle>(); - auto lowerEndDependentVolume = hierarchicalTreeDataSet.GetField("LowerEndDependentVolume") - .GetData() - .AsArrayHandle>(); - auto lowerEndSuperarcId = hierarchicalTreeDataSet.GetField("LowerEndSuperarcId") - .GetData() - .AsArrayHandle>(); - auto upperEndSuperarcId = hierarchicalTreeDataSet.GetField("UpperEndSuperarcId") - .GetData() - .AsArrayHandle>(); - auto branchRoot = hierarchicalTreeDataSet.GetField("BranchRoot") - .GetData() - .AsArrayHandle>(); - - vtkm::cont::Algorithm::Transform( - upperEndIntrinsicVolume, upperEndDependentVolume, isUpperLeaf, vtkm::Equal()); - vtkm::cont::Algorithm::Transform( - lowerEndIntrinsicVolume, lowerEndDependentVolume, isLowerLeaf, vtkm::Equal()); - - - // NOTE: special cases (one-superarc branches) exist - // if the upper end superarc == lower end superarc == branch root superarc - // then it's probably not a leaf-leaf branch (Both equality has to be satisfied!) - // exception: the entire domain has only one superarc (intrinsic == dependent == total - 1) - // then it is a leaf-leaf branch - vtkm::cont::Invoker invoke; - - vtkm::worklet::scalar_topology::select_top_volume_contours::ClarifyBranchEndSupernodeTypeWorklet - clarifyNodeTypeWorklet(totalVolume); - - invoke(clarifyNodeTypeWorklet, - lowerEndSuperarcId, - lowerEndIntrinsicVolume, - upperEndSuperarcId, - upperEndIntrinsicVolume, - branchRoot, - isLowerLeaf, - isUpperLeaf); - - vtkm::cont::UnknownArrayHandle upperEndValue = - hierarchicalTreeDataSet.GetField("UpperEndValue").GetData(); - - // Based on the direction info of the branch, store epsilon direction and isovalue of the saddle - auto resolveArray = [&](const auto& inArray) { - using InArrayHandleType = std::decay_t; - using ValueType = typename InArrayHandleType::ValueType; - - vtkm::cont::ArrayHandle branchSaddleIsoValue; - branchSaddleIsoValue.Allocate(isLowerLeaf.GetNumberOfValues()); - this->BranchSaddleEpsilon.Allocate(isLowerLeaf.GetNumberOfValues()); - - vtkm::worklet::scalar_topology::select_top_volume_contours::UpdateInfoByBranchDirectionWorklet< - ValueType> - updateInfoWorklet; - auto lowerEndValue = hierarchicalTreeDataSet.GetField("LowerEndValue") - .GetData() - .AsArrayHandle>(); - - invoke(updateInfoWorklet, - isLowerLeaf, - isUpperLeaf, - inArray, - lowerEndValue, - this->BranchSaddleEpsilon, - branchSaddleIsoValue); - this->BranchSaddleIsoValue = branchSaddleIsoValue; - }; - - upperEndValue.CastAndCallForTypes( - resolveArray); - - vtkm::worklet::contourtree_augmented::IdArrayType branchVolume; - vtkm::worklet::scalar_topology::select_top_volume_contours::GetBranchVolumeWorklet - getBranchVolumeWorklet(totalVolume); - - invoke(getBranchVolumeWorklet, // worklet - lowerEndSuperarcId, // input - lowerEndIntrinsicVolume, // input - lowerEndDependentVolume, // input - upperEndSuperarcId, // input - upperEndIntrinsicVolume, // input - upperEndDependentVolume, // input - isLowerLeaf, - isUpperLeaf, - branchVolume); // output - -#ifdef DEBUG_PRINT - std::stringstream resultStream; - resultStream << "Branch Volume In The Block" << std::endl; - const vtkm::Id nVolume = branchVolume.GetNumberOfValues(); - vtkm::worklet::contourtree_augmented::PrintHeader(nVolume, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "BranchVolume", branchVolume, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices("isLowerLeaf", isLowerLeaf, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices("isUpperLeaf", isUpperLeaf, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "LowerEndIntrinsicVol", lowerEndIntrinsicVolume, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "LowerEndDependentVol", lowerEndDependentVolume, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "UpperEndIntrinsicVol", upperEndIntrinsicVolume, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "UpperEndDependentVol", upperEndDependentVolume, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "LowerEndSuperarc", lowerEndSuperarcId, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices( - "UpperEndSuperarc", upperEndSuperarcId, -1, resultStream); - vtkm::worklet::contourtree_augmented::PrintIndices("BranchRoot", branchRoot, -1, resultStream); - resultStream << std::endl; - VTKM_LOG_S(vtkm::cont::LogLevel::Info, resultStream.str()); -#endif - - vtkm::cont::Algorithm::Copy(branchVolume, this->BranchVolume); - - const vtkm::Id nBranches = lowerEndSuperarcId.GetNumberOfValues(); - vtkm::cont::ArrayHandleIndex branchesIdx(nBranches); - vtkm::worklet::contourtree_augmented::IdArrayType sortedBranches; - vtkm::cont::Algorithm::Copy(branchesIdx, sortedBranches); - - // sort the branch volume - vtkm::cont::Algorithm::SortByKey(branchVolume, sortedBranches, vtkm::SortGreater()); - vtkm::cont::Algorithm::Copy(sortedBranches, this->SortedBranchByVolume); -} - -} // namespace internal -} // namespace scalar_topology -} // namespace filter -} // namespace vtkm diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx b/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx deleted file mode 100644 index cba0501f4e..0000000000 --- a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx +++ /dev/null @@ -1,448 +0,0 @@ -//============================================================================ -// 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 (c) 2018, The Regents of the University of California, through -// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals -// from the U.S. Dept. of Energy). All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// (1) Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// (2) Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// (3) Neither the name of the University of California, Lawrence Berkeley National -// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -// OF THE POSSIBILITY OF SUCH DAMAGE. -// -//============================================================================= -// -// This code is an extension of the algorithm presented in the paper: -// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. -// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. -// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization -// (LDAV), October 2016, Baltimore, Maryland. -// -// The PPP2 algorithm and software were jointly developed by -// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and -// Oliver Ruebel (LBNL) -//============================================================================== - -#include -#include -#include -#include - -#include - -#ifdef DEBUG_PRINT -#define DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH -#include -#endif - -namespace vtkm -{ -namespace filter -{ -namespace scalar_topology -{ -namespace internal -{ - -void SelectTopVolumeContoursFunctor::operator()( - SelectTopVolumeContoursBlock* b, - const vtkmdiy::ReduceProxy& rp // communication proxy -) const -{ - if (this->nSavedBranches < 1) - return; - // Get our rank and DIY id - //const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank(); - const auto selfid = rp.gid(); - - // Aliases to reduce verbosity - using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; - - vtkm::cont::Invoker invoke; - - if (rp.in_link().size() == 0) - { - for (int cc = 0; cc < rp.out_link().size(); ++cc) - { - auto target = rp.out_link().target(cc); - if (target.gid != selfid) - { -#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH - rp.enqueue(target, b->GlobalBlockId); - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Block " << b->GlobalBlockId << " enqueue to Block " << target.gid); -#endif - auto topVolBranchRootGRIdPortal = b->TopVolumeBranchRootGRId.ReadPortal(); - auto topVolBranchVolumePortal = b->TopVolumeBranchVolume.ReadPortal(); - auto topVolBranchSaddleEpsilonPortal = b->TopVolumeBranchSaddleEpsilon.ReadPortal(); - auto topVolBranchUpperEndPortal = b->TopVolumeBranchUpperEndGRId.ReadPortal(); - auto topVolBranchLowerEndPortal = b->TopVolumeBranchLowerEndGRId.ReadPortal(); - - vtkm::Id nBranches = topVolBranchRootGRIdPortal.GetNumberOfValues(); - - rp.enqueue(target, nBranches); - for (vtkm::Id branch = 0; branch < nBranches; ++branch) - rp.enqueue(target, topVolBranchRootGRIdPortal.Get(branch)); - for (vtkm::Id branch = 0; branch < nBranches; ++branch) - rp.enqueue(target, topVolBranchVolumePortal.Get(branch)); - for (vtkm::Id branch = 0; branch < nBranches; ++branch) - rp.enqueue(target, topVolBranchSaddleEpsilonPortal.Get(branch)); - for (vtkm::Id branch = 0; branch < nBranches; ++branch) - rp.enqueue(target, topVolBranchUpperEndPortal.Get(branch)); - for (vtkm::Id branch = 0; branch < nBranches; ++branch) - rp.enqueue(target, topVolBranchLowerEndPortal.Get(branch)); - - auto resolveArray = [&](const auto& inArray) { - using InArrayHandleType = std::decay_t; - using ValueType = typename InArrayHandleType::ValueType; - auto topVolBranchSaddleIsoValuePortal = inArray.ReadPortal(); - for (vtkm::Id branch = 0; branch < nBranches; ++branch) - rp.enqueue(target, topVolBranchSaddleIsoValuePortal.Get(branch)); - }; - b->TopVolumeBranchSaddleIsoValue - .CastAndCallForTypes(resolveArray); - - // rp.enqueue(target, b->TopVolumeBranchRootGRId); - // rp.enqueue(target, b->TopVolumeBranchVolume); - } - } - } - else - { - for (int i = 0; i < rp.in_link().size(); ++i) - { - int ingid = rp.in_link().target(i).gid; - if (ingid == selfid) - continue; - - // copy incoming to the block -#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH - int incomingGlobalBlockId; - rp.dequeue(ingid, incomingGlobalBlockId); - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Combining local block " << b->GlobalBlockId << " with incoming block " - << incomingGlobalBlockId); -#endif - - // dequeue the data from other blocks. - // nIncomingBranches - // array of incoming branch global regular ID - // array of incoming branch volume - // array of branch epsilon direction - // array of branch saddle end value - // TODO/FIXME: This is a workaround for a bug in DIY/vtk-m. - // Replace with dequeuing ArrayHandles once bug is fixed. - vtkm::Id nIncoming; - rp.dequeue(ingid, nIncoming); - - IdArrayType incomingTopVolBranchGRId; - incomingTopVolBranchGRId.Allocate(nIncoming); - auto incomingTopVolBranchGRIdPortal = incomingTopVolBranchGRId.WritePortal(); - for (vtkm::Id branch = 0; branch < nIncoming; ++branch) - { - vtkm::Id incomingTmpBranchGRId; - rp.dequeue(ingid, incomingTmpBranchGRId); - incomingTopVolBranchGRIdPortal.Set(branch, incomingTmpBranchGRId); - } - - // TODO/FIXME: This is a workaround for a bug in DIY/vtk-m. - // Replace with dequeuing ArrayHandles once bug is fixed. - // rp.dequeue(ingid, incomingTopVolBranchGRId); - - IdArrayType incomingTopVolBranchVolume; - incomingTopVolBranchVolume.Allocate(nIncoming); - auto incomingTopVolBranchVolumePortal = incomingTopVolBranchVolume.WritePortal(); - for (vtkm::Id branch = 0; branch < nIncoming; ++branch) - { - vtkm::Id incomingTmpBranchVolume; - rp.dequeue(ingid, incomingTmpBranchVolume); - incomingTopVolBranchVolumePortal.Set(branch, incomingTmpBranchVolume); - } - - // TODO/FIXME: This is a workaround for a bug in DIY/vtk-m. - // Replace with dequeuing ArrayHandles once bug is fixed. - // rp.dequeue(ingid, incomingTopVolBranchVolume); - - IdArrayType incomingTopVolBranchSaddleEpsilon; - incomingTopVolBranchSaddleEpsilon.Allocate(nIncoming); - auto incomingTopVolBranchSaddleEpsilonPortal = - incomingTopVolBranchSaddleEpsilon.WritePortal(); - for (vtkm::Id branch = 0; branch < nIncoming; ++branch) - { - vtkm::Id incomingTmpBranchSaddleEpsilon; - rp.dequeue(ingid, incomingTmpBranchSaddleEpsilon); - incomingTopVolBranchSaddleEpsilonPortal.Set(branch, incomingTmpBranchSaddleEpsilon); - } - - // TODO/FIXME: This is a workaround for a bug in DIY/vtk-m. - // Replace with dequeuing ArrayHandles once bug is fixed. - // rp.dequeue(ingid, incomingTopVolBranchSaddleEpsilon); - - IdArrayType incomingTopVolBranchUpperEnd; - incomingTopVolBranchUpperEnd.Allocate(nIncoming); - auto incomingTopVolBranchUpperEndPortal = incomingTopVolBranchUpperEnd.WritePortal(); - for (vtkm::Id branch = 0; branch < nIncoming; ++branch) - { - vtkm::Id incomingTmpBranchUpperEnd; - rp.dequeue(ingid, incomingTmpBranchUpperEnd); - incomingTopVolBranchUpperEndPortal.Set(branch, incomingTmpBranchUpperEnd); - } - - // TODO/FIXME: This is a workaround for a bug in DIY/vtk-m. - // Replace with dequeuing ArrayHandles once bug is fixed. - // rp.dequeue(ingid, incomingTopVolBranchUpperEnd); - - IdArrayType incomingTopVolBranchLowerEnd; - incomingTopVolBranchLowerEnd.Allocate(nIncoming); - auto incomingTopVolBranchLowerEndPortal = incomingTopVolBranchLowerEnd.WritePortal(); - for (vtkm::Id branch = 0; branch < nIncoming; ++branch) - { - vtkm::Id incomingTmpBranchLowerEnd; - rp.dequeue(ingid, incomingTmpBranchLowerEnd); - incomingTopVolBranchLowerEndPortal.Set(branch, incomingTmpBranchLowerEnd); - } - - // TODO/FIXME: This is a workaround for a bug in DIY/vtk-m. - // Replace with dequeuing ArrayHandles once bug is fixed. - // rp.dequeue(ingid, incomingTopVolBranchLowerEnd); - - auto resolveArray = [&](auto& inArray) { - using InArrayHandleType = std::decay_t; - using ValueType = typename InArrayHandleType::ValueType; - InArrayHandleType incomingTopVolBranchSaddleIsoValue; - incomingTopVolBranchSaddleIsoValue.Allocate(nIncoming); - auto incomingTopVolBranchSaddleIsoValuePortal = - incomingTopVolBranchSaddleIsoValue.WritePortal(); - for (vtkm::Id branch = 0; branch < nIncoming; ++branch) - { - ValueType incomingSaddleValue; - rp.dequeue(ingid, incomingSaddleValue); - incomingTopVolBranchSaddleIsoValuePortal.Set(branch, incomingSaddleValue); - } - - // TODO/FIXME: This is a workaround for a bug in DIY/vtk-m. - // Replace with dequeuing ArrayHandles once bug is fixed. - // rp.dequeue(ingid, incomingTopVolBranchSaddleIsoValue); - - vtkm::Id nSelf = b->TopVolumeBranchRootGRId.GetNumberOfValues(); - -#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "nIncoming = " << nIncoming << ", nSelf = " << nSelf); - { - std::stringstream rs; - vtkm::worklet::contourtree_augmented::PrintHeader(nIncoming, rs); - vtkm::worklet::contourtree_augmented::PrintIndices( - "incomingTopBranchId", incomingTopVolBranchGRId, -1, rs); - vtkm::worklet::contourtree_augmented::PrintIndices( - "incomingTopBranchVol", incomingTopVolBranchVolume, -1, rs); - vtkm::worklet::contourtree_augmented::PrintValues( - "incomingSaddleVal", incomingTopVolBranchSaddleIsoValue, -1, rs); - - vtkm::worklet::contourtree_augmented::PrintHeader(nSelf, rs); - vtkm::worklet::contourtree_augmented::PrintIndices( - "selfTopBranchId", b->TopVolumeBranchRootGRId, -1, rs); - vtkm::worklet::contourtree_augmented::PrintIndices( - "selfTopBranchVol", b->TopVolumeBranchVolume, -1, rs); - vtkm::worklet::contourtree_augmented::PrintValues( - "selfTopSaddleVal", inArray, -1, rs); - VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); - } -#endif - // merge incoming branches with self branches - IdArrayType mergedTopVolBranchGRId; - IdArrayType mergedTopVolBranchVolume; - IdArrayType mergedTopVolBranchSaddleEpsilon; - IdArrayType mergedTopVolBranchUpperEnd; - IdArrayType mergedTopVolBranchLowerEnd; - InArrayHandleType mergedTopVolBranchSaddleIsoValue; - mergedTopVolBranchGRId.Allocate(nIncoming + nSelf); - mergedTopVolBranchVolume.Allocate(nIncoming + nSelf); - mergedTopVolBranchSaddleEpsilon.Allocate(nIncoming + nSelf); - mergedTopVolBranchUpperEnd.Allocate(nIncoming + nSelf); - mergedTopVolBranchLowerEnd.Allocate(nIncoming + nSelf); - mergedTopVolBranchSaddleIsoValue.Allocate(nIncoming + nSelf); - - vtkm::cont::Algorithm::CopySubRange( - incomingTopVolBranchGRId, 0, nIncoming, mergedTopVolBranchGRId, 0); - vtkm::cont::Algorithm::CopySubRange( - incomingTopVolBranchVolume, 0, nIncoming, mergedTopVolBranchVolume, 0); - vtkm::cont::Algorithm::CopySubRange( - incomingTopVolBranchSaddleEpsilon, 0, nIncoming, mergedTopVolBranchSaddleEpsilon, 0); - vtkm::cont::Algorithm::CopySubRange( - incomingTopVolBranchUpperEnd, 0, nIncoming, mergedTopVolBranchUpperEnd, 0); - vtkm::cont::Algorithm::CopySubRange( - incomingTopVolBranchLowerEnd, 0, nIncoming, mergedTopVolBranchLowerEnd, 0); - vtkm::cont::Algorithm::CopySubRange( - incomingTopVolBranchSaddleIsoValue, 0, nIncoming, mergedTopVolBranchSaddleIsoValue, 0); - vtkm::cont::Algorithm::CopySubRange( - b->TopVolumeBranchRootGRId, 0, nSelf, mergedTopVolBranchGRId, nIncoming); - vtkm::cont::Algorithm::CopySubRange( - b->TopVolumeBranchVolume, 0, nSelf, mergedTopVolBranchVolume, nIncoming); - vtkm::cont::Algorithm::CopySubRange( - b->TopVolumeBranchSaddleEpsilon, 0, nSelf, mergedTopVolBranchSaddleEpsilon, nIncoming); - vtkm::cont::Algorithm::CopySubRange( - b->TopVolumeBranchUpperEndGRId, 0, nSelf, mergedTopVolBranchUpperEnd, nIncoming); - vtkm::cont::Algorithm::CopySubRange( - b->TopVolumeBranchLowerEndGRId, 0, nSelf, mergedTopVolBranchLowerEnd, nIncoming); - vtkm::cont::Algorithm::CopySubRange( - inArray, 0, nSelf, mergedTopVolBranchSaddleIsoValue, nIncoming); - - // Sort all branches (incoming + self) based on volume - // sorting key: (volume, branch global regular ID) - // the highest volume comes first, the lowest branch GR ID comes first - vtkm::cont::ArrayHandleIndex mergedBranchId(nIncoming + nSelf); - IdArrayType sortedBranchId; - vtkm::cont::Algorithm::Copy(mergedBranchId, sortedBranchId); - vtkm::worklet::scalar_topology::select_top_volume_contours::BranchVolumeComparator - branchVolumeComparator(mergedTopVolBranchGRId, mergedTopVolBranchVolume); - vtkm::cont::Algorithm::Sort(sortedBranchId, branchVolumeComparator); - - // permute the branch information based on sorting - IdArrayType permutedTopVolBranchGRId; - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - mergedTopVolBranchGRId, sortedBranchId, permutedTopVolBranchGRId); - IdArrayType permutedTopVolBranchVolume; - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - mergedTopVolBranchVolume, sortedBranchId, permutedTopVolBranchVolume); - IdArrayType permutedTopVolBranchSaddleEpsilon; - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - mergedTopVolBranchSaddleEpsilon, sortedBranchId, permutedTopVolBranchSaddleEpsilon); - IdArrayType permutedTopVolBranchUpperEnd; - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - mergedTopVolBranchUpperEnd, sortedBranchId, permutedTopVolBranchUpperEnd); - IdArrayType permutedTopVolBranchLowerEnd; - vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( - mergedTopVolBranchLowerEnd, sortedBranchId, permutedTopVolBranchLowerEnd); - InArrayHandleType permutedTopVolBranchSaddleIsoValue; - vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( - mergedTopVolBranchSaddleIsoValue, sortedBranchId, permutedTopVolBranchSaddleIsoValue); - -#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH - { - std::stringstream rs; - vtkm::worklet::contourtree_augmented::PrintHeader(nIncoming + nSelf, rs); - vtkm::worklet::contourtree_augmented::PrintIndices( - "permutedTopBranchId", permutedTopVolBranchGRId, -1, rs); - vtkm::worklet::contourtree_augmented::PrintIndices( - "permutedTopBranchVol", permutedTopVolBranchVolume, -1, rs); - vtkm::worklet::contourtree_augmented::PrintValues( - "permutedTopSaddleVal", permutedTopVolBranchSaddleIsoValue, -1, rs); - } -#endif - - // there may be duplicate branches. We remove duplicate branches based on global regular IDs - // We can reuse the filter from removing duplicate branches in the process of collecting branches - IdArrayType oneIfUniqueBranch; - oneIfUniqueBranch.Allocate(nIncoming + nSelf); - vtkm::worklet::scalar_topology::hierarchical_volumetric_branch_decomposer:: - OneIfBranchEndWorklet oneIfUniqueWorklet; - invoke(oneIfUniqueWorklet, mergedBranchId, permutedTopVolBranchGRId, oneIfUniqueBranch); - - // Remove duplicate - IdArrayType mergedUniqueBranchGRId; - IdArrayType mergedUniqueBranchVolume; - IdArrayType mergedUniqueBranchSaddleEpsilon; - IdArrayType mergedUniqueBranchUpperEnd; - IdArrayType mergedUniqueBranchLowerEnd; - InArrayHandleType mergedUniqueBranchSaddleIsoValue; - - vtkm::cont::Algorithm::CopyIf( - permutedTopVolBranchGRId, oneIfUniqueBranch, mergedUniqueBranchGRId); - vtkm::cont::Algorithm::CopyIf( - permutedTopVolBranchVolume, oneIfUniqueBranch, mergedUniqueBranchVolume); - vtkm::cont::Algorithm::CopyIf( - permutedTopVolBranchSaddleEpsilon, oneIfUniqueBranch, mergedUniqueBranchSaddleEpsilon); - vtkm::cont::Algorithm::CopyIf( - permutedTopVolBranchUpperEnd, oneIfUniqueBranch, mergedUniqueBranchUpperEnd); - vtkm::cont::Algorithm::CopyIf( - permutedTopVolBranchLowerEnd, oneIfUniqueBranch, mergedUniqueBranchLowerEnd); - vtkm::cont::Algorithm::CopyIf( - permutedTopVolBranchSaddleIsoValue, oneIfUniqueBranch, mergedUniqueBranchSaddleIsoValue); - - vtkm::Id nMergedUnique = mergedUniqueBranchGRId.GetNumberOfValues(); - -#ifdef DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH - { - std::stringstream rs; - vtkm::worklet::contourtree_augmented::PrintHeader(nMergedUnique, rs); - vtkm::worklet::contourtree_augmented::PrintIndices( - "mergedUniqueBranchId", mergedUniqueBranchGRId, -1, rs); - vtkm::worklet::contourtree_augmented::PrintIndices( - "mergedUniqueBranchVol", mergedUniqueBranchVolume, -1, rs); - vtkm::worklet::contourtree_augmented::PrintValues( - "mergedUniqueSaddleVal", mergedUniqueBranchSaddleIsoValue, -1, rs); - } -#endif - - // After removing duplicate, if there are more branches than we need - // We only save the top nSavedBranches branches - if (nMergedUnique > this->nSavedBranches) - { - vtkm::cont::Algorithm::CopySubRange( - mergedUniqueBranchGRId, 0, this->nSavedBranches, b->TopVolumeBranchRootGRId); - vtkm::cont::Algorithm::CopySubRange( - mergedUniqueBranchVolume, 0, this->nSavedBranches, b->TopVolumeBranchVolume); - vtkm::cont::Algorithm::CopySubRange(mergedUniqueBranchSaddleEpsilon, - 0, - this->nSavedBranches, - b->TopVolumeBranchSaddleEpsilon); - vtkm::cont::Algorithm::CopySubRange( - mergedUniqueBranchUpperEnd, 0, this->nSavedBranches, b->TopVolumeBranchUpperEndGRId); - vtkm::cont::Algorithm::CopySubRange( - mergedUniqueBranchLowerEnd, 0, this->nSavedBranches, b->TopVolumeBranchLowerEndGRId); - // InArrayHandleType subRangeUniqueBranchSaddleIsoValue; - inArray.Allocate(this->nSavedBranches); - vtkm::cont::Algorithm::CopySubRange( - mergedUniqueBranchSaddleIsoValue, 0, this->nSavedBranches, inArray); - // inArray = subRangeUniqueBranchSaddleIsoValue; - } - else - { - vtkm::cont::Algorithm::Copy(mergedUniqueBranchGRId, b->TopVolumeBranchRootGRId); - vtkm::cont::Algorithm::Copy(mergedUniqueBranchVolume, b->TopVolumeBranchVolume); - vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleEpsilon, - b->TopVolumeBranchSaddleEpsilon); - vtkm::cont::Algorithm::Copy(mergedUniqueBranchUpperEnd, b->TopVolumeBranchUpperEndGRId); - vtkm::cont::Algorithm::Copy(mergedUniqueBranchLowerEnd, b->TopVolumeBranchLowerEndGRId); - inArray.Allocate(nMergedUnique); - vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleIsoValue, inArray); - } - }; - b->TopVolumeBranchSaddleIsoValue - .CastAndCallForTypes(resolveArray); - } - } -} - -} // namespace internal -} // namespace scalar_topology -} // namespace filter -} // namespace vtkm diff --git a/vtkm/filter/scalar_topology/internal/UpdateParentBranchFunctor.cxx b/vtkm/filter/scalar_topology/internal/UpdateParentBranchFunctor.cxx new file mode 100644 index 0000000000..0c928e0e0a --- /dev/null +++ b/vtkm/filter/scalar_topology/internal/UpdateParentBranchFunctor.cxx @@ -0,0 +1,391 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== +#include +#include +#include +#include +#include + +#include + +#include + +#ifdef DEBUG_PRINT +#define DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE +#include +#endif + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ +namespace internal +{ + +void UpdateParentBranchFunctor::operator()( + SelectTopVolumeBranchesBlock* b, + const vtkmdiy::ReduceProxy& rp, // communication proxy + const vtkmdiy::RegularSwapPartners& // partners of the current block (unused) +) const +{ + // Get our rank and DIY id + const vtkm::Id rank = vtkm::cont::EnvironmentTracker::GetCommunicator().rank(); + const auto selfid = rp.gid(); + + // Aliases to reduce verbosity + using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; + + vtkm::cont::Invoker invoke; + + std::vector incoming; + rp.incoming(incoming); + for (const int ingid : incoming) + { + // NOTE/IMPORTANT: In each round we should have only one swap partner (despite for-loop here). + // If that assumption does not hold, it will break things. + // NOTE/IMPORTANT: This assumption only holds if the number of blocks is a power of two. + // Otherwise, we may need to process more than one incoming block + if (ingid != selfid) + { + + // copy incoming to the block +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + int incomingGlobalBlockId; + rp.dequeue(ingid, incomingGlobalBlockId); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Combining local block " << b->GlobalBlockId << " with incoming block " + << incomingGlobalBlockId); +#endif + vtkm::Id nSelfMaxBranch = b->TopVolumeData.ExtraMaximaBranchOrder.GetNumberOfValues(); + vtkm::Id nSelfMinBranch = b->TopVolumeData.ExtraMinimaBranchOrder.GetNumberOfValues(); + + // dequeue the data from other blocks. + // nExtraMaximaBranches (incoming) + // array of incoming maxima branch order + // array of incoming maxima branch isovalue + // array of incoming maxima branch saddle global regular ids + // nExtraMinimaBranches (incoming) + // array of incoming minima branch order + // array of incoming minima branch isovalue + // array of incoming minima branch saddle global regular ids + + // the dequeue'd nIncomingMaxBranch is an array + // because vtkmdiy has bugs on communicating single variables + IdArrayType nIncomingMaxBranchWrapper; + rp.dequeue(ingid, nIncomingMaxBranchWrapper); + vtkm::Id nIncomingMaxBranch = vtkm::cont::ArrayGetValue(0, nIncomingMaxBranchWrapper); + + IdArrayType incomingMaxBranchOrder; + IdArrayType incomingMaxBranchSaddleGRId; + vtkm::cont::UnknownArrayHandle incomingMaxBranchIsoValue; + + auto resolveMaxArray = [&](auto& inArray) { + using InArrayHandleType = std::decay_t; +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + { + using ValueType = typename InArrayHandleType::ValueType; + std::stringstream rs; + vtkm::worklet::contourtree_augmented::PrintHeader(nIncomingMaxBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "incomingMaxBranchOrder", incomingMaxBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "incomingMaxBranchVal", incomingMaxBranchIsoValue, -1, rs); + + vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMaxBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfMaxBranchOrder", b->TopVolumeData.ExtraMaximaBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "selfMaxBranchVal", inArray, -1, rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + InArrayHandleType incomingMaxBranchIsoValueCast = + incomingMaxBranchIsoValue.AsArrayHandle(); + + // We sort both isovalue and saddle global regular IDs by order + { + IdArrayType incomingMaxBranchOrderDup; + // Note: using two SortByKey is not the most efficient way to sort both arrays by branch order. + // But, the array size should be pretty small, so it should not cause any efficiency issue. + vtkm::cont::Algorithm::Copy(incomingMaxBranchOrder, incomingMaxBranchOrderDup); + vtkm::cont::Algorithm::SortByKey(incomingMaxBranchOrder, incomingMaxBranchIsoValueCast); + vtkm::cont::Algorithm::SortByKey(incomingMaxBranchOrderDup, incomingMaxBranchSaddleGRId); + } + + vtkm::worklet::scalar_topology::select_top_volume_branches::UpdateOuterSaddle + updateValueOnMaxBranch; + invoke(updateValueOnMaxBranch, + b->TopVolumeData.ExtraMaximaBranchOrder, + inArray, + b->TopVolumeData.ExtraMaximaBranchSaddleGRId, + incomingMaxBranchOrder, + incomingMaxBranchIsoValueCast, + incomingMaxBranchSaddleGRId); + +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + { + std::stringstream rs; + rs << "After update, block " << b->LocalBlockNo << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMaxBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfMaxBranchOrder", b->TopVolumeData.ExtraMaximaBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "selfMaxBranchVal", inArray, -1, rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + }; + + if (nIncomingMaxBranch > 0) + { + rp.dequeue(ingid, incomingMaxBranchOrder); + rp.dequeue(ingid, incomingMaxBranchIsoValue); + rp.dequeue(ingid, incomingMaxBranchSaddleGRId); + if (nSelfMaxBranch > 0) + b->TopVolumeData.ExtraMaximaBranchIsoValue + .CastAndCallForTypes( + resolveMaxArray); + } + + // Apply the same pipeline for branches with minima to extract + // the dequeue'd nIncomingMinBranch is an array + // because vtkmdiy has bugs on communicating single variables + IdArrayType nIncomingMinBranchWrapper; + rp.dequeue(ingid, nIncomingMinBranchWrapper); + vtkm::Id nIncomingMinBranch = vtkm::cont::ArrayGetValue(0, nIncomingMinBranchWrapper); + + IdArrayType incomingMinBranchOrder; + IdArrayType incomingMinBranchSaddleGRId; + vtkm::cont::UnknownArrayHandle incomingMinBranchIsoValue; + + auto resolveMinArray = [&](auto& inArray) { + using InArrayHandleType = std::decay_t; +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + { + using ValueType = typename InArrayHandleType::ValueType; + std::stringstream rs; + vtkm::worklet::contourtree_augmented::PrintHeader(nIncomingMinBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "incomingMinBranchOrder", incomingMinBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "incomingMinBranchVal", incomingMinBranchIsoValue, -1, rs); + + vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMinBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfMinBranchOrder", b->TopVolumeData.ExtraMinimaBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "selfMinBranchVal", inArray, -1, rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + InArrayHandleType incomingMinBranchIsoValueCast = + incomingMinBranchIsoValue.AsArrayHandle(); + + // We sort both isovalue and saddle global regular IDs by order + { + IdArrayType incomingMinBranchOrderDup; + // Note: using two SortByKey is not the most efficient way to sort both arrays by branch order. + // But, the array size should be pretty small, so it should not cause any efficiency issue. + vtkm::cont::Algorithm::Copy(incomingMinBranchOrder, incomingMinBranchOrderDup); + vtkm::cont::Algorithm::SortByKey(incomingMinBranchOrder, incomingMinBranchIsoValueCast); + vtkm::cont::Algorithm::SortByKey(incomingMinBranchOrderDup, incomingMinBranchSaddleGRId); + } + + vtkm::worklet::scalar_topology::select_top_volume_branches::UpdateOuterSaddle + updateValueOnMinBranch; + invoke(updateValueOnMinBranch, + b->TopVolumeData.ExtraMinimaBranchOrder, + inArray, + b->TopVolumeData.ExtraMinimaBranchSaddleGRId, + incomingMinBranchOrder, + incomingMinBranchIsoValueCast, + incomingMinBranchSaddleGRId); + +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + { + std::stringstream rs; + rs << "After update, block " << b->LocalBlockNo << std::endl; + vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMinBranch, rs); + vtkm::worklet::contourtree_augmented::PrintIndices( + "selfMinBranchOrder", b->TopVolumeData.ExtraMinimaBranchOrder, -1, rs); + vtkm::worklet::contourtree_augmented::PrintValues( + "selfMinBranchVal", inArray, -1, rs); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str()); + } +#endif + }; + if (nIncomingMinBranch > 0) + { + rp.dequeue(ingid, incomingMinBranchOrder); + rp.dequeue(ingid, incomingMinBranchIsoValue); + rp.dequeue(ingid, incomingMinBranchSaddleGRId); + if (nSelfMinBranch > 0) + b->TopVolumeData.ExtraMinimaBranchIsoValue + .CastAndCallForTypes( + resolveMinArray); + } + + IdArrayType incomingBranchRootGRId; + rp.dequeue(ingid, incomingBranchRootGRId); + vtkm::cont::ArrayHandle incomingIsParentBranch; + rp.dequeue(ingid, incomingIsParentBranch); + + VTKM_ASSERT(incomingBranchRootGRId.GetNumberOfValues() == + incomingIsParentBranch.GetNumberOfValues()); + + // one way is to combine the incoming array with local array + // and compare the adjacent entries + + // Or, we sort the incomingBranchRootGRId and utilize LowerBounds (or BinarySearch) + vtkm::cont::Algorithm::SortByKey(incomingBranchRootGRId, incomingIsParentBranch); + + // search the index of local branches in the incoming array + IdArrayType tempAlignedIndexFromIncoming; + vtkm::cont::Algorithm::LowerBounds( + incomingBranchRootGRId, b->TopVolumeData.BranchRootGRId, tempAlignedIndexFromIncoming); + + // permute the information based on the index from the previous step + IdArrayType projectBranchRootGRId; + vtkm::cont::ArrayHandle projectIsParentBranch; + vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( + incomingBranchRootGRId, tempAlignedIndexFromIncoming, projectBranchRootGRId); + vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex( + incomingIsParentBranch, tempAlignedIndexFromIncoming, projectIsParentBranch); + + // to update b->TopVolumeData.IsParentBranch, we need LogicalOr between blocks + vtkm::cont::ArrayHandle combinedIsParentBranch; + vtkm::cont::Algorithm::Transform(projectIsParentBranch, + b->TopVolumeData.IsParentBranch, + combinedIsParentBranch, + vtkm::LogicalOr()); + + // it is not guaranteed that a local branch must exist in the incoming branch + // so we validate it by checking the alignment of branch root global regular IDs after permute + vtkm::cont::ArrayHandle projectIsCorrect; + vtkm::cont::Algorithm::Transform( + projectBranchRootGRId, b->TopVolumeData.BranchRootGRId, projectIsCorrect, vtkm::Equal()); + + // for validated entries, we update the IsParentBranch information + vtkm::worklet::scalar_topology::select_top_volume_branches::AssignValueWithStencil + assignIsParent; + invoke( + assignIsParent, projectIsCorrect, combinedIsParentBranch, b->TopVolumeData.IsParentBranch); + + // The logging is commented because the size of exchange is limited by K, + // the number of top-volume branches, which is usually small + std::stringstream dataSizeStream; + // Log the amount of exchanged data + dataSizeStream << " " << std::setw(38) << std::left << "Incoming branch size" + << ": " << nIncomingMaxBranch + nIncomingMinBranch << std::endl; + + VTKM_LOG_S(this->TimingsLogLevel, + std::endl + << " ---------------- Exchange Parent Branch Step ---------------------" + << std::endl + << " Rank : " << rank << std::endl + << " DIY Id : " << selfid << std::endl + << " Inc Id : " << ingid << std::endl + << dataSizeStream.str()); + } + } + + for (int cc = 0; cc < rp.out_link().size(); ++cc) + { + auto target = rp.out_link().target(cc); + if (target.gid != selfid) + { +#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE + rp.enqueue(target, b->GlobalBlockId); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Block " << b->GlobalBlockId << " enqueue to Block " << target.gid); +#endif + // We enqueue the array of nExtraMaxBranches instead of the variable itself + // Because there is a bug when nExtraMaxBranches=0. The dequeue'd value is not 0, but a random number + // vtkmdiy seems to perform better on enqueue/dequeue with containers than single variables + vtkm::Id nExtraMaxBranches = b->TopVolumeData.ExtraMaximaBranchOrder.GetNumberOfValues(); + rp.enqueue(target, vtkm::cont::make_ArrayHandle({ nExtraMaxBranches })); + + if (nExtraMaxBranches) + { + rp.enqueue(target, b->TopVolumeData.ExtraMaximaBranchOrder); + rp.enqueue(target, b->TopVolumeData.ExtraMaximaBranchIsoValue); + rp.enqueue(target, b->TopVolumeData.ExtraMaximaBranchSaddleGRId); + } + + // We enqueue the array of nExtraMinBranches instead of the variable itself + // Because there is a bug when nExtraMinBranches=0. The dequeue'd value is not 0, but a random number + // vtkmdiy seems to perform better on enqueue/dequeue with containers than single variables + vtkm::Id nExtraMinBranches = b->TopVolumeData.ExtraMinimaBranchOrder.GetNumberOfValues(); + rp.enqueue(target, vtkm::cont::make_ArrayHandle({ nExtraMinBranches })); + + if (nExtraMinBranches) + { + rp.enqueue(target, b->TopVolumeData.ExtraMinimaBranchOrder); + rp.enqueue(target, b->TopVolumeData.ExtraMinimaBranchIsoValue); + rp.enqueue(target, b->TopVolumeData.ExtraMinimaBranchSaddleGRId); + } + + // Aside from extra contours, we also need to communicate whether the top-volume branch is a parent branch + // If inconsistent between blocks, it may lead to chaos in the label of contours + // BranchGRId is used to locate the shared branches + rp.enqueue(target, b->TopVolumeData.BranchRootGRId); + rp.enqueue(target, b->TopVolumeData.IsParentBranch); + } + } +} + +} // namespace internal +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm diff --git a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.h b/vtkm/filter/scalar_topology/internal/UpdateParentBranchFunctor.h similarity index 83% rename from vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.h rename to vtkm/filter/scalar_topology/internal/UpdateParentBranchFunctor.h index 023047defb..777e9838f3 100644 --- a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.h +++ b/vtkm/filter/scalar_topology/internal/UpdateParentBranchFunctor.h @@ -50,10 +50,10 @@ // Oliver Ruebel (LBNL) //============================================================================== -#ifndef vtk_m_filter_scalar_topology_internal_SelectTopVolumeContoursFunctor_h -#define vtk_m_filter_scalar_topology_internal_SelectTopVolumeContoursFunctor_h +#ifndef vtk_m_filter_scalar_topology_internal_UpdateParentBranchFunctor_h +#define vtk_m_filter_scalar_topology_internal_UpdateParentBranchFunctor_h -#include +#include // clang-format off VTKM_THIRDPARTY_PRE_INCLUDE @@ -70,21 +70,20 @@ namespace scalar_topology { namespace internal { - -struct SelectTopVolumeContoursFunctor +struct UpdateParentBranchFunctor { - SelectTopVolumeContoursFunctor(const vtkm::Id& nSB) - : nSavedBranches(nSB) + UpdateParentBranchFunctor(const vtkm::cont::LogLevel& timingsLogLevel) + : TimingsLogLevel(timingsLogLevel) { } - void operator()(SelectTopVolumeContoursBlock* b, - const vtkmdiy::ReduceProxy& rp // communication proxy + void operator()(SelectTopVolumeBranchesBlock* b, + const vtkmdiy::ReduceProxy& rp, // communication proxy + const vtkmdiy::RegularSwapPartners& // partners of the current block (unused) ) const; - const vtkm::Id nSavedBranches; + const vtkm::cont::LogLevel TimingsLogLevel; }; - } // namespace internal } // namespace scalar_topology } // namespace filter diff --git a/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h b/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h index a1f3513bfa..e3206c3a0b 100644 --- a/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h +++ b/vtkm/filter/scalar_topology/testing/TestingContourTreeUniformDistributedFilter.h @@ -63,7 +63,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -503,15 +504,22 @@ inline void TestContourTreeUniformDistributedBranchDecomposition8x9(int nBlocks, augmentHierarchicalTree, computeHierarchicalVolumetricBranchDecomposition); - using vtkm::filter::scalar_topology::SelectTopVolumeContoursFilter; + using vtkm::filter::scalar_topology::SelectTopVolumeBranchesFilter; vtkm::Id numBranches = 2; - SelectTopVolumeContoursFilter tp_filter; + SelectTopVolumeBranchesFilter tp_filter; tp_filter.SetSavedBranches(numBranches); auto tp_result = tp_filter.Execute(result); + // add filter for contour extraction + using vtkm::filter::scalar_topology::ExtractTopVolumeContoursFilter; + ExtractTopVolumeContoursFilter iso_filter; + + iso_filter.SetMarchingCubes(false); + auto iso_result = iso_filter.Execute(tp_result); + if (vtkm::cont::EnvironmentTracker::GetCommunicator().rank() == 0) { using Edge = vtkm::worklet::contourtree_distributed::Edge; @@ -641,6 +649,97 @@ inline void TestContourTreeUniformDistributedBranchDecomposition8x9(int nBlocks, } std::cout << "Top Branch Volume: Results Match!" << std::endl; + + if (nBlocks != 2) + return; + + for (vtkm::Id ds_no = 0; ds_no < result.GetNumberOfPartitions(); ++ds_no) + { + auto ds = iso_result.GetPartition(ds_no); + auto isosurfaceEdgesFrom = ds.GetField("IsosurfaceEdgesFrom") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesTo = ds.GetField("IsosurfaceEdgesTo") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesLabels = ds.GetField("IsosurfaceEdgesLabels") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesOrders = ds.GetField("IsosurfaceEdgesOrders") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceEdgesOffset = ds.GetField("IsosurfaceEdgesOffset") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + auto isosurfaceIsoValue = ds.GetField("IsosurfaceIsoValue") + .GetData() + .AsArrayHandle>() + .ReadPortal(); + vtkm::Id nIsosurfaceEdges = isosurfaceEdgesFrom.GetNumberOfValues(); + vtkm::Id isoSurfaceCount = 0; + std::vector computed_iso_surface_info; + + for (vtkm::Id edge = 0; edge < nIsosurfaceEdges; ++edge) + { + while (isoSurfaceCount < isosurfaceEdgesLabels.GetNumberOfValues() && + edge == isosurfaceEdgesOffset.Get(isoSurfaceCount)) + { + computed_iso_surface_info.push_back(isosurfaceEdgesLabels.Get(isoSurfaceCount)); + computed_iso_surface_info.push_back(isosurfaceEdgesOrders.Get(isoSurfaceCount)); + computed_iso_surface_info.push_back( + static_cast(isosurfaceIsoValue.Get(isoSurfaceCount))); + isoSurfaceCount++; + } + } + + VTKM_TEST_ASSERT(isoSurfaceCount == 2, "Wrong result for isoSurfaceCount"); + + std::vector expected_iso_surface_info; + vtkm::Vec3f_64 expected_from_edge0, expected_to_edge0; + + switch (ds_no) + { + case 0: + expected_iso_surface_info = { 5, 1, 50, 4, 0, 50 }; + expected_from_edge0 = vtkm::make_Vec(0.519231, 3, 0); + expected_to_edge0 = vtkm::make_Vec(0.5, 2.5, 0); + break; + case 1: + expected_iso_surface_info = { 1, 2, 30, 4, 0, 50 }; + expected_from_edge0 = vtkm::make_Vec(4.33333, 5, 0); + expected_to_edge0 = vtkm::make_Vec(4.61538, 4.61538, 0); + break; + default: + VTKM_TEST_ASSERT(false); + } + + if (computed_iso_surface_info != expected_iso_surface_info) + { + std::cout << "Expected Isosurface Info for block " << ds_no << ":" << std::endl; + for (std::size_t i = 0; i < expected_iso_surface_info.size(); i += 3) + std::cout << "Isosurface Info:" << std::setw(5) << expected_iso_surface_info[i] + << std::setw(10) << expected_iso_surface_info[i + 1] << std::setw(10) + << expected_iso_surface_info[i + 2] << std::endl; + std::cout << "Computed Isosurface Info for block " << ds_no << ":" << std::endl; + for (std::size_t i = 0; i < computed_iso_surface_info.size(); i += 3) + std::cout << "Isosurface Info:" << std::setw(5) << computed_iso_surface_info[i] + << std::setw(10) << computed_iso_surface_info[i + 1] << std::setw(10) + << computed_iso_surface_info[i + 2] << std::endl; + VTKM_TEST_FAIL("Iso Surface Info Don't Match!"); + } + + VTKM_TEST_ASSERT((ds_no == 0 && nIsosurfaceEdges == 25) || + (ds_no == 1 && nIsosurfaceEdges == 26)); + VTKM_TEST_ASSERT(test_equal(isosurfaceEdgesFrom.Get(0), expected_from_edge0)); + VTKM_TEST_ASSERT(test_equal(isosurfaceEdgesTo.Get(0), expected_to_edge0)); + } + + std::cout << "Isosurface: Results Match!" << std::endl; } } @@ -1041,13 +1140,14 @@ inline void RunContourTreePresimplification(std::string fieldName, vtkm::filter::scalar_topology::DistributedBranchDecompositionFilter bd_filter; bd_result = bd_filter.Execute(result); - // Compute SelectTopVolumeContours - vtkm::filter::scalar_topology::SelectTopVolumeContoursFilter tp_filter; + // Compute SelectTopVolumeBranches + vtkm::filter::scalar_topology::SelectTopVolumeBranchesFilter tp_filter; // numBranches needs to be large enough to include all branches // numBranches < numSuperarcs < globalSize tp_filter.SetSavedBranches(globalSize[0] * globalSize[1] * (globalSize[2] > 1 ? globalSize[2] : 1)); + tp_filter.SetPresimplifyThreshold(presimplifyThreshold); tp_result = tp_filter.Execute(bd_result); } diff --git a/vtkm/filter/scalar_topology/worklet/CMakeLists.txt b/vtkm/filter/scalar_topology/worklet/CMakeLists.txt index e0a604670a..bfb9f8621e 100644 --- a/vtkm/filter/scalar_topology/worklet/CMakeLists.txt +++ b/vtkm/filter/scalar_topology/worklet/CMakeLists.txt @@ -16,7 +16,8 @@ set(headers vtkm_declare_headers(${headers}) add_subdirectory(branch_decomposition) -add_subdirectory(select_top_volume_contours) +add_subdirectory(select_top_volume_branches) +add_subdirectory(extract_top_volume_contours) add_subdirectory(contourtree) add_subdirectory(contourtree_augmented) add_subdirectory(contourtree_distributed) diff --git a/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h b/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h index 034be5d6e8..5b78176688 100644 --- a/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h +++ b/vtkm/filter/scalar_topology/worklet/branch_decomposition/HierarchicalVolumetricBranchDecomposer.h @@ -182,6 +182,11 @@ public: vtkm::worklet::contourtree_augmented::IdArrayType LowerEndIntrinsicVolume; vtkm::worklet::contourtree_augmented::IdArrayType UpperEndDependentVolume; vtkm::worklet::contourtree_augmented::IdArrayType LowerEndDependentVolume; + // This information is only used when extracting isosurfaces + // We need the upper and lower end within the block to determine the superarc containing the isovalue + // The information should NOT be exchanged between blocks, since it's local id + vtkm::worklet::contourtree_augmented::IdArrayType UpperEndLocalId; + vtkm::worklet::contourtree_augmented::IdArrayType LowerEndLocalId; /// routines to compute branch decomposition by volume /// WARNING: we now have two types of hierarchical tree sharing a data structure: @@ -777,6 +782,8 @@ inline void HierarchicalVolumetricBranchDecomposer::CollectEndsOfBranches( vtkm::cont::make_ArrayHandlePermutation(sortedSuperarcs, actualBranchRoots); auto permutedRegularIds = vtkm::cont::make_ArrayHandlePermutation(sortedSuperarcs, actualOuterNodeRegularIds); + auto permutedLocalIds = + vtkm::cont::make_ArrayHandlePermutation(sortedSuperarcs, actualOuterNodeLocalIds); auto permutedDataValues = vtkm::cont::make_ArrayHandlePermutation(sortedSuperarcs, actualOuterNodeValues); auto permutedIntrinsicVolumes = @@ -824,6 +831,7 @@ inline void HierarchicalVolumetricBranchDecomposer::CollectEndsOfBranches( { vtkm::cont::Algorithm::CopyIf(permutedBranchRoots, oneIfBranchEnd, this->BranchRoot); vtkm::cont::Algorithm::CopyIf(branchRootGRIds, oneIfBranchEnd, this->BranchRootGRId); + vtkm::cont::Algorithm::CopyIf(permutedLocalIds, oneIfBranchEnd, this->LowerEndLocalId); vtkm::cont::Algorithm::CopyIf( actualDirectedSuperarcs, oneIfBranchEnd, this->LowerEndSuperarcId); vtkm::cont::Algorithm::CopyIf(permutedRegularIds, oneIfBranchEnd, this->LowerEndGRId); @@ -864,6 +872,7 @@ inline void HierarchicalVolumetricBranchDecomposer::CollectEndsOfBranches( vtkm::cont::Algorithm::CopyIf( actualDirectedSuperarcs, oneIfBranchEnd, this->UpperEndSuperarcId); vtkm::cont::Algorithm::CopyIf(permutedRegularIds, oneIfBranchEnd, this->UpperEndGRId); + vtkm::cont::Algorithm::CopyIf(permutedLocalIds, oneIfBranchEnd, this->UpperEndLocalId); vtkm::cont::Algorithm::CopyIf( permutedIntrinsicVolumes, oneIfBranchEnd, this->UpperEndIntrinsicVolume); vtkm::cont::Algorithm::CopyIf( diff --git a/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/BranchEndComparator.h b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/BranchEndComparator.h index 7c63e4c091..c13ac890a9 100644 --- a/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/BranchEndComparator.h +++ b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/BranchEndComparator.h @@ -87,9 +87,9 @@ public: const IdArrayType& globalRegularIds, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) - : branchRootsPortal(branchRoots.PrepareForInput(device, token)) - , dataValuesPortal(dataValues.PrepareForInput(device, token)) - , globalRegularIdsPortal(globalRegularIds.PrepareForInput(device, token)) + : BranchRootsPortal(branchRoots.PrepareForInput(device, token)) + , DataValuesPortal(dataValues.PrepareForInput(device, token)) + , GlobalRegularIdsPortal(globalRegularIds.PrepareForInput(device, token)) { // constructor } // constructor @@ -98,9 +98,9 @@ public: bool operator()(const vtkm::Id& i, const vtkm::Id& j) const { // operator() vtkm::Id branchI = - vtkm::worklet::contourtree_augmented::MaskedIndex(this->branchRootsPortal.Get(i)); + vtkm::worklet::contourtree_augmented::MaskedIndex(this->BranchRootsPortal.Get(i)); vtkm::Id branchJ = - vtkm::worklet::contourtree_augmented::MaskedIndex(this->branchRootsPortal.Get(j)); + vtkm::worklet::contourtree_augmented::MaskedIndex(this->BranchRootsPortal.Get(j)); // primary sort on branch ID if (branchI < branchJ) @@ -112,8 +112,8 @@ public: return false; } - ValueType valueI = this->dataValuesPortal.Get(i); - ValueType valueJ = this->dataValuesPortal.Get(j); + ValueType valueI = this->DataValuesPortal.Get(i); + ValueType valueJ = this->DataValuesPortal.Get(j); // secondary sort on data value // if isLower is false, lower value first @@ -128,9 +128,9 @@ public: } vtkm::Id idI = - vtkm::worklet::contourtree_augmented::MaskedIndex(this->globalRegularIdsPortal.Get(i)); + vtkm::worklet::contourtree_augmented::MaskedIndex(this->GlobalRegularIdsPortal.Get(i)); vtkm::Id idJ = - vtkm::worklet::contourtree_augmented::MaskedIndex(this->globalRegularIdsPortal.Get(j)); + vtkm::worklet::contourtree_augmented::MaskedIndex(this->GlobalRegularIdsPortal.Get(j)); // third sort on global regular id // if isLower is false, lower value first @@ -149,9 +149,9 @@ public: } // operator() private: - IdPortalType branchRootsPortal; - ValuePortalType dataValuesPortal; - IdPortalType globalRegularIdsPortal; + IdPortalType BranchRootsPortal; + ValuePortalType DataValuesPortal; + IdPortalType GlobalRegularIdsPortal; }; // BranchEndComparatorImpl diff --git a/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h index 19caa736e5..847b90a366 100644 --- a/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h @@ -111,7 +111,7 @@ public: using ControlSignature = void( FieldIn superarcId, // (input) actual ID of superarc WholeArrayIn branchRoots, // (array input) branch root (superarc) IDs of all superarcs - FieldOut branchEndIndicator // (output) 1 if + FieldOut branchEndIndicator // (output) 1 if the superarc is the last of a branch in the array ); using ExecutionSignature = _3(_1, _2); using InputDomain = _1; diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h index dee8f012ac..6df8448d1e 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h @@ -103,8 +103,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -242,7 +242,7 @@ public: void CopyBaseRegularStructure(); // subroutines for CopySuperstructure - /// gets a list of all the old supernodes to transfer at this level (ie except attachment points + /// gets a list of all the old supernodes to transfer at this level (i.e., except attachment points void RetrieveOldSupernodes(vtkm::Id roundNumber); /// resizes the arrays for the level void ResizeArrays(vtkm::Id roundNumber); @@ -1128,7 +1128,6 @@ void HierarchicalAugmenter::CopyBaseRegularStructure() } // Reset the number of regular nodes in round 0 - /* ----- MINGZHE: DEBUG ----- */ vtkm::Id regularNodesInRound0 = this->AugmentedTree->NumRegularNodesInRound.ReadPortal().Get(0) + numRegNeeded; this->AugmentedTree->NumRegularNodesInRound.WritePortal().Set(0, regularNodesInRound0); @@ -1656,22 +1655,18 @@ void HierarchicalAugmenter::CreateSuperarcs(vtkm::Id roundNumber) this->AugmentedTree->Supernodes.GetNumberOfValues(), // new value this->AugmentedTree->FirstSupernodePerIteration[roundNumber] // array ); - // The following loop should be safe in parallel since there should never be two zeros in sequence, i.e., the next - // entry after a zero will always be valid, regardless of execution order + // This was added because in rare cases there are no supernodes transferred in an iteration, for example because there // are no available upper leaves to prune. If this is case, we are guaranteed that there will be available lower leaves - // so the next iteration will have a non-zero number. We had a major bug from this, and it's cropped back up in the + // so the next iteration will have a non-zero number. We had a major bug from this, and it's cropped back up in the. // Hierarchical Augmentation, so I'm expanding the comment just in case. - { - vtkm::cont::ArrayHandleCounting tempIndex(1, 1, currNumIterations - 1); - vtkm::worklet::contourtree_distributed::hierarchical_augmenter:: - CreateSuperarcsUpdateFirstSupernodePerIterationWorklet - createSuperarcsUpdateFirstSupernodePerIterationWorklet; - this->Invoke(createSuperarcsUpdateFirstSupernodePerIterationWorklet, - tempIndex, // input index - this->AugmentedTree->FirstSupernodePerIteration[roundNumber] // input/output - ); - } + // Mingzhe: for any empty iteration, augmentedTree->FirstSupernodePerIteration[round] will be 0 + // Fill the 0 out (except when it is leading) by its following number as necessary + // There should never be two consecutive zeros, so running it in parallel should be safe + vtkm::worklet::contourtree_distributed::hierarchical_augmenter::FillEmptyIterationWorklet + fillEmptyIterationWorklet; + this->Invoke(fillEmptyIterationWorklet, + this->AugmentedTree->FirstSupernodePerIteration[roundNumber]); // We have one last bit of cleanup to do. If there were attachment points, // then the round in which they transfer has been removed diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h index af2662b1a3..32070f6f4a 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenterFunctor.h @@ -109,6 +109,15 @@ public: if (ingid != selfid) { // Receive and augment rp.dequeue(ingid, blockData->HierarchicalAugmenter.InData); + + vtkm::Id exchangeSize = + blockData->HierarchicalAugmenter.InData.Superparents.GetNumberOfValues(); + exchangeSize = + std::max(exchangeSize, + blockData->HierarchicalAugmenter.InData.GlobalRegularIds.GetNumberOfValues()); + timingsStream << " " << std::setw(38) << std::left << "Retrieved Attachment Points" + << ": " << exchangeSize << std::endl; + blockData->HierarchicalAugmenter.RetrieveInAttachmentPoints(); } } diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h index e40d5f4f15..c4a24088b6 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalContourTree.h @@ -1024,9 +1024,18 @@ void HierarchicalContourTree::AddToVTKMDataSet(vtkm::cont::DataSet& d vtkm::cont::Field superarcsField( "Superarcs", vtkm::cont::Field::Association::WholeDataSet, this->Superarcs); ds.AddField(superarcsField); + vtkm::cont::Field superchildrenField( + "Superchildren", vtkm::cont::Field::Association::WholeDataSet, this->Superchildren); + ds.AddField(superchildrenField); vtkm::cont::Field hyperparentsField( "Hyperparents", vtkm::cont::Field::Association::WholeDataSet, this->Hyperparents); ds.AddField(hyperparentsField); + vtkm::cont::Field hypernodesField( + "Hypernodes", vtkm::cont::Field::Association::WholeDataSet, this->Hypernodes); + ds.AddField(hypernodesField); + vtkm::cont::Field hyperarcsField( + "Hyperarcs", vtkm::cont::Field::Association::WholeDataSet, this->Hyperarcs); + ds.AddField(hyperarcsField); vtkm::cont::Field super2HypernodeField( "Super2Hypernode", vtkm::cont::Field::Association::WholeDataSet, this->Super2Hypernode); ds.AddField(super2HypernodeField); diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h index be34f7bdf1..6e7a5cbd22 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalHyperSweeper.h @@ -206,7 +206,6 @@ private: }; // class HierarchicalHyperSweeper - template HierarchicalHyperSweeper::HierarchicalHyperSweeper( vtkm::Id blockId, @@ -368,6 +367,7 @@ void HierarchicalHyperSweeper::LocalHyperS // TODO/FIXME: Use portal? Or is there a more efficient way? auto firstSupernodePerIterationPortal = this->HierarchicalTree.FirstSupernodePerIteration[round].ReadPortal(); + vtkm::Id firstSupernode = firstSupernodePerIterationPortal.Get(iteration); vtkm::Id lastSupernode = firstSupernodePerIterationPortal.Get(iteration + 1); @@ -432,7 +432,6 @@ void HierarchicalHyperSweeper:: vtkm::Id lastSupernode) { // ComputeSuperarcDependentWeights() vtkm::Id numSupernodesToProcess = lastSupernode - firstSupernode; - // 2. Use sorted prefix sum to compute the total weight to contribute to the super/hypertarget // Same as std::partial_sum(sweepValues.begin() + firstSupernode, sweepValues.begin() + lastSupernode, valuePrefixSum.begin() + firstSupernode); { @@ -450,7 +449,6 @@ void HierarchicalHyperSweeper:: vtkm::cont::Algorithm::ScanInclusive(dependentValuesView, // input valuePrefixSumView); // result of partial sum } - // Since the prefix sum is over *all* supernodes in the iteration, we need to break it into segments // There are two cases we have to worry about: // a. Hyperarcs made up of multiple supernodes diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt index 79a50d0a81..944230cb37 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CMakeLists.txt @@ -22,10 +22,10 @@ set(headers SetSuperparentSetDecorator.h AttachmentAndSupernodeComparator.h ResizeArraysBuildNewSupernodeIdsWorklet.h + FillEmptyIterationWorklet.h CreateSuperarcsWorklet.h CreateSuperarcsData.h CreateSuperarcsSetFirstSupernodePerIterationWorklet.h - CreateSuperarcsUpdateFirstSupernodePerIterationWorklet.h HierarchicalAugmenterInOutData.h ) diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsUpdateFirstSupernodePerIterationWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FillEmptyIterationWorklet.h similarity index 68% rename from vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsUpdateFirstSupernodePerIterationWorklet.h rename to vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FillEmptyIterationWorklet.h index 386c743993..8b76070cb7 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsUpdateFirstSupernodePerIterationWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FillEmptyIterationWorklet.h @@ -43,8 +43,8 @@ // Oliver Ruebel (LBNL) //============================================================================== -#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_create_auperarcs_update_first_supernode_per_iteration_worklet_h -#define vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_create_auperarcs_update_first_supernode_per_iteration_worklet_h +#ifndef vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_fill_empty_iteration_worklet_h +#define vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_fill_empty_iteration_worklet_h #include #include @@ -58,43 +58,34 @@ namespace contourtree_distributed namespace hierarchical_augmenter { -/// Worklet used in HierarchicalAugmenter::UpdateHyperstructure to set the hyperarcs and hypernodes -class CreateSuperarcsUpdateFirstSupernodePerIterationWorklet : public vtkm::worklet::WorkletMapField +// Worklet for a rare case where an iteration has no supernode +// need to update the FirstSupernodePerIteration array to avoid crash +class FillEmptyIterationWorklet : public vtkm::worklet::WorkletMapField { public: - /// Control signature for the worklet - using ControlSignature = void(FieldIn indexArray, - WholeArrayInOut augmentedTreeFirstSupernodePerIteration); - using ExecutionSignature = void(_1, _2); + using ControlSignature = void( + WholeArrayInOut augmentedTreeFirstSupernodePerIteration // input/output + ); + using ExecutionSignature = void(InputIndex, _1); using InputDomain = _1; - // Default Constructor VTKM_EXEC_CONT - CreateSuperarcsUpdateFirstSupernodePerIterationWorklet() {} + FillEmptyIterationWorklet() {} + /// operator() of the worklet template - VTKM_EXEC void operator()( - const vtkm::Id& iteration, - const InOutFieldPortalType& augmentedTreeFirstSupernodePerIterationPortal) const - { // operator()() - if (augmentedTreeFirstSupernodePerIterationPortal.Get(iteration) == 0) + VTKM_EXEC void operator()(const vtkm::Id& inputIndex, + InOutFieldPortalType& firstSupernodePerIteration) const + { + if (inputIndex == 0 || inputIndex == firstSupernodePerIteration.GetNumberOfValues() - 1) + return; + if (firstSupernodePerIteration.Get(inputIndex) == 0) { - augmentedTreeFirstSupernodePerIterationPortal.Set( - iteration, augmentedTreeFirstSupernodePerIterationPortal.Get(iteration + 1)); + vtkm::Id nextSupernode = firstSupernodePerIteration.Get(inputIndex + 1); + firstSupernodePerIteration.Set(inputIndex, nextSupernode); } - - /* - for (indexType iteration = 1; iteration < augmentedTree->nIterations[roundNo]; ++iteration) - { - if (augmentedTree->firstSupernodePerIteration[roundNo][iteration] == 0) - { - augmentedTree->firstSupernodePerIteration[roundNo][iteration] = - augmentedTree->firstSupernodePerIteration[roundNo][iteration+1]; - } - } - */ } // operator()() -}; // CreateSuperarcsUpdateFirstSupernodePerIterationWorklet +}; // FillEmptyIterationWorklet } // namespace hierarchical_augmenter } // namespace contourtree_distributed diff --git a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h index a9de61546d..e5292b12ef 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h @@ -118,6 +118,16 @@ public: // the hyperparent which we need to search along vtkm::Id hyperparent = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + // sanity check: if above / below does not satisfy the condition, return NO_SUCH_ELEMENT + FieldType aboveValue = this->DataValues.Get(above); + FieldType belowValue = this->DataValues.Get(below); + vtkm::Id aboveGlobalId = this->RegularNodeGlobalIds.Get(above); + vtkm::Id belowGlobalId = this->RegularNodeGlobalIds.Get(below); + if (nodeValue > aboveValue || (nodeValue == aboveValue && nodeGlobalId > aboveGlobalId)) + return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + if (nodeValue < belowValue || (nodeValue == belowValue && nodeGlobalId < belowGlobalId)) + return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + // to find the superarc, we will first have to convert the above / below to a pair of super/hypernodes vtkm::Id aboveSuperparent = this->Superparents.Get(above); vtkm::Id belowSuperparent = this->Superparents.Get(below); diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/CMakeLists.txt b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/CMakeLists.txt similarity index 81% rename from vtkm/filter/scalar_topology/worklet/select_top_volume_contours/CMakeLists.txt rename to vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/CMakeLists.txt index 234966323d..b8dcc23d36 100644 --- a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/CMakeLists.txt +++ b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/CMakeLists.txt @@ -9,10 +9,11 @@ ##============================================================================ set(headers - ClarifyBranchEndSupernodeTypeWorklet.h - UpdateInfoByBranchDirectionWorklet.h - GetBranchVolumeWorklet.h - BranchVolumeComparator.h + Types.h + CopyConstArraysWorklet.h + GetCellCasesWorklet.h + GetSuperarcByIsoValueWorklet.h + GetEdgesInCellWorklet.h ) #----------------------------------------------------------------------------- diff --git a/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/CopyConstArraysWorklet.h b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/CopyConstArraysWorklet.h new file mode 100644 index 0000000000..ecd4e45d62 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/CopyConstArraysWorklet.h @@ -0,0 +1,1110 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_extract_top_volume_contours_copy_const_arrays_worklet_h +#define vtk_m_filter_scalar_topology_worklet_extract_top_volume_contours_copy_const_arrays_worklet_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace extract_top_volume_contours +{ + +constexpr vtkm::IdComponent COPY_VERTEXOFFSET = 1; +constexpr vtkm::IdComponent COPY_EDGETABLE = 2; +constexpr vtkm::IdComponent COPY_NUMBOUNDTABLE = 3; +constexpr vtkm::IdComponent COPY_BOUNDARYTABLE = 4; +constexpr vtkm::IdComponent COPY_LABELEDGETABLE = 5; + +constexpr vtkm::Id nVertices2d = 4; +constexpr vtkm::Id nEdges2d = 5; +constexpr vtkm::Id nCases2d = 16; +constexpr vtkm::Id nLineTableElemSize2d = 8; + +constexpr vtkm::Id nVertices3d = 8; +constexpr vtkm::Id nEdgesMC3d = 12; +constexpr vtkm::Id nEdgesLT3d = 19; +constexpr vtkm::Id nCasesMC3d = 256; +constexpr vtkm::Id nCasesLT3d = 256; + +constexpr vtkm::Id nTriTableMC3dElemSize = 16; // (at most 5 triangles, each with 3 vertices) +constexpr vtkm::Id nTriTableLT3dElemSize = 37; // (at most 12 triangles, each with 3 vertices) + +constexpr vtkm::Id nLabelEdgeTableMC3dElemSize = 9; // (at most 4 label edges) +constexpr vtkm::Id nLabelEdgeTableLT3dElemSize = 13; // (at most 6 tetrahedra) + +/// Worklet for copying the const array in the MarchingCubesDataTables.h +class CopyConstArraysForMarchingCubesDataTablesWorklet : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void( + FieldIn arrayIndex, // (input) index of the array (need length specification outside) + FieldOut constArrayOut // (output) the vertex offset array based on data type / specification + ); + using ExecutionSignature = _2(_1); + using InputDomain = _1; + + ///

+ /// Constructor + /// + VTKM_EXEC_CONT + CopyConstArraysForMarchingCubesDataTablesWorklet(const bool isData2D, + const bool isMarchingCube, + const vtkm::IdComponent copyType) + : IsData2D(isData2D) + , IsMarchingCube(isMarchingCube) + , CopyType(copyType) + { + } + + /// + /// Implementation of CopyConstArraysForMarchingCubesDataTablesWorklet. + /// Based on the data dimension, whether or not using marching cubes, and the copy type, + /// call the corresponding function to return the const array. + /// + /// array index. Length is determined by worklet calls outside. + /// output, the const array value at the given index. + /// + VTKM_EXEC vtkm::Id operator()(const vtkm::Id arrayIndex) const + { + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id vertexOffset2d[nVertices2d * 2] = { + 0, 0, 1, 0, 1, 1, 0, 1 + }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id edgeTable2d[nEdges2d * 2] = { + 0, 1, 1, 2, 3, 2, 0, 3, 0, 2 + }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id numLinesTable2d[nCases2d] = { 0, 2, 1, 2, 2, 2, 2, 1, + 1, 2, 2, 2, 2, 1, 2, 0 }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id lineTable2d[nCases2d * nLineTableElemSize2d] = { +#define X -1 + X, X, X, X, X, X, X, X, 3, 4, 4, 0, X, X, X, X, 0, 1, X, X, X, X, X, X, 3, 4, + 4, 1, X, X, X, X, 2, 4, 4, 1, X, X, X, X, 3, 2, 0, 1, X, X, X, X, 2, 4, 4, 0, + X, X, X, X, 3, 2, X, X, X, X, X, X, 3, 2, X, X, X, X, X, X, 2, 4, 4, 0, X, X, + X, X, 3, 2, 0, 1, X, X, X, X, 2, 4, 4, 1, X, X, X, X, 3, 4, 4, 1, X, X, X, X, + 0, 1, X, X, X, X, X, X, 3, 4, 4, 0, X, X, X, X, X, X, X, X, X, X, X, X +#undef X + }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id vertexOffset3d[nVertices3d * 3] = { + 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1 + }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id edgeTableMC3d[nEdgesMC3d * 2] = { 0, 1, 1, 2, 3, 2, 0, 3, + 4, 5, 5, 6, 7, 6, 4, 7, + 0, 4, 1, 5, 2, 6, 3, 7 }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id edgeTableLT3d[nEdgesLT3d * 2] = { + 0, 1, 1, 2, 3, 2, 0, 3, 4, 5, 5, 6, 7, 6, 4, 7, 0, 4, 1, + 5, 2, 6, 3, 7, 0, 2, 4, 6, 0, 5, 3, 6, 0, 7, 1, 6, 0, 6 + }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id numTrianglesTableMC3d[nCasesMC3d] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 2, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, + 4, 4, 3, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, 2, 3, 3, 2, 3, 4, 4, 3, 3, 4, + 4, 3, 4, 5, 5, 2, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, 2, 3, 3, 4, 3, 4, 4, + 5, 3, 4, 4, 5, 4, 5, 5, 4, 2, 3, 3, 4, 3, 4, 2, 3, 3, 4, 4, 5, 4, 5, 3, 2, 3, 4, 4, 3, + 4, 5, 3, 2, 4, 5, 5, 4, 5, 2, 4, 1, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 3, 2, + 3, 3, 4, 3, 4, 4, 5, 3, 2, 4, 3, 4, 3, 5, 2, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, + 5, 4, 3, 4, 4, 3, 4, 5, 5, 4, 4, 3, 5, 2, 5, 4, 2, 1, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, + 5, 2, 3, 3, 2, 3, 4, 4, 5, 4, 5, 5, 2, 4, 3, 5, 4, 3, 2, 4, 1, 3, 4, 4, 5, 4, 5, 3, 4, + 4, 5, 5, 2, 3, 4, 2, 1, 2, 3, 3, 2, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 1, 0, + }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id numTrianglesTableLT3d[nCasesLT3d] = { + 0, 6, 2, 8, 2, 8, 4, 8, 2, 8, 4, 10, 4, 8, 6, 8, 2, 8, 4, 10, 4, 10, + 6, 10, 4, 10, 6, 12, 6, 10, 8, 10, 2, 8, 4, 8, 4, 10, 6, 8, 4, 10, 6, 10, + 6, 10, 8, 8, 4, 8, 6, 8, 6, 10, 8, 8, 6, 10, 8, 10, 8, 10, 10, 8, 6, 12, + 8, 10, 8, 10, 8, 8, 8, 10, 10, 8, 8, 8, 8, 6, 8, 10, 10, 8, 10, 8, 10, 6, + 10, 8, 12, 6, 10, 6, 10, 4, 8, 10, 8, 8, 10, 8, 8, 6, 10, 8, 10, 6, 10, 6, + 8, 4, 8, 8, 8, 6, 10, 6, 8, 4, 10, 6, 10, 4, 10, 4, 8, 2, 2, 8, 4, 10, + 4, 10, 6, 10, 4, 8, 6, 10, 6, 8, 8, 8, 4, 8, 6, 10, 6, 10, 8, 10, 6, 8, + 8, 10, 8, 8, 10, 8, 4, 10, 6, 10, 6, 12, 8, 10, 6, 10, 8, 10, 8, 10, 10, 8, + 6, 8, 8, 8, 8, 10, 10, 8, 8, 8, 10, 8, 10, 8, 12, 6, 8, 10, 10, 8, 10, 8, + 10, 6, 8, 8, 10, 6, 8, 6, 8, 4, 8, 8, 10, 6, 10, 6, 10, 4, 8, 6, 10, 4, + 8, 4, 8, 2, 10, 8, 10, 6, 12, 6, 10, 4, 10, 6, 10, 4, 10, 4, 8, 2, 8, 6, + 8, 4, 10, 4, 8, 2, 8, 4, 8, 2, 8, 2, 6, 0 + }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id triTableMC3d[nCasesMC3d * nTriTableMC3dElemSize] = { +#define X -1 + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 8, 3, X, X, X, X, + X, X, X, X, X, X, X, X, X, 0, 1, 9, X, X, X, X, X, X, X, X, X, X, X, + X, X, 1, 8, 3, 9, 8, 1, X, X, X, X, X, X, X, X, X, X, 1, 2, 10, X, X, + X, X, X, X, X, X, X, X, X, X, X, 0, 8, 3, 1, 2, 10, X, X, X, X, X, X, + X, X, X, X, 9, 2, 10, 0, 2, 9, X, X, X, X, X, X, X, X, X, X, 2, 8, 3, + 2, 10, 8, 10, 9, 8, X, X, X, X, X, X, X, 3, 11, 2, X, X, X, X, X, X, X, + X, X, X, X, X, X, 0, 11, 2, 8, 11, 0, X, X, X, X, X, X, X, X, X, X, 1, + 9, 0, 2, 3, 11, X, X, X, X, X, X, X, X, X, X, 1, 11, 2, 1, 9, 11, 9, 8, + 11, X, X, X, X, X, X, X, 3, 10, 1, 11, 10, 3, X, X, X, X, X, X, X, X, X, + X, 0, 10, 1, 0, 8, 10, 8, 11, 10, X, X, X, X, X, X, X, 3, 9, 0, 3, 11, 9, + 11, 10, 9, X, X, X, X, X, X, X, 9, 8, 10, 10, 8, 11, X, X, X, X, X, X, X, + X, X, X, 4, 7, 8, X, X, X, X, X, X, X, X, X, X, X, X, X, 4, 3, 0, 7, + 3, 4, X, X, X, X, X, X, X, X, X, X, 0, 1, 9, 8, 4, 7, X, X, X, X, X, + X, X, X, X, X, 4, 1, 9, 4, 7, 1, 7, 3, 1, X, X, X, X, X, X, X, 1, 2, + 10, 8, 4, 7, X, X, X, X, X, X, X, X, X, X, 3, 4, 7, 3, 0, 4, 1, 2, 10, + X, X, X, X, X, X, X, 9, 2, 10, 9, 0, 2, 8, 4, 7, X, X, X, X, X, X, X, + 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, X, X, X, X, 8, 4, 7, 3, 11, 2, X, + X, X, X, X, X, X, X, X, X, 11, 4, 7, 11, 2, 4, 2, 0, 4, X, X, X, X, X, + X, X, 9, 0, 1, 8, 4, 7, 2, 3, 11, X, X, X, X, X, X, X, 4, 7, 11, 9, 4, + 11, 9, 11, 2, 9, 2, 1, X, X, X, X, 3, 10, 1, 3, 11, 10, 7, 8, 4, X, X, X, + X, X, X, X, 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, X, X, X, X, 4, 7, 8, + 9, 0, 11, 9, 11, 10, 11, 0, 3, X, X, X, X, 4, 7, 11, 4, 11, 9, 9, 11, 10, X, + X, X, X, X, X, X, 9, 5, 4, X, X, X, X, X, X, X, X, X, X, X, X, X, 9, + 5, 4, 0, 8, 3, X, X, X, X, X, X, X, X, X, X, 0, 5, 4, 1, 5, 0, X, X, + X, X, X, X, X, X, X, X, 8, 5, 4, 8, 3, 5, 3, 1, 5, X, X, X, X, X, X, + X, 1, 2, 10, 9, 5, 4, X, X, X, X, X, X, X, X, X, X, 3, 0, 8, 1, 2, 10, + 4, 9, 5, X, X, X, X, X, X, X, 5, 2, 10, 5, 4, 2, 4, 0, 2, X, X, X, X, + X, X, X, 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, X, X, X, X, 9, 5, 4, 2, + 3, 11, X, X, X, X, X, X, X, X, X, X, 0, 11, 2, 0, 8, 11, 4, 9, 5, X, X, + X, X, X, X, X, 0, 5, 4, 0, 1, 5, 2, 3, 11, X, X, X, X, X, X, X, 2, 1, + 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, X, X, X, X, 10, 3, 11, 10, 1, 3, 9, 5, 4, + X, X, X, X, X, X, X, 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, X, X, X, X, + 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, X, X, X, X, 5, 4, 8, 5, 8, 10, 10, + 8, 11, X, X, X, X, X, X, X, 9, 7, 8, 5, 7, 9, X, X, X, X, X, X, X, X, + X, X, 9, 3, 0, 9, 5, 3, 5, 7, 3, X, X, X, X, X, X, X, 0, 7, 8, 0, 1, + 7, 1, 5, 7, X, X, X, X, X, X, X, 1, 5, 3, 3, 5, 7, X, X, X, X, X, X, + X, X, X, X, 9, 7, 8, 9, 5, 7, 10, 1, 2, X, X, X, X, X, X, X, 10, 1, 2, + 9, 5, 0, 5, 3, 0, 5, 7, 3, X, X, X, X, 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, + 5, 2, X, X, X, X, 2, 10, 5, 2, 5, 3, 3, 5, 7, X, X, X, X, X, X, X, 7, + 9, 5, 7, 8, 9, 3, 11, 2, X, X, X, X, X, X, X, 9, 5, 7, 9, 7, 2, 9, 2, + 0, 2, 7, 11, X, X, X, X, 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, X, X, X, + X, 11, 2, 1, 11, 1, 7, 7, 1, 5, X, X, X, X, X, X, X, 9, 5, 8, 8, 5, 7, + 10, 1, 3, 10, 3, 11, X, X, X, X, 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, + 10, 0, X, 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, X, 11, 10, 5, 7, + 11, 5, X, X, X, X, X, X, X, X, X, X, 10, 6, 5, X, X, X, X, X, X, X, X, + X, X, X, X, X, 0, 8, 3, 5, 10, 6, X, X, X, X, X, X, X, X, X, X, 9, 0, + 1, 5, 10, 6, X, X, X, X, X, X, X, X, X, X, 1, 8, 3, 1, 9, 8, 5, 10, 6, + X, X, X, X, X, X, X, 1, 6, 5, 2, 6, 1, X, X, X, X, X, X, X, X, X, X, + 1, 6, 5, 1, 2, 6, 3, 0, 8, X, X, X, X, X, X, X, 9, 6, 5, 9, 0, 6, 0, + 2, 6, X, X, X, X, X, X, X, 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, X, X, + X, X, 2, 3, 11, 10, 6, 5, X, X, X, X, X, X, X, X, X, X, 11, 0, 8, 11, 2, + 0, 10, 6, 5, X, X, X, X, X, X, X, 0, 1, 9, 2, 3, 11, 5, 10, 6, X, X, X, + X, X, X, X, 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, X, X, X, X, 6, 3, 11, + 6, 5, 3, 5, 1, 3, X, X, X, X, X, X, X, 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, + 11, 6, X, X, X, X, 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, X, X, X, X, 6, + 5, 9, 6, 9, 11, 11, 9, 8, X, X, X, X, X, X, X, 5, 10, 6, 4, 7, 8, X, X, + X, X, X, X, X, X, X, X, 4, 3, 0, 4, 7, 3, 6, 5, 10, X, X, X, X, X, X, + X, 1, 9, 0, 5, 10, 6, 8, 4, 7, X, X, X, X, X, X, X, 10, 6, 5, 1, 9, 7, + 1, 7, 3, 7, 9, 4, X, X, X, X, 6, 1, 2, 6, 5, 1, 4, 7, 8, X, X, X, X, + X, X, X, 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, X, X, X, X, 8, 4, 7, 9, + 0, 5, 0, 6, 5, 0, 2, 6, X, X, X, X, 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, + 6, 2, 6, 9, X, 3, 11, 2, 7, 8, 4, 10, 6, 5, X, X, X, X, X, X, X, 5, 10, + 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, X, X, X, X, 0, 1, 9, 4, 7, 8, 2, 3, 11, + 5, 10, 6, X, X, X, X, 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, X, + 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, X, X, X, X, 5, 1, 11, 5, 11, 6, 1, + 0, 11, 7, 11, 4, 0, 4, 11, X, 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, + 7, X, 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, X, X, X, X, 10, 4, 9, 6, 4, + 10, X, X, X, X, X, X, X, X, X, X, 4, 10, 6, 4, 9, 10, 0, 8, 3, X, X, X, + X, X, X, X, 10, 0, 1, 10, 6, 0, 6, 4, 0, X, X, X, X, X, X, X, 8, 3, 1, + 8, 1, 6, 8, 6, 4, 6, 1, 10, X, X, X, X, 1, 4, 9, 1, 2, 4, 2, 6, 4, X, + X, X, X, X, X, X, 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, X, X, X, X, 0, + 2, 4, 4, 2, 6, X, X, X, X, X, X, X, X, X, X, 8, 3, 2, 8, 2, 4, 4, 2, + 6, X, X, X, X, X, X, X, 10, 4, 9, 10, 6, 4, 11, 2, 3, X, X, X, X, X, X, + X, 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, X, X, X, X, 3, 11, 2, 0, 1, 6, + 0, 6, 4, 6, 1, 10, X, X, X, X, 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, + 11, 1, X, 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, X, X, X, X, 8, 11, 1, 8, + 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, X, 3, 11, 6, 3, 6, 0, 0, 6, 4, X, X, + X, X, X, X, X, 6, 4, 8, 11, 6, 8, X, X, X, X, X, X, X, X, X, X, 7, 10, + 6, 7, 8, 10, 8, 9, 10, X, X, X, X, X, X, X, 0, 7, 3, 0, 10, 7, 0, 9, 10, + 6, 7, 10, X, X, X, X, 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, X, X, X, X, + 10, 6, 7, 10, 7, 1, 1, 7, 3, X, X, X, X, X, X, X, 1, 2, 6, 1, 6, 8, 1, + 8, 9, 8, 6, 7, X, X, X, X, 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, + 9, X, 7, 8, 0, 7, 0, 6, 6, 0, 2, X, X, X, X, X, X, X, 7, 3, 2, 6, 7, + 2, X, X, X, X, X, X, X, X, X, X, 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, + X, X, X, X, 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, X, 1, 8, 0, + 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, X, 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, + 7, 1, X, X, X, X, 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, X, 0, + 9, 1, 11, 6, 7, X, X, X, X, X, X, X, X, X, X, 7, 8, 0, 7, 0, 6, 3, 11, + 0, 11, 6, 0, X, X, X, X, 7, 11, 6, X, X, X, X, X, X, X, X, X, X, X, X, + X, 7, 6, 11, X, X, X, X, X, X, X, X, X, X, X, X, X, 3, 0, 8, 11, 7, 6, + X, X, X, X, X, X, X, X, X, X, 0, 1, 9, 11, 7, 6, X, X, X, X, X, X, X, + X, X, X, 8, 1, 9, 8, 3, 1, 11, 7, 6, X, X, X, X, X, X, X, 10, 1, 2, 6, + 11, 7, X, X, X, X, X, X, X, X, X, X, 1, 2, 10, 3, 0, 8, 6, 11, 7, X, X, + X, X, X, X, X, 2, 9, 0, 2, 10, 9, 6, 11, 7, X, X, X, X, X, X, X, 6, 11, + 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, X, X, X, X, 7, 2, 3, 6, 2, 7, X, X, X, + X, X, X, X, X, X, X, 7, 0, 8, 7, 6, 0, 6, 2, 0, X, X, X, X, X, X, X, + 2, 7, 6, 2, 3, 7, 0, 1, 9, X, X, X, X, X, X, X, 1, 6, 2, 1, 8, 6, 1, + 9, 8, 8, 7, 6, X, X, X, X, 10, 7, 6, 10, 1, 7, 1, 3, 7, X, X, X, X, X, + X, X, 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, X, X, X, X, 0, 3, 7, 0, 7, + 10, 0, 10, 9, 6, 10, 7, X, X, X, X, 7, 6, 10, 7, 10, 8, 8, 10, 9, X, X, X, + X, X, X, X, 6, 8, 4, 11, 8, 6, X, X, X, X, X, X, X, X, X, X, 3, 6, 11, + 3, 0, 6, 0, 4, 6, X, X, X, X, X, X, X, 8, 6, 11, 8, 4, 6, 9, 0, 1, X, + X, X, X, X, X, X, 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, X, X, X, X, 6, + 8, 4, 6, 11, 8, 2, 10, 1, X, X, X, X, X, X, X, 1, 2, 10, 3, 0, 11, 0, 6, + 11, 0, 4, 6, X, X, X, X, 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, X, X, X, + X, 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, X, 8, 2, 3, 8, 4, 2, + 4, 6, 2, X, X, X, X, X, X, X, 0, 4, 2, 4, 6, 2, X, X, X, X, X, X, X, + X, X, X, 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, X, X, X, X, 1, 9, 4, 1, + 4, 2, 2, 4, 6, X, X, X, X, X, X, X, 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, + 1, X, X, X, X, 10, 1, 0, 10, 0, 6, 6, 0, 4, X, X, X, X, X, X, X, 4, 6, + 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, X, 10, 9, 4, 6, 10, 4, X, X, X, + X, X, X, X, X, X, X, 4, 9, 5, 7, 6, 11, X, X, X, X, X, X, X, X, X, X, + 0, 8, 3, 4, 9, 5, 11, 7, 6, X, X, X, X, X, X, X, 5, 0, 1, 5, 4, 0, 7, + 6, 11, X, X, X, X, X, X, X, 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, X, X, + X, X, 9, 5, 4, 10, 1, 2, 7, 6, 11, X, X, X, X, X, X, X, 6, 11, 7, 1, 2, + 10, 0, 8, 3, 4, 9, 5, X, X, X, X, 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, + X, X, X, X, 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, X, 7, 2, 3, + 7, 6, 2, 5, 4, 9, X, X, X, X, X, X, X, 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, + 8, 7, X, X, X, X, 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, X, X, X, X, 6, + 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, X, 9, 5, 4, 10, 1, 6, 1, 7, + 6, 1, 3, 7, X, X, X, X, 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, + X, 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, X, 7, 6, 10, 7, 10, 8, + 5, 4, 10, 4, 8, 10, X, X, X, X, 6, 9, 5, 6, 11, 9, 11, 8, 9, X, X, X, X, + X, X, X, 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, X, X, X, X, 0, 11, 8, 0, + 5, 11, 0, 1, 5, 5, 6, 11, X, X, X, X, 6, 11, 3, 6, 3, 5, 5, 3, 1, X, X, + X, X, X, X, X, 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, X, X, X, X, 0, 11, + 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, X, 11, 8, 5, 11, 5, 6, 8, 0, 5, + 10, 5, 2, 0, 2, 5, X, 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, X, X, X, X, + 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, X, X, X, X, 9, 5, 6, 9, 6, 0, 0, + 6, 2, X, X, X, X, X, X, X, 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, + 8, X, 1, 5, 6, 2, 1, 6, X, X, X, X, X, X, X, X, X, X, 1, 3, 6, 1, 6, + 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, X, 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, + X, X, X, X, 0, 3, 8, 5, 6, 10, X, X, X, X, X, X, X, X, X, X, 10, 5, 6, + X, X, X, X, X, X, X, X, X, X, X, X, X, 11, 5, 10, 7, 5, 11, X, X, X, X, + X, X, X, X, X, X, 11, 5, 10, 11, 7, 5, 8, 3, 0, X, X, X, X, X, X, X, 5, + 11, 7, 5, 10, 11, 1, 9, 0, X, X, X, X, X, X, X, 10, 7, 5, 10, 11, 7, 9, 8, + 1, 8, 3, 1, X, X, X, X, 11, 1, 2, 11, 7, 1, 7, 5, 1, X, X, X, X, X, X, + X, 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, X, X, X, X, 9, 7, 5, 9, 2, 7, + 9, 0, 2, 2, 11, 7, X, X, X, X, 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, + 8, 2, X, 2, 5, 10, 2, 3, 5, 3, 7, 5, X, X, X, X, X, X, X, 8, 2, 0, 8, + 5, 2, 8, 7, 5, 10, 2, 5, X, X, X, X, 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, + 2, X, X, X, X, 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, X, 1, 3, + 5, 3, 7, 5, X, X, X, X, X, X, X, X, X, X, 0, 8, 7, 0, 7, 1, 1, 7, 5, + X, X, X, X, X, X, X, 9, 0, 3, 9, 3, 5, 5, 3, 7, X, X, X, X, X, X, X, + 9, 8, 7, 5, 9, 7, X, X, X, X, X, X, X, X, X, X, 5, 8, 4, 5, 10, 8, 10, + 11, 8, X, X, X, X, X, X, X, 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, X, X, + X, X, 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, X, X, X, X, 10, 11, 4, 10, 4, + 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, X, 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, + X, X, X, X, 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, X, 0, 2, 5, + 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, X, 9, 4, 5, 2, 11, 3, X, X, X, X, + X, X, X, X, X, X, 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, X, X, X, X, 5, + 10, 2, 5, 2, 4, 4, 2, 0, X, X, X, X, X, X, X, 3, 10, 2, 3, 5, 10, 3, 8, + 5, 4, 5, 8, 0, 1, 9, X, 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, X, X, X, + X, 8, 4, 5, 8, 5, 3, 3, 5, 1, X, X, X, X, X, X, X, 0, 4, 5, 1, 0, 5, + X, X, X, X, X, X, X, X, X, X, 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, X, + X, X, X, 9, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, 4, 11, 7, 4, + 9, 11, 9, 10, 11, X, X, X, X, X, X, X, 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, + 11, X, X, X, X, 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, X, X, X, X, 3, 1, + 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, X, 4, 11, 7, 9, 11, 4, 9, 2, 11, + 9, 1, 2, X, X, X, X, 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, X, + 11, 7, 4, 11, 4, 2, 2, 4, 0, X, X, X, X, X, X, X, 11, 7, 4, 11, 4, 2, 8, + 3, 4, 3, 2, 4, X, X, X, X, 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, X, X, + X, X, 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, X, 3, 7, 10, 3, 10, + 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, X, 1, 10, 2, 8, 7, 4, X, X, X, X, X, X, + X, X, X, X, 4, 9, 1, 4, 1, 7, 7, 1, 3, X, X, X, X, X, X, X, 4, 9, 1, + 4, 1, 7, 0, 8, 1, 8, 7, 1, X, X, X, X, 4, 0, 3, 7, 4, 3, X, X, X, X, + X, X, X, X, X, X, 4, 8, 7, X, X, X, X, X, X, X, X, X, X, X, X, X, 9, + 10, 8, 10, 11, 8, X, X, X, X, X, X, X, X, X, X, 3, 0, 9, 3, 9, 11, 11, 9, + 10, X, X, X, X, X, X, X, 0, 1, 10, 0, 10, 8, 8, 10, 11, X, X, X, X, X, X, + X, 3, 1, 10, 11, 3, 10, X, X, X, X, X, X, X, X, X, X, 1, 2, 11, 1, 11, 9, + 9, 11, 8, X, X, X, X, X, X, X, 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, X, + X, X, X, 0, 2, 11, 8, 0, 11, X, X, X, X, X, X, X, X, X, X, 3, 2, 11, X, + X, X, X, X, X, X, X, X, X, X, X, X, 2, 3, 8, 2, 8, 10, 10, 8, 9, X, X, + X, X, X, X, X, 9, 10, 2, 0, 9, 2, X, X, X, X, X, X, X, X, X, X, 2, 3, + 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, X, X, X, X, 1, 10, 2, X, X, X, X, X, X, + X, X, X, X, X, X, X, 1, 3, 8, 9, 1, 8, X, X, X, X, X, X, X, X, X, X, + 0, 9, 1, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 3, 8, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X +#undef X + }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id triTableLT3d[nCasesLT3d * nTriTableLT3dElemSize] = { +#define X -1 + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, 12, 18, + 3, 16, 18, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, 0, 1, 17, 0, 9, 17, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, + 1, 17, 14, 18, 17, 14, 9, 17, 3, 12, 18, 3, 16, 18, 8, 14, 18, 8, 16, 18, X, X, X, + X, X, X, X, X, X, X, X, X, X, 12, 1, 10, 12, 2, 10, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 3, 18, 10, 3, 2, 10, 3, 16, 18, 8, 14, 18, 8, + 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, + 17, 12, 2, 10, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, 16, + 18, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 3, 2, 15, + 3, 11, 15, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 12, 18, 15, 12, 2, 15, + 16, 18, 15, 16, 11, 15, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, + X, X, 0, 1, 17, 0, 9, 17, 3, 2, 15, 3, 11, 15, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, + 18, 17, 14, 9, 17, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 8, 14, 18, 8, 16, 18, + X, X, X, X, X, X, X, 12, 1, 10, 3, 12, 10, 3, 15, 10, 3, 11, 15, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, + 10, 0, 1, 10, 0, 14, 18, 18, 15, 10, 16, 18, 15, 16, 11, 15, 8, 14, 18, 8, 16, 18, X, + X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 3, 12, + 10, 3, 15, 10, 3, 11, 15, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 16, 18, 15, 16, 11, 15, 8, 14, + 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 8, 4, 13, 8, 7, 13, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, 12, 18, 3, 16, 18, 14, 18, 13, + 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, + 1, 17, 0, 9, 17, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, + 9, 17, 3, 12, 18, 3, 16, 18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, + X, X, X, X, 12, 1, 10, 12, 2, 10, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, + 10, 0, 14, 18, 3, 18, 10, 3, 2, 10, 3, 16, 18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, + 7, 13, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 12, 2, 10, 8, 4, + 13, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + 18, 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, 16, 18, 14, 18, 13, 14, 4, + 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 3, 2, 15, 3, 11, 15, 8, 4, 13, + 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, 0, 12, 18, 0, 14, 18, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, + 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 0, 1, 17, 0, + 9, 17, 3, 2, 15, 3, 11, 15, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, 12, + 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, + X, 12, 1, 10, 3, 12, 10, 3, 15, 10, 3, 11, 15, 8, 4, 13, 8, 7, 13, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, + 18, 18, 15, 10, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, + X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 3, 12, 10, 3, 15, 10, 3, 11, + 15, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, + 14, 18, 17, 14, 9, 17, 18, 15, 10, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, + 13, 16, 7, 13, X, X, X, X, X, X, X, 14, 9, 5, 14, 4, 5, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 3, 12, 18, 3, 16, 18, 8, 18, 5, 8, 4, 5, + 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, + 17, 5, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 16, 18, 8, + 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 1, + 10, 14, 9, 5, 12, 2, 10, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, + 5, 3, 18, 10, 3, 2, 10, 3, 16, 18, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, + X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 12, 2, 10, 14, 4, 5, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, + 3, 18, 10, 3, 2, 10, 3, 16, 18, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, + X, X, X, X, X, X, X, X, 14, 9, 5, 3, 2, 15, 3, 11, 15, 14, 4, 5, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, + 12, 18, 0, 18, 5, 0, 9, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 8, 18, 5, + 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 3, + 2, 15, 3, 11, 15, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, + 11, 15, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, + 5, 3, 12, 10, 3, 15, 10, 3, 11, 15, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, + 10, 16, 18, 15, 16, 11, 15, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, + 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 3, 12, 10, 3, 15, 10, 3, 11, 15, 14, 4, + 5, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 18, 15, 10, + 16, 18, 15, 16, 11, 15, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, X, + X, X, X, X, X, 14, 9, 5, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, + 18, 5, 0, 9, 5, 3, 12, 18, 3, 16, 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, + X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 8, 14, 5, 8, + 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 16, 18, 18, 13, 5, 16, 18, 13, 16, + 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 12, 2, + 10, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 3, 18, 10, 3, 2, + 10, 3, 16, 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 0, 12, 10, + 0, 17, 10, 0, 14, 5, 0, 17, 5, 12, 2, 10, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, + X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 3, 18, 10, 3, 2, 10, + 3, 16, 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, X, X, X, X, + X, X, 14, 9, 5, 3, 2, 15, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, + 9, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 18, 13, 5, 16, 18, 13, 16, 7, 13, + X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 3, 2, 15, 3, 11, 15, 8, + 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, + 17, 12, 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 18, 13, 5, 16, + 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 3, 12, 10, 3, 15, + 10, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, + X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, 10, 16, 18, 15, 16, 11, + 15, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, + 0, 14, 5, 0, 17, 5, 3, 12, 10, 3, 15, 10, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, + 13, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 18, 15, 10, 16, 18, 15, 16, 11, 15, + 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, + 17, 10, 18, 17, 5, 18, 15, 10, 18, 15, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, + 17, 5, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, + 8, 13, 6, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, 10, 18, 15, 6, 18, + 13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, + 5, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, + 13, 6, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, + 15, 18, 15, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, + 0, 1, 17, 0, 14, 5, 0, 17, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 8, 14, 5, 8, 13, + 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, + 12, 18, 15, 12, 2, 15, 18, 15, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, + X, X, X, X, X, 14, 9, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 8, 14, 5, 8, 13, 5, + 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, + 17, 5, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 18, 13, 5, 18, 13, 6, X, X, X, + X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 12, + 2, 10, 16, 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, + X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, + 11, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 12, 2, + 10, 16, 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, + X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 18, 6, 3, 11, + 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 1, 17, + 0, 14, 5, 0, 17, 5, 16, 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, + X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 3, 12, 18, + 3, 18, 6, 3, 11, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, + X, X, 14, 9, 5, 16, 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 18, + 15, 10, 18, 15, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, + X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 3, 12, 10, 3, + 15, 10, 3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, X, 0, 18, + 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, 10, 18, 15, 6, 8, 18, 5, 8, 4, 5, 8, + 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 3, 12, 10, 3, 15, + 10, 3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, + X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 18, 15, 6, 8, 18, + 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, + 0, 17, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, + X, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 12, 18, 15, 12, 2, 15, + 18, 15, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 14, + 9, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 3, 18, 10, 3, + 2, 10, 3, 18, 6, 3, 11, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, + X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 12, 2, 10, 16, 11, 6, 14, + 4, 5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, + 10, 0, 18, 5, 0, 9, 5, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 8, 18, 5, 8, + 4, 5, 8, 18, 6, 8, 7, 6, X, 12, 1, 10, 14, 9, 5, 12, 2, 10, 16, 11, 6, 14, 4, + 5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + 12, 18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, 8, 18, 5, 8, 4, + 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, + 16, 11, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, + 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 14, 9, 5, 16, + 11, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 18, + 15, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, + X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 8, + 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, + 18, 18, 15, 10, 18, 15, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, + X, X, X, X, X, X, 12, 1, 10, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 8, 4, + 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, + 12, 1, 17, 14, 18, 17, 14, 9, 17, 12, 18, 15, 12, 2, 15, 18, 15, 6, 14, 18, 13, 14, 4, + 13, 18, 13, 6, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, 3, 2, 15, 3, 16, 6, + 3, 15, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, + X, X, 0, 12, 18, 0, 14, 18, 12, 18, 15, 12, 2, 15, 18, 15, 6, 14, 18, 13, 14, 4, 13, + 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 3, 2, 15, 3, 16, 6, 3, + 15, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, + 18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, 0, 12, + 10, 0, 17, 10, 0, 9, 17, 12, 2, 10, 16, 11, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, + X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 3, 18, + 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, + X, X, X, 12, 1, 10, 12, 2, 10, 16, 11, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, + 14, 18, 17, 14, 9, 17, 3, 12, 18, 3, 18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, + 6, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, 16, 11, 6, 8, 4, 13, 8, 16, 6, + 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, + 12, 18, 0, 14, 18, 3, 12, 18, 3, 18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, + X, X, X, X, X, X, X, X, X, X, X, X, X, 16, 11, 6, 8, 4, 13, 8, 16, 6, 8, + 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 18, 15, 6, 8, 14, 18, 8, + 18, 6, 8, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, + 10, 0, 9, 17, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 16, 7, 6, X, X, X, X, + X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 18, 15, 10, 18, 15, + 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, + 12, 1, 10, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 16, 7, 6, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, + 14, 9, 17, 12, 18, 15, 12, 2, 15, 18, 15, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, + X, X, X, X, X, 0, 1, 17, 0, 9, 17, 3, 2, 15, 3, 16, 6, 3, 15, 6, 16, 7, 6, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, + 14, 18, 12, 18, 15, 12, 2, 15, 18, 15, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, + X, X, X, X, X, X, X, X, X, X, 3, 2, 15, 3, 16, 6, 3, 15, 6, 16, 7, 6, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 8, + 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, + 17, 12, 2, 10, 16, 11, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 3, 18, 10, 3, 2, 10, 3, 18, + 6, 3, 11, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 12, 1, 10, + 12, 2, 10, 16, 11, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, + 3, 12, 18, 3, 18, 6, 3, 11, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, + X, X, 0, 1, 17, 0, 9, 17, 16, 11, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, + 12, 18, 3, 18, 6, 3, 11, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, + X, X, X, X, X, X, X, 16, 11, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 16, 11, + 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, 12, 18, 3, 18, + 6, 3, 11, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, X, X, X, + X, X, X, 0, 1, 17, 0, 9, 17, 16, 11, 6, 16, 7, 6, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, + 14, 18, 17, 14, 9, 17, 3, 12, 18, 3, 18, 6, 3, 11, 6, 8, 14, 18, 8, 18, 6, 8, 7, + 6, X, X, X, X, X, X, X, 12, 1, 10, 12, 2, 10, 16, 11, 6, 16, 7, 6, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, + 18, 10, 0, 1, 10, 0, 14, 18, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 8, 14, 18, + 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 12, + 2, 10, 16, 11, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, + 11, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 3, 2, 15, 3, 16, + 6, 3, 15, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 12, 18, 15, 12, 2, 15, 18, 15, + 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, + 0, 1, 17, 0, 9, 17, 3, 2, 15, 3, 16, 6, 3, 15, 6, 16, 7, 6, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, + 14, 9, 17, 12, 18, 15, 12, 2, 15, 18, 15, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, + X, X, X, X, X, 12, 1, 10, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 16, 7, 6, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, + 1, 10, 0, 14, 18, 18, 15, 10, 18, 15, 6, 8, 14, 18, 8, 18, 6, 8, 7, 6, X, X, X, + X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 3, 12, 10, 3, + 15, 10, 3, 16, 6, 3, 15, 6, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, + X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 18, 15, 6, 8, 14, 18, 8, 18, 6, 8, + 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 16, 11, 6, 8, 4, 13, 8, 16, + 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, 12, 18, 3, 18, 6, 3, 11, 6, 14, 18, + 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 1, 17, + 0, 9, 17, 16, 11, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, + 3, 12, 18, 3, 18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, + X, X, 12, 1, 10, 12, 2, 10, 16, 11, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, + 14, 18, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, + X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 12, 2, 10, 16, 11, 6, 8, + 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, + 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 14, 18, 13, 14, + 4, 13, 18, 13, 6, X, X, X, X, X, X, X, 3, 2, 15, 3, 16, 6, 3, 15, 6, 8, 4, + 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, 0, 12, 18, 0, 14, 18, 12, 18, 15, 12, 2, 15, 18, 15, 6, 14, 18, 13, 14, 4, + 13, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, + 3, 2, 15, 3, 16, 6, 3, 15, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, + X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, 12, 18, 15, + 12, 2, 15, 18, 15, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, 12, + 1, 10, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, + X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 18, + 15, 10, 18, 15, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, X, X, X, X, X, + X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, + 15, 6, 8, 4, 13, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, 18, 17, 10, 14, 18, + 17, 14, 9, 17, 18, 15, 10, 18, 15, 6, 14, 18, 13, 14, 4, 13, 18, 13, 6, X, X, X, X, + X, X, X, X, X, X, X, X, X, 14, 9, 5, 16, 11, 6, 14, 4, 5, 16, 7, 6, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + 0, 12, 18, 0, 18, 5, 0, 9, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, 8, 18, 5, 8, 4, + 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, + 16, 11, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, + 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 12, 1, 10, 14, + 9, 5, 12, 2, 10, 16, 11, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 3, + 18, 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, + X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 12, 2, 10, 16, 11, 6, 14, 4, 5, 16, + 7, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 3, 18, + 10, 3, 2, 10, 3, 18, 6, 3, 11, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, + X, X, X, X, X, X, 14, 9, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7, + 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, + 0, 18, 5, 0, 9, 5, 12, 18, 15, 12, 2, 15, 18, 15, 6, 8, 18, 5, 8, 4, 5, 8, 18, + 6, 8, 7, 6, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 3, 2, 15, + 3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, X, X, X, X, X, + X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 18, 15, 6, 8, 18, 5, + 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 3, + 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 14, 4, 5, 16, 7, 6, X, X, X, X, X, X, + X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, 10, 18, + 15, 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, 0, 12, + 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 14, + 4, 5, 16, 7, 6, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 18, 15, 10, 18, 15, + 6, 8, 18, 5, 8, 4, 5, 8, 18, 6, 8, 7, 6, X, X, X, X, X, X, X, X, X, X, + X, X, X, 14, 9, 5, 16, 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, + 0, 9, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, + X, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 16, 11, 6, 8, 14, 5, + 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, + 18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 18, 6, 3, 11, 6, 18, 13, 5, 18, 13, 6, + X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 12, 2, 10, 16, + 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, X, X, + X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 3, 18, 10, 3, 2, 10, 3, + 18, 6, 3, 11, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, + 10, 0, 14, 5, 0, 17, 5, 12, 2, 10, 16, 11, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, + 13, 6, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 3, 18, 10, 3, 2, 10, 3, 18, + 6, 3, 11, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, + 14, 9, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, + 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, + 12, 18, 15, 12, 2, 15, 18, 15, 6, 18, 13, 5, 18, 13, 6, X, X, X, X, X, X, X, X, + X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 3, 2, 15, 3, 16, 6, 3, 15, 6, + 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, X, 12, 18, 17, 12, + 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 18, 15, 6, 18, 13, 5, 18, 13, 6, X, X, X, + X, X, X, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 3, 12, 10, 3, 15, 10, 3, + 16, 6, 3, 15, 6, 8, 14, 5, 8, 13, 5, 8, 16, 6, 8, 13, 6, X, X, X, X, X, X, + X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, 10, 18, 15, 6, 18, 13, 5, 18, + 13, 6, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, + 5, 0, 17, 5, 3, 12, 10, 3, 15, 10, 3, 16, 6, 3, 15, 6, 8, 14, 5, 8, 13, 5, 8, + 16, 6, 8, 13, 6, X, 18, 17, 10, 18, 17, 5, 18, 15, 10, 18, 15, 6, 18, 13, 5, 18, 13, + 6, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, + 18, 17, 5, 18, 15, 10, 16, 18, 15, 16, 11, 15, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, + X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, + 3, 12, 10, 3, 15, 10, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, + X, X, 0, 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, 10, 16, 18, 15, 16, 11, 15, + 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 3, + 12, 10, 3, 15, 10, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, + X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 16, + 18, 15, 16, 11, 15, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 0, 1, + 17, 0, 14, 5, 0, 17, 5, 3, 2, 15, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, + X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 12, 18, + 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, + X, X, X, 14, 9, 5, 3, 2, 15, 3, 11, 15, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, + 3, 18, 10, 3, 2, 10, 3, 16, 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, + X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 12, 2, 10, + 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, + 18, 10, 0, 1, 10, 0, 18, 5, 0, 9, 5, 3, 18, 10, 3, 2, 10, 3, 16, 18, 18, 13, 5, + 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 12, 2, 10, 8, + 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 16, 18, 18, 13, 5, 16, + 18, 13, 16, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, + 5, 0, 17, 5, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 3, 12, 18, 3, 16, + 18, 18, 13, 5, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, + 14, 9, 5, 8, 14, 5, 8, 13, 5, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 18, 15, 10, + 16, 18, 15, 16, 11, 15, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, X, + X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 3, 12, 10, 3, 15, 10, + 3, 11, 15, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, + 1, 10, 0, 18, 5, 0, 9, 5, 18, 15, 10, 16, 18, 15, 16, 11, 15, 8, 18, 5, 8, 4, 5, + 8, 16, 18, X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 3, 12, 10, 3, 15, 10, 3, + 11, 15, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, 12, 18, 17, 12, 1, 17, 18, 17, 5, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 8, + 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, + 5, 3, 2, 15, 3, 11, 15, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 12, 18, 15, 12, 2, 15, 16, 18, + 15, 16, 11, 15, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, 14, 9, 5, + 3, 2, 15, 3, 11, 15, 14, 4, 5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, 18, 17, 10, 18, 17, 5, 3, 18, 10, 3, 2, 10, + 3, 16, 18, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, + X, X, 0, 12, 10, 0, 17, 10, 0, 14, 5, 0, 17, 5, 12, 2, 10, 14, 4, 5, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, + 18, 5, 0, 9, 5, 3, 18, 10, 3, 2, 10, 3, 16, 18, 8, 18, 5, 8, 4, 5, 8, 16, 18, + X, X, X, X, X, X, X, 12, 1, 10, 14, 9, 5, 12, 2, 10, 14, 4, 5, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, + 17, 12, 1, 17, 18, 17, 5, 3, 12, 18, 3, 16, 18, 8, 18, 5, 8, 4, 5, 8, 16, 18, X, + X, X, X, X, X, X, X, X, X, X, X, X, 0, 1, 17, 0, 14, 5, 0, 17, 5, 14, 4, + 5, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, 0, 12, 18, 0, 18, 5, 0, 9, 5, 3, 12, 18, 3, 16, 18, 8, 18, 5, 8, 4, + 5, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 14, 9, 5, 14, 4, 5, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 16, 18, 15, + 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 0, + 12, 10, 0, 17, 10, 0, 9, 17, 3, 12, 10, 3, 15, 10, 3, 11, 15, 8, 4, 13, 8, 7, 13, + X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 18, + 15, 10, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, + X, X, X, X, 12, 1, 10, 3, 12, 10, 3, 15, 10, 3, 11, 15, 8, 4, 13, 8, 7, 13, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, + 17, 14, 18, 17, 14, 9, 17, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, + 4, 13, 16, 18, 13, 16, 7, 13, X, 0, 1, 17, 0, 9, 17, 3, 2, 15, 3, 11, 15, 8, 4, + 13, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + 0, 12, 18, 0, 14, 18, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 14, 18, 13, 14, 4, + 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 3, 2, 15, 3, 11, 15, 8, 4, 13, + 8, 7, 13, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, 16, 18, + 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, X, 0, 12, 10, 0, + 17, 10, 0, 9, 17, 12, 2, 10, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 3, 18, 10, 3, + 2, 10, 3, 16, 18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, X, X, X, X, + X, 12, 1, 10, 12, 2, 10, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, + 17, 14, 9, 17, 3, 12, 18, 3, 16, 18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, + X, X, X, X, X, X, 0, 1, 17, 0, 9, 17, 8, 4, 13, 8, 7, 13, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, + 0, 14, 18, 3, 12, 18, 3, 16, 18, 14, 18, 13, 14, 4, 13, 16, 18, 13, 16, 7, 13, X, X, + X, X, X, X, X, X, X, X, X, X, X, 8, 4, 13, 8, 7, 13, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, 18, 17, 10, 14, 18, 17, 14, 9, 17, 18, 15, 10, 16, 18, 15, 16, 11, 15, 8, 14, 18, + 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, + 9, 17, 3, 12, 10, 3, 15, 10, 3, 11, 15, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 18, 15, 10, 16, 18, 15, 16, + 11, 15, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 1, + 10, 3, 12, 10, 3, 15, 10, 3, 11, 15, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, + 17, 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 8, 14, 18, 8, 16, 18, X, X, X, X, + X, X, X, 0, 1, 17, 0, 9, 17, 3, 2, 15, 3, 11, 15, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, + 12, 18, 15, 12, 2, 15, 16, 18, 15, 16, 11, 15, 8, 14, 18, 8, 16, 18, X, X, X, X, X, + X, X, X, X, X, X, X, X, 3, 2, 15, 3, 11, 15, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, 18, + 17, 10, 14, 18, 17, 14, 9, 17, 3, 18, 10, 3, 2, 10, 3, 16, 18, 8, 14, 18, 8, 16, 18, + X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 10, 0, 17, 10, 0, 9, 17, 12, + 2, 10, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, 0, 18, 10, 0, 1, 10, 0, 14, 18, 3, 18, 10, 3, 2, 10, 3, 16, 18, 8, + 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, 12, 1, 10, 12, 2, + 10, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, 12, 18, 17, 12, 1, 17, 14, 18, 17, 14, 9, 17, 3, 12, + 18, 3, 16, 18, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, + 0, 1, 17, 0, 9, 17, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, 0, 12, 18, 0, 14, 18, 3, 12, 18, + 3, 16, 18, 8, 14, 18, 8, 16, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X +#undef X + }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id + labelEdgeTableMC3d[nCasesMC3d * nLabelEdgeTableMC3dElemSize] = { +#define X -1 + X, X, X, X, X, X, X, X, X, 1, 0, X, X, X, X, X, X, X, 1, 0, X, X, X, X, X, X, X, 2, 1, X, X, + X, X, X, X, X, 1, 1, X, X, X, X, X, X, X, 1, 0, 1, 1, X, X, X, X, X, 2, 0, X, X, X, X, X, X, + X, 3, 2, X, X, X, X, X, X, X, 1, 2, X, X, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 1, 0, 1, + 2, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 2, 1, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, + X, X, 3, 0, X, X, X, X, X, X, X, 2, 8, X, X, X, X, X, X, X, 1, 4, X, X, X, X, X, X, X, 2, 0, + X, X, X, X, X, X, X, 1, 0, 1, 4, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 1, 1, 1, 4, X, X, + X, X, X, 2, 0, 1, 1, X, X, X, X, X, 2, 0, 1, 4, X, X, X, X, X, 4, 2, X, X, X, X, X, X, X, 1, + 4, 1, 2, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 1, 0, 1, 4, 1, 2, X, X, X, 4, 1, X, X, X, + X, X, X, X, 2, 1, 1, 4, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 4, 3, 0, X, X, X, X, X, + 3, 4, X, X, X, X, X, X, X, 1, 4, X, X, X, X, X, X, X, 1, 4, 1, 0, X, X, X, X, X, 2, 0, X, X, + X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 1, 1, 1, 4, X, X, X, X, X, 1, 0, 1, 1, 1, 4, X, X, + X, 3, 0, X, X, X, X, X, X, X, 4, 2, X, X, X, X, X, X, X, 1, 4, 1, 2, X, X, X, X, X, 2, 0, 1, + 4, X, X, X, X, X, 2, 0, 1, 2, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 2, 1, 1, 4, X, X, X, + X, X, 1, 4, 3, 0, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 3, 4, X, X, X, X, X, X, X, 2, 5, + X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 2, 1, X, X, X, X, + X, X, X, 2, 5, 1, 1, X, X, X, X, X, 1, 1, 3, 0, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 3, + 2, X, X, X, X, X, X, X, 2, 5, 1, 2, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 2, 3, 0, X, + X, X, X, X, 3, 1, X, X, X, X, X, X, X, 2, 5, 2, 1, X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, + 5, 0, X, X, X, X, X, X, X, 2, 5, X, X, X, X, X, X, X, 1, 5, X, X, X, X, X, X, X, 1, 0, 1, 5, + X, X, X, X, X, 1, 0, 1, 5, X, X, X, X, X, 2, 1, 1, 5, X, X, X, X, X, 2, 1, X, X, X, X, X, X, + X, 2, 1, 1, 0, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 4, 2, X, X, X, X, X, X, X, 1, 2, 1, + 5, X, X, X, X, X, 2, 0, 1, 5, X, X, X, X, X, 1, 0, 1, 2, 1, 5, X, X, X, 1, 5, 3, 1, X, X, X, + X, X, 3, 1, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 3, 5, + X, X, X, X, X, X, X, 1, 5, 1, 4, X, X, X, X, X, 2, 0, 1, 5, X, X, X, X, X, 1, 0, 1, 5, 1, 4, + X, X, X, 1, 5, 3, 1, X, X, X, X, X, 2, 1, 1, 4, X, X, X, X, X, 2, 1, 2, 0, X, X, X, X, X, 1, + 4, 3, 0, X, X, X, X, X, 5, 2, X, X, X, X, X, X, X, 1, 2, 1, 4, 1, 5, X, X, X, 1, 5, 3, 0, X, + X, X, X, X, 1, 0, 1, 4, 1, 2, 1, 5, X, 4, 1, 1, 5, X, X, X, X, X, 1, 4, 3, 1, X, X, X, X, X, + 5, 0, X, X, X, X, X, X, X, 4, 0, 1, 4, X, X, X, X, X, 4, 4, X, X, X, X, X, X, X, 2, 4, X, X, + X, X, X, X, X, 2, 4, 1, 0, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 4, 1, X, X, X, X, X, X, + X, 3, 1, X, X, X, X, X, X, X, 1, 0, 3, 1, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 3, 2, X, + X, X, X, X, X, X, 2, 4, 1, 2, X, X, X, X, X, 2, 0, 2, 4, X, X, X, X, X, 1, 2, 3, 0, X, X, X, + X, X, 5, 1, X, X, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 3, 0, + X, X, X, X, X, X, X, 2, 4, X, X, X, X, X, X, X, 3, 6, X, X, X, X, X, X, X, 4, 0, X, X, X, X, + X, X, X, 4, 0, X, X, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 5, + 0, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 2, 2, X, X, X, X, X, X, X, 1, 2, 3, 6, X, + X, X, X, X, 5, 0, X, X, X, X, X, X, X, 4, 0, 1, 2, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, + 5, 1, X, X, X, X, X, X, X, 1, 0, 1, 6, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 6, X, X, + X, X, X, X, X, 1, 6, X, X, X, X, X, X, X, 1, 0, 1, 6, X, X, X, X, X, 1, 0, 1, 6, X, X, X, X, + X, 2, 1, 1, 6, X, X, X, X, X, 1, 1, 1, 6, X, X, X, X, X, 1, 1, 1, 0, 1, 6, X, X, X, 2, 0, 1, + 6, X, X, X, X, X, 1, 6, 3, 2, X, X, X, X, X, 2, 2, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, + X, X, 2, 2, 1, 0, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 4, 0, + X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 3, 6, X, X, X, X, X, X, X, 2, 4, X, X, X, X, + X, X, X, 3, 0, X, X, X, X, X, X, X, 2, 4, 1, 0, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 2, + 4, 1, 1, X, X, X, X, X, 1, 1, 3, 0, X, X, X, X, X, 2, 4, 2, 0, X, X, X, X, X, 5, 2, X, X, X, + X, X, X, X, 3, 2, X, X, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 1, 0, 3, 2, X, X, X, X, X, + 3, 1, X, X, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 5, 0, X, X, + X, X, X, X, X, 2, 4, X, X, X, X, X, X, X, 1, 4, 1, 6, X, X, X, X, X, 1, 0, 1, 4, 1, 6, X, X, + X, 2, 0, 1, 6, X, X, X, X, X, 1, 6, 3, 1, X, X, X, X, X, 1, 4, 1, 1, 1, 6, X, X, X, 1, 6, 1, + 1, 1, 0, 1, 4, X, 1, 6, 3, 0, X, X, X, X, X, 4, 2, 1, 6, X, X, X, X, X, 2, 2, 1, 4, X, X, X, + X, X, 1, 4, 3, 0, X, X, X, X, X, 2, 2, 2, 0, X, X, X, X, X, 5, 1, X, X, X, X, X, X, X, 1, 4, + 3, 1, X, X, X, X, X, 4, 0, 1, 4, X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 4, 4, X, X, X, X, + X, X, X, 3, 5, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 3, + 1, X, X, X, X, X, X, X, 1, 1, 3, 5, X, X, X, X, X, 4, 0, 1, 1, X, X, X, X, X, 5, 0, X, X, X, + X, X, X, X, 4, 2, X, X, X, X, X, X, X, 4, 2, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, + 5, 0, X, X, X, X, X, X, X, 2, 1, X, X, X, X, X, X, X, 5, 1, X, X, X, X, X, X, X, 4, 0, X, X, + X, X, X, X, X, 1, 0, 1, 5, X, X, X, X, X, 1, 5, X, X, X, X, X, X, X, 2, 5, X, X, X, X, X, X, + X, 2, 5, 1, 0, X, X, X, X, X, 2, 5, 1, 0, X, X, X, X, X, 2, 5, 2, 1, X, X, X, X, X, 3, 1, X, + X, X, X, X, X, X, 1, 0, 3, 1, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 5, 2, X, X, X, X, X, + X, X, 3, 2, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 0, 3, 2, X, X, X, X, X, 5, 1, + X, X, X, X, X, X, X, 2, 1, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 3, 0, X, X, X, X, + X, X, X, 2, 5, X, X, X, X, X, X, X, 3, 4, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, + 0, 3, 4, X, X, X, X, X, 5, 1, X, X, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 5, 0, X, X, X, + X, X, X, X, 5, 0, X, X, X, X, X, X, X, 1, 4, 1, 2, X, X, X, X, X, 4, 2, X, X, X, X, X, X, X, + 3, 0, X, X, X, X, X, X, X, 4, 2, 1, 0, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 3, 1, X, X, + X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 4, X, X, X, X, X, X, + X, 3, 4, X, X, X, X, X, X, X, 1, 0, 3, 4, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 5, 1, X, + X, X, X, X, X, X, 4, 1, X, X, X, X, X, X, X, 4, 1, 1, 0, X, X, X, X, X, 3, 0, X, X, X, X, X, + X, X, 4, 2, X, X, X, X, X, X, X, 4, 2, X, X, X, X, X, X, X, 5, 0, X, X, X, X, X, X, X, 5, 0, + X, X, X, X, X, X, X, 1, 1, 1, 4, X, X, X, X, X, 3, 1, X, X, X, X, X, X, X, 4, 0, X, X, X, X, + X, X, X, 2, 0, X, X, X, X, X, X, X, 1, 4, X, X, X, X, X, X, X, 2, 8, X, X, X, X, X, X, X, 3, + 0, X, X, X, X, X, X, X, 3, 0, X, X, X, X, X, X, X, 2, 1, X, X, X, X, X, X, X, 3, 1, X, X, X, + X, X, X, X, 4, 0, X, X, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 1, 2, X, X, X, X, X, X, X, + 3, 2, X, X, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, 4, 0, X, X, X, X, X, X, X, 1, 1, X, X, + X, X, X, X, X, 2, 1, X, X, X, X, X, X, X, 1, 0, X, X, X, X, X, X, X, 1, 0, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X +#undef X + }; + + VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Id + labelEdgeTableLT3d[nCasesLT3d * nLabelEdgeTableLT3dElemSize] = { +#define X -1 + X, X, X, X, X, X, X, X, X, X, X, X, X, 6, 18, X, X, X, X, X, X, X, X, X, X, X, + 2, 0, X, X, X, X, X, X, X, X, X, X, X, 2, 12, 2, 14, 4, 18, X, X, X, X, X, X, X, + 2, 12, X, X, X, X, X, X, X, X, X, X, X, 2, 0, 1, 18, 2, 3, 3, 18, X, X, X, X, X, + 3, 0, 1, 12, X, X, X, X, X, X, X, X, X, 1, 18, 2, 14, 2, 3, 3, 18, X, X, X, X, X, + 2, 3, X, X, X, X, X, X, X, X, X, X, X, 2, 18, 2, 12, 2, 16, 2, 18, X, X, X, X, X, + 2, 0, 2, 3, X, X, X, X, X, X, X, X, X, 2, 12, 2, 14, 2, 12, 2, 16, 2, 18, X, X, X, + 1, 12, 3, 3, X, X, X, X, X, X, X, X, X, 2, 0, 2, 18, 2, 16, 2, 18, X, X, X, X, X, + 3, 0, 3, 3, X, X, X, X, X, X, X, X, X, 1, 18, 2, 14, 1, 18, 2, 16, 2, 18, X, X, X, + 2, 8, X, X, X, X, X, X, X, X, X, X, X, 4, 18, 2, 14, 2, 16, X, X, X, X, X, X, X, + 2, 0, 2, 8, X, X, X, X, X, X, X, X, X, 2, 12, 2, 14, 2, 18, 2, 14, 2, 16, X, X, X, + 2, 12, 2, 8, X, X, X, X, X, X, X, X, X, 2, 0, 1, 18, 2, 3, 1, 18, 2, 14, 2, 16, X, + 3, 0, 1, 12, 2, 8, X, X, X, X, X, X, X, 1, 18, 2, 14, 2, 3, 1, 18, 2, 14, 2, 16, X, + 2, 3, 2, 8, X, X, X, X, X, X, X, X, X, 2, 18, 2, 12, 2, 16, 2, 14, 2, 16, X, X, X, + 2, 0, 2, 3, 2, 8, X, X, X, X, X, X, X, 2, 12, 2, 14, 2, 12, 2, 16, 2, 14, 2, 16, X, + 1, 12, 3, 3, 2, 8, X, X, X, X, X, X, X, 2, 0, 2, 18, 2, 16, 2, 14, 2, 16, X, X, X, + 3, 0, 3, 3, 2, 8, X, X, X, X, X, X, X, 1, 18, 2, 14, 1, 18, 2, 16, 2, 14, 2, 16, X, + 2, 14, X, X, X, X, X, X, X, X, X, X, X, 1, 18, 2, 0, 2, 18, 2, 8, 1, 18, X, X, X, + 3, 0, 1, 14, X, X, X, X, X, X, X, X, X, 2, 12, 3, 18, 2, 8, 1, 18, X, X, X, X, X, + 1, 12, 1, 14, 1, 12, 1, 14, X, X, X, X, X, 4, 0, 2, 3, 1, 18, 2, 8, 1, 18, X, X, X, + 4, 0, 1, 12, 1, 14, X, X, X, X, X, X, X, 2, 18, 2, 3, 1, 18, 2, 8, 1, 18, X, X, X, + 1, 14, 2, 3, 1, 14, X, X, X, X, X, X, X, 1, 18, 2, 0, 2, 12, 2, 16, 2, 8, 1, 18, X, + 3, 0, 2, 3, 1, 14, X, X, X, X, X, X, X, 2, 12, 1, 18, 2, 12, 2, 16, 2, 8, 1, 18, X, + 1, 12, 1, 14, 3, 3, 1, 14, X, X, X, X, X, 4, 0, 1, 18, 2, 16, 2, 8, 1, 18, X, X, X, + 4, 0, 3, 3, 1, 14, X, X, X, X, X, X, X, 3, 18, 2, 16, 2, 8, 1, 18, X, X, X, X, X, + 1, 14, 3, 8, X, X, X, X, X, X, X, X, X, 1, 18, 2, 0, 3, 18, 2, 16, X, X, X, X, X, + 3, 0, 3, 8, X, X, X, X, X, X, X, X, X, 2, 12, 4, 18, 2, 16, X, X, X, X, X, X, X, + 1, 12, 1, 14, 1, 12, 3, 8, X, X, X, X, X, 4, 0, 2, 3, 2, 18, 2, 16, X, X, X, X, X, + 4, 0, 1, 12, 3, 8, X, X, X, X, X, X, X, 2, 18, 2, 3, 2, 18, 2, 16, X, X, X, X, X, + 1, 14, 2, 3, 3, 8, X, X, X, X, X, X, X, 1, 18, 2, 0, 2, 12, 2, 16, 1, 18, 2, 16, X, + 3, 0, 2, 3, 3, 8, X, X, X, X, X, X, X, 2, 12, 1, 18, 2, 12, 2, 16, 1, 18, 2, 16, X, + 1, 12, 1, 14, 3, 3, 3, 8, X, X, X, X, X, 4, 0, 1, 18, 2, 16, 1, 18, 2, 16, X, X, X, + 4, 0, 3, 3, 3, 8, X, X, X, X, X, X, X, 3, 18, 2, 16, 1, 18, 2, 16, X, X, X, X, X, + 6, 18, X, X, X, X, X, X, X, X, X, X, X, 4, 0, 4, 3, 4, 8, X, X, X, X, X, X, X, + 4, 0, 4, 18, X, X, X, X, X, X, X, X, X, 1, 12, 1, 14, 4, 3, 4, 8, X, X, X, X, X, + 2, 12, 1, 18, 2, 12, 3, 18, X, X, X, X, X, 3, 0, 3, 3, 4, 8, X, X, X, X, X, X, X, + 1, 18, 2, 0, 2, 12, 3, 18, X, X, X, X, X, 1, 14, 3, 3, 4, 8, X, X, X, X, X, X, X, + 2, 18, 4, 3, 2, 18, X, X, X, X, X, X, X, 4, 0, 1, 12, 1, 16, 4, 8, X, X, X, X, X, + 4, 0, 4, 3, 2, 18, X, X, X, X, X, X, X, 1, 12, 1, 14, 1, 12, 1, 16, 4, 8, X, X, X, + 2, 12, 2, 18, 2, 3, 2, 18, X, X, X, X, X, 3, 0, 1, 16, 4, 8, X, X, X, X, X, X, X, + 1, 18, 2, 0, 1, 18, 2, 3, 2, 18, X, X, X, 1, 14, 1, 16, 4, 8, X, X, X, X, X, X, X, + 4, 18, 4, 8, X, X, X, X, X, X, X, X, X, 4, 0, 4, 3, 1, 14, 1, 16, X, X, X, X, X, + 4, 0, 2, 18, 4, 8, X, X, X, X, X, X, X, 1, 12, 1, 14, 4, 3, 1, 14, 1, 16, X, X, X, + 2, 12, 1, 18, 2, 12, 1, 18, 4, 8, X, X, X, 3, 0, 3, 3, 1, 14, 1, 16, X, X, X, X, X, + 1, 18, 2, 0, 2, 12, 1, 18, 4, 8, X, X, X, 1, 14, 3, 3, 1, 14, 1, 16, X, X, X, X, X, + 2, 18, 4, 3, 4, 8, X, X, X, X, X, X, X, 4, 0, 1, 12, 1, 16, 1, 14, 1, 16, X, X, X, + 4, 0, 4, 3, 4, 8, X, X, X, X, X, X, X, 1, 12, 1, 14, 1, 12, 1, 16, 1, 14, 1, 16, X, + 2, 12, 2, 18, 2, 3, 4, 8, X, X, X, X, X, 3, 0, 1, 16, 1, 14, 1, 16, X, X, X, X, X, + 1, 18, 2, 0, 1, 18, 2, 3, 4, 8, X, X, X, 1, 14, 1, 16, 1, 14, 1, 16, X, X, X, X, X, + 1, 18, 2, 14, 2, 18, 2, 14, 1, 18, X, X, X, 3, 0, 4, 3, 3, 8, X, X, X, X, X, X, X, + 2, 0, 3, 18, 2, 14, 1, 18, X, X, X, X, X, 1, 12, 4, 3, 3, 8, X, X, X, X, X, X, X, + 2, 12, 2, 14, 2, 12, 1, 18, 2, 14, 1, 18, X, 2, 0, 3, 3, 3, 8, X, X, X, X, X, X, X, + 2, 18, 2, 12, 1, 18, 2, 14, 1, 18, X, X, X, 3, 3, 3, 8, X, X, X, X, X, X, X, X, X, + 1, 18, 2, 14, 4, 3, 2, 14, 1, 18, X, X, X, 3, 0, 1, 12, 1, 16, 3, 8, X, X, X, X, X, + 2, 0, 1, 18, 4, 3, 2, 14, 1, 18, X, X, X, 2, 12, 1, 16, 3, 8, X, X, X, X, X, X, X, + 2, 12, 2, 14, 1, 18, 2, 3, 2, 14, 1, 18, X, 2, 0, 1, 16, 3, 8, X, X, X, X, X, X, X, + 3, 18, 2, 3, 2, 14, 1, 18, X, X, X, X, X, 1, 16, 3, 8, X, X, X, X, X, X, X, X, X, + 1, 18, 2, 14, 3, 18, 2, 8, X, X, X, X, X, 3, 0, 4, 3, 1, 16, X, X, X, X, X, X, X, + 2, 0, 4, 18, 2, 8, X, X, X, X, X, X, X, 1, 12, 4, 3, 1, 16, X, X, X, X, X, X, X, + 2, 12, 2, 14, 2, 12, 2, 18, 2, 8, X, X, X, 2, 0, 3, 3, 1, 16, X, X, X, X, X, X, X, + 2, 18, 2, 12, 2, 18, 2, 8, X, X, X, X, X, 3, 3, 1, 16, X, X, X, X, X, X, X, X, X, + 1, 18, 2, 14, 4, 3, 1, 18, 2, 8, X, X, X, 3, 0, 1, 12, 2, 16, X, X, X, X, X, X, X, + 2, 0, 1, 18, 4, 3, 1, 18, 2, 8, X, X, X, 2, 12, 2, 16, X, X, X, X, X, X, X, X, X, + 2, 12, 2, 14, 1, 18, 2, 3, 1, 18, 2, 8, X, 2, 0, 2, 16, X, X, X, X, X, X, X, X, X, + 3, 18, 2, 3, 1, 18, 2, 8, X, X, X, X, X, 2, 16, X, X, X, X, X, X, X, X, X, X, X, + 2, 16, X, X, X, X, X, X, X, X, X, X, X, 3, 18, 2, 3, 1, 18, 2, 8, X, X, X, X, X, + 2, 0, 2, 16, X, X, X, X, X, X, X, X, X, 2, 12, 2, 14, 1, 18, 2, 3, 1, 18, 2, 8, X, + 2, 12, 2, 16, X, X, X, X, X, X, X, X, X, 2, 0, 1, 18, 4, 3, 1, 18, 2, 8, X, X, X, + 3, 0, 1, 12, 2, 16, X, X, X, X, X, X, X, 1, 18, 2, 14, 4, 3, 1, 18, 2, 8, X, X, X, + 3, 3, 1, 16, X, X, X, X, X, X, X, X, X, 2, 18, 2, 12, 2, 18, 2, 8, X, X, X, X, X, + 2, 0, 3, 3, 1, 16, X, X, X, X, X, X, X, 2, 12, 2, 14, 2, 12, 2, 18, 2, 8, X, X, X, + 1, 12, 4, 3, 1, 16, X, X, X, X, X, X, X, 2, 0, 4, 18, 2, 8, X, X, X, X, X, X, X, + 3, 0, 4, 3, 1, 16, X, X, X, X, X, X, X, 1, 18, 2, 14, 3, 18, 2, 8, X, X, X, X, X, + 1, 16, 3, 8, X, X, X, X, X, X, X, X, X, 3, 18, 2, 3, 2, 14, 1, 18, X, X, X, X, X, + 2, 0, 1, 16, 3, 8, X, X, X, X, X, X, X, 2, 12, 2, 14, 1, 18, 2, 3, 2, 14, 1, 18, X, + 2, 12, 1, 16, 3, 8, X, X, X, X, X, X, X, 2, 0, 1, 18, 4, 3, 2, 14, 1, 18, X, X, X, + 3, 0, 1, 12, 1, 16, 3, 8, X, X, X, X, X, 1, 18, 2, 14, 4, 3, 2, 14, 1, 18, X, X, X, + 3, 3, 3, 8, X, X, X, X, X, X, X, X, X, 2, 18, 2, 12, 1, 18, 2, 14, 1, 18, X, X, X, + 2, 0, 3, 3, 3, 8, X, X, X, X, X, X, X, 2, 12, 2, 14, 2, 12, 1, 18, 2, 14, 1, 18, X, + 1, 12, 4, 3, 3, 8, X, X, X, X, X, X, X, 2, 0, 3, 18, 2, 14, 1, 18, X, X, X, X, X, + 3, 0, 4, 3, 3, 8, X, X, X, X, X, X, X, 1, 18, 2, 14, 2, 18, 2, 14, 1, 18, X, X, X, + 1, 14, 1, 16, 1, 14, 1, 16, X, X, X, X, X, 1, 18, 2, 0, 1, 18, 2, 3, 4, 8, X, X, X, + 3, 0, 1, 16, 1, 14, 1, 16, X, X, X, X, X, 2, 12, 2, 18, 2, 3, 4, 8, X, X, X, X, X, + 1, 12, 1, 14, 1, 12, 1, 16, 1, 14, 1, 16, X, 4, 0, 4, 3, 4, 8, X, X, X, X, X, X, X, + 4, 0, 1, 12, 1, 16, 1, 14, 1, 16, X, X, X, 2, 18, 4, 3, 4, 8, X, X, X, X, X, X, X, + 1, 14, 3, 3, 1, 14, 1, 16, X, X, X, X, X, 1, 18, 2, 0, 2, 12, 1, 18, 4, 8, X, X, X, + 3, 0, 3, 3, 1, 14, 1, 16, X, X, X, X, X, 2, 12, 1, 18, 2, 12, 1, 18, 4, 8, X, X, X, + 1, 12, 1, 14, 4, 3, 1, 14, 1, 16, X, X, X, 4, 0, 2, 18, 4, 8, X, X, X, X, X, X, X, + 4, 0, 4, 3, 1, 14, 1, 16, X, X, X, X, X, 4, 18, 4, 8, X, X, X, X, X, X, X, X, X, + 1, 14, 1, 16, 4, 8, X, X, X, X, X, X, X, 1, 18, 2, 0, 1, 18, 2, 3, 2, 18, X, X, X, + 3, 0, 1, 16, 4, 8, X, X, X, X, X, X, X, 2, 12, 2, 18, 2, 3, 2, 18, X, X, X, X, X, + 1, 12, 1, 14, 1, 12, 1, 16, 4, 8, X, X, X, 4, 0, 4, 3, 2, 18, X, X, X, X, X, X, X, + 4, 0, 1, 12, 1, 16, 4, 8, X, X, X, X, X, 2, 18, 4, 3, 2, 18, X, X, X, X, X, X, X, + 1, 14, 3, 3, 4, 8, X, X, X, X, X, X, X, 1, 18, 2, 0, 2, 12, 3, 18, X, X, X, X, X, + 3, 0, 3, 3, 4, 8, X, X, X, X, X, X, X, 2, 12, 1, 18, 2, 12, 3, 18, X, X, X, X, X, + 1, 12, 1, 14, 4, 3, 4, 8, X, X, X, X, X, 4, 0, 4, 18, X, X, X, X, X, X, X, X, X, + 4, 0, 4, 3, 4, 8, X, X, X, X, X, X, X, 6, 18, X, X, X, X, X, X, X, X, X, X, X, + 3, 18, 2, 16, 1, 18, 2, 16, X, X, X, X, X, 4, 0, 3, 3, 3, 8, X, X, X, X, X, X, X, + 4, 0, 1, 18, 2, 16, 1, 18, 2, 16, X, X, X, 1, 12, 1, 14, 3, 3, 3, 8, X, X, X, X, X, + 2, 12, 1, 18, 2, 12, 2, 16, 1, 18, 2, 16, X, 3, 0, 2, 3, 3, 8, X, X, X, X, X, X, X, + 1, 18, 2, 0, 2, 12, 2, 16, 1, 18, 2, 16, X, 1, 14, 2, 3, 3, 8, X, X, X, X, X, X, X, + 2, 18, 2, 3, 2, 18, 2, 16, X, X, X, X, X, 4, 0, 1, 12, 3, 8, X, X, X, X, X, X, X, + 4, 0, 2, 3, 2, 18, 2, 16, X, X, X, X, X, 1, 12, 1, 14, 1, 12, 3, 8, X, X, X, X, X, + 2, 12, 4, 18, 2, 16, X, X, X, X, X, X, X, 3, 0, 3, 8, X, X, X, X, X, X, X, X, X, + 1, 18, 2, 0, 3, 18, 2, 16, X, X, X, X, X, 1, 14, 3, 8, X, X, X, X, X, X, X, X, X, + 3, 18, 2, 16, 2, 8, 1, 18, X, X, X, X, X, 4, 0, 3, 3, 1, 14, X, X, X, X, X, X, X, + 4, 0, 1, 18, 2, 16, 2, 8, 1, 18, X, X, X, 1, 12, 1, 14, 3, 3, 1, 14, X, X, X, X, X, + 2, 12, 1, 18, 2, 12, 2, 16, 2, 8, 1, 18, X, 3, 0, 2, 3, 1, 14, X, X, X, X, X, X, X, + 1, 18, 2, 0, 2, 12, 2, 16, 2, 8, 1, 18, X, 1, 14, 2, 3, 1, 14, X, X, X, X, X, X, X, + 2, 18, 2, 3, 1, 18, 2, 8, 1, 18, X, X, X, 4, 0, 1, 12, 1, 14, X, X, X, X, X, X, X, + 4, 0, 2, 3, 1, 18, 2, 8, 1, 18, X, X, X, 1, 12, 1, 14, 1, 12, 1, 14, X, X, X, X, X, + 2, 12, 3, 18, 2, 8, 1, 18, X, X, X, X, X, 3, 0, 1, 14, X, X, X, X, X, X, X, X, X, + 1, 18, 2, 0, 2, 18, 2, 8, 1, 18, X, X, X, 2, 14, X, X, X, X, X, X, X, X, X, X, X, + 1, 18, 2, 14, 1, 18, 2, 16, 2, 14, 2, 16, X, 3, 0, 3, 3, 2, 8, X, X, X, X, X, X, X, + 2, 0, 2, 18, 2, 16, 2, 14, 2, 16, X, X, X, 1, 12, 3, 3, 2, 8, X, X, X, X, X, X, X, + 2, 12, 2, 14, 2, 12, 2, 16, 2, 14, 2, 16, X, 2, 0, 2, 3, 2, 8, X, X, X, X, X, X, X, + 2, 18, 2, 12, 2, 16, 2, 14, 2, 16, X, X, X, 2, 3, 2, 8, X, X, X, X, X, X, X, X, X, + 1, 18, 2, 14, 2, 3, 1, 18, 2, 14, 2, 16, X, 3, 0, 1, 12, 2, 8, X, X, X, X, X, X, X, + 2, 0, 1, 18, 2, 3, 1, 18, 2, 14, 2, 16, X, 2, 12, 2, 8, X, X, X, X, X, X, X, X, X, + 2, 12, 2, 14, 2, 18, 2, 14, 2, 16, X, X, X, 2, 0, 2, 8, X, X, X, X, X, X, X, X, X, + 4, 18, 2, 14, 2, 16, X, X, X, X, X, X, X, 2, 8, X, X, X, X, X, X, X, X, X, X, X, + 1, 18, 2, 14, 1, 18, 2, 16, 2, 18, X, X, X, 3, 0, 3, 3, X, X, X, X, X, X, X, X, X, + 2, 0, 2, 18, 2, 16, 2, 18, X, X, X, X, X, 1, 12, 3, 3, X, X, X, X, X, X, X, X, X, + 2, 12, 2, 14, 2, 12, 2, 16, 2, 18, X, X, X, 2, 0, 2, 3, X, X, X, X, X, X, X, X, X, + 2, 18, 2, 12, 2, 16, 2, 18, X, X, X, X, X, 2, 3, X, X, X, X, X, X, X, X, X, X, X, + 1, 18, 2, 14, 2, 3, 3, 18, X, X, X, X, X, 3, 0, 1, 12, X, X, X, X, X, X, X, X, X, + 2, 0, 1, 18, 2, 3, 3, 18, X, X, X, X, X, 2, 12, X, X, X, X, X, X, X, X, X, X, X, + 2, 12, 2, 14, 4, 18, X, X, X, X, X, X, X, 2, 0, X, X, X, X, X, X, X, X, X, X, X, + 6, 18, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X +#undef X + }; + + // Return the const array at the index of "arrayIndex". + // An array boundary check is performed via VTKM_ASSERT. + if (this->CopyType == COPY_VERTEXOFFSET) + { + if (this->IsData2D) + { + VTKM_ASSERT(arrayIndex < nVertices2d * 2); + return vertexOffset2d[arrayIndex]; + } + else + { + VTKM_ASSERT(arrayIndex < nVertices3d * 3); + return vertexOffset3d[arrayIndex]; + } + } + else if (this->CopyType == COPY_EDGETABLE) + { + if (this->IsData2D) + { + VTKM_ASSERT(arrayIndex < nEdges2d * 2); + return edgeTable2d[arrayIndex]; + } + else + { + if (this->IsMarchingCube) + { + VTKM_ASSERT(arrayIndex < nEdgesMC3d * 2); + return edgeTableMC3d[arrayIndex]; + } + else + { + VTKM_ASSERT(arrayIndex < nEdgesLT3d * 2); + return edgeTableLT3d[arrayIndex]; + } + } + } + else if (this->CopyType == COPY_NUMBOUNDTABLE) + { + if (this->IsData2D) + { + VTKM_ASSERT(arrayIndex < nCases2d); + return numLinesTable2d[arrayIndex]; + } + else + { + if (this->IsMarchingCube) + { + VTKM_ASSERT(arrayIndex < nCasesMC3d); + return numTrianglesTableMC3d[arrayIndex]; + } + else + { + VTKM_ASSERT(arrayIndex < nCasesLT3d); + return numTrianglesTableLT3d[arrayIndex]; + } + } + } + else if (this->CopyType == COPY_BOUNDARYTABLE) + { + if (this->IsData2D) + { + VTKM_ASSERT(arrayIndex < nLineTableElemSize2d * nCases2d); + return lineTable2d[arrayIndex]; + } + else + { + if (this->IsMarchingCube) + { + VTKM_ASSERT(arrayIndex < nCasesMC3d * nTriTableMC3dElemSize); + return triTableMC3d[arrayIndex]; + } + else + { + VTKM_ASSERT(arrayIndex < nCasesLT3d * nTriTableLT3dElemSize); + return triTableLT3d[arrayIndex]; + } + } + } + else if (this->CopyType == COPY_LABELEDGETABLE) + { + VTKM_ASSERT(!this->IsData2D && "There is no label edge table for 2D data!"); + if (this->IsMarchingCube) + { + VTKM_ASSERT(arrayIndex < nCasesMC3d * nLabelEdgeTableMC3dElemSize); + return labelEdgeTableMC3d[arrayIndex]; + } + else + { + VTKM_ASSERT(arrayIndex < nCasesLT3d * nLabelEdgeTableLT3dElemSize); + return labelEdgeTableLT3d[arrayIndex]; + } + } + // this should not happen! Just in case of fallouts. + VTKM_ASSERT(false && "Undefined const array type for copy!"); + return -1; + } + +private: + const bool IsData2D; + const bool IsMarchingCube; + const vtkm::IdComponent CopyType; +}; // CopyConstArraysForMarchingCubesDataTablesWorklet + +} // namespace extract_top_volume_contours +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetCellCasesWorklet.h b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetCellCasesWorklet.h new file mode 100644 index 0000000000..02d87a90e7 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetCellCasesWorklet.h @@ -0,0 +1,204 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_extract_top_volume_contours_get_cell_cases_worklet_h +#define vtk_m_filter_scalar_topology_worklet_extract_top_volume_contours_get_cell_cases_worklet_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace extract_top_volume_contours +{ +/// Worklet for getting the polarity case of a cell compared to the isovalue. +/// Only consider 2D and 3D data. +/// The output for each cell is an integer ([0, 7] if 2D, or [0, 255] if 3D) +/// indicating the polarity at each vertex of the cell compared to the isovalue. +template +class GetCellCasesWorklet : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void( + FieldIn localIdx, // (input) local point index + WholeArrayIn dataValues, // (array input) data values within block + WholeArrayIn globalIds, // (array input) global regular IDs within block + WholeArrayIn vertexOffset, // (array input) vertex offset look-up table + FieldOut caseCell // (output) the polarity case (in binary) of the cell + ); + using ExecutionSignature = _5(_1, _2, _3, _4); + using InputDomain = _1; + + using IdArrayPortalType = typename vtkm::cont::ArrayHandle::ReadPortalType; + using ValueArrayPortalType = typename vtkm::cont::ArrayHandle::ReadPortalType; + + /// + /// Constructor + /// + /// dimension of points in the grid + /// the direction for tiebreaking when comparing values + /// isovalue for the isosurface to extract + /// + VTKM_EXEC_CONT + GetCellCasesWorklet(const vtkm::Id3 ptDimensions, + const vtkm::Id branchSaddleEpsilon, + const ValueType isoValue, + const bool shiftIsovalueByEps, + const vtkm::Id globalRegularId) + : PointDimensions(ptDimensions) + , BranchSaddleEpsilon(branchSaddleEpsilon) + , IsoValue(isoValue) + , ShiftIsovalueByEpsilon(shiftIsovalueByEps) + , GlobalRegularId(globalRegularId) + { + CellDimensions[0] = ptDimensions[0] - 1; + CellDimensions[1] = ptDimensions[1] - 1; + CellDimensions[2] = ptDimensions[2] - 1; + } + + /// + /// Computes the polarity case of cells + /// + /// the local index of the point in the local grid + /// all data values on the local grid points + /// integer indicating the polarity case of the cell originating at the input point + VTKM_EXEC vtkm::Id operator()(const vtkm::Id localIndex, + const ValueArrayPortalType& dataValuesPortal, + const IdArrayPortalType& globalIdsPortal, + const IdArrayPortalType& vertexOffset) const + { + const vtkm::Id nPoints = PointDimensions[0] * PointDimensions[1] * PointDimensions[2]; + VTKM_ASSERT(dataValuesPortal.GetNumberOfValues() == nPoints); + if (CellDimensions[2] <= 0) + { + // the 2D local coordinate of the input point + vtkm::Id2 localPt(localIndex % CellDimensions[0], localIndex / CellDimensions[0]); + vtkm::Id caseCell = 0; + // iterate over all points of the cell + for (vtkm::Id i = 0; i < nVertices2d; i++) + { + vtkm::Id currPtIdx = i * 2; + vtkm::Id currPt = vertexOffset.Get(currPtIdx) + localPt[0] + + (vertexOffset.Get(currPtIdx + 1) + localPt[1]) * PointDimensions[0]; + VTKM_ASSERT(currPt < nPoints); + // when point value == IsoValue + // the index is 1 only if the branch is lower-end + // Update 01/09/2025: + // We need to bring in the global regular ID of vertices for simulation of simplicity. + // When BranchSaddleEpsilon == -1 (lower end is leaf), the extracted contour has the isovalue of value.(GlobalRegularId - 0.5); + // When BranchSaddleEpsilon == 1 (upper end is leaf), the extracted contour has the isovalue of value.(GlobalRegularId + 0.5); + // We only mark the polarity of the vertex as positive when the value and global regular id is strictly larger than the extracted contour. + if (dataValuesPortal.Get(currPt) > IsoValue || + (dataValuesPortal.Get(currPt) == IsoValue && + ((ShiftIsovalueByEpsilon && BranchSaddleEpsilon < 0) || + (!ShiftIsovalueByEpsilon && + (BranchSaddleEpsilon > 0 ? globalIdsPortal.Get(currPt) > GlobalRegularId + : globalIdsPortal.Get(currPt) >= GlobalRegularId))))) + caseCell |= vtkm::Id(1) << i; + } + return caseCell; + } + else + { + // the 3D local coordinate of the input point + vtkm::Id3 localPt(localIndex % CellDimensions[0], + (localIndex / CellDimensions[0]) % CellDimensions[1], + localIndex / (CellDimensions[0] * CellDimensions[1])); + vtkm::Id caseCell = 0; + // iterate over all points of the cell + for (vtkm::Id i = 0; i < nVertices3d; i++) + { + vtkm::Id currPtIdx = i * 3; + vtkm::Id currPt = vertexOffset.Get(currPtIdx) + localPt[0] + + (vertexOffset.Get(currPtIdx + 1) + localPt[1]) * PointDimensions[0] + + (vertexOffset.Get(currPtIdx + 2) + localPt[2]) * + (PointDimensions[0] * PointDimensions[1]); + VTKM_ASSERT(currPt < nPoints); + // when point value == IsoValue + // the index is 1 only if the branch is lower-end + // Update 01/09/2025: + // We need to bring in the global regular ID of vertices for simulation of simplicity. + // When BranchSaddleEpsilon == -1 (lower end is leaf), the extracted contour has the isovalue of value.(GlobalRegularId - 0.5); + // When BranchSaddleEpsilon == 1 (upper end is leaf), the extracted contour has the isovalue of value.(GlobalRegularId + 0.5); + // We only mark the polarity of the vertex as positive when the value and global regular id is strictly larger than the extracted contour. + if (dataValuesPortal.Get(currPt) > IsoValue || + (dataValuesPortal.Get(currPt) == IsoValue && + ((ShiftIsovalueByEpsilon && BranchSaddleEpsilon < 0) || + (!ShiftIsovalueByEpsilon && + (BranchSaddleEpsilon > 0 ? globalIdsPortal.Get(currPt) > GlobalRegularId + : globalIdsPortal.Get(currPt) >= GlobalRegularId))))) + caseCell |= vtkm::Id(1) << i; + } + return caseCell; + } + } + +private: + vtkm::Id3 PointDimensions; + vtkm::Id BranchSaddleEpsilon; + ValueType IsoValue; + bool ShiftIsovalueByEpsilon; + vtkm::Id GlobalRegularId; + vtkm::Id3 CellDimensions; + +}; // GetCellCasesWorklet + +} // namespace extract_top_volume_contours +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetEdgesInCellWorklet.h b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetEdgesInCellWorklet.h new file mode 100644 index 0000000000..93686c9122 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetEdgesInCellWorklet.h @@ -0,0 +1,492 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_extract_top_volume_contours_get_edges_in_cell_worklet_h +#define vtk_m_filter_scalar_topology_worklet_extract_top_volume_contours_get_edges_in_cell_worklet_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace extract_top_volume_contours +{ + +constexpr vtkm::IdComponent MAX_MARCHING_CUBE_TRIANGLES = static_cast(5); +constexpr vtkm::IdComponent MAX_LINEAR_INTERPOLATION_TRIANGLES = static_cast(12); + +/// Worklet for calculating the edges to be drawn in the cell +/// NOTE: this worklet can only work on 2D and 3D data +template +class GetEdgesInCellWorklet : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void( + FieldIn edgeOffset, // (input) offset of output edge in the output array + FieldIn caseCell, // (input) the marching cube case of the cell + WholeArrayIn localIds, // (array input) local ids of points + WholeArrayIn dataValues, // (array input) data values within block + WholeArrayIn globalIds, // (array input) global regular ids within block + WholeArrayIn vertexOffset, // (array input) vertex offset look-up table + WholeArrayIn edgeTable, // (array input) edge-in-cell look-up table + WholeArrayIn numBoundTable, // (array input) number of boundaries look-up table + WholeArrayIn boundaryTable, // (array input) edge-of-boundary look-up table + WholeArrayIn labelEdgeTable, // (array input) label edge (only for 3D) look-up table + WholeArrayOut edgesFrom, // (array output) array of start-points of edges on the isosurface + WholeArrayOut edgesTo, // (array output) array of end-points of edges on the isosurface + WholeArrayOut + isValidEdges, // (array output) whether the edge plan to draw belongs to the branch + ExecObject + findSuperarcForNode // (execution object) detector for the superarc of interpolated nodes + ); + using ExecutionSignature = + void(InputIndex, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14); + using InputDomain = _1; + + using IdArrayReadPortalType = typename vtkm::cont::ArrayHandle::ReadPortalType; + using IdArrayWritePortalType = typename vtkm::cont::ArrayHandle::WritePortalType; + using ValueArrayPortalType = typename vtkm::cont::ArrayHandle::ReadPortalType; + using EdgePointArrayPortalType = + typename vtkm::cont::ArrayHandle::WritePortalType; + + /// Constructor + /// ptDimensions: dimension of points in the grid + /// branchSuperarc: the superarc on the given branch intersecting the isosurface + /// isoValue: isovalue for the isosurface to extract + VTKM_EXEC_CONT + GetEdgesInCellWorklet(const vtkm::Id3 ptDimensions, + const vtkm::Id3 globalPointIndexStart, + const ValueType isoValue, + const vtkm::Id globalRegularId, + const vtkm::Id branchSuperarc, + const vtkm::Id branchSaddleEpsilon, + const vtkm::Id totNumPoints, + const bool marchingCubes, + const bool contourByValue) + : PointDimensions(ptDimensions) + , GlobalPointIndexStart(globalPointIndexStart) + , IsoValue(isoValue) + , GlobalRegularId(globalRegularId) + , BranchSuperarc(branchSuperarc) + , BranchSaddleEpsilon(branchSaddleEpsilon) + , TotalNumPoints(totNumPoints) + , IsMarchingCubes(marchingCubes) + , IsContourByValue(contourByValue) + { + CellDimensions[0] = ptDimensions[0] - 1; + CellDimensions[1] = ptDimensions[1] - 1; + CellDimensions[2] = ptDimensions[2] - 1; + } + + // cell index: the point index within the local cell + VTKM_EXEC vtkm::Id CellIndexToNodeIndex2D(const vtkm::Id2& localPt, + const vtkm::Id cellIndex, + const IdArrayReadPortalType& vertOffset) const + { + return vertOffset.Get(cellIndex * 2) + localPt[0] + + (vertOffset.Get(cellIndex * 2 + 1) + localPt[1]) * PointDimensions[0]; + } + + // cell index: the point index within the local cell + VTKM_EXEC vtkm::Vec3f_64 CellIndexToNodeCoord2D(const vtkm::Id2& localPt, + const vtkm::Id cellIndex, + const IdArrayReadPortalType& vertOffset) const + { + return vtkm::Vec3f_64( + static_cast(vertOffset.Get(cellIndex * 2) + localPt[0]), + static_cast(vertOffset.Get(cellIndex * 2 + 1) + localPt[1]), + static_cast(0)); + } + + // cell index: the point index within the local cell + VTKM_EXEC vtkm::Id CellIndexToNodeIndex3D(const vtkm::Id3& localPt, + const vtkm::Id cellIndex, + const IdArrayReadPortalType& vertOffset) const + { + return vertOffset.Get(cellIndex * 3) + localPt[0] + + (vertOffset.Get(cellIndex * 3 + 1) + localPt[1]) * PointDimensions[0] + + (vertOffset.Get(cellIndex * 3 + 2) + localPt[2]) * (PointDimensions[0] * PointDimensions[1]); + } + + // cell index: the point index within the local cell + VTKM_EXEC vtkm::Vec3f_64 CellIndexToNodeCoord3D(const vtkm::Id3& localPt, + const vtkm::Id cellIndex, + const IdArrayReadPortalType& vertOffset) const + { + return vtkm::Vec3f_64( + static_cast(vertOffset.Get(cellIndex * 3) + localPt[0]), + static_cast(vertOffset.Get(cellIndex * 3 + 1) + localPt[1]), + static_cast(vertOffset.Get(cellIndex * 3 + 2) + localPt[2])); + } + + // Implementation to draw isosurface edges + // all hard-coded numbers in this function depends on the dimension of the data + // The number of vertices/lines/faces of a cell is fixed for a certain dimension + // The number of cases for the marching cube algorithm are also hard-coded + // Check MarchingCubesDataTables.h for more details + template + VTKM_EXEC void operator()( + const vtkm::Id localIndex, // refers to the index in the grid + const vtkm::Id edgeOffset, + const vtkm::Id caseCell, + const IdArrayReadPortalType& localIdsPortal, // refers to the index in (superarc etc.) arrays + const ValueArrayPortalType& dataValuesPortal, + const IdArrayReadPortalType& globalIdsPortal, + const IdArrayReadPortalType& vertexOffset, + const IdArrayReadPortalType& edgeTable, + const IdArrayReadPortalType& numBoundTable, + const IdArrayReadPortalType& boundaryTable, + const IdArrayReadPortalType& labelEdgeTable, + EdgePointArrayPortalType& edgesFromPortal, + EdgePointArrayPortalType& edgesToPortal, + IdArrayWritePortalType& isValidEdgesPortal, + const FindSuperarcExecType& findSuperarcForNode) const + { + const vtkm::Id nPoints = PointDimensions[0] * PointDimensions[1] * PointDimensions[2]; + // 2D + if (CellDimensions[2] <= 0) + { + const vtkm::Id2 localPt(localIndex % CellDimensions[0], localIndex / CellDimensions[0]); + + VTKM_ASSERT(localIdsPortal.GetNumberOfValues() == nPoints); + VTKM_ASSERT(dataValuesPortal.GetNumberOfValues() == nPoints); + + const vtkm::Id numEdges = numBoundTable.Get(caseCell); + if (numEdges < 1) + return; + for (vtkm::Id edgeIndex = 0; edgeIndex < numEdges; edgeIndex++) + { + const vtkm::Id lineForCaseOffset = caseCell * nLineTableElemSize2d; // 8; + const vtkm::Id lineOffset = lineForCaseOffset + edgeIndex * 2; + // lineFrom and lineTo are two edges where the isosurface edge intersects + const vtkm::Id lineFrom = boundaryTable.Get(lineOffset); + const vtkm::Id lineTo = boundaryTable.Get(lineOffset + 1); + + // We need to assure that both lineFrom and lineTo belong to the branch + // all 0 and 1 in the variables below refer to the two vertices of the line + const vtkm::Id lineFromVert0 = + CellIndexToNodeIndex2D(localPt, edgeTable.Get(lineFrom * 2), vertexOffset); + const vtkm::Id lineFromVert1 = + CellIndexToNodeIndex2D(localPt, edgeTable.Get(lineFrom * 2 + 1), vertexOffset); + VTKM_ASSERT(lineFromVert0 < nPoints); + VTKM_ASSERT(lineFromVert1 < nPoints); + const vtkm::Id lineFromVert0LocalId = localIdsPortal.Get(lineFromVert0); + const vtkm::Id lineFromVert1LocalId = localIdsPortal.Get(lineFromVert1); + const ValueType lineFromVert0Value = dataValuesPortal.Get(lineFromVert0); + const ValueType lineFromVert1Value = dataValuesPortal.Get(lineFromVert1); + const vtkm::Id lineFromVert0GlobalId = globalIdsPortal.Get(lineFromVert0); + const vtkm::Id lineFromVert1GlobalId = globalIdsPortal.Get(lineFromVert1); + // due to simulation of simplicity + // vert0 < vert1 if their values are equal + const vtkm::Id lowVertFrom = + lineFromVert0Value <= lineFromVert1Value ? lineFromVert0LocalId : lineFromVert1LocalId; + const vtkm::Id highVertFrom = + lineFromVert0Value > lineFromVert1Value ? lineFromVert0LocalId : lineFromVert1LocalId; + + const vtkm::Id lineToVert0 = + CellIndexToNodeIndex2D(localPt, edgeTable.Get(lineTo * 2), vertexOffset); + const vtkm::Id lineToVert1 = + CellIndexToNodeIndex2D(localPt, edgeTable.Get(lineTo * 2 + 1), vertexOffset); + VTKM_ASSERT(lineToVert0 < nPoints); + VTKM_ASSERT(lineToVert1 < nPoints); + const vtkm::Id lineToVert0LocalId = localIdsPortal.Get(lineToVert0); + const vtkm::Id lineToVert1LocalId = localIdsPortal.Get(lineToVert1); + const ValueType lineToVert0Value = dataValuesPortal.Get(lineToVert0); + const ValueType lineToVert1Value = dataValuesPortal.Get(lineToVert1); + const vtkm::Id lineToVert0GlobalId = globalIdsPortal.Get(lineToVert0); + const vtkm::Id lineToVert1GlobalId = globalIdsPortal.Get(lineToVert1); + // due to simulation of simplicity + // vert0 < vert1 if their values are equal + const vtkm::Id lowVertTo = + lineToVert0Value <= lineToVert1Value ? lineToVert0LocalId : lineToVert1LocalId; + const vtkm::Id highVertTo = + lineToVert0Value > lineToVert1Value ? lineToVert0LocalId : lineToVert1LocalId; + + vtkm::Id lineFromSuperarc = -1; + vtkm::Id lineToSuperarc = -1; + + // We always extract the isosurface above/below the isovalue by 0+ + // If we extract contours by value (i.e., ignore simulation of simplicity), + // the global regular ID of the contour should be inf small or large; + // otherwise, it is +/-1 by the global regular id of the saddle end of the branch. + VTKM_ASSERT(BranchSaddleEpsilon != 0); + vtkm::Id contourGRId = IsContourByValue ? (BranchSaddleEpsilon > 0 ? TotalNumPoints : -1) + : GlobalRegularId + BranchSaddleEpsilon; + lineFromSuperarc = findSuperarcForNode.FindSuperArcForUnknownNode( + contourGRId, IsoValue, highVertFrom, lowVertFrom); + lineToSuperarc = findSuperarcForNode.FindSuperArcForUnknownNode( + contourGRId, IsoValue, highVertTo, lowVertTo); + + // we only draw the line if both lineFrom and lineTo belongs to the branch of query + if (lineFromSuperarc != BranchSuperarc || lineToSuperarc != BranchSuperarc) + { + isValidEdgesPortal.Set(edgeOffset + edgeIndex, 0); + continue; + } + isValidEdgesPortal.Set(edgeOffset + edgeIndex, 1); + + // Now let's draw the line + vtkm::Vec3f_64 lineFromVert0Coord = + CellIndexToNodeCoord2D(localPt, edgeTable.Get(lineFrom * 2), vertexOffset); + vtkm::Vec3f_64 lineFromVert1Coord = + CellIndexToNodeCoord2D(localPt, edgeTable.Get(lineFrom * 2 + 1), vertexOffset); + vtkm::Vec3f_64 lineToVert0Coord = + CellIndexToNodeCoord2D(localPt, edgeTable.Get(lineTo * 2), vertexOffset); + vtkm::Vec3f_64 lineToVert1Coord = + CellIndexToNodeCoord2D(localPt, edgeTable.Get(lineTo * 2 + 1), vertexOffset); + + vtkm::Vec3f_64 fromPt(lineFromVert0Coord); + vtkm::Vec3f_64 toPt(lineToVert0Coord); + + // when values of two vertices in the cell are equal, we rely on the simulation of simplicity + vtkm::Float64 fromRatio = lineFromVert1Value == lineFromVert0Value + ? vtkm::Float64(GlobalRegularId - lineFromVert0GlobalId) / + (lineFromVert1GlobalId - lineFromVert0GlobalId) + : vtkm::Float64(IsoValue - lineFromVert0Value) / + (lineFromVert1Value - lineFromVert0Value); + vtkm::Float64 toRatio = lineToVert1Value == lineToVert0Value + ? vtkm::Float64(GlobalRegularId - lineToVert0GlobalId) / + (lineToVert1GlobalId - lineToVert0GlobalId) + : vtkm::Float64(IsoValue - lineToVert0Value) / (lineToVert1Value - lineToVert0Value); + + VTKM_ASSERT(fromRatio >= 0.0 && fromRatio <= 1.0); + VTKM_ASSERT(toRatio >= 0.0 && toRatio <= 1.0); + + fromPt += (lineFromVert1Coord - lineFromVert0Coord) * fromRatio; + toPt += (lineToVert1Coord - lineToVert0Coord) * toRatio; + + edgesFromPortal.Set(edgeOffset + edgeIndex, fromPt + GlobalPointIndexStart); + edgesToPortal.Set(edgeOffset + edgeIndex, toPt + GlobalPointIndexStart); + } + } + else // 3D + { + vtkm::Id3 localPt(localIndex % CellDimensions[0], + (localIndex / CellDimensions[0]) % CellDimensions[1], + localIndex / (CellDimensions[0] * CellDimensions[1])); + + const vtkm::Id numTriangles = numBoundTable.Get(caseCell); + if (numTriangles < 1) + return; + + // we check a specific edge to know the superarc of the triangle + // the edge label of the triangle is stored in labelEdgeTable in MarchingCubesDataTables.h + // there are at most 5 triangles to draw in each 3D cell (for marching cubes) + // for linear interpolation, there are at most 12 triangles + if (IsMarchingCubes) + VTKM_ASSERT(numTriangles <= MAX_MARCHING_CUBE_TRIANGLES); + else + VTKM_ASSERT(numTriangles <= MAX_LINEAR_INTERPOLATION_TRIANGLES); + vtkm::Id triangleSuperarc[MAX_LINEAR_INTERPOLATION_TRIANGLES + 1]; + + vtkm::Id triangleLabelIdx = 0; + const vtkm::Id nLabelEdgeElemSize = + IsMarchingCubes ? nLabelEdgeTableMC3dElemSize : nLabelEdgeTableLT3dElemSize; + vtkm::Id labelPtr = caseCell * nLabelEdgeElemSize; + while (labelEdgeTable.Get(labelPtr) != -1) + { + vtkm::Id labelCount = labelEdgeTable.Get(labelPtr++); + vtkm::Id labelEdge = labelEdgeTable.Get(labelPtr++); + + // compute the superarc of the labelEdge belong to the branch + const vtkm::Id labelEdgeVert0 = + CellIndexToNodeIndex3D(localPt, edgeTable.Get(labelEdge * 2), vertexOffset); + const vtkm::Id labelEdgeVert1 = + CellIndexToNodeIndex3D(localPt, edgeTable.Get(labelEdge * 2 + 1), vertexOffset); + VTKM_ASSERT(labelEdgeVert0 < nPoints); + VTKM_ASSERT(labelEdgeVert1 < nPoints); + + const vtkm::Id labelEdgeVert0LocalId = localIdsPortal.Get(labelEdgeVert0); + const vtkm::Id labelEdgeVert1LocalId = localIdsPortal.Get(labelEdgeVert1); + const ValueType labelEdgeVert0Value = dataValuesPortal.Get(labelEdgeVert0); + const ValueType labelEdgeVert1Value = dataValuesPortal.Get(labelEdgeVert1); + // due to simulation of simplicity + // vert0 < vert1 if their values are equal + const vtkm::Id lowVert = labelEdgeVert0Value <= labelEdgeVert1Value ? labelEdgeVert0LocalId + : labelEdgeVert1LocalId; + const vtkm::Id highVert = + labelEdgeVert0Value > labelEdgeVert1Value ? labelEdgeVert0LocalId : labelEdgeVert1LocalId; + + vtkm::Id labelEdgeSuperarc = -1; + + // We always extract the isosurface above/below the isovalue.globalRegularId by 0+ + // If we extract contours by value (i.e., ignore simulation of simplicity), + // the global regular ID of the contour should be inf small or large; + // otherwise, it is +/-1 by the global regular id of the saddle end of the branch. + VTKM_ASSERT(BranchSaddleEpsilon != 0); + vtkm::Id contourGRId = IsContourByValue ? (BranchSaddleEpsilon > 0 ? TotalNumPoints : -1) + : GlobalRegularId + BranchSaddleEpsilon; + + labelEdgeSuperarc = + findSuperarcForNode.FindSuperArcForUnknownNode(contourGRId, IsoValue, highVert, lowVert); + for (vtkm::Id i = 0; i < labelCount; i++) + triangleSuperarc[triangleLabelIdx++] = labelEdgeSuperarc; + } + + VTKM_ASSERT(triangleLabelIdx == numTriangles); + + const vtkm::Id nTriTableElemSize = + IsMarchingCubes ? nTriTableMC3dElemSize : nTriTableLT3dElemSize; + for (vtkm::Id triIndex = 0; triIndex < numTriangles; triIndex++) + { + const vtkm::Id lineFroms[3] = { + boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3), + boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3 + 1), + boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3 + 2) + }; + const vtkm::Id lineTos[3] = { + boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3 + 1), + boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3 + 2), + boundaryTable.Get(caseCell * nTriTableElemSize + triIndex * 3) + }; + + const vtkm::Id labelEdgeSuperarc = triangleSuperarc[triIndex]; + // we only draw the triangle if the triangle lies on the branch of query + if (labelEdgeSuperarc != BranchSuperarc) + { + isValidEdgesPortal.Set(edgeOffset + triIndex * 3, 0); + isValidEdgesPortal.Set(edgeOffset + triIndex * 3 + 1, 0); + isValidEdgesPortal.Set(edgeOffset + triIndex * 3 + 2, 0); + continue; + } + isValidEdgesPortal.Set(edgeOffset + triIndex * 3, 1); + isValidEdgesPortal.Set(edgeOffset + triIndex * 3 + 1, 1); + isValidEdgesPortal.Set(edgeOffset + triIndex * 3 + 2, 1); + + for (vtkm::Id edgeIndex = 0; edgeIndex < 3; edgeIndex++) + { + // lineFrom and lineTo are two edges where the edge of the triangle intersects + const vtkm::Id lineFrom = lineFroms[edgeIndex]; + const vtkm::Id lineTo = lineTos[edgeIndex]; + + // Now let's draw the line + vtkm::Vec3f_64 lineFromVert0Coord = + CellIndexToNodeCoord3D(localPt, edgeTable.Get(lineFrom * 2), vertexOffset); + vtkm::Vec3f_64 lineFromVert1Coord = + CellIndexToNodeCoord3D(localPt, edgeTable.Get(lineFrom * 2 + 1), vertexOffset); + vtkm::Vec3f_64 lineToVert0Coord = + CellIndexToNodeCoord3D(localPt, edgeTable.Get(lineTo * 2), vertexOffset); + vtkm::Vec3f_64 lineToVert1Coord = + CellIndexToNodeCoord3D(localPt, edgeTable.Get(lineTo * 2 + 1), vertexOffset); + + vtkm::Vec3f_64 fromPt(lineFromVert0Coord); + vtkm::Vec3f_64 toPt(lineToVert0Coord); + + const vtkm::Id lineFromVert0 = + CellIndexToNodeIndex3D(localPt, edgeTable.Get(lineFrom * 2), vertexOffset); + const vtkm::Id lineFromVert1 = + CellIndexToNodeIndex3D(localPt, edgeTable.Get(lineFrom * 2 + 1), vertexOffset); + VTKM_ASSERT(lineFromVert0 < nPoints); + VTKM_ASSERT(lineFromVert1 < nPoints); + const ValueType lineFromVert0Value = dataValuesPortal.Get(lineFromVert0); + const ValueType lineFromVert1Value = dataValuesPortal.Get(lineFromVert1); + const vtkm::Id lineFromVert0GlobalId = globalIdsPortal.Get(lineFromVert0); + const vtkm::Id lineFromVert1GlobalId = globalIdsPortal.Get(lineFromVert1); + + const vtkm::Id lineToVert0 = + CellIndexToNodeIndex3D(localPt, edgeTable.Get(lineTo * 2), vertexOffset); + const vtkm::Id lineToVert1 = + CellIndexToNodeIndex3D(localPt, edgeTable.Get(lineTo * 2 + 1), vertexOffset); + VTKM_ASSERT(lineToVert0 < nPoints); + VTKM_ASSERT(lineToVert1 < nPoints); + const ValueType lineToVert0Value = dataValuesPortal.Get(lineToVert0); + const ValueType lineToVert1Value = dataValuesPortal.Get(lineToVert1); + const vtkm::Id lineToVert0GlobalId = globalIdsPortal.Get(lineToVert0); + const vtkm::Id lineToVert1GlobalId = globalIdsPortal.Get(lineToVert1); + + vtkm::Float64 fromRatio = lineFromVert1Value == lineFromVert0Value + ? vtkm::Float64(GlobalRegularId - lineFromVert0GlobalId) / + (lineFromVert1GlobalId - lineFromVert0GlobalId) + : vtkm::Float64(IsoValue - lineFromVert0Value) / + (lineFromVert1Value - lineFromVert0Value); + vtkm::Float64 toRatio = lineToVert1Value == lineToVert0Value + ? vtkm::Float64(GlobalRegularId - lineToVert0GlobalId) / + (lineToVert1GlobalId - lineToVert0GlobalId) + : vtkm::Float64(IsoValue - lineToVert0Value) / (lineToVert1Value - lineToVert0Value); + VTKM_ASSERT(fromRatio >= 0.0 && fromRatio <= 1.0); + VTKM_ASSERT(toRatio >= 0.0 && toRatio <= 1.0); + + fromPt += (lineFromVert1Coord - lineFromVert0Coord) * fromRatio; + toPt += (lineToVert1Coord - lineToVert0Coord) * toRatio; + + edgesFromPortal.Set(edgeOffset + triIndex * 3 + edgeIndex, + fromPt + GlobalPointIndexStart); + edgesToPortal.Set(edgeOffset + triIndex * 3 + edgeIndex, toPt + GlobalPointIndexStart); + } + } + } + } + +private: + vtkm::Id3 PointDimensions; + vtkm::Id3 GlobalPointIndexStart; + ValueType IsoValue; + vtkm::Id GlobalRegularId; + vtkm::Id BranchSuperarc; + vtkm::Id BranchSaddleEpsilon; + vtkm::Id TotalNumPoints; + bool IsMarchingCubes; + bool IsContourByValue; + vtkm::Id3 CellDimensions; + +}; // GetEdgesInCellWorklet + +} // namespace extract_top_volume_contours +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetSuperarcByIsoValueWorklet.h b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetSuperarcByIsoValueWorklet.h new file mode 100644 index 0000000000..af0fb390b5 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/GetSuperarcByIsoValueWorklet.h @@ -0,0 +1,140 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_extract_top_volume_contours_get_superarc_by_isovalue_worklet_h +#define vtk_m_filter_scalar_topology_worklet_extract_top_volume_contours_get_superarc_by_isovalue_worklet_h + +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace extract_top_volume_contours +{ +/// Worklet for getting the superarc of a branch given an isovalue +class GetSuperarcByIsoValueWorklet : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = + void(FieldIn upperEndLocalId, // (input) upper end of the branch + FieldIn lowerEndLocalId, // (input) lower end of the branch + FieldIn isoValue, // (input) isoValue + FieldIn saddleEndGRId, // (input) saddle end global regular id + FieldIn branchSaddleEpsilon, // (input) whether the branch is on top or at the bottom + FieldOut superarc, // (output) local superarc that intersects the isosurface + ExecObject findSuperarcByNode); + using ExecutionSignature = _6(_1, _2, _3, _4, _5, _7); + using InputDomain = _1; + + /// + /// Constructor + /// + VTKM_EXEC_CONT + GetSuperarcByIsoValueWorklet(vtkm::Id totNumPoints, bool isContourByValue) + : HighValue(totNumPoints) + , IsContourByValue(isContourByValue) + { + } + + /// + /// Implementation of GetSuperarcByIsoValueWorklet. + /// Check vtkm::worklet::contourtree_distributed::FindSuperArcForUnknownNode + /// for the execution object description. + /// + /// data value type + /// execution object type of findSuperarc + /// local id of the upper end vertex of the branch + /// local id of the lower end vertex of the branch + /// isovalue + /// the direction for tiebreaking when comparing values + /// execution object + /// + template + VTKM_EXEC vtkm::Id operator()(const vtkm::Id upperEndLocalId, + const vtkm::Id lowerEndLocalId, + const ValueType isoValue, + const vtkm::Id saddleEndGRId, + const vtkm::Id branchSaddleEpsilon, + const findSuperarcType& findSuperarc) const + { + VTKM_ASSERT(branchSaddleEpsilon != 0); + + // Update 01/06/2025: + // We need the global regular ID of the saddle end, + // which is used for simulation of simplicity when looking for the closest superarc to the saddle end. + // If we extract contours solely by value (i.e., ignore simulation of simplicity), + // the contour global regular ID should either be inf small or inf large; + // otherwise, it is offset by 1 from the saddle end of the branch. + vtkm::Id contourGRId; + if (IsContourByValue) + contourGRId = branchSaddleEpsilon < 0 ? -1 : HighValue; + else + contourGRId = branchSaddleEpsilon < 0 ? saddleEndGRId - 1 : saddleEndGRId + 1; + + return findSuperarc.FindSuperArcForUnknownNode( + contourGRId, isoValue, upperEndLocalId, lowerEndLocalId); + } + +private: + const vtkm::Id HighValue; + const bool IsContourByValue; +}; // GetSuperarcByIsoValueWorklet + +} // namespace extract_top_volume_contours +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/Types.h b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/Types.h new file mode 100644 index 0000000000..1c67fcb009 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/extract_top_volume_contours/Types.h @@ -0,0 +1,76 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_extract_top_volume_contours_types_h +#define vtk_m_filter_scalar_topology_worklet_extract_top_volume_contours_types_h + +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace extract_top_volume_contours +{ + +constexpr vtkm::Id BRANCH_SADDLE = static_cast(1); // 1 << 0 +constexpr vtkm::Id BRANCH_COVER = static_cast(2); // 1 << 1 +constexpr vtkm::Id MAXIMA_CONTOUR = static_cast(4); // 1 << 2 + +} // namespace extract_top_volume_contours +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/AboveThresholdWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/AboveThresholdWorklet.h new file mode 100644 index 0000000000..d630867a96 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/AboveThresholdWorklet.h @@ -0,0 +1,108 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_above_threshold_worklet_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_above_threshold_worklet_h + +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_branches +{ + +using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; + +/// +/// worklet to for the stencil indicating +/// whether the branch volume is above the threshold +/// +class AboveThresholdWorklet : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void( + FieldIn branchVolume, // (input) lower end superarc ID with direction information + FieldOut aboveThresholdStencil // (output) volume of the branch + ); + using ExecutionSignature = _2(_1); + using InputDomain = _1; + + /// Constructor + VTKM_EXEC_CONT + AboveThresholdWorklet(const vtkm::Id presimpThres) + : PresimplifyThreshold(presimpThres) + { + } + + /// The functor checks the direction of the branch + VTKM_EXEC bool operator()(const vtkm::Id& branchVolume) const + { + if (branchVolume > this->PresimplifyThreshold) + return true; + return false; + } + +private: + const vtkm::Id PresimplifyThreshold; +}; // GetBranchVolumeWorklet + + +} // namespace select_top_volume_branches +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/AssignValueWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/AssignValueWorklet.h new file mode 100644 index 0000000000..3716890888 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/AssignValueWorklet.h @@ -0,0 +1,160 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_AssignValueWorklet_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_AssignValueWorklet_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_branches +{ + +/// +/// worklet to assign values to arrayhandle with given index +/// this is different from permutation: we do not want to change the size of valueOut +/// we also don't want to touch the default values in valueOut +/// index - value is one to one +/// +class AssignValueByIndex : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void(FieldIn index, // (input) index + FieldIn value, // (input) value + WholeArrayOut valueOut // (array output) valueOut[index] = value + ); + using ExecutionSignature = void(_1, _2, _3); + using InputDomain = _1; + + /// Constructor + VTKM_EXEC_CONT + AssignValueByIndex() {} + + template + VTKM_EXEC void operator()(const vtkm::Id& index, + const ValueType& value, + ValueArrayPortalType& valueOut) const + { + if (vtkm::worklet::contourtree_augmented::NoSuchElement(index)) + return; + valueOut.Set(index, value); + } +}; // AssignValueByIndex + + +/// +/// worklet to assign values based on a stencil +/// +class AssignValueWithStencil : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void( + FieldIn stencil, // (input) stencil + FieldIn value, // (input) value + FieldInOut valueOut // (output) valueOut = stencil ? value : valueOut + ); + using ExecutionSignature = void(_1, _2, _3); + using InputDomain = _1; + + /// Constructor + VTKM_EXEC_CONT + AssignValueWithStencil() {} + + template + VTKM_EXEC void operator()(const bool stencil, const ValueType& value, ValueType& valueOut) const + { + if (stencil) + valueOut = value; + } +}; // AssignValueWithStencil + + +/// +/// worklet to assign values based on the positivity of stencil +/// +class AssignValueByPositivity : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void( + FieldIn stencil, // (input) stencil + FieldIn value, // (input) value + FieldInOut valueOut // (output) valueOut = stencil > 0 ? value : valueOut + ); + using ExecutionSignature = void(_1, _2, _3); + using InputDomain = _1; + + /// Constructor + VTKM_EXEC_CONT + AssignValueByPositivity() {} + + template + VTKM_EXEC void operator()(const vtkm::Id stencil, + const ValueType& value, + ValueType& valueOut) const + { + if (stencil > 0) + valueOut = value; + } +}; // AssignValueByPositivity + +} // namespace select_top_volume_branches +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BinarySearchWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BinarySearchWorklet.h new file mode 100644 index 0000000000..b48c90bce0 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BinarySearchWorklet.h @@ -0,0 +1,125 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_binary_search_worklet_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_binary_search_worklet_h + +#include + + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_branches +{ + +/// Worklet of a binary search. +/// This is used to check whether the global regular id in inside the block. +/// The global regular Ids in block should be sorted beforehand. +/// This worklet may be reused for many other processes with binary search. +class BinarySearchWorklet : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void( + FieldIn regularId, // (input) global regular id + WholeArrayIn idsArray, // (array input) all known global regular ids + FieldOut inBlockIndicator, // (output) 1 if the regularId is inside the idsArray + FieldOut inBlockIdx // (output) the index of regularId in idsArray + ); + using ExecutionSignature = void(_1, _2, _3, _4); + using InputDomain = _1; + + /// Constructor + VTKM_EXEC_CONT + BinarySearchWorklet() {} + + /// The functor uses binary search to locate regularId in idsArray + /// if the search fails, return NO_SUCH_ELEMENT; otherwise, return the location + /// idsArray should be sorted + template + VTKM_EXEC void operator()(const vtkm::Id& regularId, + const InIdPortalType& idsArray, + vtkm::Id& inBlockIndicator, + vtkm::Id& inBlockIdx) const + { + vtkm::Id head = 0; + vtkm::Id tail = idsArray.GetNumberOfValues() - 1; + inBlockIndicator = 0; + inBlockIdx = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + + while (head <= tail) + { + vtkm::Id mid = (head + tail) >> 1; + vtkm::Id midValue = idsArray.Get(mid); + if (regularId == midValue) + { + inBlockIdx = mid; + inBlockIndicator = 1; + return; + } + else if (regularId > midValue) + head = mid + 1; + else + tail = mid - 1; + } + } +}; // BinarySearchWorklet + +} // namespace select_top_volume_branches +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchDecompositionTreeMaker.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchDecompositionTreeMaker.h new file mode 100644 index 0000000000..355a1f0ef9 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchDecompositionTreeMaker.h @@ -0,0 +1,539 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//======================================================================================= +// +// Parallel Peak Pruning v. 2.0 +// +// Started June 15, 2017 +// +// Copyright Hamish Carr, University of Leeds +// +// BranchDecompositionTreeMaker.h +// +//======================================================================================= +// +// COMMENTS: +// +// This class computes the branch decomposition tree of top-volume branches +// +//======================================================================================= + + +#ifndef vtk_m_filter_scalar_topology_worklet_BranchDecompositionTreeMaker_h +#define vtk_m_filter_scalar_topology_worklet_BranchDecompositionTreeMaker_h + + +#ifdef DEBUG_PRINT +#define DEBUG_BRANCH_DECOMPOSITION_TREE_MAKER +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ + +/// Facture class for augmenting the hierarchical contour tree to enable computations of measures, e.g., volumne +class BranchDecompositionTreeMaker +{ // class BranchDecompositionTreeMaker +public: + void ComputeTopVolumeBranchHierarchy(const vtkm::cont::DataSet& bdDataSet, + TopVolumeBranchData& topVolumeData); +}; // class BranchDecompositionTreeMaker + + +/// +/// Pipeline to compute the hierarchy of top branches by volume +/// +inline void BranchDecompositionTreeMaker::ComputeTopVolumeBranchHierarchy( + const vtkm::cont::DataSet& bdDataSet, + TopVolumeBranchData& topVolumeData) +{ + using vtkm::worklet::contourtree_augmented::IdArrayType; + + // Used internally to Invoke worklets + vtkm::cont::Invoker invoke; + + // NOTE: Any variables without "LocalEnd" refer to branch global ends + // we need upper/lower local ends and global ends for hierarchy of branches + auto upperLocalEndIds = bdDataSet.GetField("UpperEndLocalIds") + .GetData() + .AsArrayHandle>(); + auto lowerLocalEndIds = bdDataSet.GetField("LowerEndLocalIds") + .GetData() + .AsArrayHandle>(); + auto globalRegularIds = bdDataSet.GetField("RegularNodeGlobalIds") + .GetData() + .AsArrayHandle>(); + IdArrayType upperEndGRIds = + bdDataSet.GetField("UpperEndGlobalRegularIds").GetData().AsArrayHandle(); + IdArrayType lowerEndGRIds = + bdDataSet.GetField("LowerEndGlobalRegularIds").GetData().AsArrayHandle(); + + // let's check which top volume branches are known by the block + // We check the branchGRId of top volume branches to see whether there are matches within the block + const vtkm::Id nTopVolBranches = topVolumeData.TopVolumeBranchLowerEndGRId.GetNumberOfValues(); + // sortedBranchOrder: the branch order (in the ascending order of branch root) + // the high-level idea is to sort the branch root global regular ids + // and for each top-volume branch, we use binary search to get the original branch index + // if the top-volume branch does not exist in the block, it will be dropped out + IdArrayType sortedBranchGRId; + IdArrayType sortedBranchOrder; + vtkm::cont::Algorithm::Copy( + vtkm::cont::ArrayHandleIndex(topVolumeData.BranchRootGRId.GetNumberOfValues()), + sortedBranchOrder); + vtkm::cont::Algorithm::Copy(topVolumeData.BranchRootGRId, sortedBranchGRId); + vtkm::cont::Algorithm::SortByKey(sortedBranchGRId, sortedBranchOrder); + + topVolumeData.TopVolBranchKnownByBlockStencil.Allocate(nTopVolBranches); + topVolumeData.TopVolBranchGROrder.Allocate(nTopVolBranches); + + // We use a custom BinarySearchWorklet. + // This worklet searches for given values in a sorted array and returns the stencil & index if the value exists in the array. + // topVolumeData.TopVolBranchGROrder: the order of the topVolBranch (by global regular ids) + // among all known branches. + auto idxIfBranchWithinBlockWorklet = + vtkm::worklet::scalar_topology::select_top_volume_branches::BinarySearchWorklet(); + invoke(idxIfBranchWithinBlockWorklet, + topVolumeData.TopVolumeBranchRootGRId, + sortedBranchGRId, + topVolumeData.TopVolBranchKnownByBlockStencil, + topVolumeData.TopVolBranchGROrder); + + // Dropping out top-volume branches that are not known by the block. + + // the index of top-volume branches known by the block among all top-volume branches + IdArrayType topVolBranchKnownByBlockIndex; + vtkm::cont::ArrayHandleIndex topVolBranchesIndex(nTopVolBranches); + vtkm::cont::Algorithm::CopyIf(topVolBranchesIndex, + topVolumeData.TopVolBranchKnownByBlockStencil, + topVolBranchKnownByBlockIndex); + + const vtkm::Id nTopVolBranchKnownByBlock = topVolBranchKnownByBlockIndex.GetNumberOfValues(); + + // filtered topVolumeData.TopVolBranchGROrder, by removing NO_SUCH_ELEMENT + IdArrayType topVolBranchFilteredGROrder; + + // topVolumeData.TopVolBranchInfoActualIndex: the information index of the top-volume branch + vtkm::cont::Algorithm::CopyIf(topVolumeData.TopVolBranchGROrder, + topVolumeData.TopVolBranchKnownByBlockStencil, + topVolBranchFilteredGROrder); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + sortedBranchOrder, topVolBranchFilteredGROrder, topVolumeData.TopVolBranchInfoActualIndex); + + // filtered branch saddle epsilons, global lower/upper end GR ids, + IdArrayType topVolFilteredBranchSaddleEpsilon; + IdArrayType topVolFilteredLowerEndGRId; + IdArrayType topVolFilteredUpperEndGRId; + vtkm::cont::Algorithm::CopyIf(topVolumeData.TopVolumeBranchSaddleEpsilon, + topVolumeData.TopVolBranchKnownByBlockStencil, + topVolFilteredBranchSaddleEpsilon); + vtkm::cont::Algorithm::CopyIf(topVolumeData.TopVolumeBranchUpperEndGRId, + topVolumeData.TopVolBranchKnownByBlockStencil, + topVolFilteredUpperEndGRId); + vtkm::cont::Algorithm::CopyIf(topVolumeData.TopVolumeBranchLowerEndGRId, + topVolumeData.TopVolBranchKnownByBlockStencil, + topVolFilteredLowerEndGRId); + + // for each top-vol branch known by the block + // we get their upper end and lower end local ids + IdArrayType topVolBranchUpperLocalEnd; + IdArrayType topVolBranchLowerLocalEnd; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + upperLocalEndIds, topVolumeData.TopVolBranchInfoActualIndex, topVolBranchUpperLocalEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + lowerLocalEndIds, topVolumeData.TopVolBranchInfoActualIndex, topVolBranchLowerLocalEnd); + + IdArrayType topVolLowerLocalEndGRId; + IdArrayType topVolUpperLocalEndGRId; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + globalRegularIds, topVolBranchLowerLocalEnd, topVolLowerLocalEndGRId); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + globalRegularIds, topVolBranchUpperLocalEnd, topVolUpperLocalEndGRId); + + // Below is the code to compute the branch hierarchy of top-volume branches + // We need this information because we not only want to visualize the contour + // on top-volume branches, but also on their parent branches. + // Because we use volume as the metric, the parent branch of a top-volume branch + // is either a top-volume branch or the root branch (where both ends are leaf nodes) + vtkm::worklet::scalar_topology::select_top_volume_branches::BranchSaddleIsKnownWorklet + branchSaddleIsKnownWorklet; + // the branch saddle local ID if the saddle end is known by the block + IdArrayType branchSaddleIsKnown; + branchSaddleIsKnown.Allocate(nTopVolBranchKnownByBlock); + + invoke(branchSaddleIsKnownWorklet, // worklet + topVolFilteredLowerEndGRId, // input + topVolBranchLowerLocalEnd, // input + topVolLowerLocalEndGRId, // input + topVolFilteredUpperEndGRId, // input + topVolBranchUpperLocalEnd, // input + topVolUpperLocalEndGRId, // input + topVolFilteredBranchSaddleEpsilon, // input + branchSaddleIsKnown); // output + // the order of top volume branches with parents known by the block + IdArrayType topVolChildBranch; + IdArrayType topVolChildBranchSaddle; + + vtkm::cont::Algorithm::CopyIf(topVolBranchKnownByBlockIndex, + branchSaddleIsKnown, + topVolChildBranch, + vtkm::worklet::contourtree_augmented::NotNoSuchElementPredicate()); + vtkm::cont::Algorithm::CopyIf(branchSaddleIsKnown, + branchSaddleIsKnown, + topVolChildBranchSaddle, + vtkm::worklet::contourtree_augmented::NotNoSuchElementPredicate()); + + const vtkm::Id nChildBranch = topVolChildBranch.GetNumberOfValues(); + // to compute the parent branch, we need to + // 1. for the branch saddle end, collect all superarcs involving it + // 2. get the branch information for selected superarcs + // 3. eliminate branch information for branches sharing the same saddle end + auto superarcs = + bdDataSet.GetField("Superarcs").GetData().AsArrayHandle>(); + auto branchRoots = + bdDataSet.GetField("BranchRoots").GetData().AsArrayHandle>(); + VTKM_ASSERT(superarcs.GetNumberOfValues() == branchRoots.GetNumberOfValues()); + + // we sort all superarcs by target to allow binary search + IdArrayType superarcsByTarget; + vtkm::worklet::scalar_topology::select_top_volume_branches::SuperarcTargetComparator + superarcComparator(superarcs); + vtkm::cont::Algorithm::Copy(vtkm::cont::ArrayHandleIndex(superarcs.GetNumberOfValues()), + superarcsByTarget); + vtkm::cont::Algorithm::Sort(superarcsByTarget, superarcComparator); + + IdArrayType permutedSuperarcs; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + superarcs, superarcsByTarget, permutedSuperarcs); + + IdArrayType permutedBranchRoots; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + branchRoots, superarcsByTarget, permutedBranchRoots); + + // the branch root of the superarc of the branch saddle supernode + IdArrayType topVolChildBranchSaddleBranchRoot; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + branchRoots, topVolChildBranchSaddle, topVolChildBranchSaddleBranchRoot); + + // the GR Ids of the superarc of the branch saddle supernode + IdArrayType topVolChildBranchSaddleGRIds; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + globalRegularIds, topVolChildBranchSaddle, topVolChildBranchSaddleGRIds); + // there is a debate to find all superarcs connect to a supernode + // strategy 1. iterate through saddles and parallelize over superarcs for search + // time complexity: O(nTopVolBranches) + // (nTopVolBranches usually <= 100, based on input parameter setting) + // + // strategy 2. parallelize over all saddles and use binary search to find superarcs + // time complexity: O(log_2(nSuperarcs)) (nSuperarcs can be considerably large) + // + // here, we choose strategy 2 for better scalability to high nTopVolBranches + // but when nTopVolBranches <= 10, strategy 1 is theoretically faster + + // note: after getting the branch root superarc, we use binary search to get the branch order + // because BranchRootByBranch is sorted by branch root (superarc) id + +#ifdef DEBUG_PRINT + std::stringstream parentBranchStream; + vtkm::worklet::contourtree_augmented::PrintHeader(nChildBranch, parentBranchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Child Branch Saddle", topVolChildBranchSaddle, -1, parentBranchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Child Saddle Root", topVolChildBranchSaddleBranchRoot, -1, parentBranchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Child Saddle GR Id", topVolChildBranchSaddleGRIds, -1, parentBranchStream); + // the volume of the child branch + IdArrayType topVolChildBranchVolume; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + topVolumeData.TopVolumeBranchVolume, topVolChildBranch, topVolChildBranchVolume); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Child Branch Volume", topVolChildBranchVolume, -1, parentBranchStream); + + vtkm::worklet::contourtree_augmented::PrintHeader(superarcs.GetNumberOfValues(), + parentBranchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Permuted Superarcs", permutedSuperarcs, -1, parentBranchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "Permuted Branch roots", permutedBranchRoots, -1, parentBranchStream); + vtkm::worklet::contourtree_augmented::PrintIndices( + "BranchRootByBranch", topVolumeData.BranchRootByBranch, -1, parentBranchStream); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, parentBranchStream.str()); +#endif // DEBUG_PRINT + + // the corresponding parent branch of child branches + IdArrayType topVolChildBranchParent; + topVolChildBranchParent.AllocateAndFill(nChildBranch, + vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT); + vtkm::worklet::scalar_topology::select_top_volume_branches::GetParentBranchWorklet + getParentBranchWorklet; + invoke(getParentBranchWorklet, + topVolChildBranchSaddle, + topVolChildBranchSaddleBranchRoot, + topVolChildBranchSaddleGRIds, + permutedSuperarcs, + permutedBranchRoots, + topVolumeData.BranchRootByBranch, + upperEndGRIds, + lowerEndGRIds, + topVolChildBranchParent); + + topVolumeData.TopVolumeBranchParent.AllocateAndFill( + nTopVolBranches, vtkm::Id(vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT)); + + vtkm::worklet::scalar_topology::select_top_volume_branches::AssignValueByIndex assignParentBranch; + // for each top volume branch, assign the parent branch info id in the block + invoke(assignParentBranch, + topVolChildBranch, + topVolChildBranchParent, + topVolumeData.TopVolumeBranchParent); + // for each branch, assign true if it is a parent branch + invoke(assignParentBranch, + topVolChildBranchParent, + vtkm::cont::ArrayHandleConstant(true, nChildBranch), + topVolumeData.IsParentBranch); + // sort all top-volume branches based on + // 1. parent branch info id: topVolumeData.TopVolumeBranchParent + // 2. saddle-end value: topVolumeData.TopVolumeBranchSaddleIsovalue + // 3. branch root global regular id (anything that can break tie) + IdArrayType topVolSortForOuterSaddleIdx; + vtkm::cont::Algorithm::Copy(topVolBranchesIndex, topVolSortForOuterSaddleIdx); + + auto resolveBranchParentSorter = [&](const auto& inArray) { + using InArrayHandleType = std::decay_t; + using ValueType = typename InArrayHandleType::ValueType; + + vtkm::worklet::scalar_topology::select_top_volume_branches::BranchParentComparator + parentComparator( + topVolumeData.TopVolumeBranchParent, inArray, topVolumeData.TopVolumeBranchRootGRId); + + // sort index for all top volume branches + vtkm::cont::Algorithm::Sort(topVolSortForOuterSaddleIdx, parentComparator); + }; + topVolumeData.TopVolumeBranchSaddleIsoValue + .CastAndCallForTypes( + resolveBranchParentSorter); + + IdArrayType parentPermutation; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + topVolumeData.TopVolumeBranchParent, topVolSortForOuterSaddleIdx, parentPermutation); + + // When parent is NO_SUCH_ELEMENT, parentSaddleEps obtains 0 + // However, the corresponding element will be discarded in collecting outer saddles + IdArrayType parentSaddleEpsPermutation; + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + topVolumeData.BranchSaddleEpsilon, parentPermutation, parentSaddleEpsPermutation); + + // Some branches have parent=NO_SUCH_ELEMENT (no parent) + // we collect the isovalue of the first and/or the last branches for each parent branch + // we collect the first if branchSaddleEpsilon(parent) < 0 + // or the last if branchSaddleEpsilon(parent) > 0 + // or both if branchSaddleEpsilon(parent) == 0 + IdArrayType IsOuterSaddle; + IsOuterSaddle.Allocate(nTopVolBranches); + vtkm::worklet::scalar_topology::select_top_volume_branches::CollectOuterSaddle + collectOuterSaddleWorklet; + invoke(collectOuterSaddleWorklet, parentSaddleEpsPermutation, parentPermutation, IsOuterSaddle); + + // after sorting by index back + // each top volume branch know whether it is the outer saddle of its parent + vtkm::cont::Algorithm::SortByKey(topVolSortForOuterSaddleIdx, IsOuterSaddle); + + // collect branches that need contours on extra minima/maxima + // we store the information of the parent branches (on both directions) + IdArrayType extraMaximaParentBranch; + IdArrayType extraMinimaParentBranch; + IdArrayType extraMaximaParentBranchRootGRId; + IdArrayType extraMinimaParentBranchRootGRId; + + IdArrayType allBranchGRIdByVolume; + IdArrayType branchGRIdByVolumeIdx; + + // we need global branch order including the root branch + // this information should be consistent globally + allBranchGRIdByVolume.Allocate(nTopVolBranches + 1); + vtkm::cont::Algorithm::CopySubRange( + topVolumeData.TopVolumeBranchRootGRId, 0, nTopVolBranches, allBranchGRIdByVolume, 1); + + // we manually insert the main branch into allBranchGRIdByVolume + auto topBranchGRIdWritePortal = allBranchGRIdByVolume.WritePortal(); + auto sortedBranchByVolPortal = topVolumeData.SortedBranchByVolume.ReadPortal(); + auto branchGRIdReadPortal = topVolumeData.BranchRootGRId.ReadPortal(); + topBranchGRIdWritePortal.Set(0, branchGRIdReadPortal.Get(sortedBranchByVolPortal.Get(0))); + + // sort branches by branch root global regular ids + vtkm::cont::Algorithm::Copy( + vtkm::cont::ArrayHandleIndex(allBranchGRIdByVolume.GetNumberOfValues()), branchGRIdByVolumeIdx); + vtkm::cont::Algorithm::SortByKey(allBranchGRIdByVolume, branchGRIdByVolumeIdx); + + // find out which branches are parents for the saddle-maxima branches + vtkm::cont::Algorithm::CopyIf( + topVolumeData.TopVolumeBranchParent, + IsOuterSaddle, + extraMaximaParentBranch, + vtkm::worklet::scalar_topology::select_top_volume_branches::IsExtraMaximum()); + + // find out which branches are parents for the saddle-minima branches + vtkm::cont::Algorithm::CopyIf( + topVolumeData.TopVolumeBranchParent, + IsOuterSaddle, + extraMinimaParentBranch, + vtkm::worklet::scalar_topology::select_top_volume_branches::IsExtraMinimum()); + + // Update 01/09/2025 + // We record the saddle end global regular IDs for each parent branch. + // This array will be used for extra branches on both sides. + IdArrayType topVolumeBranchSaddleEndGRId; + vtkm::cont::Algorithm::Copy(topVolumeData.TopVolumeBranchUpperEndGRId, + topVolumeBranchSaddleEndGRId); + invoke(vtkm::worklet::scalar_topology::select_top_volume_branches::AssignValueByPositivity{}, + topVolumeData.TopVolumeBranchSaddleEpsilon, + topVolumeData.TopVolumeBranchLowerEndGRId, + topVolumeBranchSaddleEndGRId); + + // if we have parent branches to extract contours above the saddle ends of the child branch + if (extraMaximaParentBranch.GetNumberOfValues()) + { + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + upperLocalEndIds, extraMaximaParentBranch, topVolumeData.ExtraMaximaBranchUpperEnd); + // WARNING: the lower end of these extra branches should be the separating saddle + // i.e., the saddle that splits the child branch and the other upper side of the parent branch. + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + lowerLocalEndIds, extraMaximaParentBranch, topVolumeData.ExtraMaximaBranchLowerEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + topVolumeData.BranchRootGRId, extraMaximaParentBranch, extraMaximaParentBranchRootGRId); + + // it is safe to use lower bounds here because the branch should be findable + IdArrayType permutedExtraMaximaBranchOrder; + vtkm::cont::Algorithm::LowerBounds( + allBranchGRIdByVolume, extraMaximaParentBranchRootGRId, permutedExtraMaximaBranchOrder); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + branchGRIdByVolumeIdx, permutedExtraMaximaBranchOrder, topVolumeData.ExtraMaximaBranchOrder); + + // Update 01/09/2025 + // We record the saddle end global regular IDs for each parent branch. + vtkm::cont::Algorithm::CopyIf( + topVolumeBranchSaddleEndGRId, + IsOuterSaddle, + topVolumeData.ExtraMaximaBranchSaddleGRId, + vtkm::worklet::scalar_topology::select_top_volume_branches::IsExtraMaximum()); + } + + // if we have parent branches to extract contours below the saddle ends of the child branch + if (extraMinimaParentBranch.GetNumberOfValues()) + { + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + upperLocalEndIds, extraMinimaParentBranch, topVolumeData.ExtraMinimaBranchUpperEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + lowerLocalEndIds, extraMinimaParentBranch, topVolumeData.ExtraMinimaBranchLowerEnd); + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + topVolumeData.BranchRootGRId, extraMinimaParentBranch, extraMinimaParentBranchRootGRId); + + // it is safe to use lower bounds here because the branch should be findable + IdArrayType permutedExtraMinimaBranchOrder; + vtkm::cont::Algorithm::LowerBounds( + allBranchGRIdByVolume, extraMinimaParentBranchRootGRId, permutedExtraMinimaBranchOrder); + + vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex( + branchGRIdByVolumeIdx, permutedExtraMinimaBranchOrder, topVolumeData.ExtraMinimaBranchOrder); + + // Update 01/09/2025 + // We record the saddle end global regular IDs for each parent branch. + vtkm::cont::Algorithm::CopyIf( + topVolumeBranchSaddleEndGRId, + IsOuterSaddle, + topVolumeData.ExtraMinimaBranchSaddleGRId, + vtkm::worklet::scalar_topology::select_top_volume_branches::IsExtraMinimum()); + } + + // Update saddle isovalues for extra contours + auto resolveExtraContourSaddleValue = [&](const auto& inArray) { + using InArrayHandleType = std::decay_t; + + if (extraMaximaParentBranch.GetNumberOfValues()) + { + InArrayHandleType extraMaximaBranchIsoValue; + vtkm::cont::Algorithm::CopyIf( + inArray, + IsOuterSaddle, + extraMaximaBranchIsoValue, + vtkm::worklet::scalar_topology::select_top_volume_branches::IsExtraMaximum()); + topVolumeData.ExtraMaximaBranchIsoValue = extraMaximaBranchIsoValue; + } + + if (extraMinimaParentBranch.GetNumberOfValues()) + { + InArrayHandleType extraMinimaBranchIsoValue; + vtkm::cont::Algorithm::CopyIf( + inArray, + IsOuterSaddle, + extraMinimaBranchIsoValue, + vtkm::worklet::scalar_topology::select_top_volume_branches::IsExtraMinimum()); + topVolumeData.ExtraMinimaBranchIsoValue = extraMinimaBranchIsoValue; + } + }; + topVolumeData.TopVolumeBranchSaddleIsoValue + .CastAndCallForTypes( + resolveExtraContourSaddleValue); +} + + +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchParentComparator.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchParentComparator.h new file mode 100644 index 0000000000..2623c33a7e --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchParentComparator.h @@ -0,0 +1,236 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_branch_decomposition_select_top_volume_branches_BranchParentComparator_h +#define vtk_m_filter_scalar_topology_worklet_branch_decomposition_select_top_volume_branches_BranchParentComparator_h + +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_branches +{ + +using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; + +// Implementation of BranchParentComparator +template +class BranchParentComparatorImpl +{ +public: + using ValueArrayType = typename vtkm::cont::ArrayHandle; + using IdPortalType = typename IdArrayType::ReadPortalType; + using ValuePortalType = typename ValueArrayType::ReadPortalType; + + // constructor + VTKM_CONT + BranchParentComparatorImpl(const IdArrayType& branchParent, + const ValueArrayType& saddleIsoValue, + const IdArrayType& branchRootGRId, + vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) + : branchParentPortal(branchParent.PrepareForInput(device, token)) + , saddleIsoValuePortal(saddleIsoValue.PrepareForInput(device, token)) + , branchRootGRIdPortal(branchRootGRId.PrepareForInput(device, token)) + { // constructor + } // constructor + + // () operator - gets called to do comparison + VTKM_EXEC + bool operator()(const vtkm::Id& i, const vtkm::Id& j) const + { // operator() + vtkm::Id parentI = this->branchParentPortal.Get(i); + vtkm::Id parentJ = this->branchParentPortal.Get(j); + + // primary sort on branch parent + if (parentI < parentJ) + return true; + if (parentI > parentJ) + return false; + + ValueType valueI = this->saddleIsoValuePortal.Get(i); + ValueType valueJ = this->saddleIsoValuePortal.Get(j); + + // secondary sort on branch saddle isovalue + if (valueI < valueJ) + return true; + if (valueI > valueJ) + return false; + + vtkm::Id rootI = this->branchRootGRIdPortal.Get(i); + vtkm::Id rootJ = this->branchRootGRIdPortal.Get(j); + + return (rootI < rootJ); + } // operator() + +private: + IdPortalType branchParentPortal; + ValuePortalType saddleIsoValuePortal; + IdPortalType branchRootGRIdPortal; + +}; // BranchParentComparatorImpl + +/// +/// Comparator of branch parent. Lower parent comes first +/// +template +class BranchParentComparator : public vtkm::cont::ExecutionObjectBase +{ + using ValueArrayType = typename vtkm::cont::ArrayHandle; + +public: + // constructor + VTKM_CONT + BranchParentComparator(const IdArrayType& branchParent, + const ValueArrayType& saddleIsoValue, + const IdArrayType& branchRootGRId) + : BranchParent(branchParent) + , SaddleIsoValue(saddleIsoValue) + , BranchRootGRId(branchRootGRId) + { + } + + VTKM_CONT BranchParentComparatorImpl PrepareForExecution( + vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) const + { + return BranchParentComparatorImpl( + this->BranchParent, this->SaddleIsoValue, this->BranchRootGRId, device, token); + } + +private: + IdArrayType BranchParent; + ValueArrayType SaddleIsoValue; + IdArrayType BranchRootGRId; +}; // BranchParentComparator + + +// Implementation of SuperarcTargetComparator +class SuperarcTargetComparatorImpl +{ +public: + using IdPortalType = typename IdArrayType::ReadPortalType; + + // constructor + VTKM_CONT + SuperarcTargetComparatorImpl(const IdArrayType& superarcTarget, + vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) + : superarcPortal(superarcTarget.PrepareForInput(device, token)) + { // constructor + } // constructor + + // () operator - gets called to do comparison + VTKM_EXEC + bool operator()(const vtkm::Id& i, const vtkm::Id& j) const + { // operator() + VTKM_ASSERT(i < superarcPortal.GetNumberOfValues() && i >= 0); + VTKM_ASSERT(j < superarcPortal.GetNumberOfValues() && j >= 0); + vtkm::Id superarcI = this->superarcPortal.Get(i); + vtkm::Id superarcJ = this->superarcPortal.Get(j); + + bool isNullI = vtkm::worklet::contourtree_augmented::NoSuchElement(superarcI); + bool isNullJ = vtkm::worklet::contourtree_augmented::NoSuchElement(superarcJ); + + // let the NULL superarc always go first + if (isNullI && !isNullJ) + return true; + if (isNullJ && !isNullI) + return false; + + vtkm::Id targetI = vtkm::worklet::contourtree_augmented::MaskedIndex(superarcI); + vtkm::Id targetJ = vtkm::worklet::contourtree_augmented::MaskedIndex(superarcJ); + + // primary sort on the superarc target + return (targetI < targetJ); + } // operator() + +private: + IdPortalType superarcPortal; + +}; // SuperarcTargetComparatorImpl + +/// +/// Comparator of superarc target. The NULL superarc always comes first. +/// +class SuperarcTargetComparator : public vtkm::cont::ExecutionObjectBase +{ + +public: + // constructor + VTKM_CONT + SuperarcTargetComparator(const IdArrayType& superarcTarget) + : SuperarcTarget(superarcTarget) + { + } + + VTKM_CONT SuperarcTargetComparatorImpl PrepareForExecution(vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) const + { + return SuperarcTargetComparatorImpl(this->SuperarcTarget, device, token); + } + +private: + IdArrayType SuperarcTarget; +}; // SuperarcTargetComparator + + +} // namespace select_top_volume_branches +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchSaddleIsKnownWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchSaddleIsKnownWorklet.h new file mode 100644 index 0000000000..b1c6463d42 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchSaddleIsKnownWorklet.h @@ -0,0 +1,131 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_BranchSaddleIsKnownWorklet_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_BranchSaddleIsKnownWorklet_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_branches +{ + +using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; + +/// +/// worklet to check whether the saddle end of branch is known by the block +/// if true, we return the saddle end supernode id +/// if false (or main branch), we return NO_SUCH_ELEMENT +/// +class BranchSaddleIsKnownWorklet : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void( + FieldIn lowerEndGRId, // (input) branch lower end global regular id + FieldIn lowerLocalEnd, // (input) branch local lower end + FieldIn lowerLocalEndGRId, // (input) branch local lower end global regular id + FieldIn upperEndGRId, // (input) branch upper end global regular id + FieldIn upperLocalEnd, // (input) branch local upper end + FieldIn upperLocalEndGRId, // (input) branch local upper end global regular id + FieldIn branchSaddleEps, // (input) branch saddle epsilon + FieldOut branchSaddle // (output) the branch saddle (if known by the block) + ); + using ExecutionSignature = _8(_1, _2, _3, _4, _5, _6, _7); + using InputDomain = _1; + + /// Constructor + VTKM_EXEC_CONT + BranchSaddleIsKnownWorklet() {} + + /// The functor checks the direction of the branch + VTKM_EXEC vtkm::Id operator()(const vtkm::Id& lowerEndGRId, + const vtkm::Id& lowerLocalEnd, + const vtkm::Id& lowerLocalEndGRId, + const vtkm::Id& upperEndGRId, + const vtkm::Id& upperLocalEnd, + const vtkm::Id& upperLocalEndGRId, + const vtkm::Id& branchSaddleEps) const + { + // if main branch + if (branchSaddleEps == 0) + return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + + // if the branch is a maximum-saddle branch + if (branchSaddleEps > 0) + return lowerEndGRId == lowerLocalEndGRId + ? lowerLocalEnd + : vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + + // if the branch is a minimum-saddle branch + if (branchSaddleEps < 0) + return upperEndGRId == upperLocalEndGRId + ? upperLocalEnd + : vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + + // in case of fallout, should never reach + return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + } + +}; // BranchSaddleIsKnownWorklet + +} // namespace select_top_volume_branches +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchVolumeComparator.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchVolumeComparator.h similarity index 88% rename from vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchVolumeComparator.h rename to vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchVolumeComparator.h index ddb9edc8fd..f8254fe4dd 100644 --- a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/BranchVolumeComparator.h +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/BranchVolumeComparator.h @@ -50,8 +50,8 @@ // Oliver Ruebel (LBNL) //============================================================================== -#ifndef vtk_m_filter_scalar_topology_worklet_branch_decomposition_select_top_volume_contours_BranchVolumeComparator_h -#define vtk_m_filter_scalar_topology_worklet_branch_decomposition_select_top_volume_contours_BranchVolumeComparator_h +#ifndef vtk_m_filter_scalar_topology_worklet_branch_decomposition_select_top_volume_branches_BranchVolumeComparator_h +#define vtk_m_filter_scalar_topology_worklet_branch_decomposition_select_top_volume_branches_BranchVolumeComparator_h #include @@ -61,7 +61,7 @@ namespace worklet { namespace scalar_topology { -namespace select_top_volume_contours +namespace select_top_volume_branches { using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; @@ -78,8 +78,8 @@ public: const IdArrayType& branchVolume, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) - : branchRootsPortal(branchRoots.PrepareForInput(device, token)) - , branchVolumePortal(branchVolume.PrepareForInput(device, token)) + : BranchRootsPortal(branchRoots.PrepareForInput(device, token)) + , BranchVolumePortal(branchVolume.PrepareForInput(device, token)) { // constructor } // constructor @@ -87,8 +87,8 @@ public: VTKM_EXEC bool operator()(const vtkm::Id& i, const vtkm::Id& j) const { // operator() - vtkm::Id volumeI = this->branchVolumePortal.Get(i); - vtkm::Id volumeJ = this->branchVolumePortal.Get(j); + vtkm::Id volumeI = this->BranchVolumePortal.Get(i); + vtkm::Id volumeJ = this->BranchVolumePortal.Get(j); // primary sort on branch volume if (volumeI > volumeJ) @@ -97,17 +97,17 @@ public: return false; vtkm::Id branchI = - vtkm::worklet::contourtree_augmented::MaskedIndex(this->branchRootsPortal.Get(i)); + vtkm::worklet::contourtree_augmented::MaskedIndex(this->BranchRootsPortal.Get(i)); vtkm::Id branchJ = - vtkm::worklet::contourtree_augmented::MaskedIndex(this->branchRootsPortal.Get(j)); + vtkm::worklet::contourtree_augmented::MaskedIndex(this->BranchRootsPortal.Get(j)); // secondary sort on branch ID return (branchI < branchJ); } // operator() private: - IdPortalType branchRootsPortal; - IdPortalType branchVolumePortal; + IdPortalType BranchRootsPortal; + IdPortalType BranchVolumePortal; }; // BranchVolumeComparatorImpl @@ -138,7 +138,7 @@ private: }; // BranchVolumeComparator -} // namespace select_top_volume_contours +} // namespace select_top_volume_branches } // namespace scalar_topology } // namespace worklet } // namespace vtkm diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/CMakeLists.txt b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/CMakeLists.txt new file mode 100644 index 0000000000..b2539e1cd3 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/CMakeLists.txt @@ -0,0 +1,31 @@ +##============================================================================ +## 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. +##============================================================================ + +set(headers + Predicates.h + TopVolumeBranchData.h + AboveThresholdWorklet.h + AssignValueWorklet.h + BinarySearchWorklet.h + BranchSaddleIsKnownWorklet.h + ClarifyBranchEndSupernodeTypeWorklet.h + CollectOuterSaddleWorklet.h + UpdateOuterSaddleWorklet.h + UpdateInfoByBranchDirectionWorklet.h + GetParentBranchWorklet.h + GetBranchHierarchyWorklet.h + GetBranchVolumeWorklet.h + BranchParentComparator.h + BranchVolumeComparator.h + BranchDecompositionTreeMaker.h +) +#----------------------------------------------------------------------------- + +vtkm_declare_headers(${headers}) diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/ClarifyBranchEndSupernodeTypeWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/ClarifyBranchEndSupernodeTypeWorklet.h similarity index 93% rename from vtkm/filter/scalar_topology/worklet/select_top_volume_contours/ClarifyBranchEndSupernodeTypeWorklet.h rename to vtkm/filter/scalar_topology/worklet/select_top_volume_branches/ClarifyBranchEndSupernodeTypeWorklet.h index 39ecdbdf3b..e219ed987b 100644 --- a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/ClarifyBranchEndSupernodeTypeWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/ClarifyBranchEndSupernodeTypeWorklet.h @@ -50,9 +50,10 @@ // Oliver Ruebel (LBNL) //============================================================================== -#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_ClarifyBranchEndSupernodeTypeWorklet_h -#define vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_ClarifyBranchEndSupernodeTypeWorklet_h +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_ClarifyBranchEndSupernodeTypeWorklet_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_ClarifyBranchEndSupernodeTypeWorklet_h +#include #include namespace vtkm @@ -61,7 +62,7 @@ namespace worklet { namespace scalar_topology { -namespace select_top_volume_contours +namespace select_top_volume_branches { // For special branches that only have one superarc @@ -125,7 +126,7 @@ private: const vtkm::Id totalVolume; }; // ClarifyBranchEndSupernodeTypeWorklet -} // namespace select_top_volume_contours +} // namespace select_top_volume_branches } // namespace scalar_topology } // namespace worklet } // namespace vtkm diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/CollectOuterSaddleWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/CollectOuterSaddleWorklet.h new file mode 100644 index 0000000000..2e9176161a --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/CollectOuterSaddleWorklet.h @@ -0,0 +1,123 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_CollectOuterSaddleWorklet_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_CollectOuterSaddleWorklet_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_branches +{ + +/// +/// worklet to get the outer saddles of parent branches from branch-decomposition tree +/// This is to visualize the isosurface belong to the parent branch +/// that is symmetrical to the outer-most child branch +/// we collect the first saddle isovalue if branchSaddleEpsilon(parent) < 0 +/// or the last saddle isovalue if branchSaddleEpsilon(parent) > 0 +/// or both if branchSaddleEpsilon(parent) == 0 +/// +class CollectOuterSaddle : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void( + FieldIn parentSaddleEpsilon, // parent saddle epsilon + WholeArrayIn branchParent, // (array input) parent branch root ID (local) + FieldOut IsOuterSaddle // (output) whether the branch is an outer saddle of the parent + ); + using ExecutionSignature = _3(InputIndex, _1, _2); + using InputDomain = _1; + + using IdArrayPortalType = typename IdArrayType::ReadPortalType; + + /// Constructor + VTKM_EXEC_CONT + CollectOuterSaddle() {} + + VTKM_EXEC vtkm::Id operator()(const vtkm::Id& inputIndex, + const vtkm::Id& parentSaddleEpsilon, + const IdArrayPortalType& branchParent) const + { + const vtkm::Id selfParent = branchParent.Get(inputIndex); + vtkm::Id isOuterSaddle = 0; + if (vtkm::worklet::contourtree_augmented::NoSuchElement(selfParent)) + { + return isOuterSaddle; + } + const bool isFirst = (inputIndex == 0) || (branchParent.Get(inputIndex - 1) != selfParent); + const bool isLast = (inputIndex == branchParent.GetNumberOfValues() - 1) || + (branchParent.Get(inputIndex + 1) != selfParent); + if (isFirst && parentSaddleEpsilon <= 0) + { + isOuterSaddle |= 1; + } + if (isLast && parentSaddleEpsilon >= 0) + { + isOuterSaddle |= 2; + } + return isOuterSaddle; + } +}; // CollectOuterSaddle + +} // namespace select_top_volume_branches +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/GetBranchHierarchyWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/GetBranchHierarchyWorklet.h new file mode 100644 index 0000000000..535bc7822a --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/GetBranchHierarchyWorklet.h @@ -0,0 +1,61 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_GetBranchHierarchyWorklet_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_GetBranchHierarchyWorklet_h + +#include +#include +#include +#include + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/GetBranchVolumeWorklet.h similarity index 94% rename from vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h rename to vtkm/filter/scalar_topology/worklet/select_top_volume_branches/GetBranchVolumeWorklet.h index 6d79508862..50d6b2fa67 100644 --- a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchVolumeWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/GetBranchVolumeWorklet.h @@ -50,9 +50,10 @@ // Oliver Ruebel (LBNL) //============================================================================== -#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_GetBranchVolumeWorklet_h -#define vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_GetBranchVolumeWorklet_h +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_GetBranchVolumeWorklet_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_GetBranchVolumeWorklet_h +#include #include namespace vtkm @@ -61,7 +62,7 @@ namespace worklet { namespace scalar_topology { -namespace select_top_volume_contours +namespace select_top_volume_branches { using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; @@ -108,6 +109,7 @@ public: { if (isLowerLeaf && isUpperLeaf) return totalVolume; + // if the branch is a minimum-saddle branch // if the upper end superarc direction is pointing up, then dependent; otherwise, reverse if (isLowerLeaf) @@ -129,7 +131,7 @@ private: const vtkm::Id totalVolume; }; // GetBranchVolumeWorklet -} // namespace select_top_volume_contours +} // namespace select_top_volume_branches } // namespace scalar_topology } // namespace worklet } // namespace vtkm diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/GetParentBranchWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/GetParentBranchWorklet.h new file mode 100644 index 0000000000..47b4bf1819 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/GetParentBranchWorklet.h @@ -0,0 +1,214 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_GetParentBranchWorklet_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_GetParentBranchWorklet_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_branches +{ + +constexpr vtkm::IdComponent MAX_CONNECTIVITY_3D = static_cast(14); +using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType; + +/// +/// worklet to compute the parent branch of branches +/// +class GetParentBranchWorklet : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void( + FieldIn branchSaddle, // (input) branch saddle supernode id + FieldIn saddleBranchRoot, // (input) the branch root of the superarc starting from the saddle + FieldIn saddleGRId, // (input) branch saddle supernode global regular id + WholeArrayIn superarcs, // (array input) all superarc targets in ascending order + WholeArrayIn branchRoots, // (array input) all branchRoots of superarcs + WholeArrayIn branchRootByBranch, // (array input) branch roots of branches in ascending order + WholeArrayIn upperEndGRIds, // (array input) upper local end of branches + WholeArrayIn lowerEndGRIds, // (array input) lower local end of branches + FieldOut parentBranch // (output) the information index of the parent branch + ); + using ExecutionSignature = _9(_1, _2, _3, _4, _5, _6, _7, _8); + using InputDomain = _1; + + /// Constructor + VTKM_EXEC_CONT + GetParentBranchWorklet() {} + + template + VTKM_EXEC vtkm::Id GetSuperarcEndPoint(const vtkm::Id& branchSaddle, + const IdArrayPortalType& sortedSuperarcs, + const bool isStart) const + { + VTKM_ASSERT(vtkm::worklet::contourtree_augmented::NoSuchElement(sortedSuperarcs.Get(0))); + using vtkm::worklet::contourtree_augmented::MaskedIndex; + vtkm::Id nSuperarcs = sortedSuperarcs.GetNumberOfValues(); + vtkm::Id endpoint = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + vtkm::Id head = 1; + vtkm::Id tail = nSuperarcs - 1; + while (head <= tail) + { + vtkm::Id mid = (head + tail) >> 1; + vtkm::Id midSuperarc = MaskedIndex(sortedSuperarcs.Get(mid)); + if (midSuperarc > branchSaddle) + tail = mid - 1; + else if (midSuperarc < branchSaddle) + head = mid + 1; + else if (isStart && + (mid == 1 || (mid > 1 && MaskedIndex(sortedSuperarcs.Get(mid - 1)) < branchSaddle))) + { + endpoint = mid; + break; + } + else if (!isStart && + (mid == nSuperarcs - 1 || + (mid < nSuperarcs - 1 && MaskedIndex(sortedSuperarcs.Get(mid + 1)) > branchSaddle))) + { + endpoint = mid; + break; + } + else if (isStart) + tail = mid - 1; + else + head = mid + 1; + } + VTKM_ASSERT(endpoint >= 1); + return endpoint; + } + + template + VTKM_EXEC vtkm::Id GetBranchRootIdx(const vtkm::Id& branchRoot, + const IdArrayPortalType& branchRootByBranch) const + { + vtkm::Id nBranchRoot = branchRootByBranch.GetNumberOfValues(); + vtkm::Id head = 0; + vtkm::Id tail = nBranchRoot - 1; + while (head <= tail) + { + vtkm::Id mid = (head + tail) >> 1; + vtkm::Id midBranchRoot = branchRootByBranch.Get(mid); + if (midBranchRoot == branchRoot) + { + return mid; + } + else if (midBranchRoot > branchRoot) + tail = mid - 1; + else + head = mid + 1; + } + // Update 10/04/2024: + // we use this binary search to filter the removed superarcs/branches in presimplification, + // so we do not report error, but use NO_SUCH_ELEMENT to indicate the nonexistence of the branch + return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + } + + template + VTKM_EXEC vtkm::Id operator()(const vtkm::Id& branchSaddle, + const vtkm::Id& saddleBranchRoot, + const vtkm::Id& saddleGRId, + const IdArrayPortalType& sortedSuperarcs, + const IdArrayPortalType& permutedBranchRoots, + const IdArrayPortalType& branchRootByBranch, + const IdArrayPortalType& upperEndGRIds, + const IdArrayPortalType& lowerEndGRIds) const + { + // there are at most MAX_CONNECTIVITY_3D superarcs connected to the branchSaddle + vtkm::Id candidateBranchRoot[MAX_CONNECTIVITY_3D]; + vtkm::Id nCandidate = 1; + candidateBranchRoot[0] = saddleBranchRoot; + + const vtkm::Id superarcStartIdx = GetSuperarcEndPoint(branchSaddle, sortedSuperarcs, true); + const vtkm::Id superarcEndIdx = GetSuperarcEndPoint(branchSaddle, sortedSuperarcs, false); + VTKM_ASSERT(superarcEndIdx >= superarcStartIdx); + // avoid integer type overflow + VTKM_ASSERT(superarcEndIdx - superarcStartIdx + 2 > 0); + VTKM_ASSERT(superarcEndIdx - superarcStartIdx + 2 <= MAX_CONNECTIVITY_3D); + for (vtkm::Id superarc = superarcStartIdx; superarc <= superarcEndIdx; superarc++) + { + candidateBranchRoot[nCandidate++] = permutedBranchRoots.Get(superarc); + } + + for (vtkm::Id branchRoot = 0; branchRoot < nCandidate; branchRoot++) + { + // Update 10/04/2024: + // The superarc starting from the saddle may not be valid, + // because it can be a virtual superarc or a presimplified superarc. + // We use the indicator (branchIdx == NO_SUCH_ELEMENT) to handle it. + const vtkm::Id branchIdx = + GetBranchRootIdx(candidateBranchRoot[branchRoot], branchRootByBranch); + if (vtkm::worklet::contourtree_augmented::NoSuchElement(branchIdx)) + continue; + if (upperEndGRIds.Get(branchIdx) != saddleGRId && lowerEndGRIds.Get(branchIdx) != saddleGRId) + return branchIdx; + } + + // Unfortunately, it seems possible that the parent branch cannot be found + // in which case NO_SUCH_ELEMENT is returned + // VTKM_ASSERT(false && "Cannot find the parent branch!"); + return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT; + } +}; // GetParentBranchWorklet + +} // namespace select_top_volume_branches +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/Predicates.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/Predicates.h new file mode 100644 index 0000000000..fdfd148027 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/Predicates.h @@ -0,0 +1,94 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_predicates_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_predicates_h + +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_branches +{ + +// we use two digits to identify whether the branch is a parent branch of other branches +// the lowest digit (i.e., 1) is a flag for parent branches of minima-saddle branches +struct IsExtraMinimum +{ + VTKM_EXEC_CONT + IsExtraMinimum() {} + + VTKM_EXEC_CONT + bool operator()(const vtkm::Id x) const { return (x & vtkm::Id(1)) != 0; } +}; + +// we use two digits to identify whether the branch is a parent branch of other branches +// the second lowest digit (i.e., 2) is a flag for parent branches of saddle-maxima branches +struct IsExtraMaximum +{ + VTKM_EXEC_CONT + IsExtraMaximum() {} + + VTKM_EXEC_CONT + bool operator()(const vtkm::Id x) const { return (x & vtkm::Id(2)) != 0; } +}; + +} // namespace select_top_volume_branches +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/TopVolumeBranchData.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/TopVolumeBranchData.h new file mode 100644 index 0000000000..ffb61f9cc4 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/TopVolumeBranchData.h @@ -0,0 +1,152 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//======================================================================================= +// +// Parallel Peak Pruning v. 2.0 +// +// Started June 15, 2017 +// +// Copyright Hamish Carr, University of Leeds +// +// BranchDecompositionTreeMaker.h +// +//======================================================================================= +// +// COMMENTS: +// +// This class computes the branch decomposition tree of top-volume branches +// +//======================================================================================= + + +#ifndef vtk_m_filter_scalar_topology_worklet_TopVolumeBranchData_h +#define vtk_m_filter_scalar_topology_worklet_TopVolumeBranchData_h + +#include + +#ifdef DEBUG_PRINT +#define DEBUG_BRANCH_DECOMPOSITION_TREE_MAKER +#endif + +namespace vtkm +{ +namespace filter +{ +namespace scalar_topology +{ + +/// Data to store all information about top branches by volume +struct TopVolumeBranchData +{ // struct TopVolumeBranchData + + // Data from DHCT. Size: nBranches + vtkm::worklet::contourtree_augmented::IdArrayType BranchRootByBranch; + vtkm::worklet::contourtree_augmented::IdArrayType BranchRootGRId; + vtkm::worklet::contourtree_augmented::IdArrayType BranchVolume; + vtkm::worklet::contourtree_augmented::IdArrayType BranchSaddleEpsilon; + vtkm::worklet::contourtree_augmented::IdArrayType SortedBranchByVolume; + vtkm::cont::UnknownArrayHandle BranchSaddleIsoValue; + + // True if it is the parent of a branch in the branch decomposition tree + vtkm::cont::ArrayHandle IsParentBranch; + + // Output Datasets. Information of top branches by volume. + // Size: nTopVolBranches + vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchRootGRId; + vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchVolume; + // the parent branch of top branches (if no parent branch, then NO_SUCH_ELEMENT) + vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchParent; + vtkm::cont::UnknownArrayHandle TopVolumeBranchSaddleIsoValue; + vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchSaddleEpsilon; + vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchUpperEndGRId; + vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchLowerEndGRId; + + // Other top-volume branch information + + // Whether the top volume branch is known by the block. + // size: nTopVolBranches + // value range: [0, 1] + vtkm::worklet::contourtree_augmented::IdArrayType TopVolBranchKnownByBlockStencil; + + // Branch order (among all branches) by branch root global regular ids. + // size: nTopVolBranches + // value range: {NO_SUCH_ELEMENT} U [0, nBranches - 1] + vtkm::worklet::contourtree_augmented::IdArrayType TopVolBranchGROrder; + + // Branch information index (among all branches) of the top volume branch. + // Top-volume branches outside the block are excluded. + // size: nTopVolBranchKnownByBlock + // value range: [0, nBranches - 1] + vtkm::worklet::contourtree_augmented::IdArrayType TopVolBranchInfoActualIndex; + + // Information to extract extra contours + // For each top-volume branch, we extract a contour on the branch near the saddle, + // and an extra contour on the parent branch near the same saddle + // The extra contour is dependent on the saddle of top-volume branches + + // For top-volume branches with a maximum as the end, + // the isovalue of the extra contour will be higher than the saddle end of the top-volume branch + vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchUpperEnd; + vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchLowerEnd; + // We copy the branch order of the top-volume branch + vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchOrder; + // We record the saddle end global regular IDs + vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchSaddleGRId; + vtkm::cont::UnknownArrayHandle ExtraMaximaBranchIsoValue; + + // For top-volume branches with a minimum as the end, + // the isovalue of the extra contour will be lower than the saddle end of the top-volume branch + vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchUpperEnd; + vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchLowerEnd; + // We copy the branch order of the top-volume branch + vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchOrder; + // We record the saddle end global regular IDs + vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchSaddleGRId; + vtkm::cont::UnknownArrayHandle ExtraMinimaBranchIsoValue; +}; // class TopVolumeBranchData + + + +} // namespace scalar_topology +} // namespace filter +} // namespace vtkm + + +#endif diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/UpdateInfoByBranchDirectionWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/UpdateInfoByBranchDirectionWorklet.h similarity index 94% rename from vtkm/filter/scalar_topology/worklet/select_top_volume_contours/UpdateInfoByBranchDirectionWorklet.h rename to vtkm/filter/scalar_topology/worklet/select_top_volume_branches/UpdateInfoByBranchDirectionWorklet.h index 39568d0b7a..4cb5e40723 100644 --- a/vtkm/filter/scalar_topology/worklet/select_top_volume_contours/UpdateInfoByBranchDirectionWorklet.h +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/UpdateInfoByBranchDirectionWorklet.h @@ -50,8 +50,8 @@ // Oliver Ruebel (LBNL) //============================================================================== -#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_UpdateInfoByBranchDirectionWorklet_h -#define vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_UpdateInfoByBranchDirectionWorklet_h +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_UpdateInfoByBranchDirectionWorklet_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_UpdateInfoByBranchDirectionWorklet_h #include @@ -61,7 +61,7 @@ namespace worklet { namespace scalar_topology { -namespace select_top_volume_contours +namespace select_top_volume_branches { /// @@ -116,7 +116,7 @@ public: }; // EpsilonFromBranchDirection -} // namespace select_top_volume_contours +} // namespace select_top_volume_branches } // namespace scalar_topology } // namespace worklet } // namespace vtkm diff --git a/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/UpdateOuterSaddleWorklet.h b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/UpdateOuterSaddleWorklet.h new file mode 100644 index 0000000000..9db6595354 --- /dev/null +++ b/vtkm/filter/scalar_topology/worklet/select_top_volume_branches/UpdateOuterSaddleWorklet.h @@ -0,0 +1,141 @@ +//============================================================================ +// 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 (c) 2018, The Regents of the University of California, through +// Lawrence Berkeley National Laboratory (subject to receipt of any required approvals +// from the U.S. Dept. of Energy). All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// (3) Neither the name of the University of California, Lawrence Berkeley National +// Laboratory, U.S. Dept. of Energy nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +//============================================================================= +// +// This code is an extension of the algorithm presented in the paper: +// Parallel Peak Pruning for Scalable SMP Contour Tree Computation. +// Hamish Carr, Gunther Weber, Christopher Sewell, and James Ahrens. +// Proceedings of the IEEE Symposium on Large Data Analysis and Visualization +// (LDAV), October 2016, Baltimore, Maryland. +// +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== + +#ifndef vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_UpdateOuterSaddleWorklet_h +#define vtk_m_filter_scalar_topology_worklet_select_top_volume_branches_UpdateOuterSaddleWorklet_h + +#include +#include + +namespace vtkm +{ +namespace worklet +{ +namespace scalar_topology +{ +namespace select_top_volume_branches +{ + +/// +/// worklet to update the value of outer saddles for parent branches +/// +template +class UpdateOuterSaddle : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void( + FieldIn branchOrder, // (input) the order of the (top-volume) branch by volume + FieldInOut branchValue, // (input/output) the isovalue to extract + FieldInOut + branchSaddleGRId, // (input/output) the global regular ID come along with the isovalue + WholeArrayIn incomingOrders, // (array input) (sorted) orders of branches from the other block + WholeArrayIn + incomingValues, // (array input) isovalues to extract on branches from the other block + WholeArrayIn incomingSaddleGRIds // (array input) saddle global regular IDs from the other block + ); + using ExecutionSignature = void(_1, _2, _3, _4, _5, _6); + using InputDomain = _1; + + using IdArrayPortalType = typename IdArrayType::ReadPortalType; + + /// Constructor + VTKM_EXEC_CONT + UpdateOuterSaddle() {} + + template + VTKM_EXEC void operator()(const vtkm::Id& branchOrder, + ValueType& branchValue, + vtkm::Id& branchSaddleGRId, + const IdArrayPortalType& incomingOrders, + const ValuePortalType& incomingValues, + const IdArrayPortalType& incomingSaddleGRIds) const + { + vtkm::Id head = 0; + vtkm::Id tail = incomingOrders.GetNumberOfValues() - 1; + while (head <= tail) + { + vtkm::Id mid = (head + tail) >> 1; + vtkm::Id midOrder = incomingOrders.Get(mid); + if (midOrder == branchOrder) + { + const ValueType midValue = incomingValues.Get(mid); + const vtkm::Id midSaddleGRId = incomingSaddleGRIds.Get(mid); + if (isMaximum && + (midValue > branchValue || + (midValue == branchValue && midSaddleGRId > branchSaddleGRId))) + { + branchSaddleGRId = midSaddleGRId; + branchValue = midValue; + } + else if (!isMaximum && + (midValue < branchValue || + (midValue == branchValue && midSaddleGRId < branchSaddleGRId))) + { + branchSaddleGRId = midSaddleGRId; + branchValue = midValue; + } + return; + } + else if (midOrder > branchOrder) + tail = mid - 1; + else + head = mid + 1; + } + } +}; // UpdateOuterSaddle + +} // namespace select_top_volume_branches +} // namespace scalar_topology +} // namespace worklet +} // namespace vtkm + +#endif -- GitLab From 911c258c9cfeffe739aabd3831e05c3b45fe4b62 Mon Sep 17 00:00:00 2001 From: Vicente Adolfo Bolea Sanchez Date: Wed, 2 Apr 2025 18:35:08 -0400 Subject: [PATCH 71/73] release: update version and License --- LICENSE.txt | 4 ++-- version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 9175695b44..5d6d8f09c9 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,7 +1,7 @@ -VTKm License Version 2.2 +VTKm License Version 2.3 ======================================================================== -Copyright (c) 2014-2024 +Copyright (c) 2014-2025 Kitware Inc., National Technology & Engineering Solutions of Sandia, LLC (NTESS), UT-Battelle, LLC., diff --git a/version.txt b/version.txt index a15cec5bf1..ea579818db 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.2.9999 +2.3.9999 -- GitLab From 72f4cb43ad34188b4b28a8c315493dbde8042369 Mon Sep 17 00:00:00 2001 From: Vicente Adolfo Bolea Sanchez Date: Wed, 2 Apr 2025 18:47:33 -0400 Subject: [PATCH 72/73] release: 2.3.0-rc1 release notes --- docs/changelog/2.3/release-notes.md | 254 ++++++++++++++++++ docs/changelog/add-array-set-values.md | 4 - docs/changelog/cell-interpolation.global.md | 8 - .../cell-neighborhood-deprecation-warning.md | 6 - ...rove-performance-and-memory-consumption.md | 38 --- docs/changelog/contour-polys.md | 18 -- docs/changelog/document-field-creation.md | 10 - docs/changelog/env-options.md | 9 - ...prove-performance-and-memory-consuption.md | 20 -- docs/changelog/fallback-log.md | 10 - docs/changelog/flipped-contour-tris.md | 11 - docs/changelog/kokkos-force-init.md | 7 - docs/changelog/missing-template-arg-list.md | 10 - docs/changelog/simplify-locator-bases.md | 11 - docs/changelog/swap-cub-include.md | 10 - docs/changelog/thrust-swap-warnings.md | 17 -- docs/changelog/vec-constexpr.md | 8 - 17 files changed, 254 insertions(+), 197 deletions(-) create mode 100644 docs/changelog/2.3/release-notes.md delete mode 100644 docs/changelog/add-array-set-values.md delete mode 100644 docs/changelog/cell-interpolation.global.md delete mode 100644 docs/changelog/cell-neighborhood-deprecation-warning.md delete mode 100644 docs/changelog/clip-improve-performance-and-memory-consumption.md delete mode 100644 docs/changelog/contour-polys.md delete mode 100644 docs/changelog/document-field-creation.md delete mode 100644 docs/changelog/env-options.md delete mode 100644 docs/changelog/external-faces-improve-performance-and-memory-consuption.md delete mode 100644 docs/changelog/fallback-log.md delete mode 100644 docs/changelog/flipped-contour-tris.md delete mode 100644 docs/changelog/kokkos-force-init.md delete mode 100644 docs/changelog/missing-template-arg-list.md delete mode 100644 docs/changelog/simplify-locator-bases.md delete mode 100644 docs/changelog/swap-cub-include.md delete mode 100644 docs/changelog/thrust-swap-warnings.md delete mode 100644 docs/changelog/vec-constexpr.md diff --git a/docs/changelog/2.3/release-notes.md b/docs/changelog/2.3/release-notes.md new file mode 100644 index 0000000000..2d154611cf --- /dev/null +++ b/docs/changelog/2.3/release-notes.md @@ -0,0 +1,254 @@ +VTK-m 2.3.0 Release Notes +======================= + +# Table of Contents + +1. [Core](#Core) +2. [ArrayHandle](#ArrayHandle) + - Add ArraySetValues. +3. [Filters and Worklets](#Filters_and_Worklets) + - Add a form of CellInterpolate that operates on whole cell sets. + - Fix deprecation warning in WorkletCellNeighborhood. + - Clip: Improve performance and memory consumption. + - Contour Polygons with Marching Cubes. + - Better document the creation of Field and CoordinateSystem. + - ExternalFaces: Improve performance and memory consumption. + - Fixed winding of triangles of flying edges on GPUs. +4. [Control Environment](#Control_Environment) + - Load options from environment variables. + - Added log entry when a cast and call fallback is used. +5. [Execution Environment](#Execution_Environment) + - Automatically initialize Kokkos. +6. [Build](#Build) + - Fix clang compile issue with a missing tempolate arg list. + - Fix include for cub::Swap. + - Circumvent shadow warnings with thrust swap. +7. [Others](#Others) + - Simplify CellLocatorBase and PointLocatorBase. + - Add constexpr to Vec methods. + +# Core + +# ArrayHandle + +## Add ArraySetValues + +ArraySetValues.h is a new header file that includes functions to set 1 or many values in an array +similarly to how ArrayGetValues.h gets 1 or many values from an array. + +# Filters and Worklets + +## Add a form of CellInterpolate that operates on whole cell sets + +The initial implementation of `CellInterpolate` takes arguments that are +expected from a topology map worklet. However, sometimes you want to +interplate cells that are queried from locators or otherwise come from a +`WholeCellSet` control signature argument. + +A new form of `CellInterpolate` is added to handle this case. + +## Fix deprecation warning in WorkletCellNeighborhood + +There was a use of a deprecated method buried in a support class of +`WorkletCellNeighborhood`. This fixes that deprecation and also adds a +missing test for `WorkletCellNeighborhood` to prevent such things in the +future. + +## Clip: Improve performance and memory consumption + +The following set of improvements have been implemented for the Clip algorithm: + +1. Input points that are kept are determined by comparing their scalar value with the isovalue, instead of checking the + output cells' connectivity. +2. Output arrays are written only once, and they are not transformed. Due to that, no auxiliary arrays are needed to + perform the transformations. +3. A fast path for discarded and kept cells has been added, which are the most common cell cases. +4. ClipTables are now more descriptive, and the non-inverted case tables have been imported from VTK, such that both VTK + and VTK-m produce the same results. +5. Employ batching of points and cells to use less memory and perform less and faster computations. + +The new `Clip` algorithm: + +On the CPU: + +1. Batch size = min(1000, max(1, numberOfElements / 250000)). +2. Memory-footprint (the bigger the dataset the greater the benefit): + 1. For almost nothing is kept: 10.22x to 99.67x less memory footprint + 2. For almost half is kept: 2.62x to 4.30x less memory footprint + 3. For almost everything is kept: 2.38x to 3.21x less memory footprint +3. Performance (the bigger the dataset the greater the benefit): + 1. For almost nothing is kept: 1.63x to 7.79x faster + 2. For almost half is kept: 1.75x to 5.28x faster + 3. For almost everything is kept: 1.71x to 5.35x faster + +On the GPU: + +1. Batch size = 6. +2. Memory-footprint (the bigger the dataset the greater the benefit): + 1. For almost nothing is kept: 1.71x to 7.75x less memory footprint + 2. For almost half is kept: 1.11x to 1.36x less memory footprint + 3. For almost everything is kept: 1.09x to 1.31x less memory footprint +3. Performance (the bigger the dataset the greater the benefit): + 1. For almost nothing is kept: 1.54x to 9.67x faster + 2. For almost half is kept: 1.38x to 4.68x faster + 3. For almost everything is kept: 1.21x to 4.46x faster + +## Contour Polygons with Marching Cubes + +Previously, the Marching Cubes contouring algorithm only had case tables for 3D +polyhedra. This means that if you attempted to contour or slice surfaces or +lines, you would not get any output. However, there are many valid use cases for +contouring this type of data. + +This change adds case tables for triangles, quadrilaterals, and lines. It also +adds some special cases for general polygons and poly-lines. These special cases +do not use tables. Rather, there is a special routine that iterates over the +points since these cells types can have any number of points. + +Note that to preserve the speed of the operation, contours for cells of a single +dimension type are done at one time. By default, the contour filter will try 3D +cells, then 2D cells, then 1D cells. It is also possible to select a particular +cell dimension or to append results from all cell types together. In this latest +case, the output cells will be of type `CellSetExplicit` instead of +`CellSetSingleType`. + +## Better document the creation of Field and CoordinateSystem + +The constructors for `vtkm::cont::Field` and `vtkm::cont::CoordinateSystem` +were missing from the built user's guide. The construction of these classes +from names, associations, and arrays are now provided in the documentation. + +Also added new versions of `AddField` and `AddCoordinateSystem` to +`vtkm::cont::DataSet` that mimic the constructors. This adds some sytatic +sugar so you can just emplace the field instead of constructing and +passing. + +## ExternalFaces: Improve performance and memory consumption + +`ExternalFaces` now uses a new algorithm that has the following combination of novel characteristics: + +1. employs minimum point id as its face hash function instead of FNV1A of canonicalFaceID, that yields cache-friendly + memory accesses which leads to enhanced performance. +2. employs an atomic hash counting approach, instead of hash sorting, to perform a reduce-by-key operation on the faces' + hashes. +3. enhances performance by ensuring that the computation of face properties occurs only once and by avoiding the + processing of a known internal face more than once. +4. embraces frugality in memory consumption, enabling the processing of larger datasets, especially on GPUs. + +When evaluated on Frontier super-computer on 4 large datasets: + +The new `ExternalFaces` algorithm: + +1. has 4.73x to 5.99x less memory footprint +2. is 4.96x to 7.37x faster on the CPU +3. is 1.54x faster on the GPU, and the original algorithm could not execute on large datasets due to its high memory + footprint + +## Fixed winding of triangles of flying edges on GPUs + +The flying edges implementation has an optimization where it will traverse +meshes in the Y direction rather than the X direction on the GPU. It +created mostly correct results, but the triangles' winding was the opposite +from the CPU. This was mostly problematic when normals were generated from +the gradients. In this case, the gradient would point from the "back" of +the face, and that can cause shading problems with some renderers. + +This has been fixed to make the windings consistent on the GPU with the CPU +and the gradients. + +# Control Environment + +## Load options from environment variables + +Some common VTK-m options such as the device and log level could be +specified on the command line but not through environment variables. It is +not always possible to set VTK-m command line options, so environment +variables are added. + +Also added documentation to the user's guide about what options are +available and how to set them. + +## Added log entry when a cast and call fallback is used + +Several places in VTK-m use the `CastAndCallForTypesWithFallback` method in +`UnknownArrayHandle`. The method works well for catching both common and +corner cases. However, there was no way to know if the efficient direct +method or the (supposedly) less likely fallback of copying data to a float +array was used. VTK-m now adds a log event, registered at the "INFO" level, +whenever data is copied to a fallback float array. This helps developers +monitor the eficiency of their code. + +# Execution Environment + +## Automatically initialize Kokkos + +Calling `vtkm::cont::Initialize()` is supposed to be optional. However, Kokkos +needs to have `Kokkos::initialize()` called before using some devices such as +HIP. To make sure that Kokkos is properly initialized, the VTK-m allocation for +the Kokkos device now checks to see if `Kokkos::is_initialized()` is true. If it +is not, then `vtkm::cont::Initialize()` is called. + +# Build + +## Fix clang compile issue with a missing tempolate arg list + +Apparently, starting with LLVM clang version 20, if you use the `template` +keyword to highlight a sub-element, you have to provide a template argument +list. This is true even for a method where the template arguments can be +completely determined by the types of the arguments. Fix this problem by +providing an empty template arg list (so the compiler knows what is +templated but still figures out its own types). + +Fixes #830 + +## Fix include for cub::Swap + +A problem we have with the `vtkm::Swap` method is that it can be +ambiguous with the `cub::Swap` method that is part of the CUDA CUB +library. We get around this problem by using the CUB version of the +function when it is available. + +However, we were missing an include statement that necessarily provided +`cub::Swap`. This function is now explicitly provided so that we no +longer rely on including it indirectly elsewhere. + +## Circumvent shadow warnings with thrust swap + +We have run into issues with the `nvcc` compiler giving shadow warnings for +the internals of thrust like this: + +``` +/usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h: In constructor 'thrust::detail::unary_negate::unary_negate(const Predicate&)': +/usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h:45:46: warning: declaration of 'pred' shadows a member of 'thrust::detail::unary_negate' [-Wshadow] + explicit unary_negate(const Predicate& pred) : pred(pred) {} + ^ +/usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h:42:11: note: shadowed declaration is here + Predicate pred; + ^ +``` + +These warnings seem to be caused by the inclusion of `thrust/swap.h`. To +prevent this, this header file is no longer included from `vtkm/Swap.h`. + +# Others + +## Simplify CellLocatorBase and PointLocatorBase + +`CellLocatorBase` and `PointLocatorBase` used to use CRTP. However, this +pattern is unnecessary as the only method in the subclass they call is +`Build`, which does not need templating. The base class does not have to +call the `PrepareForExecution` method, so it can provide its own features +to derived classes more easily. + +Also moved `CellLocatorBase` and `PointLocatorBase` out of the `internal` +namespace. Although they provide little benefit other than a base class, it +will make documenting its methods easier. + +## Add constexpr to Vec methods + +The `constexpr` keyword is helpful to add to functions and macros where +possible. Better than `inline`, it tells the compiler that it can perform +optimizations based on analysis of expressions and literals given in the +code. In particular, this should help code that loops over components have +proper optimizations like loop unrolling when using `Vec` types that have +the number of components fixed. diff --git a/docs/changelog/add-array-set-values.md b/docs/changelog/add-array-set-values.md deleted file mode 100644 index fc837e57dd..0000000000 --- a/docs/changelog/add-array-set-values.md +++ /dev/null @@ -1,4 +0,0 @@ -## Add ArraySetValues - -ArraySetValues.h is a new header file that includes functions to set 1 or many values in an array -similarly to how ArrayGetValues.h gets 1 or many values from an array. diff --git a/docs/changelog/cell-interpolation.global.md b/docs/changelog/cell-interpolation.global.md deleted file mode 100644 index a011bb601b..0000000000 --- a/docs/changelog/cell-interpolation.global.md +++ /dev/null @@ -1,8 +0,0 @@ -## Add a form of CellInterpolate that operates on whole cell sets - -The initial implementation of `CellInterpolate` takes arguments that are -expected from a topology map worklet. However, sometimes you want to -interplate cells that are queried from locators or otherwise come from a -`WholeCellSet` control signature argument. - -A new form of `CellInterpolate` is added to handle this case. diff --git a/docs/changelog/cell-neighborhood-deprecation-warning.md b/docs/changelog/cell-neighborhood-deprecation-warning.md deleted file mode 100644 index 5427a61f02..0000000000 --- a/docs/changelog/cell-neighborhood-deprecation-warning.md +++ /dev/null @@ -1,6 +0,0 @@ -## Fix deprecation warning in WorkletCellNeighborhood - -There was a use of a deprecated method buried in a support class of -`WorkletCellNeighborhood`. This fixes that deprecation and also adds a -missing test for `WorkletCellNeighborhood` to prevent such things in the -future. diff --git a/docs/changelog/clip-improve-performance-and-memory-consumption.md b/docs/changelog/clip-improve-performance-and-memory-consumption.md deleted file mode 100644 index bc34ff5659..0000000000 --- a/docs/changelog/clip-improve-performance-and-memory-consumption.md +++ /dev/null @@ -1,38 +0,0 @@ -## Clip: Improve performance and memory consumption - -The following set of improvements have been implemented for the Clip algorithm: - -1. Input points that are kept are determined by comparing their scalar value with the isovalue, instead of checking the - output cells' connectivity. -2. Output arrays are written only once, and they are not transformed. Due to that, no auxiliary arrays are needed to - perform the transformations. -3. A fast path for discarded and kept cells has been added, which are the most common cell cases. -4. ClipTables are now more descriptive, and the non-inverted case tables have been imported from VTK, such that both VTK - and VTK-m produce the same results. -5. Employ batching of points and cells to use less memory and perform less and faster computations. - -The new `Clip` algorithm: - -On the CPU: - -1. Batch size = min(1000, max(1, numberOfElements / 250000)). -2. Memory-footprint (the bigger the dataset the greater the benefit): - 1. For almost nothing is kept: 10.22x to 99.67x less memory footprint - 2. For almost half is kept: 2.62x to 4.30x less memory footprint - 3. For almost everything is kept: 2.38x to 3.21x less memory footprint -3. Performance (the bigger the dataset the greater the benefit): - 1. For almost nothing is kept: 1.63x to 7.79x faster - 2. For almost half is kept: 1.75x to 5.28x faster - 3. For almost everything is kept: 1.71x to 5.35x faster - -On the GPU: - -1. Batch size = 6. -2. Memory-footprint (the bigger the dataset the greater the benefit): - 1. For almost nothing is kept: 1.71x to 7.75x less memory footprint - 2. For almost half is kept: 1.11x to 1.36x less memory footprint - 3. For almost everything is kept: 1.09x to 1.31x less memory footprint -3. Performance (the bigger the dataset the greater the benefit): - 1. For almost nothing is kept: 1.54x to 9.67x faster - 2. For almost half is kept: 1.38x to 4.68x faster - 3. For almost everything is kept: 1.21x to 4.46x faster diff --git a/docs/changelog/contour-polys.md b/docs/changelog/contour-polys.md deleted file mode 100644 index 0a0d0b4bec..0000000000 --- a/docs/changelog/contour-polys.md +++ /dev/null @@ -1,18 +0,0 @@ -## Contour Polygons with Marching Cubes - -Previously, the Marching Cubes contouring algorithm only had case tables for 3D -polyhedra. This means that if you attempted to contour or slice surfaces or -lines, you would not get any output. However, there are many valid use cases for -contouring this type of data. - -This change adds case tables for triangles, quadrilaterals, and lines. It also -adds some special cases for general polygons and poly-lines. These special cases -do not use tables. Rather, there is a special routine that iterates over the -points since these cells types can have any number of points. - -Note that to preserve the speed of the operation, contours for cells of a single -dimension type are done at one time. By default, the contour filter will try 3D -cells, then 2D cells, then 1D cells. It is also possible to select a particular -cell dimension or to append results from all cell types together. In this latest -case, the output cells will be of type `CellSetExplicit` instead of -`CellSetSingleType`. diff --git a/docs/changelog/document-field-creation.md b/docs/changelog/document-field-creation.md deleted file mode 100644 index 9339623436..0000000000 --- a/docs/changelog/document-field-creation.md +++ /dev/null @@ -1,10 +0,0 @@ -## Better document the creation of Field and CoordinateSystem - -The constructors for `vtkm::cont::Field` and `vtkm::cont::CoordinateSystem` -were missing from the built user's guide. The construction of these classes -from names, associations, and arrays are now provided in the documentation. - -Also added new versions of `AddField` and `AddCoordinateSystem` to -`vtkm::cont::DataSet` that mimic the constructors. This adds some sytatic -sugar so you can just emplace the field instead of constructing and -passing. diff --git a/docs/changelog/env-options.md b/docs/changelog/env-options.md deleted file mode 100644 index 857d8446ba..0000000000 --- a/docs/changelog/env-options.md +++ /dev/null @@ -1,9 +0,0 @@ -## Load options from environment variables - -Some common VTK-m options such as the device and log level could be -specified on the command line but not through environment variables. It is -not always possible to set VTK-m command line options, so environment -variables are added. - -Also added documentation to the user's guide about what options are -available and how to set them. diff --git a/docs/changelog/external-faces-improve-performance-and-memory-consuption.md b/docs/changelog/external-faces-improve-performance-and-memory-consuption.md deleted file mode 100644 index 164ea375ec..0000000000 --- a/docs/changelog/external-faces-improve-performance-and-memory-consuption.md +++ /dev/null @@ -1,20 +0,0 @@ -## ExternalFaces: Improve performance and memory consumption - -`ExternalFaces` now uses a new algorithm that has the following combination of novel characteristics: - -1. employs minimum point id as its face hash function instead of FNV1A of canonicalFaceID, that yields cache-friendly - memory accesses which leads to enhanced performance. -2. employs an atomic hash counting approach, instead of hash sorting, to perform a reduce-by-key operation on the faces' - hashes. -3. enhances performance by ensuring that the computation of face properties occurs only once and by avoiding the - processing of a known internal face more than once. -4. embraces frugality in memory consumption, enabling the processing of larger datasets, especially on GPUs. - -When evaluated on Frontier super-computer on 4 large datasets: - -The new `ExternalFaces` algorithm: - -1. has 4.73x to 5.99x less memory footprint -2. is 4.96x to 7.37x faster on the CPU -3. is 1.54x faster on the GPU, and the original algorithm could not execute on large datasets due to its high memory - footprint diff --git a/docs/changelog/fallback-log.md b/docs/changelog/fallback-log.md deleted file mode 100644 index be6e6d065d..0000000000 --- a/docs/changelog/fallback-log.md +++ /dev/null @@ -1,10 +0,0 @@ -## Added log entry when a cast and call fallback is used - -Several places in VTK-m use the `CastAndCallForTypesWithFallback` method in -`UnknownArrayHandle`. The method works well for catching both common and -corner cases. However, there was no way to know if the efficient direct -method or the (supposedly) less likely fallback of copying data to a float -array was used. VTK-m now adds a log event, registered at the "INFO" level, -whenever data is copied to a fallback float array. This helps developers -monitor the eficiency of their code. - diff --git a/docs/changelog/flipped-contour-tris.md b/docs/changelog/flipped-contour-tris.md deleted file mode 100644 index d643cf6fda..0000000000 --- a/docs/changelog/flipped-contour-tris.md +++ /dev/null @@ -1,11 +0,0 @@ -## Fixed winding of triangles of flying edges on GPUs - -The flying edges implementation has an optimization where it will traverse -meshes in the Y direction rather than the X direction on the GPU. It -created mostly correct results, but the triangles' winding was the opposite -from the CPU. This was mostly problematic when normals were generated from -the gradients. In this case, the gradient would point from the "back" of -the face, and that can cause shading problems with some renderers. - -This has been fixed to make the windings consistent on the GPU with the CPU -and the gradients. diff --git a/docs/changelog/kokkos-force-init.md b/docs/changelog/kokkos-force-init.md deleted file mode 100644 index 89f34b6511..0000000000 --- a/docs/changelog/kokkos-force-init.md +++ /dev/null @@ -1,7 +0,0 @@ -## Automatically initialize Kokkos - -Calling `vtkm::cont::Initialize()` is supposed to be optional. However, Kokkos -needs to have `Kokkos::initialize()` called before using some devices such as -HIP. To make sure that Kokkos is properly initialized, the VTK-m allocation for -the Kokkos device now checks to see if `Kokkos::is_initialized()` is true. If it -is not, then `vtkm::cont::Initialize()` is called. diff --git a/docs/changelog/missing-template-arg-list.md b/docs/changelog/missing-template-arg-list.md deleted file mode 100644 index 6f7da3d463..0000000000 --- a/docs/changelog/missing-template-arg-list.md +++ /dev/null @@ -1,10 +0,0 @@ -## Fix clang compile issue with a missing tempolate arg list - -Apparently, starting with LLVM clang version 20, if you use the `template` -keyword to highlight a sub-element, you have to provide a template argument -list. This is true even for a method where the template arguments can be -completely determined by the types of the arguments. Fix this problem by -providing an empty template arg list (so the compiler knows what is -templated but still figures out its own types). - -Fixes #830 diff --git a/docs/changelog/simplify-locator-bases.md b/docs/changelog/simplify-locator-bases.md deleted file mode 100644 index 55ce5c3b77..0000000000 --- a/docs/changelog/simplify-locator-bases.md +++ /dev/null @@ -1,11 +0,0 @@ -## Simplify CellLocatorBase and PointLocatorBase - -`CellLocatorBase` and `PointLocatorBase` used to use CRTP. However, this -pattern is unnecessary as the only method in the subclass they call is -`Build`, which does not need templating. The base class does not have to -call the `PrepareForExecution` method, so it can provide its own features -to derived classes more easily. - -Also moved `CellLocatorBase` and `PointLocatorBase` out of the `internal` -namespace. Although they provide little benefit other than a base class, it -will make documenting its methods easier. diff --git a/docs/changelog/swap-cub-include.md b/docs/changelog/swap-cub-include.md deleted file mode 100644 index f68367b093..0000000000 --- a/docs/changelog/swap-cub-include.md +++ /dev/null @@ -1,10 +0,0 @@ -Fix include for cub::Swap - -A problem we have with the `vtkm::Swap` method is that it can be -ambiguous with the `cub::Swap` method that is part of the CUDA CUB -library. We get around this problem by using the CUB version of the -function when it is available. - -However, we were missing an include statement that necessarily provided -`cub::Swap`. This function is now explicitly provided so that we no -longer rely on including it indirectly elsewhere. diff --git a/docs/changelog/thrust-swap-warnings.md b/docs/changelog/thrust-swap-warnings.md deleted file mode 100644 index 8d55a122c9..0000000000 --- a/docs/changelog/thrust-swap-warnings.md +++ /dev/null @@ -1,17 +0,0 @@ -## Circumvent shadow warnings with thrust swap - -We have run into issues with the `nvcc` compiler giving shadow warnings for -the internals of thrust like this: - -``` -/usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h: In constructor 'thrust::detail::unary_negate::unary_negate(const Predicate&)': -/usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h:45:46: warning: declaration of 'pred' shadows a member of 'thrust::detail::unary_negate' [-Wshadow] - explicit unary_negate(const Predicate& pred) : pred(pred) {} - ^ -/usr/local/cuda/bin/../targets/x86_64-linux/include/thrust/detail/internal_functional.h:42:11: note: shadowed declaration is here - Predicate pred; - ^ -``` - -These warnings seem to be caused by the inclusion of `thrust/swap.h`. To -prevent this, this header file is no longer included from `vtkm/Swap.h`. diff --git a/docs/changelog/vec-constexpr.md b/docs/changelog/vec-constexpr.md deleted file mode 100644 index b51f389643..0000000000 --- a/docs/changelog/vec-constexpr.md +++ /dev/null @@ -1,8 +0,0 @@ -## Add constexpr to Vec methods - -The `constexpr` keyword is helpful to add to functions and macros where -possible. Better than `inline`, it tells the compiler that it can perform -optimizations based on analysis of expressions and literals given in the -code. In particular, this should help code that loops over components have -proper optimizations like loop unrolling when using `Vec` types that have -the number of components fixed. -- GitLab From 9cbe04e575f117a7740ba8b7cfd50e9118e96f11 Mon Sep 17 00:00:00 2001 From: Vicente Adolfo Bolea Sanchez Date: Wed, 2 Apr 2025 18:49:02 -0400 Subject: [PATCH 73/73] 2.3.0-rc1 is our 18th official release of VTK-m. The major changes to VTK-m from (previous release) can be found in: docs/changelog/2.3.0/release-notes.md --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index ea579818db..d62e3a5a6d 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.3.9999 +2.3.0-rc1 -- GitLab