diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3ff62cd54c4ee34b886d99848041a991c0a30337..380c83ddc0c6142193ccb7521c3e9bf0099a917e 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 @@ -126,7 +126,7 @@ stages: .use_minimum_supported_cmake: variables: - CMAKE_VERSION: "3.13.5" + CMAKE_VERSION: "3.15.5" .warning_policy: allow_failure: @@ -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/config/cmake.sh b/.gitlab/ci/config/cmake.sh index 17c9550991bfa07b95e515db99d5d0e9e7bc40e3..07d4ac83742e894697086689a8a17bd37a99b3e3 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.15.5']='03cfd669d0f990040ec89bb63a3ae7f6d61fd17c1c4d5e7ec3d1a35fe1f043f0' + ['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" ;; 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 b9232768f7f33c80d8d72a1dbc1adc377180f951..e69c5a18bc49baffcd37f9d2f9bb617fec829e4a 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 7b71f7b656378660e4dfbcae3e7909b338b61082..0bbf33e6b3fd0d63052f143fb3c4267dc18979d0 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 b22934e2c2540ce8dae6c32960cd84dd5b720f9c..469983ba9bcba3125d075304d30e532f780edcd9 100644 --- a/.gitlab/ci/ubuntu1804.yml +++ b/.gitlab/ci/ubuntu1804.yml @@ -186,7 +186,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 @@ -202,7 +202,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 @@ -215,7 +215,7 @@ test:ubuntu1804_kokkos: - .cmake_test_linux - .run_automatically 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 5e1bbe9fe8df89d40b714310e0aaf21216fe200d..20c90dd7bb6c6b915e9bffb306189145c8f1f512 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,51 +36,4 @@ test:ubuntu2004_kokkos: - .cmake_test_linux - .run_automatically 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" - 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 0000000000000000000000000000000000000000..a070320b006fee27682c55cd47182088a54205fd --- /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/.gitlab/ci/windows10.yml b/.gitlab/ci/windows10.yml index 0f86de33e8fc8ac24f24ff6929618fcb354b168a..776cb0d0850a9fdbfd51d3c416cca86dfa4f81cf 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 diff --git a/CMake/VTKmWrappers.cmake b/CMake/VTKmWrappers.cmake index c1833a374490ad760686b2b02e58d8135812797c..0feb646bb28369a535f314c1fb775be74ad1bb54 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) diff --git a/CMakeLists.txt b/CMakeLists.txt index 561bee9835a5369ff0622b1cb46e2ecdc41e396b..cfa85247d23d25537031a28a845b317979bc09a9 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 @@ -264,11 +263,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/LICENSE.txt b/LICENSE.txt index 9175695b4467ab6495baf40085a719b648cf9352..5d6d8f09c9e1831aa845dfda258ccd64e4230f49 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/README.md b/README.md index aad908b955324fa0a4b84f54ea51a338ef28d5a0..3aff451a8e63d022bd081d635921535caf54aa3b 100644 --- a/README.md +++ b/README.md @@ -74,13 +74,15 @@ 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: + 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 @@ -126,7 +128,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]. @@ -152,7 +154,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 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 143be4cd8f5ccc057232632136220d844256dd30..0000000000000000000000000000000000000000 --- 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/baseline/5x6x7.presimplification.ct_txt b/data/baseline/5x6x7.presimplification.ct_txt new file mode 100644 index 0000000000000000000000000000000000000000..5de211fa57b58d6f7c4498e0867e8f860a196208 --- /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/filter/contour-poly.png b/data/baseline/filter/contour-poly.png new file mode 100644 index 0000000000000000000000000000000000000000..b2d1e86af71725efb5caa0b48e316de4408f1222 --- /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/baseline/vanc.presimplification.ct_txt b/data/baseline/vanc.presimplification.ct_txt new file mode 100644 index 0000000000000000000000000000000000000000..f7ac4ca022a9d1f32a765d67949a19fafacde6f0 --- /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/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 29df1903ab5067a55b5c3a8c047f8c0f51c5fdad..0000000000000000000000000000000000000000 --- 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 d0c215acd17154c880eb91ef0a9eebacef72b2aa..0000000000000000000000000000000000000000 --- 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 26e36963dda69d71c993a6a94405d411dfc7d6d9..0000000000000000000000000000000000000000 --- 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 a2572ad25884e9f775c8e31a7a1c267afd4d29ee..0000000000000000000000000000000000000000 --- 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 a9614127410cae0e2024e05592d93bc3ac53d646..0000000000000000000000000000000000000000 --- 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 b4e1f46d2a5378831952d06305650aa3428c3548..0000000000000000000000000000000000000000 --- 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 diff --git a/data/data/unstructured/mixed-cell-shapes.vtk b/data/data/unstructured/mixed-cell-shapes.vtk new file mode 100644 index 0000000000000000000000000000000000000000..3da0212071ae186b21f0cd7b55ebe0d7e9a4f53b --- /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 0000000000000000000000000000000000000000..5876e7f421e401149be7e30839a8bff0e0da660e --- /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/2.3/release-notes.md b/docs/changelog/2.3/release-notes.md new file mode 100644 index 0000000000000000000000000000000000000000..2d154611cfd0368509f0a340ea1cb71a5d73d039 --- /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/cell-neighborhood-deprecation-warning.md b/docs/changelog/cell-neighborhood-deprecation-warning.md deleted file mode 100644 index 5427a61f02be25c3ad128ca4d6d2a30667451040..0000000000000000000000000000000000000000 --- 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/kokkos-force-init.md b/docs/changelog/kokkos-force-init.md deleted file mode 100644 index 89f34b6511305913417f7e64b01c532c9b76ca92..0000000000000000000000000000000000000000 --- 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 6f7da3d463544b849dcbe29cebf559583c21a65f..0000000000000000000000000000000000000000 --- 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/swap-cub-include.md b/docs/changelog/swap-cub-include.md deleted file mode 100644 index f68367b093d4b2995fdaa5d5926ffa08e8f20ac5..0000000000000000000000000000000000000000 --- 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/users-guide/acknowledgements.rst b/docs/users-guide/acknowledgements.rst index 7a054dfaf8a03d1166b3db99218ddf01376b83f5..eabbc2175c809a91826af6365eb7fa0a3b16ef63 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/dataset.rst b/docs/users-guide/dataset.rst index 207d0f7bd7f87e7a448028e970c8cb22b1d88fd6..d518af78df692f0ed30b7be4922b549b6e41bdbf 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 @@ -474,6 +524,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 @@ -500,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 @@ -513,21 +608,65 @@ 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. + +.. 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 +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 @@ -589,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 a08a18b77d226041d8c426655f7d69bf445b4036..f915df88682f59c6514eb415bc3fddda052e9ead 100644 --- a/docs/users-guide/examples/CMakeLists.txt +++ b/docs/users-guide/examples/CMakeLists.txt @@ -46,8 +46,10 @@ set(examples_device GuideExampleCellOperations.cxx GuideExampleDataSetCreation.cxx GuideExampleErrorHandling.cxx + 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 aa40a2cd9f97429c99864962a9927884f146c6c5..3b5216876fae7b7031f15d0daec2bb3381379af2 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/GuideExampleCellOperations.cxx b/docs/users-guide/examples/GuideExampleCellOperations.cxx index 17deb288706695d985678522d28208cf72da971c..78958de9f05f3c96a80c3b34e756440717ba94a6 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/examples/GuideExampleDataSetCreation.cxx b/docs/users-guide/examples/GuideExampleDataSetCreation.cxx index ad480e00b72885732929a13ab2d6fbbf15908568..9a5f6eb3f84423020a1f741f60bf2c78e8a5fac1 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 0000000000000000000000000000000000000000..b0c5ef59dee5d75816e492b9b324a8258545cf9e --- /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/examples/GuideExamplePointLocator.cxx b/docs/users-guide/examples/GuideExamplePointLocator.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f7e731740975098f23caf7e9d91e891b461b67ec --- /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/examples/GuideExampleUnknownArrayHandle.cxx b/docs/users-guide/examples/GuideExampleUnknownArrayHandle.cxx index 1d138d616e1a6fff4497bb8ecf7ee37119c42763..5540a79ae3f1c117a9371409d140bb60a4632c88 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/initialization.rst b/docs/users-guide/initialization.rst index 16fd8fc60c5d043fc943e73cf498c4c34ece8638..ae02d1f69b4f4d3724d0e1480e68464c20d991b1 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/docs/users-guide/locators.rst b/docs/users-guide/locators.rst new file mode 100644 index 0000000000000000000000000000000000000000..9e2a47425db74e2a88377ecd28bbbaca92526ed5 --- /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 7615895aff5f8cf7761141727e76387efc3f211a..af1c8f62914cd34fd86d1a70cfc85d188262241b 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/docs/users-guide/provided-filters.rst b/docs/users-guide/provided-filters.rst index 3f83bcc9428e3fc845ee043e15c6663f3e589295..95dad99a2e4dee213ff8cf101af60727ead8de8d 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`. @@ -422,8 +425,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 +868,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 +991,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/docs/users-guide/unknown-array-handle.rst b/docs/users-guide/unknown-array-handle.rst index d8d35c6de81371c0a4bceff3e2fe4fcf3910826e..ca6506ee9ee60109d94e95727a010722dd0d8ce3 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/docs/users-guide/working-with-cells.rst b/docs/users-guide/working-with-cells.rst index a36d27afd27adc40c6d70f440318224af3b7df97..0002a13e870dd670c416ecb3ae1dc0650e153c3b 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/examples/contour_tree_distributed/ContourTreeApp.cxx b/examples/contour_tree_distributed/ContourTreeApp.cxx index b3ff965680f32e8e96a3894bdf89a84d7b6d26b5..fabee4386a05ecf57875be02ec7432d1234e28a3 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 @@ -220,6 +221,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")) { @@ -266,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")) { @@ -285,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"; @@ -366,8 +412,19 @@ 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" + << 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,11 +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] << "," @@ -479,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 @@ -648,11 +714,16 @@ 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"); // Execute the contour tree analysis auto result = filter.Execute(useDataSet); + const vtkm::Id nPartitions = result.GetNumberOfPartitions(); currTime = totalTime.GetElapsedTime(); vtkm::Float64 computeContourTreeTime = currTime - prevTime; @@ -670,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; @@ -677,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) { @@ -691,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_") + @@ -727,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>() @@ -761,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) @@ -778,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; @@ -823,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)); @@ -840,7 +1059,6 @@ int main(int argc, char* argv[]) } } - currTime = totalTime.GetElapsedTime(); vtkm::Float64 saveOutputDataTime = currTime - prevTime; prevTime = currTime; @@ -892,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 acbf32b1c1d1806da962d4b6b29809a28b42ccf8..847e43add4b15a226b499995fa5b2f50e617f001 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/version.txt b/version.txt index ccbccc3dc62631f22ff358ac418e52401ec770b4..d62e3a5a6d34ee369a221deb0d732c816c02bd25 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.2.0 +2.3.0-rc1 diff --git a/vtkm/CellClassification.h b/vtkm/CellClassification.h index 6f001c9a7bd1baf8d72a68dccc5808011a0d543c..2072eea58b3489fb74f1c8ec3f230afb9f6fc4e9 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/Swap.h b/vtkm/Swap.h index 6a77e1ec298bdd81c9139b1af8f5654683c89993..e54adb90d423779b6d6c652a0c75ac4337a6f651 100644 --- a/vtkm/Swap.h +++ b/vtkm/Swap.h @@ -13,11 +13,7 @@ #include -#ifdef VTKM_CUDA -#include -#else #include -#endif // Below there is some code to deal with conflicts between VTK-m's Swap and the Swap that comes // with CUDA's CUB. We need to make sure that the Swap function gets included so that our @@ -31,21 +27,21 @@ 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) diff --git a/vtkm/Types.h b/vtkm/Types.h index 8ec4944c78d174e1fe45c4f425bb56af3fb07b6a..447f9e898783adf3e6ba9ed09ff0865eed036e21 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); } diff --git a/vtkm/cont/ArrayCopy.h b/vtkm/cont/ArrayCopy.h index 174efc9fcf50932f86e12285d9d19d426fb7078d..55504fb3a2ddf12649c7e02f8b9cc6231dec103e 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/ArraySetValues.cxx b/vtkm/cont/ArraySetValues.cxx new file mode 100644 index 0000000000000000000000000000000000000000..54c8ebd321b5633ede158915327255cdc7ed2412 --- /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 0000000000000000000000000000000000000000..fa7f88244445c0f1669da0829109a5ecd6a3fe50 --- /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 0c19f40a6c297cd2e2fbbba564c12e64a58de3f8..ca47f3cbb673e02b74e6e188fe27d2e0ce40a0b4 100644 --- a/vtkm/cont/CMakeLists.txt +++ b/vtkm/cont/CMakeLists.txt @@ -52,12 +52,14 @@ set(headers ArrayRangeCompute.h ArrayRangeComputeTemplate.h ArrayRangeComputeTemplateInstantiationsIncludes.h + ArraySetValues.h AssignerPartitionedDataSet.h AtomicArray.h BitField.h BoundsCompute.h BoundsGlobalCompute.h CastAndCall.h + CellLocatorBase.h CellLocatorBoundingIntervalHierarchy.h CellLocatorChooser.h CellLocatorGeneral.h @@ -109,6 +111,7 @@ set(headers MergePartitionedDataSet.h ParticleArrayCopy.h PartitionedDataSet.h + PointLocatorBase.h PointLocatorSparseGrid.h RuntimeDeviceInformation.h RuntimeDeviceTracker.h @@ -140,6 +143,7 @@ set(sources BitField.cxx BoundsCompute.cxx BoundsGlobalCompute.cxx + CellLocatorBase.cxx CellLocatorGeneral.cxx CellLocatorPartitioned.cxx CellLocatorRectilinearGrid.cxx @@ -171,6 +175,7 @@ set(sources Logging.cxx RuntimeDeviceTracker.cxx PartitionedDataSet.cxx + PointLocatorBase.cxx Storage.cxx Token.cxx TryExecute.cxx @@ -186,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/CellLocatorBase.cxx b/vtkm/cont/CellLocatorBase.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ed0c61c497ed1728622b7c7f8e5bd34e12ee88e3 --- /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 0000000000000000000000000000000000000000..fd531563fb66dc2450883b9a5f7315549eb78cca --- /dev/null +++ b/vtkm/cont/CellLocatorBase.h @@ -0,0 +1,93 @@ +//============================================================================ +// 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 +{ + +/// @brief Base class for all `CellLocator` classes. +/// +/// `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 +/// 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(); + } + /// @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. + /// + /// 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 c890110f5c7d4872c160b72cd692ac50545729e2..7a31e15ff65617447d4e133b93e7e7be06900d7f 100644 --- a/vtkm/cont/CellLocatorBoundingIntervalHierarchy.h +++ b/vtkm/cont/CellLocatorBoundingIntervalHierarchy.h @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include @@ -27,11 +27,19 @@ namespace vtkm namespace cont { -class VTKM_CONT_EXPORT CellLocatorBoundingIntervalHierarchy - : public vtkm::cont::internal::CellLocatorBase +/// @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 { - using Superclass = vtkm::cont::internal::CellLocatorBase; - public: using SupportedCellSets = VTKM_DEFAULT_CELL_SET_LIST; @@ -41,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) @@ -51,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; @@ -80,8 +101,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 85b4d63f6324693bf55386a28be656d5bb68beb7..6d3d7cf5fca0a5ee3c1c58ddb922331502035c28 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 2ced485e983777c51b76ddfcd1724015bcd6a99e..a9988ffa2e89e40ef88fb22031418402a60fdb7f 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,14 @@ namespace vtkm namespace cont { -class VTKM_CONT_EXPORT CellLocatorRectilinearGrid - : public vtkm::cont::internal::CellLocatorBase +/// @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 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 +51,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 8fceca777a0b016b8ea955c4fb1b03ba2773b884..8c0aea5402be0e8b5a41300a2b4c6fec31d62ffe 100644 --- a/vtkm/cont/CellLocatorTwoLevel.h +++ b/vtkm/cont/CellLocatorTwoLevel.h @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include @@ -37,14 +37,12 @@ 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::internal::CellLocatorBase +class VTKM_CONT_EXPORT CellLocatorTwoLevel : public vtkm::cont::CellLocatorBase { - using Superclass = vtkm::cont::internal::CellLocatorBase; - template using CellSetContToExec = typename CellSetCont::template ExecConnectivityTypeDensityL1 = 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, vtkm::cont::Token& token) const; private: - friend Superclass; - VTKM_CONT void Build(); + VTKM_CONT void Build() override; vtkm::FloatDefault DensityL1, DensityL2; diff --git a/vtkm/cont/CellLocatorUniformBins.h b/vtkm/cont/CellLocatorUniformBins.h index aa806cdfd45540063bef098bf0fe5970430fede1..3c23676e9c5a83cbda680dfcd4d7647bd93044ff 100644 --- a/vtkm/cont/CellLocatorUniformBins.h +++ b/vtkm/cont/CellLocatorUniformBins.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include @@ -34,12 +34,8 @@ 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::internal::CellLocatorBase +class VTKM_CONT_EXPORT CellLocatorUniformBins : public vtkm::cont::CellLocatorBase { - using Superclass = vtkm::cont::internal::CellLocatorBase; - template using CellSetContToExec = typename CellSetCont::template ExecConnectivityType; 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: @@ -66,8 +71,7 @@ public: vtkm::cont::Token& token) const; private: - friend Superclass; - VTKM_CONT void Build(); + VTKM_CONT void Build() override; vtkm::Vec3f InvSpacing; vtkm::Vec3f MaxPoint; diff --git a/vtkm/cont/CellLocatorUniformGrid.h b/vtkm/cont/CellLocatorUniformGrid.h index a1384a4abdf60186f2fcbca2b6161ebe89fefc22..00e6b34a4946060525f4aa569b8f6bab33403670 100644 --- a/vtkm/cont/CellLocatorUniformGrid.h +++ b/vtkm/cont/CellLocatorUniformGrid.h @@ -10,7 +10,7 @@ #ifndef vtkm_cont_CellLocatorUniformGrid_h #define vtkm_cont_CellLocatorUniformGrid_h -#include +#include #include @@ -19,11 +19,16 @@ namespace vtkm namespace cont { -class VTKM_CONT_EXPORT CellLocatorUniformGrid - : public vtkm::cont::internal::CellLocatorBase +/// @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 { - using Superclass = vtkm::cont::internal::CellLocatorBase; - public: using LastCell = vtkm::exec::CellLocatorUniformGrid::LastCell; @@ -39,8 +44,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/DataSet.cxx b/vtkm/cont/DataSet.cxx index bc4e84cc26ca71b0cc9b0e2b613eb85a40f2d41a..23a0b311d871734e3f29dab708eb8f65698d7807 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 c0c21b8ae65db5d3f6046668c2b9790243dd0179..49814c4968222c1fad9524d1d6656b717e72987b 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/Initialize.cxx b/vtkm/cont/Initialize.cxx index bee25c5ec960a119a36cc379b87a36561abdafc3..cdbb29d701a3a905cbe34c9e0ef16fd145ebed5f 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 e4d8510abc69b296e94607ea967ab418f4f2c6b5..51f5418258e535e057833fdb2509190e55a51a78 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 a251cde14c9f357eb18496981a53d789fd89e272..617e00d5155f84fb576137ee15af5c72e06c8d4e 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(); diff --git a/vtkm/cont/PartitionedDataSet.h b/vtkm/cont/PartitionedDataSet.h index 51421acfe536b754b1d435e4b1f100db43ec7a97..77cc765ea84df458d9c6ce076140fd130e4805e9 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 diff --git a/vtkm/cont/PointLocatorBase.cxx b/vtkm/cont/PointLocatorBase.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a4b37c769d014253774db7571aa44c0e4673e952 --- /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 3d53760112c40756131235c74b5f79163d35f029..43c017409b0efe8083081fde27fa940667f1fcc6 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 2e72387ee9d90d6cfc63b9201305ba6f430f7584..917d55582ec46b75f08abbb39c080f7202bd6170 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,14 +32,15 @@ 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; + /// @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) @@ -48,7 +49,7 @@ public: this->SetModified(); } } - + /// @copydoc SetRange const RangeType& GetRange() const { return this->Range; } void SetComputeRangeFromCoordinates() @@ -60,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) @@ -68,7 +77,7 @@ public: this->SetModified(); } } - + /// @copydoc SetNumberOfBins const vtkm::Id3& GetNumberOfBins() const { return this->Dims; } VTKM_CONT @@ -82,8 +91,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/UnknownArrayHandle.h b/vtkm/cont/UnknownArrayHandle.h index 714a8f255d47ea1fda6b5e4dd8bf64a1e21b5bb4..717142b64b62e413878897cbd6959290ad407269 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; @@ -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/internal/CMakeLists.txt b/vtkm/cont/internal/CMakeLists.txt index 74c4459cece15b09b70ecb734fa56d6928c2576d..a512057197fb59ff883cc9def7757b5cd07a5e9c 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 @@ -33,7 +32,6 @@ set(headers OptionParserArguments.h ParallelRadixSort.h ParallelRadixSortInterface.h - PointLocatorBase.h ReverseConnectivityBuilder.h RuntimeDeviceConfiguration.h RuntimeDeviceConfigurationOptions.h diff --git a/vtkm/cont/internal/CellLocatorBase.h b/vtkm/cont/internal/CellLocatorBase.h deleted file mode 100644 index 8b9a72545d706f1b970e11dbc8e0dd4ff0632666..0000000000000000000000000000000000000000 --- 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 diff --git a/vtkm/cont/kokkos/internal/CMakeLists.txt b/vtkm/cont/kokkos/internal/CMakeLists.txt index 7777005f3e3f18d95959c37553b6701a38620a08..210d9ed25c568c025dafe5728cb4408498cf35ba 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() diff --git a/vtkm/cont/testing/CMakeLists.txt b/vtkm/cont/testing/CMakeLists.txt index 850ecd49728a129afc2c75470611ac1cf6cc1eab..250a5f787a7ba92acc5a7a42ed06a98565d4c793 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 0000000000000000000000000000000000000000..a92ee64594e391d03d867d50fa46861c069bc735 --- /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); +} diff --git a/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx b/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx index fa66c6f2a8eb870d939aba6d8598484be1b793fd..f411af3ad803eb1ce82a56b0d2665a58f1985c1c 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(); diff --git a/vtkm/exec/BoundaryState.h b/vtkm/exec/BoundaryState.h index bde079c89f3f6668d19029ad14ff6108b796ad47..dd75f96c7586ed24c2dbace3183ee4a9efa5b614 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 diff --git a/vtkm/exec/CellFace.h b/vtkm/exec/CellFace.h index 1f14d77b4690c589e599e9b83aee14ac20e67d05..26dfa61c255c4e7109e08d95414bbfd088d1a283 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 diff --git a/vtkm/exec/CellInterpolate.h b/vtkm/exec/CellInterpolate.h index 3be4b503519a4d2433682c34084d7180b06241ed..c65a10fa0bc018b59154aeddc62240868da791a5 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/CellLocatorBoundingIntervalHierarchy.h b/vtkm/exec/CellLocatorBoundingIntervalHierarchy.h index d60a52c3d6859e7e7cd2d33a796bd2db956befcd..74622d5191b6497a2c85c66d057184ee722162f9 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 5129fc2eb1fc106447065896ab46409f4dbde729..9cae23ff64530a08682c0c99ae80451ed0205417 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 5d0caea8a61b9a7d653817850676dd7a0da73c56..2e22aabc675428a3d48500368a1bc8dd54fc3395 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 cd71f661c175e5bab49f16e46162c152472ddc73..b81879bded9019405aeb3e40c930eb0db7b2f6a1 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 63293ae272af59853d5c2594437fe3245487f997..2816d1743b0119c0afddda339fb8433fbb04a5c0 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 1ad0b3a7320d3f74b5b035964d11353fe92d64e3..e5372f16d7b51b79e6c514a57701a1b36e1e3a3c 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 diff --git a/vtkm/exec/testing/UnitTestCellDerivative.cxx b/vtkm/exec/testing/UnitTestCellDerivative.cxx index eed69eaa8676c316f8bf0a58985cde73f22f03ef..cc1b562d6f0f57cbeb3e10ad27528a8916d45398 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 5c951b161af1482665c95242186823e9c47d3ed4..0b617d07e122808649632feb17f623872d2c01ac 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 31cbe919035dc83c1a0d4c0b28b077aa090873fd..e3854733d31746fd0b9f9e91f71366d6c886ac7f 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); } diff --git a/vtkm/filter/contour/AbstractContour.h b/vtkm/filter/contour/AbstractContour.h index 5181f0ba596c97a9911fb3feccdd9d63c2a79833..a1a1bda9974ad460aa5e689c49491b3d2bd62de6 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 c7a4105b887cab7609433ed6e872eeb56e439a13..ac885f386ae0aa93b00f40f4cbba47f547bec453 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/ClipWithField.cxx b/vtkm/filter/contour/ClipWithField.cxx index 71d32bba5e5e25064dfde558e2fa38a53ff3ff26..b1b93843e063ccfd2176b8c3cb3b32631e262c6d 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 24f2f699134f49807d34d6da2d80bd0c3f24ce79..48df7aafa2573d5fe6f638df9901bc55277ead88 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/Contour.cxx b/vtkm/filter/contour/Contour.cxx index 466a3657d2007a4827abf27b5bc76fea925378a0..e59dd727026320f5221423587ec06ccdd4fa967b 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 0000000000000000000000000000000000000000..033cb82963d4c02e9afa112b414da2913eecdf14 --- /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 38a3ca5005346181fafbc6c6f1c79ff3a3dfbf53..bf6a4ffd468bc074b1b3397f158fcd2ae493082e 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 653a1eaadfa0d9e0b33070ea32606f9fb8500b35..218e4c8cd1d5232fca933474182b88e95280e403 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 607abbd6f4f145cd79d0dc285bf53d1d95c5c52b..e813d0229d95ab8e8a38ad4bcff879d4e7437dde 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/UnitTestClipWithFieldFilter.cxx b/vtkm/filter/contour/testing/UnitTestClipWithFieldFilter.cxx index 92a42c8e2cfdc4e901626c8932aa73149cbca1c5..eccce126a5d64dcb453c46e161db782d56ed34f0 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 055865ae043313d2a8c0f3c661a6d933defaa7bb..79954bc82a0d005bc2438b8dc79f9ea3ebc769b8 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"); } } diff --git a/vtkm/filter/contour/testing/UnitTestContourFilter.cxx b/vtkm/filter/contour/testing/UnitTestContourFilter.cxx index 61ccdc97b0af026130aedb56add33b6f30661b66..51e9b32bee0901b267a1f47c42c67ccb1471c206 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 be37c3057734ef73f7d75955b28a5e1b048c566e..16fd69ea00429a832b7e640a8579ed5c583ee73f 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::UnknownCellSet cellSet = contour.GetCellSet(); + + 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_TEST_ASSERT(cellSet.GetNumberOfPointsInCell(triId) == 3); + vtkm::Id3 pointIds; + cellSet.GetCellPointIds(triId, &pointIds[0]); + + 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/Clip.h b/vtkm/filter/contour/worklet/Clip.h index ce630a88be3fd15c570b3c7dccef98522c7afa8f..4622804897632d13668ad0d7db63275f8a5baf6a 100644 --- a/vtkm/filter/contour/worklet/Clip.h +++ b/vtkm/filter/contour/worklet/Clip.h @@ -10,28 +10,28 @@ #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 + +#include +#include -#include -#include #if defined(THRUST_MAJOR_VERSION) && THRUST_MAJOR_VERSION == 1 && THRUST_MINOR_VERSION == 8 && \ THRUST_SUBMINOR_VERSION < 3 @@ -48,725 +48,708 @@ 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: + 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) { - VTKM_EXEC_CONT - ClipStats operator()(const ClipStats& stat1, const ClipStats& stat2) const - { - 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; - } - }; -}; - -struct EdgeInterpolation -{ - vtkm::Id Vertex1 = -1; - vtkm::Id Vertex2 = -1; - vtkm::Float64 Weight = 0; + 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); + } - struct LessThanOp + static BatchesHandle CreateBatches(const vtkm::Id& numberOfElements) { - VTKM_EXEC - bool operator()(const EdgeInterpolation& v1, const EdgeInterpolation& v2) const + auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); + if (tracker.CanRunOn(vtkm::cont::DeviceAdapterTagCuda{}) || + tracker.CanRunOn(vtkm::cont::DeviceAdapterTagKokkos{})) { - return (v1.Vertex1 < v2.Vertex1) || (v1.Vertex1 == v2.Vertex1 && v1.Vertex2 < v2.Vertex2); + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Creating batches with batch size 6 for GPUs."); + return CreateBatches(numberOfElements, 6); } - }; - - struct EqualToOp - { - VTKM_EXEC - bool operator()(const EdgeInterpolation& v1, const EdgeInterpolation& v2) const + else { - return v1.Vertex1 == v2.Vertex1 && v1.Vertex2 == v2.Vertex2; + 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); } - }; -}; - -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) + struct PointBatchData { - this->NumberOfIndices.Set(cellIndex, numIndices); - } + vtkm::Id NumberOfKeptPoints = 0; - VTKM_EXEC - void SetIndexOffset(vtkm::Id cellIndex, vtkm::Id indexOffset) - { - this->Offsets.Set(cellIndex, indexOffset); - } + struct SumOp + { + VTKM_EXEC_CONT + PointBatchData operator()(const PointBatchData& stat1, const PointBatchData& stat2) const + { + PointBatchData sum = stat1; + sum.NumberOfKeptPoints += stat2.NumberOfKeptPoints; + return sum; + } + }; + }; - VTKM_EXEC - void SetConnectivity(vtkm::Id connectivityIndex, vtkm::Id pointIndex) + struct CellBatchData { - this->Connectivity.Set(connectivityIndex, pointIndex); - } + vtkm::Id NumberOfCells = 0; + vtkm::Id NumberOfCellIndices = 0; + vtkm::Id NumberOfEdges = 0; + vtkm::Id NumberOfCentroids = 0; + vtkm::Id NumberOfCentroidIndices = 0; -private: - UInt8Portal Shapes; - IdComponentPortal NumberOfIndices; - IdPortal Connectivity; - IdPortal Offsets; -}; - -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) - { - } + struct SumOp + { + VTKM_EXEC_CONT + CellBatchData operator()(const CellBatchData& stat1, const CellBatchData& stat2) const + { + CellBatchData sum = stat1; + sum.NumberOfCells += stat2.NumberOfCells; + sum.NumberOfCellIndices += stat2.NumberOfCellIndices; + sum.NumberOfEdges += stat2.NumberOfEdges; + sum.NumberOfCentroids += stat2.NumberOfCentroids; + sum.NumberOfCentroidIndices += stat2.NumberOfCentroidIndices; + return sum; + } + }; + }; - template - VTKM_CONT ExecutionConnectivityExplicit PrepareForExecution( - Device, - vtkm::cont::Token& token) const + struct EdgeInterpolation { - ExecutionConnectivityExplicit execConnectivity( - this->Shapes, this->NumberOfIndices, this->Connectivity, this->Offsets, this->Stats, token); - return execConnectivity; - } + vtkm::Id Vertex1 = -1; + vtkm::Id Vertex2 = -1; + vtkm::Float64 Weight = 0; -private: - vtkm::cont::ArrayHandle Shapes; - vtkm::cont::ArrayHandle NumberOfIndices; - vtkm::cont::ArrayHandle Connectivity; - vtkm::cont::ArrayHandle Offsets; - vtkm::worklet::ClipStats Stats; -}; - - -} // namespace internal - -class Clip -{ - // Add support for invert -public: - using TypeClipStats = vtkm::List; + struct LessThanOp + { + VTKM_EXEC + bool operator()(const EdgeInterpolation& v1, const EdgeInterpolation& v2) const + { + return (v1.Vertex1 < v2.Vertex1) || (v1.Vertex1 == v2.Vertex1 && v1.Vertex2 < v2.Vertex2); + } + }; - using TypeEdgeInterp = vtkm::List; + struct EqualToOp + { + VTKM_EXEC + bool operator()(const EdgeInterpolation& v1, const EdgeInterpolation& v2) const + { + return v1.Vertex1 == v2.Vertex1 && v1.Vertex2 == v2.Vertex2; + } + }; + }; - class ComputeStats : public vtkm::worklet::WorkletVisitCellsWithPoints + /** + * This worklet identifies the input points that are kept, i.e. are inside the implicit function. + */ + template + class MarkKeptPoints : public vtkm::worklet::WorkletMapField { public: + using ControlSignature = void(FieldIn pointBatch, + FieldOut pointBatchData, + FieldOut batchWithKeptPointsMask, + WholeArrayIn scalars, + WholeArrayOut keptPointsMask); + using ExecutionSignature = void(_1, _2, _3, _4, _5); + VTKM_CONT - ComputeStats(vtkm::Float64 value, bool invert) - : Value(value) - , Invert(invert) + explicit MarkKeptPoints(vtkm::Float64 isoValue) + : IsoValue(isoValue) { } - using ControlSignature = - void(CellSetIn, FieldInPoint, ExecObject clippingData, FieldOutCell, FieldOutCell); + template + VTKM_EXEC void operator()(const BatchType& pointBatch, + PointBatchData& pointBatchData, + vtkm::UInt8& batchWithKeptPointsMask, + const PointScalars& scalars, + KeptPointsMask& keptPointsMask) const + { + 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; + } - using ExecutionSignature = void(CellShape, PointCount, _2, _3, _4, _5); + private: + vtkm::Float64 IsoValue; + }; - using InputDomain = _1; + class ComputePointMaps : public vtkm::worklet::WorkletMapField + { + public: + using ControlSignature = void(FieldIn pointBatch, + FieldIn pointBatchDataOffsets, + WholeArrayIn keptPointsMask, + WholeArrayOut pointsInputToOutput, + WholeArrayOut pointsOutputToInput); + using ExecutionSignature = void(_1, _2, _3, _4, _5); + + using MaskType = vtkm::worklet::MaskSelect; + + template + VTKM_EXEC void operator()(const BatchType& pointBatch, + const PointBatchData& pointBatchDataOffsets, + const KeptPointsMask& keptPointsMask, + PointsInputToOutput& pointsInputToOutput, + PointsOutputToInput& pointsOutputToInput) const + { + vtkm::Id pointOffset = pointBatchDataOffsets.NumberOfKeptPoints; + for (vtkm::IdComponent id = 0, size = pointBatch.GetNumberOfComponents(); id < size; ++id) + { + const vtkm::Id& pointId = pointBatch[id]; + if (keptPointsMask.Get(pointId)) + { + pointsInputToOutput.Set(pointId, pointOffset); + pointsOutputToInput.Set(pointOffset, pointId); + pointOffset++; + } + } + } + }; - template - VTKM_EXEC void operator()(CellShapeTag shape, - vtkm::IdComponent pointCount, - const ScalarFieldVec& scalars, - const internal::ClipTables::DevicePortal& clippingData, - ClipStats& clipStat, - vtkm::Id& clipDataIndex) const + 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 { - (void)shape; // C4100 false positive workaround - vtkm::Id caseId = 0; - for (vtkm::IdComponent iter = pointCount - 1; iter >= 0; iter--) + namespace CTI = vtkm::worklet::internal::ClipTablesInformation; + for (vtkm::IdComponent id = 0, size = cellBatch.GetNumberOfComponents(); id < size; ++id) { - if (!this->Invert && static_cast(scalars[iter]) <= this->Value) + 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) { - caseId++; + static constexpr auto InvertUint8 = static_cast(Invert); + caseIndex |= (InvertUint8 != keptPointsMask.Get(points[ptId])) << ptId; } - else if (this->Invert && static_cast(scalars[iter]) >= this->Value) + + if (CT::IsCellDiscarded(pointCount, caseIndex)) // discarded cell { - caseId++; + // we do that to determine if a cell is discarded using only the caseIndex + caseIndices.Set(cellId, CT::GetDiscardedCellCase()); } - 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++) - { - vtkm::Id cellShape = clippingData.ValueAt(index++); - vtkm::Id numberOfIndices = clippingData.ValueAt(index++); - if (cellShape == 0) + else if (CT::IsCellKept(pointCount, caseIndex)) // kept cell { - --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; - } + // 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 + else // clipped cell { - // 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++) + caseIndices.Set(cellId, caseIndex); + + 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) { - //Find how many points need to found using edge interpolation. - vtkm::Id element = clippingData.ValueAt(index); - if (element == 255) + const vtkm::UInt8 cellShape = CT::ValueAt(index++); + const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); + + for (vtkm::IdComponent pointId = 0; pointId < numberOfCellIndices; pointId++, index++) { - clipStat.NumberOfInCellIndices++; + // 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); } - else if (element < 100) + if (cellShape != CTI::ST_PNT) // normal cell { - clipStat.NumberOfEdgeIndices++; + // 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; } } } } + batchWithClippedCellsMask = cellBatchData.NumberOfCells > 0 && + (cellBatchData.NumberOfEdges > 0 || cellBatchData.NumberOfCentroids > 0); + batchWithKeptOrClippedCellsMask = cellBatchData.NumberOfCells > 0; } - - private: - vtkm::Float64 Value; - bool Invert; }; - class GenerateCellSet : public vtkm::worklet::WorkletVisitCellsWithPoints + template + class ExtractEdges : public vtkm::worklet::WorkletMapField { public: VTKM_CONT - GenerateCellSet(vtkm::Float64 value) - : Value(value) + explicit ExtractEdges(vtkm::Float64 isoValue) + : IsoValue(isoValue) { } - 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); - - template - VTKM_EXEC void operator()(CellShapeTag shape, - vtkm::Id workIndex, - 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 + using ControlSignature = void(FieldIn cellBatch, + FieldIn cellBatchDataOffsets, + WholeCellSetIn<> cellSet, + WholeArrayIn scalars, + WholeArrayIn caseIndices, + WholeArrayOut edges); + using ExecutionSignature = void(_1, _2, _3, _4, _5, _6); + + using MaskType = vtkm::worklet::MaskSelect; + + using CT = internal::ClipTables; + + template + VTKM_EXEC void operator()(const BatchType& cellBatch, + const CellBatchData& cellBatchDataOffsets, + const CellSetType& cellSet, + const PointScalars& scalars, + const CaseIndices& caseIndices, + EdgesArray& edges) 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 edgeOffset = cellBatchDataOffsets.NumberOfEdges; + + for (vtkm::IdComponent id = 0, size = cellBatch.GetNumberOfComponents(); id < size; ++id) { - vtkm::UInt8 cellShape = clippingData.ValueAt(clipIndex++); - vtkm::IdComponent numberOfPoints = clippingData.ValueAt(clipIndex++); - if (cellShape == 0) - { - // 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++) - { - vtkm::IdComponent entry = - static_cast(clippingData.ValueAt(clipIndex)); - inCellInterpolationKeys.Set(inCellInterpPointIndex, workIndex); - if (entry >= 100) - { - inCellInterpolationInfo.Set(inCellInterpPointIndex, points[entry - 100]); - } - else - { - 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) - { - this->swap(ei.Vertex1, ei.Vertex2); - this->swap(edge[0], edge[1]); - } - ei.Weight = (static_cast(scalars[edge[0]]) - this->Value) / - static_cast(scalars[edge[1]] - scalars[edge[0]]); + const vtkm::Id& cellId = cellBatch[id]; + const vtkm::UInt8 caseIndex = caseIndices.Get(cellId); - inCellEdgeReverseConnectivity.Set(inCellEdgeInterpIndex, inCellInterpPointIndex); - inCellEdgeInterpolation.Set(inCellEdgeInterpIndex, ei); - inCellEdgeInterpIndex++; - } - } - } - else + if (caseIndex != CT::GetDiscardedCellCase() && + caseIndex != CT::GetKeptCellCase()) // clipped cell { - // 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++) + 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++) { - 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 + /*vtkm::UInt8 cellShape = */ CT::ValueAt(index++); + const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); + + 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::E00 && pointIndex <= CTI::E11) { - this->swap(ei.Vertex1, ei.Vertex2); - this->swap(edge[0], edge[1]); + 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->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::Float64 IsoValue; }; - class ScatterEdgeConnectivity : public vtkm::worklet::WorkletMapField + template + class GenerateCellSet : public vtkm::worklet::WorkletMapField { public: VTKM_CONT - ScatterEdgeConnectivity(vtkm::Id edgePointOffset) - : EdgePointOffset(edgePointOffset) + GenerateCellSet(vtkm::Id edgePointsOffset, vtkm::Id centroidPointsOffset) + : EdgePointsOffset(edgePointsOffset) + , CentroidPointsOffset(centroidPointsOffset) { } - using ControlSignature = void(FieldIn sourceValue, - FieldIn destinationIndices, - WholeArrayOut destinationData); - - using ExecutionSignature = void(_1, _2, _3); - - using InputDomain = _1; - - template - VTKM_EXEC void operator()(vtkm::Id sourceValue, - vtkm::Id destinationIndex, - ConnectivityDataType& destinationData) const + using ControlSignature = void(FieldIn cellBatch, + FieldIn cellBatchDataOffsets, + WholeCellSetIn<> cellSet, + WholeArrayIn caseIndices, + WholeArrayIn pointMapOutputToInput, + WholeArrayIn edgeIndexToUnique, + WholeArrayOut centroidOffsets, + WholeArrayOut centroidConnectivity, + WholeArrayOut cellMapOutputToInput, + WholeArrayOut shapes, + WholeArrayOut offsets, + WholeArrayOut connectivity); + 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()(const BatchType& cellBatch, + const CellBatchData& cellBatchDataOffsets, + const CellSetType& cellSet, + const CaseIndices& caseIndices, + const PointMapInputToOutput pointMapInputToOutput, + const EdgeIndexToUnique& edgeIndexToUnique, + CentroidOffsets& centroidOffsets, + CentroidConnectivity& centroidConnectivity, + CellMapOutputToInput& cellMapOutputToInput, + Shapes& shapes, + Offsets& offsets, + Connectivity& connectivity) 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); + namespace CTI = vtkm::worklet::internal::ClipTablesInformation; + 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; + + for (vtkm::IdComponent id = 0, size = cellBatch.GetNumberOfComponents(); id < size; ++id) + { + const vtkm::Id& cellId = cellBatch[id]; + const vtkm::UInt8 caseIndex = caseIndices.Get(cellId); + if (caseIndex != CT::GetDiscardedCellCase()) // not discarded cell + { + const auto shape = cellSet.GetCellShape(cellId); + const auto points = cellSet.GetIndices(cellId); + 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::Id centroidIndex = 0; - using ExecutionSignature = void(_1, _2); + vtkm::Id index = CT::GetCaseIndex(shape.Id, caseIndex); + const vtkm::UInt8 numberOfShapes = CT::ValueAt(index++); - using InputDomain = _1; + for (vtkm::IdComponent shapeId = 0; shapeId < numberOfShapes; shapeId++) + { + const vtkm::UInt8 cellShape = CT::ValueAt(index++); + const vtkm::UInt8 numberOfCellIndices = CT::ValueAt(index++); - template - VTKM_EXEC void operator()(vtkm::Id destinationIndex, - ConnectivityDataType& destinationData) const - { - auto sourceValue = destinationData.Get(destinationIndex); - destinationData.Set(destinationIndex, (sourceValue + InCellPointOffset)); + if (cellShape != CTI::ST_PNT) // normal 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++) + { + // 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 // cellShape == CTI::ST_PNT + { + // 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++)); + } + } + } + } + } + } + } } private: - vtkm::Id InCellPointOffset; + vtkm::Id EdgePointsOffset; + vtkm::Id CentroidPointsOffset; }; - Clip() - : ClipTablesInstance() - , EdgePointsInterpolation() - , InCellInterpolationKeys() - , InCellInterpolationInfo() - , CellMapOutputToInput() - , EdgePointsOffset() - , InCellPointsOffset() - { - } + Clip() = default; - 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 batches of points to process. + auto pointBatches = CreateBatches(numberOfInputPoints); - //Send this CellSet to process - ComputeStats statsWorklet(value, invert); - invoke(statsWorklet, cellSet, scalars, this->ClipTablesInstance, clipStats, clipTableIndices); + // Create an array to store the point batch statistics. + vtkm::cont::ArrayHandle pointBatchesData; + pointBatchesData.Allocate(pointBatches.GetNumberOfValues()); - ClipStats zero; - vtkm::cont::ArrayHandle cellSetStats; - ClipStats total = - vtkm::cont::Algorithm::ScanExclusive(clipStats, cellSetStats, ClipStats::SumOp(), zero); - clipStats.ReleaseResources(); + // Create a mask to only process the batches that have kept points. + vtkm::cont::ArrayHandle batchesWithKeptPointsMask; + batchesWithKeptPointsMask.Allocate(pointBatches.GetNumberOfValues()); - 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 mask of kept points. + vtkm::cont::ArrayHandle keptPointsMask; + keptPointsMask.Allocate(numberOfInputPoints); + + // Mark the points that are kept. + 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 batches of cells to process. + auto cellBatches = CreateBatches(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 batches that have clipped cells, to extract the edges. + vtkm::cont::ArrayHandle batchesWithClippedCellsMask; + batchesWithClippedCellsMask.Allocate(cellBatches.GetNumberOfValues()); + + // Create a mask to only process the batches that have kept or clipped cells. + vtkm::cont::ArrayHandle batchesWithKeptOrClippedCellsMask; + batchesWithKeptOrClippedCellsMask.Allocate(cellBatches.GetNumberOfValues()); + + // 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, + caseIndices); + keptPointsMask.ReleaseResources(); // Release keptPointsMask since it's no longer needed. - //Begin Process of Constructing the new CellSet. - vtkm::cont::ArrayHandle pointsOnlyConnectivityIndices; - pointsOnlyConnectivityIndices.Allocate(total.NumberOfIndices - total.NumberOfEdgeIndices - - total.NumberOfInCellIndices); + // Compute the total of cellBatchesData, and convert cellBatchesData to offsets in-place. + const CellBatchData cellBatchTotal = vtkm::cont::Algorithm::ScanExclusive( + cellBatchesData, cellBatchesData, CellBatchData::SumOp(), CellBatchData{}); - vtkm::cont::ArrayHandle edgePointReverseConnectivity; - edgePointReverseConnectivity.Allocate(total.NumberOfEdgeIndices); + // Create an array to store the edge interpolations. vtkm::cont::ArrayHandle edgeInterpolation; - edgeInterpolation.Allocate(total.NumberOfEdgeIndices); - - 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); - - //Send this CellSet to process - GenerateCellSet cellSetWorklet(value); - invoke(cellSetWorklet, + edgeInterpolation.Allocate(cellBatchTotal.NumberOfEdges); + + // Extract the edges. + invoke(ExtractEdges(value), + vtkm::worklet::MaskSelect(batchesWithClippedCellsMask), + cellBatches, + cellBatchesData, // cellBatchesDataOffsets 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()); + caseIndices, + edgeInterpolation); + // Release batchesWithClippedCellsMask since it's no longer needed. + batchesWithClippedCellsMask.ReleaseResources(); + + // 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 = cellBatchTotal.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(cellBatchTotal.NumberOfCentroidIndices); + this->CentroidPointsInterpolation = + vtkm::cont::make_ArrayHandleGroupVecVariable(centroidConnectivity, centroidOffsets); + + // Allocate the output cell set. + vtkm::cont::ArrayHandle shapes; + shapes.Allocate(cellBatchTotal.NumberOfCells); + vtkm::cont::ArrayHandle offsets; + offsets.Allocate(cellBatchTotal.NumberOfCells + 1); + vtkm::cont::ArrayHandle connectivity; + connectivity.Allocate(cellBatchTotal.NumberOfCellIndices); - this->EdgePointsOffset = this->PointMapOutputToInput.GetNumberOfValues(); - this->InCellPointsOffset = - this->EdgePointsOffset + this->EdgePointsInterpolation.GetNumberOfValues(); + // Allocate Cell Map output to Input. + this->CellMapOutputToInput.Allocate(cellBatchTotal.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(batchesWithKeptOrClippedCellsMask), + cellBatches, + cellBatchesData, // cellBatchesDataOffsets + cellSet, + caseIndices, + 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( + cellBatchTotal.NumberOfCells, cellBatchTotal.NumberOfCellIndices, offsets); + vtkm::cont::ArraySetValue( + numberOfCentroids, cellBatchTotal.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 +758,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 +777,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 +787,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 +825,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 +836,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 +879,32 @@ public: template void ProcessPointField(const InputType& input, OutputType& output) { - if (!this->InterpolationKeysBuilt) - { - this->InterpolationKeys.BuildArrays(this->InCellInterpolationKeys, KeysSortType::Unstable); - } - - vtkm::Id numberOfVertexPoints = this->PointMapOutputToInput.GetNumberOfValues(); - vtkm::Id numberOfEdgePoints = this->EdgePointsInterpolation.GetNumberOfValues(); - vtkm::Id numberOfInCellPoints = this->InterpolationKeys.GetUniqueKeys().GetNumberOfValues(); + const vtkm::Id numberOfKeptPoints = this->PointMapOutputToInput.GetNumberOfValues(); + const vtkm::Id numberOfEdgePoints = this->EdgePointsInterpolation.GetNumberOfValues(); + const vtkm::Id numberOfCentroidPoints = this->CentroidPointsInterpolation.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 +913,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 @@ -958,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 { }; } diff --git a/vtkm/filter/contour/worklet/ContourMarchingCells.h b/vtkm/filter/contour/worklet/ContourMarchingCells.h index 9ecdde569215e396244b38172c531e5eae0d0937..079c9e30df98f4437c2197ec438155edd714f562 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/MIR.h b/vtkm/filter/contour/worklet/MIR.h index 4ceea7af09491f00978e3e5e6fac8484cacff742..376039ec2c7ac3040f40937087434f934a858ab1 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 5e3dd18c56ff14cedba69e58ac36ad8ce395d61a..1c46f857d1a4f47cb35efc29b06e548963b291cc 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 -5, 3, 3, 2, 103, -5, 3, 1, 0, 101, -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, 128, 135, //cases 0 - 7 -141, 153, 160, 171, 177, 184, 190, 196, //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 -//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 -//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 -//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 -}; +// 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; } }; } } diff --git a/vtkm/filter/contour/worklet/contour/CMakeLists.txt b/vtkm/filter/contour/worklet/contour/CMakeLists.txt index afae869d041b6cf26fbba40c13ef5aa2cc22ba5e..b0faf708af5f14d5c1386808c2b43c89ec061ea9 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/FlyingEdgesHelpers.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesHelpers.h index 7a03a0e46ac5f34dddc6fb7bd67d5438ec9cc342..f6491d09b8a857709ecb203364e6c2c86ff3fcd9 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/FlyingEdgesPass1.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass1.h index cfea94c1d280443c5b77ee14d81857e83c9014cd..498855d7c1ac2c807308b701a5986ca7d5b22d46 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 088f12dd9ac2471a8fa6402da4ce4e02b7b85f0f..b2e1efc73cf921e125ad93d2b7cbbf45c46d1df8 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)...); } }; } diff --git a/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4Common.h b/vtkm/filter/contour/worklet/contour/FlyingEdgesPass4Common.h index 96aaf95ac80fbe516de0c1aaf4dc56e78abc3a9c..79bf0978da7f022d61fffe4978e8e3800c036f18 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 f6cddc9b8c035036d37ee79abdb3d9ad001af203..a1dbf72b64deace226428feb296f165137aab518 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 678052299f6e85f9418952174bba206287668531..1b058cd86f76c99b53775d908b4a44a6d3fa3e32 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 08a16f3b441b9598d196326118e6090fb406cfac..46e0a57cae06f56a7f0ece51b692e2c9277c0400 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 diff --git a/vtkm/filter/contour/worklet/contour/MarchingCellTables.h b/vtkm/filter/contour/worklet/contour/MarchingCellTables.h index f6929c162f1a61545a6da0a9e0b45a6a22cb4fab..446b81d3cf7683e2549eae2a8d2cc30003f591be 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,750 @@ 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, -}; - -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, +// 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); - // 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, +template +class MarchingCellTables; - // 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 +template <> +class MarchingCellTables<3> +{ + 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 - // 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 + return offsets[inCellType]; + } -#undef X -}; + 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_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, -}; + vtkm::IdComponent offset = GetNumOutCellsOffset(inCellType); + return tableOffset[offset + caseNumber]; + } -// clang-format on -class CellClassifyTable : public vtkm::cont::ExecutionObjectBase -{ public: - template - class ExecObject + VTKM_EXEC static inline vtkm::UInt8 GetNumOutCells(vtkm::UInt8 inCellType, vtkm::UInt8 caseNumber) { - public: - VTKM_EXEC - vtkm::IdComponent GetNumVerticesPerCell(vtkm::Id shape) const + // 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::IdComponent offset = GetNumOutCellsOffset(inCellType); + if (offset == GetNumOutCellsOffset(inCellType + 1)) { - return this->NumVerticesPerCellPortal.Get(shape); + // This part of the table is empty. + return 0; } - - VTKM_EXEC - vtkm::IdComponent GetNumTriangles(vtkm::Id shape, vtkm::IdComponent caseNumber) const + else { - vtkm::IdComponent offset = this->NumTrianglesTableOffsetPortal.Get(shape); - return this->NumTrianglesTablePortal.Get(offset + caseNumber); + return numOutCells[offset + caseNumber]; } - - private: - typename vtkm::cont::ArrayHandle::ReadPortalType NumVerticesPerCellPortal; - typename vtkm::cont::ArrayHandle::ReadPortalType NumTrianglesTablePortal; - typename vtkm::cont::ArrayHandle::ReadPortalType - NumTrianglesTableOffsetPortal; - - friend class CellClassifyTable; - }; - - 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) + VTKM_EXEC static inline const vtkm::UInt8* GetCellEdges(vtkm::UInt8 inCellType, + vtkm::UInt8 caseNumber, + vtkm::UInt8 outCellNumber) { - 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; - } + // 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 NumVerticesPerCellArray; - vtkm::cont::ArrayHandle NumTrianglesTableOffsetArray; - vtkm::cont::ArrayHandle NumTrianglesTableArray; + vtkm::IdComponent offset = GetOutCellTableOffset(inCellType, caseNumber); + return triTable[offset + outCellNumber]; + } }; -class TriangleGenerationTable : public vtkm::cont::ExecutionObjectBase +template <> +class MarchingCellTables<2> { -public: - template - class ExecObject + VTKM_EXEC static inline vtkm::IdComponent GetNumOutCellsOffset(vtkm::UInt8 inCellType) { - 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, - }; + // 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 triOffset = TriangleTableOffsetPortal.Get(shape) + - NumEntriesPerCase[shape] * caseNumber + triangleNumber * 3; - vtkm::IdComponent edgeIndex = TriangleTablePortal.Get(triOffset + vertexNumber); - vtkm::IdComponent edgeOffset = EdgeTableOffsetPortal.Get(shape); + return offsets[inCellType]; + } - return { EdgeTablePortal.Get(edgeOffset + edgeIndex * 2 + 0), - EdgeTablePortal.Get(edgeOffset + edgeIndex * 2 + 1) }; - } + 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 - 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::IdComponent offset = GetNumOutCellsOffset(inCellType); + return tableOffset[offset + caseNumber]; + } - template - ExecObject PrepareForExecution(DeviceAdapter, vtkm::cont::Token& token) +public: + VTKM_EXEC static inline vtkm::UInt8 GetNumOutCells(vtkm::UInt8 inCellType, vtkm::UInt8 caseNumber) { - 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; + // 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]; + } } - 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 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 -private: - vtkm::cont::ArrayHandle EdgeTableArray; - vtkm::cont::ArrayHandle EdgeTableOffsetArray; - vtkm::cont::ArrayHandle TriangleTableArray; - vtkm::cont::ArrayHandle TriangleTableOffsetArray; + vtkm::IdComponent offset = GetOutCellTableOffset(inCellType, caseNumber); + return lineTable[offset + outCellNumber]; + } }; + +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); } + } } -#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 0000000000000000000000000000000000000000..eb1745557f03e7593409328b2053d83bc4601827 --- /dev/null +++ b/vtkm/filter/contour/worklet/contour/MarchingCellTables.h.in @@ -0,0 +1,793 @@ +//============================================================================= +// +// 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_2D = { + "CELL_SHAPE_EMPTY" : [], + "CELL_SHAPE_VERTEX" : [], + "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" : [], + "CELL_SHAPE_TRIANGLE" : [], + "CELL_SHAPE_POLYGON" : [], + "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_3D)\ +VTKM_STATIC_ASSERT(vtkm::$(shape_names[shape_index]) == $(shape_index)); +$endif\ +$endfor\ + +template +class MarchingCellTables; + +template <> +class MarchingCellTables<3> +{ + 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_3D: + offset = offset + len(shape_tables_3D[shape_name]) +else: + shape_tables_3D[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 = " " + 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 = " " + start_case = 0 + display_case = 0 + 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 " + 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 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> +{ + 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 = " " + start_case = 0 + display_case = 0 + for line_list in shape_tables_2D[shape_name]: + result = result + str(offset) + ", " + offset += len(line_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 = " " + 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 line in line_list: + result += "{ " + str(line[0]) + ", " + str(line[1]) + " }, " + return result +)\ +$extend(lines)\ +$for(shape_name in shape_names)\ + // $(shape_name) +$py(casenum = 0)\ +$for(line_list in shape_tables_2D[shape_name])\ + $lines(line_list)// case $(casenum) +$py(casenum += 1)\ +$endfor\ +$endfor\ + { 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) +{ + 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 + +#endif //vtk_m_MarchingCellTables_h diff --git a/vtkm/filter/contour/worklet/contour/MarchingCells.h b/vtkm/filter/contour/worklet/contour/MarchingCells.h index 770307b7cde11a91d212fd42a06cac3e75510123..fbf4839c3c2d8ce3bbaa2d7d5c3800661c962727 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 @@ -73,41 +74,139 @@ 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, - ExecObject classifyTable); - using ExecutionSignature = void(CellShape, _1, _2, _4, _5); + FieldOutCell outNumTriangles); + using ExecutionSignature = void(CellShape, _1, _2, _4); using InputDomain = _3; - template + template VTKM_EXEC void operator()(CellShapeType shape, 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) - { - caseNumber |= (fieldIn[j] > isovalues.Get(i)) << j; - } - - sum += classifyTable.GetNumTriangles(shape.Id, caseNumber); + sum += NumOutCellsSpecialCases( + std::integral_constant{}, shape.Id, isovalues.Get(i), fieldIn); } numTriangles = sum; } @@ -132,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; @@ -154,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) @@ -170,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, @@ -180,6 +285,7 @@ public: } private: + vtkm::UInt8 NumPointsPerOutCell; vtkm::Id Size; vtkm::cont::ArrayHandle InterpWeights; vtkm::cont::ArrayHandle InterpIds; @@ -187,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: @@ -205,86 +454,116 @@ 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, 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()); - vtkm::IdComponent numVerticesPerCell = classifyTable.GetNumVerticesPerCell(shape.Id); - - for (i = 0; i < numIsoValues; ++i) + // Interpolate for vertex positions and associated scalar values + 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++) { - 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 < numVerticesPerCell; ++j) + vtkm::IdComponent2 edgeVertices; + vtkm::Vec fieldValues; + for (vtkm::IdComponent edgePointId = 0; edgePointId < 2; ++edgePointId) { - caseNumber |= (fieldIn[j] > ivalue) << j; + vtkm::ErrorCode errorCode = this->CrossingLocalIndex( + numVertices, edgePointId, edges[triVertex], shape, edgeVertices[edgePointId]); + if (errorCode != vtkm::ErrorCode::Success) + { + this->RaiseError(vtkm::ErrorString(errorCode)); + return; + } + fieldValues[edgePointId] = fieldIn[edgeVertices[edgePointId]]; } - sum += classifyTable.GetNumTriangles(shape.Id, caseNumber); - if (sum > visitIndex) - { - break; - } - } - - visitIndex = sum - visitIndex - 1; - - // Interpolate for vertex positions and associated scalar values - 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]; - // Store the input cell id so that we can properly generate the normals // 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.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(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 @@ -610,9 +890,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; @@ -623,35 +900,34 @@ vtkm::cont::CellSetSingleType<> execute( // for each cell, and the number of vertices to be generated vtkm::cont::ArrayHandle numOutputTrisPerCell; { - marching_cells::ClassifyCell classifyCell; - invoker(classifyCell, isoValuesHandle, inputField, cells, numOutputTrisPerCell, classTable); + marching_cells::ClassifyCell classifyCell; + invoker(classifyCell, isoValuesHandle, inputField, cells, numOutputTrisPerCell); } //Pass 2 Generate the edges 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 isoValuesHandle, inputField, - metaData, - classTable, - triTable); + metaData); } if (isovalues.size() <= 1 || !sharedState.MergeDuplicatePoints) @@ -704,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) diff --git a/vtkm/filter/contour/worklet/mir/MIRTables.h b/vtkm/filter/contour/worklet/mir/MIRTables.h index 3dff3329eb4d129a6dd7929be63eb8bf061fb0c3..a6f4d4f1fdb9f9b35e7c6157619caf59cb6e8a77 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; diff --git a/vtkm/filter/entity_extraction/worklet/ExternalFaces.h b/vtkm/filter/entity_extraction/worklet/ExternalFaces.h index f89b2e2ea22b4b4a406bc04ac43808850612f90c..8ccb9a9a8596441f4bfddf3c27dade7c1de19c6d 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. diff --git a/vtkm/filter/flow/CMakeLists.txt b/vtkm/filter/flow/CMakeLists.txt index ffbc522adf450454c68c1cb9c46963b5fb6e6324..5c274d3899808e22de21fbb0756271e8cac76361 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 8083905e3635f7f34510ba84ad4803c9e6c96a1f..5cb8660234d65ff08a08cc5593ef3d46aa55c04b 100644 --- a/vtkm/filter/flow/FilterParticleAdvection.h +++ b/vtkm/filter/flow/FilterParticleAdvection.h @@ -11,10 +11,12 @@ #ifndef vtk_m_filter_flow_FilterParticleAdvection_h #define vtk_m_filter_flow_FilterParticleAdvection_h +#include #include #include #include #include +#include #include namespace vtkm @@ -89,28 +91,34 @@ public: VTKM_CONT void SetUseThreadedAlgorithm(bool val) { this->UseThreadedAlgorithm = val; } + VTKM_DEPRECATED(2.2, "All communication is asynchronous now.") VTKM_CONT - void SetUseAsynchronousCommunication() { this->UseAsynchronousCommunication = true; } + void SetUseAsynchronousCommunication() {} + + VTKM_DEPRECATED(2.2, "All communication is asynchronous now.") VTKM_CONT - bool GetUseAsynchronousCommunication() { return this->UseAsynchronousCommunication; } + bool GetUseAsynchronousCommunication() { return true; } + VTKM_DEPRECATED(2.2, "All communication is asynchronous now.") VTKM_CONT - void SetUseSynchronousCommunication() { this->UseAsynchronousCommunication = false; } + void SetUseSynchronousCommunication() {} + + VTKM_DEPRECATED(2.2, "All communication is asynchronous now.") VTKM_CONT - bool GetUseSynchronousCommunication() { return !this->GetUseAsynchronousCommunication(); } + bool GetUseSynchronousCommunication() { return false; } + protected: VTKM_CONT virtual void ValidateOptions() const; 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 = 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 c70c2b3d6e56338a4ed3cb77d2a7b2e18d25634c..751e9f3918704b9e3cd4d094fa2bc0202e771ab2 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); vtkm::cont::ArrayHandle particles; this->Seeds.AsArrayHandle(particles); diff --git a/vtkm/filter/flow/FilterParticleAdvectionUnsteadyState.cxx b/vtkm/filter/flow/FilterParticleAdvectionUnsteadyState.cxx index 7d72cfcc867ce5e34ff16ccf713eccd007d30aa1..c85045fe9b81830457c8d57f56a9cf4521efe967 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); 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 112d98501fad01bd20522ee9f7755d3daa243ad4..e12348a8a14d3cb73c7fa7e05dce899c55887d21 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithm.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithm.h @@ -11,10 +11,16 @@ #ifndef vtk_m_filter_flow_internal_AdvectAlgorithm_h #define vtk_m_filter_flow_internal_AdvectAlgorithm_h + #include #include #include -#include +#ifdef VTKM_ENABLE_MPI +#include +#include +#include +#include +#endif namespace vtkm { @@ -31,14 +37,15 @@ 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) +#ifdef VTKM_ENABLE_MPI + , Exchanger(this->Comm) + , Terminator(this->Comm) +#endif , NumRanks(this->Comm.size()) , Rank(this->Comm.rank()) - , UseAsynchronousCommunication(useAsyncComm) { } @@ -46,6 +53,7 @@ public: { this->SetStepSize(stepSize); this->SetSeeds(seeds); + this->Go(); } @@ -91,33 +99,43 @@ public: this->SetSeedArray(particles, blockIDs); } - //Advect all the particles. - virtual void Go() + virtual bool HaveWork() { - vtkm::filter::flow::internal::ParticleMessenger messenger( - this->Comm, this->UseAsynchronousCommunication, this->BoundsMap, 1, 128); + const bool haveParticles = !this->Active.empty() || !this->Inactive.empty(); +#ifndef VTKM_ENABLE_MPI + return haveParticles; +#else + return haveParticles || this->Exchanger.HaveWork(); +#endif + } - this->ComputeTotalNumParticles(); + virtual bool GetDone() + { +#ifndef VTKM_ENABLE_MPI + return !this->HaveWork(); +#else + return this->Terminator.Done(); +#endif + } - while (this->TotalNumTerminatedParticles < this->TotalNumParticles) + //Advect all the particles. + virtual void Go() + { + while (!this->GetDone()) { 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->ExchangeParticles(); } } @@ -128,19 +146,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,39 +218,50 @@ public: return !particles.empty(); } - void Communicate(vtkm::filter::flow::internal::ParticleMessenger& messenger, - vtkm::Id numLocalTerminations, - vtkm::Id& numTermMessages) + 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; - numTermMessages = 0; - bool block = false; -#ifdef VTKM_ENABLE_MPI - block = this->GetBlockAndWait(messenger.UsingSyncCommunication(), numLocalTerminations); -#endif + std::vector incoming; + std::unordered_map> incomingBlockIDs; - messenger.Exchange(outgoing, - outgoingRanks, - this->ParticleBlockIDsMap, - numLocalTerminations, - incoming, - incomingBlockIDs, - numTermMessages, - block); + 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) { @@ -266,6 +282,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) @@ -281,7 +298,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(); @@ -305,23 +322,27 @@ public: if (!particlesStaying.empty()) this->UpdateActive(particlesStaying, particlesStayingBlockIDs); } +#endif virtual void UpdateActive(const std::vector& particles, const std::unordered_map>& idsMap) { 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); - } + 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; + for (const auto& it : idsMap) + this->ParticleBlockIDsMap[it.first] = it.second; + } } virtual void UpdateInactive(const std::vector& particles, @@ -350,37 +371,16 @@ public: return numTerm; } - - virtual bool GetBlockAndWait(const bool& syncComm, const vtkm::Id& numLocalTerm) - { - 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 (haveNoWork && - (numLocalTerm + this->TotalNumTerminatedParticles < this->TotalNumParticles)) - return true; - - return false; - } - } - //Member data // {blockId, std::vector of particles} std::unordered_map> Active; 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; @@ -388,9 +388,6 @@ public: std::unordered_map> ParticleBlockIDsMap; vtkm::Id Rank; vtkm::FloatDefault StepSize; - vtkm::Id TotalNumParticles = 0; - vtkm::Id TotalNumTerminatedParticles = 0; - bool UseAsynchronousCommunication = true; }; } diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h new file mode 100644 index 0000000000000000000000000000000000000000..3a0895d9b7276889c4bc4f67fa66562c39ec489a --- /dev/null +++ b/vtkm/filter/flow/internal/AdvectAlgorithmTerminator.h @@ -0,0 +1,165 @@ +//============================================================================ +// 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 +{ + +// 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 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: 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: +#ifdef VTKM_ENABLE_MPI + AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& comm) + : AllDirty(1) + , Dirty(1) + , LocalWork(0) + , MPIComm(vtkmdiy::mpi::mpi_cast(comm.handle())) + , Rank(comm.rank()) + , State(STATE_0) +#else + AdvectAlgorithmTerminator(vtkmdiy::mpi::communicator& vtkmNotUsed(comm)) + : HaveWork(false) +#endif + { + this->FirstCall = true; + } + + bool Done() const + { +#ifdef VTKM_ENABLE_MPI + return this->State == AdvectAlgorithmTerminatorState::DONE; +#else + return !this->HaveWork; +#endif + } + + void Control(bool haveLocalWork) + { +#ifdef VTKM_ENABLE_MPI + if (this->FirstCall) + { + haveLocalWork = true; + this->FirstCall = false; + } + + if (haveLocalWork) + this->Dirty = 1; + + 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; + } + else if (this->State == STATE_1) + { + MPI_Status status; + int flag; + MPI_Test(&this->StateReq, &flag, &status); + if (flag == 1) + { + 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) + { + MPI_Status status; + int flag; + MPI_Test(&this->StateReq, &flag, &status); + if (flag == 1) + { + //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; + } + } +#else + this->HaveWork = haveLocalWork; +#endif + } + +private: + bool FirstCall; + +#ifdef VTKM_ENABLE_MPI + enum AdvectAlgorithmTerminatorState + { + STATE_0, + STATE_1, + STATE_2, + DONE + }; + + int AllDirty; + //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; +#else + bool HaveWork; +#endif +}; + +} +} +} +} //vtkm::filter::flow::internal + + +#endif //vtk_m_filter_flow_internal_AdvectAlgorithmTerminator_h diff --git a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h index db4e651fbc99ad31841467a181d5db22b42210c6..a0e823dae6b363f754fd1f23293adcea73eb0122 100644 --- a/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h +++ b/vtkm/filter/flow/internal/AdvectAlgorithmThreaded.h @@ -15,7 +15,6 @@ #include #include #include -#include #include @@ -35,11 +34,9 @@ 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) - , 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. @@ -50,8 +47,6 @@ public: void Go() override { - this->ComputeTotalNumParticles(); - std::vector workerThreads; workerThreads.emplace_back(std::thread(AdvectAlgorithmThreaded::Worker, this)); this->Manage(); @@ -63,6 +58,28 @@ public: } protected: + bool HaveWork() override + { + std::lock_guard lock(this->Mutex); + 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); @@ -85,12 +102,6 @@ protected: } } - bool CheckDone() - { - std::lock_guard lock(this->Mutex); - return this->Done; - } - void SetDone() { std::lock_guard lock(this->Mutex); @@ -115,7 +126,7 @@ protected: void Work() { - while (!this->CheckDone()) + while (!this->WorkerGetDone()) { std::vector v; vtkm::Id blockId = -1; @@ -133,51 +144,21 @@ protected: void Manage() { - if (!this->UseAsynchronousCommunication) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Synchronous communication not supported for AdvectAlgorithmThreaded." - "Forcing asynchronous communication."); - } - - bool useAsync = true; - vtkm::filter::flow::internal::ParticleMessenger messenger( - this->Comm, useAsync, this->BoundsMap, 1, 128); - - while (this->TotalNumTerminatedParticles < this->TotalNumParticles) + while (!this->GetDone()) { 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->TotalNumTerminatedParticles += (numTerm + numTermMessages); - if (this->TotalNumTerminatedParticles > this->TotalNumParticles) - throw vtkm::cont::ErrorFilterExecution("Particle count error"); + this->ExchangeParticles(); } - - //Let the workers know that we are done. this->SetDone(); } - bool GetBlockAndWait(const bool& syncComm, const vtkm::Id& numLocalTerm) override - { - std::lock_guard lock(this->Mutex); - if (this->Done) - return true; - - return (this->AdvectAlgorithm::GetBlockAndWait(syncComm, numLocalTerm) && - !this->WorkerActivate && this->WorkerResults.empty()); - } - void GetWorkerResults( std::unordered_map>>& results) { @@ -191,9 +172,15 @@ protected: } } +private: + bool CheckHaveWork() + { + return this->AdvectAlgorithm::HaveWork() || this->WorkerActivate; + } + std::atomic Done; std::mutex Mutex; - bool WorkerActivate; + bool WorkerActivate = false; std::condition_variable WorkerActivateCondition; std::unordered_map>> WorkerResults; }; diff --git a/vtkm/filter/flow/internal/CMakeLists.txt b/vtkm/filter/flow/internal/CMakeLists.txt index b59dedcebdeb97907fa5e651559a490b25dc7b29..31f79e451049c89ae2ca94d8dec13a5ed4d75e19 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 @@ -17,9 +18,8 @@ set(headers DataSetIntegratorUnsteadyState.h GridMetaData.h LagrangianStructureHelpers.h - Messenger.h ParticleAdvector.h - ParticleMessenger.h + ParticleExchanger.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 52c582f2017a458b881e2a376a51e3f6b4acb364..0000000000000000000000000000000000000000 --- 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 42d554ae5174bf3afcb4fd489573d0d5dde2080f..0000000000000000000000000000000000000000 --- 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 4409411758aac91b9c879caf5110ecd0a71b3cc2..a864eb00df84b211cf22b644d53c1a0f1e282cb5 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/ParticleExchanger.h b/vtkm/filter/flow/internal/ParticleExchanger.h new file mode 100644 index 0000000000000000000000000000000000000000..b8a0964c9ef36be1b569888efee36d8d83eddca7 --- /dev/null +++ b/vtkm/filter/flow/internal/ParticleExchanger.h @@ -0,0 +1,257 @@ +//============================================================================ +// 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(false); } +#endif + + bool HaveWork() const { return !this->SendBuffers.empty(); } + + void Exchange(const std::vector& outData, + const std::vector& outRanks, + const std::unordered_map>& outBlockIDsMap, + std::vector& inData, + std::unordered_map>& inDataBlockIDsMap) + { + VTKM_ASSERT(outData.size() == outRanks.size()); + + if (this->NumRanks == 1) + this->SerialExchange(outData, outBlockIDsMap, inData, inDataBlockIDsMap); +#ifdef VTKM_ENABLE_MPI + else + { + this->CleanupSendBuffers(true); + this->SendParticles(outData, outRanks, outBlockIDsMap); + this->RecvParticles(inData, inDataBlockIDsMap); + } +#endif + } + +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 + using ParticleCommType = std::pair>; + + void CleanupSendBuffers(bool checkRequests) + { + if (!checkRequests) + { + for (auto& entry : this->SendBuffers) + delete entry.second; + this->SendBuffers.clear(); + return; + } + + 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"); + + 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<Rank<<" SendBuffer: Delete"<& 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))); + sendData[outRanks[i]].emplace_back(std::make_pair(outData[i], 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); + bb->reset(); + + MPI_Request 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; + } + + void RecvParticles(std::vector& inData, + std::unordered_map>& inDataBlockIDsMap) const + { + inData.resize(0); + inDataBlockIDsMap.clear(); + + std::vector buffers; + + MPI_Status status; + while (true) + { + int flag = 0; + 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"); + + if (flag == 0) //no message arrived we are done. + break; + + //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(); + + 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()] = 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. + } + } + + MPI_Comm MPIComm; + vtkm::Id NumRanks; + vtkm::Id Rank; + std::unordered_map SendBuffers; + int Tag = 100; +#else + vtkm::Id NumRanks = 1; + vtkm::Id Rank = 0; +#endif +}; + +} +} +} +} //vtkm::filter::flow::internal + + +#endif //vtk_m_filter_flow_internal_ParticleExchanger_h diff --git a/vtkm/filter/flow/internal/ParticleMessenger.h b/vtkm/filter/flow/internal/ParticleMessenger.h deleted file mode 100644 index fb56354ac0b2bb8f6f40e4fc95d49f019833be37..0000000000000000000000000000000000000000 --- 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 1f2562c06141f2916cf4d92c5becd40ebb26ce8d..5a28b4c770be7b4533fc001adf65bfc3e2c5fed3 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/worklet/testing/GenerateTestDataSets.h b/vtkm/filter/flow/testing/GenerateTestDataSets.h similarity index 99% rename from vtkm/worklet/testing/GenerateTestDataSets.h rename to vtkm/filter/flow/testing/GenerateTestDataSets.h index 6ec53a9ec2dbb7d7eb682d79ef3c1d79e0db55d6..fd699643f7ba2dc21d32d589520cce13a9b2a336 100644 --- a/vtkm/worklet/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 6ece724d0479c621ab4611d42573b6a2d9e6e334..36ff44549f136f04bd8db15d2b5038307c06fe3d 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) { @@ -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); @@ -265,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], @@ -274,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) { @@ -284,15 +275,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 016b7db793f030bef629abca3172b30dd88ef6c3..5eec5196aa4415d15d625d52dbd4e138cb456045 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/UnitTestAdvectionMPI.cxx similarity index 53% rename from vtkm/filter/flow/testing/UnitTestAdvectionAsynchronousMPI.cxx rename to vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx index be6439f64beb641fac1340e8cfedfbf26a65b43c..23e470d60171a0cd0fb335603b90403206c45e68 100644 --- a/vtkm/filter/flow/testing/UnitTestAdvectionAsynchronousMPI.cxx +++ b/vtkm/filter/flow/testing/UnitTestAdvectionMPI.cxx @@ -14,20 +14,55 @@ #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) { - 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 +85,7 @@ void DoTest() } // anonymous namespace -int UnitTestAdvectionAsynchronousMPI(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/UnitTestAdvectionSynchronousMPI.cxx b/vtkm/filter/flow/testing/UnitTestAdvectionSynchronousMPI.cxx deleted file mode 100644 index a6363b7718b16fe8adf7785219978afbde29dfb7..0000000000000000000000000000000000000000 --- a/vtkm/filter/flow/testing/UnitTestAdvectionSynchronousMPI.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 = 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 UnitTestAdvectionSynchronousMPI(int argc, char* argv[]) -{ - return vtkm::cont::testing::Testing::Run(DoTest, argc, argv); -} diff --git a/vtkm/filter/flow/testing/UnitTestLagrangianFilter.cxx b/vtkm/filter/flow/testing/UnitTestLagrangianFilter.cxx index ddb850c83b9d24f3da59fdfe4e939d96811109bb..572bc7a9b9a1c2cdd9e706cfb5997dbedd41b1a6 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 d6b1a53c9516fec913e20a81eb7553b10428dfec..e44b22676030e35847ac044a0a7ba2657b7549b1 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/UnitTestParticleMessengerMPI.cxx b/vtkm/filter/flow/testing/UnitTestParticleMessengerMPI.cxx deleted file mode 100644 index ed2a8a24920b5db3e68acaafeeccc8d516e11c31..0000000000000000000000000000000000000000 --- 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 5eabb9df8d0b38a048889ce8ae440d9fa5592ee0..59fc358293095f398a581c10b11a879f305e2cd1 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 1fe976c087365f2d4c1766f28f531aaeab724db6..0000000000000000000000000000000000000000 --- 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/UnitTestStreamlineAMRMPI.cxx b/vtkm/filter/flow/testing/UnitTestStreamlineAMRMPI.cxx index 32358e25fe045ea77424850c5f604f1ff2296ec7..c01c2532712258cb6e15a648b9d99fe5cf8aadbd 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 { @@ -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); } } } diff --git a/vtkm/filter/flow/testing/UnitTestStreamlineFilter.cxx b/vtkm/filter/flow/testing/UnitTestStreamlineFilter.cxx index 75a7ddbfb24f908ff98979af279c644fc02ad895..90d7d423d2414fa33a4bfa10a1bad862c1af84eb 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 { @@ -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. 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 26fad71aafdd6cd1ca6591135f1e87e9416d4d60..ce7bb1ab85c4e49c8c9228e5bc19a2fadc2f0f7d 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 9550c4dc9542cc04264ba082cd9f82c3e6dca226..0000000000000000000000000000000000000000 --- 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); -} diff --git a/vtkm/filter/flow/testing/UnitTestWorkletParticleAdvection.cxx b/vtkm/filter/flow/testing/UnitTestWorkletParticleAdvection.cxx index dfca21a2b47282498431ccc67f57db502118f757..d13a529b3eafe6fff12f3101b9889f00ba892bb7 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 diff --git a/vtkm/filter/scalar_topology/CMakeLists.txt b/vtkm/filter/scalar_topology/CMakeLists.txt index 5603ebaa15fb007e03ef53c8760c2a93be27d2ac..aeb7e2f468021d48b37205d56de335ffcbba95d3 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.cxx b/vtkm/filter/scalar_topology/ContourTreeUniformDistributed.cxx index 9992fd64ab047037488e1ccdf7f4b724238d683b..ad572e6b53ad2f268f4bb06cc7480b437e1b3bbf 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,14 +1219,60 @@ 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 ( - [](DistributedContourTreeBlockData* blockData, const vtkmdiy::Master::ProxyWithLink&) { - blockData->HierarchicalAugmenter.Initialize( - blockData->GlobalBlockId, &blockData->HierarchicalTree, &blockData->AugmentedTree); - }); + 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->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 + ); + }); timingsStream << " " << std::setw(38) << std::left << "Initalize Hierarchical Trees" << ": " << timer.GetElapsedTime() << " seconds" << std::endl; @@ -1153,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&) { @@ -1259,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 23610971aab80ac77ba0172dddd44afb0ed4ede7..6093cd55f1cf1f4e9b526f70bca5b3c84988486e 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 = 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 0f522424bcb1764adef206981375f033418a1268..316d07b921def9296cccc765b15daebeca85b0f6 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 be1b677a0e93341041aed1f686e627feb3ff62bf..e2ecd7f516da2d2ed62f2df140c3ced6a8abe8bf 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 0000000000000000000000000000000000000000..f7b4e487fd3b425fd9f6157eb30ff29527a9737a --- /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 cdc8c2f3ff98b993cd7ab90af066dff0d7602fba..8be2168ea507e5a8ace27dbe5caaa72733f388c5 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 0000000000000000000000000000000000000000..ad7522a8bac6ffff1d1005d9c404765f67805c2f --- /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 0000000000000000000000000000000000000000..e0be7a21eed5a425112a56977f7a4b127f8abdcc --- /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 c7d0b8f312d17781a0c265cf6d94870c6ba30e0c..0000000000000000000000000000000000000000 --- a/vtkm/filter/scalar_topology/SelectTopVolumeContoursFilter.cxx +++ /dev/null @@ -1,183 +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>(); - - 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); - - 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 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 3280d72b8a9de7a30e437db38567beae471f6fb0..7cc8d3e720a060ef7c8a5692ff634c2bef0232eb 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 3223beb397e9edd47e99fb9b1980813e2615121a..45a2779979129d6a1c75455487728597aa2eec22 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 c9a51ea1d5a3c97391241c93b2691d5d6e348d25..6061b1b1a76678220ec9b7a3ab9d0479bdb81be7 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 d0b95f1092fe5f5609cc44cb71d8e1a310bd753f..2a5b409ce42abdef0b66f1d6c9753de48f68eafa 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 9eace991af04e41b1037f0ecd2fac01c3277ff76..f5165dc50f82215aa7e3ffc8200c2133c29badd9 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 0000000000000000000000000000000000000000..caa0f96ea50adb254d6edc77ee4f6c7c68ba5150 --- /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 0000000000000000000000000000000000000000..58589899d45b25952ffe75577d691bb2814f1acc --- /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 0000000000000000000000000000000000000000..6ae6aeb93afdef9b51df67a4203dd2fe6c614c33 --- /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 0000000000000000000000000000000000000000..0840560a78ce07733c38ee52009510a2ea69732b --- /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 7d08433edbf4871ac66ac05487fc46c71c75de2c..1d123fbb06e343a5d161f25665c5b8c5b305ca05 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,30 +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; + // 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 0000000000000000000000000000000000000000..85e49f2b8035bbef887ab19946936ecc3b4b1150 --- /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 0000000000000000000000000000000000000000..9a731cbd3b25b82d6d2b0b9c080cfe51d7f5628d --- /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 9a3e9d2ca59f03edc7be169e0e93d872e4762a30..0000000000000000000000000000000000000000 --- 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 485c14d2e34640ee9f89acec41a3dae5ea7c7151..0000000000000000000000000000000000000000 --- a/vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.cxx +++ /dev/null @@ -1,384 +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(); - - 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)); - - 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); - - 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; - InArrayHandleType mergedTopVolBranchSaddleIsoValue; - mergedTopVolBranchGRId.Allocate(nIncoming + nSelf); - mergedTopVolBranchVolume.Allocate(nIncoming + nSelf); - mergedTopVolBranchSaddleEpsilon.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( - 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( - 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); - 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; - 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( - 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); - // 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); - 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 0000000000000000000000000000000000000000..0c928e0e0a32569e6f9e017d9a1f086f8d74948e --- /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 023047defb62861a419eea2cf12ea2483b45eb7d..777e9838f31f825bf0795faf492c9d00d36d5b56 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 909cf7076a0726cd8213b74b49663c086d926219..e3206c3a0b5799c9c566297a11808607e211eea4 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 @@ -231,6 +232,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 +273,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 +326,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) @@ -467,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; @@ -605,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; } } @@ -878,6 +1013,241 @@ 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 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); +} + +// 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) + { + std::cout << "Testing ContourTreeUniformDistributed with " + << (marchingCubes ? "marching cubes" : "Freudenthal") << " mesh connectivity on \"" + << datasetName << "\" divided into " << nBlocks + << " blocks. Using presimplification threshold = " << presimplifyThreshold + << std::endl; + } + + // get the output of contour tree + presimplification + vtkm::cont::PartitionedDataSet tp_result; + RunContourTreePresimplification(fieldName, + input_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); + } +} + +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 48e131c8fd3fd0741210b79162fcda2f79fbb907..180280f58c8aed6c090dffd91ca3d09c52b3a4b5 100644 --- a/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx +++ b/vtkm/filter/scalar_topology/testing/UnitTestContourTreeUniformDistributedFilter.cxx @@ -50,16 +50,22 @@ // 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 { 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; -using vtkm::filter::testing::contourtree_uniform_distributed:: - TestContourTreeUniformDistributedBranchDecomposition8x9; class TestContourTreeUniformDistributedFilter { @@ -67,25 +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); - TestContourTreeUniformDistributedBranchDecomposition8x9(2); - TestContourTreeUniformDistributedBranchDecomposition8x9(4); - TestContourTreeUniformDistributedBranchDecomposition8x9(8); - TestContourTreeUniformDistributedBranchDecomposition8x9(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"), @@ -94,6 +102,7 @@ public: "var", Testing::RegressionImagePath("vanc.ct_txt"), 4); +#endif TestContourTreeFile(Testing::DataPath("rectilinear/vanc.vtk"), "var", Testing::RegressionImagePath("vanc.ct_txt"), @@ -130,6 +139,92 @@ public: true, false, false); + + // tests for contour tree presimplification on 2D vanc dataset +#ifdef ENABLE_ADDITIONAL_TESTS + TestContourTreePresimplification( + "vanc", // dataset name + "var", // field name + vtkm::cont::testing::Testing::RegressionImagePath( + "vanc.presimplification.ct_txt"), // ground truth file name + 2, // nBlocks + vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), // dataset file path + 1 // presimplifyThreshold + ); +#endif + TestContourTreePresimplification( + "vanc", + "var", + vtkm::cont::testing::Testing::RegressionImagePath("vanc.presimplification.ct_txt"), + 4, + vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), + 1); +#ifdef ENABLE_ADDITIONAL_TESTS + TestContourTreePresimplification( + "vanc", + "var", + vtkm::cont::testing::Testing::RegressionImagePath("vanc.presimplification.ct_txt"), + 2, + vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), + 4); +#endif + TestContourTreePresimplification( + "vanc", + "var", + vtkm::cont::testing::Testing::RegressionImagePath("vanc.presimplification.ct_txt"), + 4, + vtkm::cont::testing::Testing::DataPath("rectilinear/vanc.vtk"), + 4); + + // test for contour tree presimplification on 3D 5x6x7 dataset +#ifdef ENABLE_ADDITIONAL_TESTS + 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, + 2 // presimplifyThreshold + ); + TestContourTreePresimplification( + "5x6x7", + "pointvar", + vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), + 4, + vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), + 2); +#endif + TestContourTreePresimplification( + "5x6x7", + "pointvar", + vtkm::cont::testing::Testing::RegressionImagePath("5x6x7.presimplification.ct_txt"), + 8, + vtkm::cont::testing::MakeTestDataSet().Make3DUniformDataSet4(), + 2); +#ifdef ENABLE_ADDITIONAL_TESTS + 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); +#endif + 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/testing/UnitTestDistributedBranchDecompositionFilter.cxx b/vtkm/filter/scalar_topology/testing/UnitTestDistributedBranchDecompositionFilter.cxx index 9f9cd3e19014ca2295ba730058b20ae18fb348e5..fb750175ebb79c5bc23082e1113f9ae1bbb7af99 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"), diff --git a/vtkm/filter/scalar_topology/worklet/CMakeLists.txt b/vtkm/filter/scalar_topology/worklet/CMakeLists.txt index e0a604670ac919c506e711a99a27851b7c1491f2..bfb9f8621e6e5ae1ad2e606867bb942b8e489540 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 35a8b8609347f121e24792ba81ca6d36cfc39e36..5b78176688dea49e21a5a65d5f31870ec339bbac 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: @@ -506,12 +511,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 @@ -770,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 = @@ -817,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); @@ -857,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 7c63e4c091c6a53943d6d87ddc93fb9e58ed8ef8..c13ac890a9d002efb28cea5173f45dd2df7dc48e 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/CollapseBranchesWorklet.h b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/CollapseBranchesWorklet.h index 498f7eb5f28eaf5418763ccf994bcd6f303bcbab..d16e8b1083beea8a8afaf07604a13a746a5e327c 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/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h b/vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h index 19caa736e51d6b2392a18dbb598218b081b9691d..847b90a366c9f5ab15d7c4cf7f15eee1f2a031ff 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_augmented/NotNoSuchElementPredicate.h b/vtkm/filter/scalar_topology/worklet/contourtree_augmented/NotNoSuchElementPredicate.h index 3cb674c6558e1313c2ded13ff62bfe0575364476..67b8f6f5dea7ddd2f5ce6e53804f545b1d23f573 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 289bc686badceee7ea4c42c6d9ae4808e75b1d7a..76634060f07926d86038016b9f917f4710f8b794 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 e1d02f68b9d003d120e5e435b451d6ceb5dab5e6..e31d660e93cf3c8dcc6f5583e7627b9f58c89941 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 3ef88ae91e0bc4e60f34ab6c3719eb2c6660b2a8..93e82e5a1a35c7d7f72819db3a18c2d7ff9bda9a 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 a9ad55f7f11b5a201bf2998ee05498b4353c608d..6df8448d1ecac62da1a220f7a178da18d8de806c 100644 --- a/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/HierarchicalAugmenter.h @@ -101,7 +101,10 @@ #include #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 { @@ -127,6 +134,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 @@ -173,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; @@ -198,7 +210,12 @@ 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, + 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); @@ -220,12 +237,12 @@ 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(); // 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); @@ -249,24 +266,140 @@ 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, + vtkm::worklet::contourtree_augmented::IdArrayType* volumeArray, + vtkm::Id presimplifyThreshold) { // Initialize() // copy the parameters for use this->BlockId = blockId; this->BaseTree = baseTree; this->AugmentedTree = augmentedTree; + this->MeshBlockOrigin = meshBlockOrigin; + 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, @@ -280,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 @@ -319,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. @@ -333,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() @@ -408,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)); @@ -476,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() @@ -504,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 @@ -511,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 @@ -525,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:: @@ -542,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 @@ -560,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 @@ -569,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() @@ -623,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() @@ -702,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 @@ -728,12 +1004,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 @@ -748,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; @@ -759,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(); @@ -831,12 +1127,23 @@ void HierarchicalAugmenter::CopyBaseRegularStructure() ); } + // Reset the number of regular nodes in round 0 + vtkm::Id regularNodesInRound0 = + this->AugmentedTree->NumRegularNodesInRound.ReadPortal().Get(0) + numRegNeeded; + this->AugmentedTree->NumRegularNodesInRound.WritePortal().Set(0, regularNodesInRound0); + // Finally, we resort the regular node sort order { vtkm::worklet::contourtree_distributed::PermuteComparator // hierarchical_contour_tree:: 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() @@ -847,26 +1154,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( @@ -874,15 +1176,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, @@ -890,10 +1193,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"), @@ -980,7 +1283,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"), @@ -1004,7 +1307,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"), @@ -1013,6 +1316,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 = @@ -1053,7 +1359,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"), @@ -1061,7 +1367,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 @@ -1104,7 +1413,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"), @@ -1121,7 +1430,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"), @@ -1135,18 +1444,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"), @@ -1161,160 +1479,228 @@ 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 + ); + + // 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. + // 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 + // 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( @@ -1322,6 +1708,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(); @@ -1339,7 +1733,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; @@ -1347,13 +1741,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( @@ -1369,7 +1766,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( @@ -1383,37 +1780,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( @@ -1427,7 +1832,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( @@ -1453,7 +1859,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 bd2dcdcba4445b8a38c5d56d7e173e68048a383d..32070f6f4aada9d95093cacd08e0dd47b2de43dc 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 @@ -110,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(); } } @@ -125,7 +133,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 acd5eca2b128b2f1559d70a55a4287992a65cff6..c4a24088b6904d08ad1bed2ead7825694948e7d0 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"; @@ -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); @@ -1054,6 +1063,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 da7126a9b02270df82a1b0dd94b0b3ac09441f5f..6e7a5cbd22697f3698016b109a267daefc92f8bd 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 { @@ -200,7 +206,6 @@ private: }; // class HierarchicalHyperSweeper - template HierarchicalHyperSweeper::HierarchicalHyperSweeper( vtkm::Id blockId, @@ -236,16 +241,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 +274,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 +287,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 +313,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 +329,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 +340,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 +354,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) + @@ -360,13 +367,14 @@ 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); // 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) + @@ -424,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); { @@ -442,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 @@ -530,6 +536,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 +582,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 +611,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 +622,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 +660,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 +682,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 +691,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 +775,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 56a3b61c1db18833c04d65067e23f6ca540dde9a..944230cb3791ad6d39a325d5003b8af083239090 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,7 +22,10 @@ set(headers SetSuperparentSetDecorator.h AttachmentAndSupernodeComparator.h ResizeArraysBuildNewSupernodeIdsWorklet.h + FillEmptyIterationWorklet.h CreateSuperarcsWorklet.h + CreateSuperarcsData.h + CreateSuperarcsSetFirstSupernodePerIterationWorklet.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 0000000000000000000000000000000000000000..40d64a221044268c114f4a1d6c92b6d937e8db81 --- /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 0000000000000000000000000000000000000000..1345551008cfc22c5cf2d8e1ba3a1623f1101066 --- /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/CreateSuperarcsWorklet.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.h index 32bcc60375997b9836e2ed5fdf169d1348b2faf1..8a674115f592a00bb43dbd1a923153b1ef69c212 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/worklet/testing/TestingContourTreeUniformDistributedLoadArrays.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FillEmptyIterationWorklet.h similarity index 51% rename from vtkm/worklet/testing/TestingContourTreeUniformDistributedLoadArrays.h rename to vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FillEmptyIterationWorklet.h index 290532cda6a6643d4f4e6b8ef554fe886123b94e..8b76070cb71e505e862fcb806f5163684611a161 100644 --- a/vtkm/worklet/testing/TestingContourTreeUniformDistributedLoadArrays.h +++ b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FillEmptyIterationWorklet.h @@ -37,83 +37,58 @@ // 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 +//============================================================================= +// The PPP2 algorithm and software were jointly developed by +// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and +// Oliver Ruebel (LBNL) +//============================================================================== -#include -#include +#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 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) +namespace hierarchical_augmenter { - 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) +// 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 { - 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]); - } -} +public: + using ControlSignature = void( + WholeArrayInOut augmentedTreeFirstSupernodePerIteration // input/output + ); + using ExecutionSignature = void(InputIndex, _1); + using InputDomain = _1; -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(); + VTKM_EXEC_CONT + FillEmptyIterationWorklet() {} - for (vtkm::Id i = 0; i < static_cast(sz); ++i) + /// operator() of the worklet + template + VTKM_EXEC void operator()(const vtkm::Id& inputIndex, + InOutFieldPortalType& firstSupernodePerIteration) const { - 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. - } -} + if (inputIndex == 0 || inputIndex == firstSupernodePerIteration.GetNumberOfValues() - 1) + return; + if (firstSupernodePerIteration.Get(inputIndex) == 0) + { + vtkm::Id nextSupernode = firstSupernodePerIteration.Get(inputIndex + 1); + firstSupernodePerIteration.Set(inputIndex, nextSupernode); + } + } // operator()() +}; // FillEmptyIterationWorklet +} // namespace hierarchical_augmenter } // namespace contourtree_distributed -} // namespace testing } // namespace worklet } // namespace vtkm 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 f4c7454789dca2ed866393f29801606b5e1280e0..fbc48c5decad86f2cfb99c69f60fd35268a3007a 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,78 @@ 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::Id3 pos{ globalId % this->MeshGlobalSize[0], + (globalId / this->MeshGlobalSize[0]) % this->MeshGlobalSize[1], + globalId / (this->MeshGlobalSize[0] * this->MeshGlobalSize[1]) }; + + // test validity + if (pos[2] < this->MeshBlockOrigin[2]) + { + return false; + } + if (pos[2] >= this->MeshBlockOrigin[2] + this->MeshBlockSize[2]) + { + return false; + } + if (pos[1] < this->MeshBlockOrigin[1]) + { + return false; + } + if (pos[1] >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1]) + { + return false; + } + if (pos[0] < this->MeshBlockOrigin[0]) + { + return false; + } + if (pos[0] >= 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::Id2 pos{ globalId % this->MeshGlobalSize[0], globalId / this->MeshGlobalSize[0] }; + + // test validity + if (pos[1] < this->MeshBlockOrigin[1]) + { + return false; + } + if (pos[1] >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1]) + { + return false; + } + if (pos[0] < this->MeshBlockOrigin[0]) + { + return false; + } + if (pos[0] >= 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 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 dd79e4ce7cf2a6f7f70a78df00f5208d90373da3..a85ccfd949856c8e201300f2a6afd44040b91f98 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 0903f85f40b49ca91c914945034790533e6b1c89..927854844625d72791ff2000e16d91fe6f5429ed 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 c81154f9ef68094c79007298d16cf84df5af52ec..e25c63f703ff27bb11b86ba7208219dba353cbfd 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_contour_tree/FindSuperArcForUnknownNode.h b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h index a9de61546d78b8d3b4aa1f43e1a2b2d22191ca37..e5292b12ef96fff28910b4b034b70306cdeb9333 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/contourtree_distributed/hierarchical_hyper_sweeper/CMakeLists.txt b/vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_hyper_sweeper/CMakeLists.txt index eb302fd40c6b562f7c1e1537892beffc4662e832..18b089ddb4974ae3b0d2950d962a05d613d06fbe 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 b885cb11621c582217aeedd4878bc85328a9201a..93882ac2b5068ee172006c6e5dbd5694771d6225 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 2f703c98161ca9241cb6bed77e9335b9666e9b76..7fd4358ae9cc2b7e0fc839da4b904ebe525df802 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 0d5c1ca981a22fec4feeb9ba3aa2caf20156627e..86c8cb8588539da121b481f047a35ddea6e81520 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 0000000000000000000000000000000000000000..9a0eade3026e418f510cb42fbd8247fef9a62238 --- /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 f1265b88a31e0137b12813c89d444c0087c3b5eb..5c33c288c6fc0d8d7bbe23d44732bcfa6889826d 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 0000000000000000000000000000000000000000..12b196bf8d71e894f47f4f473899c9c67855c8b9 --- /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/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 234966323d0ceb27852ab4e7db0cd020aa62f2bc..b8dcc23d366d624b8d2547f180f60aefdb822407 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 0000000000000000000000000000000000000000..ecd4e45d62a58fa48bbd8a3a65f7952e349b5e1b --- /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 0000000000000000000000000000000000000000..02d87a90e76f8e7eccccf286ffd310fd1a656a1a --- /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 0000000000000000000000000000000000000000..93686c91229ed0aa1e1f7f8488c78428400fcdcb --- /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 0000000000000000000000000000000000000000..af0fb390b5066cba5c4278d2e08f5288d9d04dbe --- /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 0000000000000000000000000000000000000000..1c67fcb0097b88f64e57a3d53fcb23858336ac18 --- /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 0000000000000000000000000000000000000000..d630867a9696bf5024051714db3ca79a03916725 --- /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 0000000000000000000000000000000000000000..3716890888c121ad969a7a15aa68589b97cc26b8 --- /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 0000000000000000000000000000000000000000..b48c90bce0b83c931ede277b09f947cf3e9c3c4d --- /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 0000000000000000000000000000000000000000..355a1f0ef92193cfd112ebff45a6e46622fdfed3 --- /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 0000000000000000000000000000000000000000..2623c33a7efd9eda6753013df273cd1509d27211 --- /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 0000000000000000000000000000000000000000..b1c6463d420b3e318afadcdff325f3f9659fd944 --- /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 ddb9edc8fd0dbc398486b41cbe886f797617fab3..f8254fe4ddcc0a3feec229d071c4de5636726723 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 0000000000000000000000000000000000000000..b2539e1cd3107ccfc7d4186631c13ddc2951baef --- /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 39ecdbdf3bb9e7f28448ea38472b00fe7e783553..e219ed987bdba9742faa2c8f34cab1b6c38b7df2 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 0000000000000000000000000000000000000000..2e9176161af0ce4dd8f68a86703a4ebf64ab5d0a --- /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 0000000000000000000000000000000000000000..535bc7822aa1720f498b60d885c4367c822582a7 --- /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 92% 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 4d808f8edf07b5fdc09b3cacc05f0a48c14ba3c2..50d6b2fa6704f45fabb74b2e455795effae06bf3 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,18 +109,19 @@ 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) 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; @@ -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 0000000000000000000000000000000000000000..47b4bf1819d04f3cdbb2efa79e25857ee9ff8608 --- /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 0000000000000000000000000000000000000000..fdfd1480276fde390752c8644823e29b27ad1821 --- /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 0000000000000000000000000000000000000000..ffb61f9cc49e8f814395d0cb770e1891d5ac1cc3 --- /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 39568d0b7a24e17d900f0b3223d3cd45a4183cd5..4cb5e407231ae3be5ec8e10c948004c2bbd397c7 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 0000000000000000000000000000000000000000..9db6595354e249d6c8653ed05016d30e40a91d82 --- /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 diff --git a/vtkm/thirdparty/diy/update.sh b/vtkm/thirdparty/diy/update.sh index a6fdcf8b3d48bed84ac65908ec34102a4da16dfc..97cc5ad57f0b62de157b81d795d3b230b04db4a8 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 diff --git a/vtkm/thirdparty/diy/vtkmdiy/CMakeLists.txt b/vtkm/thirdparty/diy/vtkmdiy/CMakeLists.txt index 344398ea9dc8b6d5253e27de08e44e588279943b..748372e5ba322591332e5fe2e84303ea8e6921c4 100644 --- a/vtkm/thirdparty/diy/vtkmdiy/CMakeLists.txt +++ b/vtkm/thirdparty/diy/vtkmdiy/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) diff --git a/vtkm/worklet/testing/CMakeLists.txt b/vtkm/worklet/testing/CMakeLists.txt index d8556b2c31bc9e01095eee4b681317b68c4f4458..94bf9ec6550cafac0c81479dcde2e5733260b137 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/UnitTestContourTreeUniformDistributed.cxx b/vtkm/worklet/testing/UnitTestContourTreeUniformDistributed.cxx deleted file mode 100644 index e5c68258f025966b3ae44d8e85d48750e43d94dc..0000000000000000000000000000000000000000 --- 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); -}