Compare commits

...

7 Commits

Author SHA1 Message Date
Mingzhe Li
01934f7e4a Merge branch 'distributed-contours' into 'master'
Distributed Isosurface extraction from branch decomposition

See merge request vtk/vtk-m!3226
2024-07-01 09:35:08 -04:00
Kenneth Moreland
5a3289b8db Merge branch 'release-2.2' 2024-07-01 08:54:02 -04:00
Kenneth Moreland
ee0e7d0bfb Merge topic 'guide-unknown-arrays'
9a8638aef Update version, acknowledgements, and other meta information
41a088f6c Add guide chapter on unknown array handle

Acked-by: Kitware Robot <kwrobot@kitware.com>
Reviewed-by: Vicente Bolea <vicente.bolea@kitware.com>
Merge-request: !3241
2024-07-01 08:54:02 -04:00
Kenneth Moreland
3f0defc4a5 Merge topic 'guide-unknown-arrays' into release-2.2
9a8638aef Update version, acknowledgements, and other meta information
41a088f6c Add guide chapter on unknown array handle

Acked-by: Kitware Robot <kwrobot@kitware.com>
Reviewed-by: Vicente Bolea <vicente.bolea@kitware.com>
Merge-request: !3241
2024-07-01 08:54:02 -04:00
Kenneth Moreland
9a8638aef2 Update version, acknowledgements, and other meta information 2024-06-28 13:42:23 -04:00
Kenneth Moreland
41a088f6ce Add guide chapter on unknown array handle 2024-06-28 13:42:23 -04:00
Mingzhe Li
a3edc365a8 Implementation of isosurface extraction.
Fixing logs to distinguish candidate meshes in isosurfaces vs. actual meshes on branches
2024-06-18 12:51:37 -07:00
51 changed files with 5395 additions and 179 deletions

@ -90,5 +90,8 @@ Sandia National Laboratories is a multimission laboratory managed and operated b
This research was supported by the Exascale Computing Project (17-SC-20-SC), a joint project of the U.S.
Department of Energy's Office of Science and National Nuclear Security Administration, responsible for delivering a capable exascale ecosystem, including software, applications, and hardware technology, to support the nation's exascale computing imperative.
This material is based upon work supported by the U.S.
Department of Energy, Office of Science, Office of Advanced Scientific Computing Research, Scientific Discovery through Advanced Computing (SciDAC) program.
This work was supported in part by the U.S. Department of Energy (DOE) RAPIDS SciDAC project under contract number DE-AC05-00OR22725 and by the Exascale Computing Project (17-SC-20-SC), a collaborative effort of the U.S. Department of Energy Office of Science and the National Nuclear Security Administration.
This research used resources of the Oak Ridge Leadership Computing Facility at the Oak Ridge National Laboratory, which is supported by the Office of Science of the U.S. Department of Energy under Contract No. DE-AC05-00OR22725.
This research used resources of the Argonne Leadership Computing Facility, a U.S. Department of Energy (DOE) Office of Science user facility at Argonne National Laboratory and is based on research supported by the U.S. DOE Office of Science-Advanced Scientific Computing Research Program, under Contract No. DE-AC02-06CH11357.

@ -636,9 +636,7 @@ This is most typically used with C++ run-time type information to convert a run-
.. doxygenfunction:: vtkm::ListForEach(Functor &&f, vtkm::List<Ts...>, Args&&... args)
The following example shows a rudimentary version of converting a dynamically-typed array to a statically-typed array similar to what is done in |VTKm| classes like :class:`vtkm::cont::UnknownArrayHandle` (which is documented in Chapter~\ref{chap:UnknownArrayHandle}).
.. todo:: Fix ``UnknownArrayHandle`` chapter reference above.
The following example shows a rudimentary version of converting a dynamically-typed array to a statically-typed array similar to what is done in |VTKm| classes like :class:`vtkm::cont::UnknownArrayHandle`, which is documented in :chapref:`unknown-array-handle:Unknown Array Handles`.
.. load-example:: ListForEach
:file: GuideExampleLists.cxx

@ -72,9 +72,7 @@ The filter implementation can get the appropriate field to operate on using the
One of the challenges with writing filters is determining the actual types the algorithm is operating on.
The :class:`vtkm::cont::Field` object pulled from the input :class:`vtkm::cont::DataSet` contains a :class:`vtkm::cont::ArrayHandle` (see :chapref:`basic-array-handles:Basic Array Handles`), but you do not know what the template parameters of the :class:`vtkm::cont::ArrayHandle` are.
There are numerous ways to extract an array of an unknown type out of a :class:`vtkm::cont::ArrayHandle` (many of which will be explored later in Chapter \ref{chap:UnknownArrayHandle}), but the :class:`vtkm::filter::Filter` contains some convenience functions to simplify this.
.. todo:: Fix above reference to unknown array handle chapter.
There are numerous ways to extract an array of an unknown type out of a :class:`vtkm::cont::ArrayHandle`, many of which will be explored later in :chapref:`unknown-array-handle:Unknown Array Handles`, but the :class:`vtkm::filter::Filter` contains some convenience functions to simplify this.
In particular, this filter operates specifically on scalar fields.
For this purpose, :class:`vtkm::filter::Filter` provides the :func:`vtkm::filter::Filter::CastAndCallScalarField` helper method.

@ -20,7 +20,7 @@ project = "The VTK-m User's Guide"
copyright = 'Kitware Inc., National Technology & Engineering Solutions of Sandia LLC, UT-Battelle LLC, Los Alamos National Security LLC'
author = 'Kenneth Moreland'
version = '@VTKm_VERSION_FULL@'
release = '@VTKm_VERSION_FULL@'
release = '@VTKm_VERSION_MAJOR@.@VTKm_VERSION_MINOR@'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@ -76,8 +76,8 @@ today_fmt = '%B %d, %Y'
rst_prolog = '''
.. |VTKm| replace:: VTKm
.. |Veclike| replace:: ``Vec``-like
.. |report-year| replace:: 2023
.. |report-number| replace:: ORNL/TM-2023/3182
.. |report-year| replace:: 2024
.. |report-number| replace:: ORNL/TM-2024/3443
'''
breathe_projects = { 'vtkm': '@doxygen_xml_output_dir@' }

@ -345,7 +345,7 @@ void TryPrintArrayContents()
////
//// BEGIN-EXAMPLE CastAndCallWithFloatFallback
////
uncertainArray.CastAndCall(PrintArrayContentsFunctor{});
uncertainArray.CastAndCallWithFloatFallback(PrintArrayContentsFunctor{});
////
//// END-EXAMPLE CastAndCallWithFloatFallback
////

@ -88,7 +88,7 @@ That calls a lambda function that invokes a worklet to create the output field.
.. didyouknow::
The filter implemented in :numref:`ex:FilterFieldImpl` is limited to only find the magnitude of :class:`vtkm::Vec`'s with 3 components.
It may be the case you wish to implement a filter that operates on :class:`vtkm::Vec`'s of multiple sizes (or perhaps even any size).
Chapter \ref{chap:UnknownArrayHandle} discusses how you can use the :class:`vtkm::cont::UnknownArrayHandle` contained in the :class:`vtkm::cont::Field` to more expressively decide what types to check for.
:chapref:`unknown-array-handle:Unknown Array Handles` discusses how you can use the :class:`vtkm::cont::UnknownArrayHandle` contained in the :class:`vtkm::cont::Field` to more expressively decide what types to check for.
.. doxygenfunction:: vtkm::filter::Filter::CastAndCallVariableVecField(const vtkm::cont::UnknownArrayHandle&, Functor&&, Args&&...) const
.. doxygenfunction:: vtkm::filter::Filter::CastAndCallVariableVecField(const vtkm::cont::Field&, Functor&&, Args&&...) const

@ -2,6 +2,8 @@
Fancy Array Handles
==============================
.. todo:: Document :class:`vtkm::cont::ArrayHandleMultiplexer`.
.. index::
double: array handle; fancy

@ -23,9 +23,9 @@ David Pugmire,
Nick Thompson,
Allison Vacanti,
Abhishek Yenpure,
and the |VTKm| community
and the |VTKm| community.
Moreland, K. (|report-year|). *The VTK-m User's Guide*, Tech report |report-number|, Oak Ridge National Laboratory.
Moreland, K. (|report-year|). *The VTK-m User's Guide*, version |release|, Tech report |report-number|, Oak Ridge National Laboratory.
.. centered:: Join the VTK-m Community at http://m.vtk.org.

@ -83,8 +83,6 @@ Additionally, you can use its constructors or the :func:`vtkm::cont::make_ArrayH
Strided Arrays
--------------------
.. todo:: Should this be moved to the chapter/section on transformed arrays?
.. index::
double: array handle; stride
double: array handle; offset
@ -163,3 +161,14 @@ This is convenient for operations that want to operate on arrays with an unknown
.. load-example:: GetRuntimeVec
:file: GuideExampleArrayHandleRuntimeVec.cxx
:caption: Using :class:`vtkm::cont::ArrayHandleRuntimeVec` to get an array regardless of the size of the contained :class:`vtkm::Vec` values.
---------------------------------------------
Recombined Vec Arrays of Strided Components
---------------------------------------------
|VTKm| contains a special array, :class:`vtkm::cont::ArrayHandleRecombineVec`, to combine component arrays represented in :class:`vtkm::cont::ArrayHandleStride` together to form `Vec` values.
:class:`vtkm::cont::ArrayHandleRecombineVec` is similar to :class:`vtkm::cont::ArrayHandleSOA` (see :secref:`memory-layout:Structure of Arrays`) except that (1) it holds stride arrays for its components instead of basic arrays and that (2) the number of components can be specified at runtime.
:class:`vtkm::cont::ArrayHandleRecombineVec` is mainly provided for the implementation of extracting arrays out of a :class:`vtkm::cont::UnknownArrayHandle` (see :secref:`unknown-array-handle:Extracting All Components`).
.. doxygenclass:: vtkm::cont::ArrayHandleRecombineVec

@ -9,3 +9,4 @@ Developing Algorithms
basic-array-handles.rst
simple-worklets.rst
basic-filter-impl.rst
unknown-array-handle.rst

@ -0,0 +1,457 @@
==============================
Unknown Array Handles
==============================
.. index::
single: unknown array handle
single: array handle; unknown
The :class:`vtkm::cont::ArrayHandle` class uses templating to make very efficient and type-safe access to data.
However, it is sometimes inconvenient or impossible to specify the element type and storage at run-time.
The :class:`vtkm::cont::UnknownArrayHandle` class provides a mechanism to manage arrays of data with unspecified types.
:class:`vtkm::cont::UnknownArrayHandle` holds a reference to an array.
Unlike :class:`vtkm::cont::ArrayHandle`, :class:`vtkm::cont::UnknownArrayHandle` is *not* templated.
Instead, it uses C++ run-type type information to store the array without type and cast it when appropriate.
.. doxygenclass:: vtkm::cont::UnknownArrayHandle
.. index:: unknown array handle; construct
An :class:`vtkm::cont::UnknownArrayHandle` can be established by constructing it with or assigning it to an :class:`vtkm::cont::ArrayHandle`.
The following example demonstrates how an :class:`vtkm::cont::UnknownArrayHandle` might be used to load an array whose type is not known until run-time.
.. load-example:: CreateUnknownArrayHandle
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Creating an :class:`vtkm::cont::UnknownArrayHandle`.
It is possible to construct a :class:`vtkm::cont::UnknownArrayHandle` that does not point to any :class:`vtkm::cont::ArrayHandle`.
In this case, the :class:`vtkm::cont::UnknownArrayHandle` is considered not "valid."
Validity can be tested with the :func:`vtkm::cont::UnknownArrayHandle::IsValid` method.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::IsValid
Most of the following operations on :class:`vtkm::cont::UnknownArrayHandle` will fail by throwing an exception if it is not valid.
Note that it is also possible for a :class:`vtkm::cont::UnknownArrayHandle` to contain an empty :class:`vtkm::cont::ArrayHandle`.
A :class:`vtkm::cont::UnknownArrayHandle` that contains a :class:`vtkm::cont::ArrayHandle` but has no memory allocated is still considered valid.
Some basic, human-readable information can be retrieved using the :func:`vtkm::cont::UnknownArrayHandle::PrintSummary` method.
It will print the type and size of the array along with some or all of the values.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::PrintSummary
------------------------------
Allocation
------------------------------
.. index:: unknown array handle; allocation
Data pointed to by an :class:`vtkm::cont::UnknownArrayHandle` is not directly accessible.
However, it is still possible to do some type-agnostic manipulation of the array allocations.
First, it is always possible to call :func:`vtkm::cont::UnknownArrayHandle::GetNumberOfValues` to retrieve the current size of the array.
It is also possible to call :func:`vtkm::cont::UnknownArrayHandle::Allocate` to change the size of an unknown array.
:class:`vtkm::cont::UnknownArrayHandle`'s :func:`vtkm::cont::UnknownArrayHandle::Allocate` works exactly the same as the :func:`vtkm::cont::ArrayHandle::Allocate` in the basic :class:`vtkm::cont::ArrayHandle`.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::GetNumberOfValues
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::Allocate(vtkm::Id, vtkm::CopyFlag, vtkm::cont::Token&) const
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::Allocate(vtkm::Id, vtkm::CopyFlag) const
.. load-example:: UnknownArrayHandleResize
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Checking the size of a :class:`vtkm::cont::ArrayHandle` and resizing it.
It is often the case where you have an :class:`vtkm::cont::UnknownArrayHandle` as the input to an operation and you want to generate an output of the same type.
To handle this case, use the :func:`vtkm::cont::UnknownArrayHandle::NewInstance` method to create a new array of the same type (without having to determine the type).
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::NewInstance
.. load-example:: NonTypeUnknownArrayHandleNewInstance
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Creating a new instance of an unknown array handle.
That said, there are many special array handles described in :chapref:`memory-layout:Memory Layout of Array Handles` and :chapref:`fancy-array-handles:Fancy Array Handles` that either cannot be directly constructed or cannot be used as outputs.
Thus, if you do not know the storage of the array, the similar array returned by :func:`vtkm::cont::UnknownArrayHandle::NewInstance` could be infeasible for use as an output.
Thus, :class:`vtkm::cont::UnknownArrayHandle` also contains the :func:`vtkm::cont::UnknownArrayHandle::NewInstanceBasic` method to create a new array with the same value type but using the basic array storage, which can always be resized and written to.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::NewInstanceBasic
.. load-example:: UnknownArrayHandleBasicInstance
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Creating a new basic instance of an unknown array handle.
It is sometimes the case that you need a new array of a similar type, but that type has to hold floating point values.
For example, if you had an operation that computed a discrete cosine transform on an array, the result would be very inaccurate if stored as integers.
In this case, you would actually want to store the result in an array of floating point values.
For this case, you can use the :func:`vtkm::cont::UnknownArrayHandle::NewInstanceFloatBasic` to create a new basic :class:`vtkm::cont::ArrayHandle` with the component type changed to :type:`vtkm::FloatDefault`.
For example, if the :class:`vtkm::cont::UnknownArrayHandle` stores an :class:`vtkm::cont::ArrayHandle` of type :type:`vtkm::Id`, :func:`vtkm::cont::UnknownArrayHandle::NewInstanceFloatBasic` will create an :class:`vtkm::cont::ArrayHandle` of type :type:`vtkm::FloatDefault`.
If the :class:`vtkm::cont::UnknownArrayHandle` stores an :class:`vtkm::cont::ArrayHandle` of type :type:`vtkm::Id3`, :func:`vtkm::cont::UnknownArrayHandle::NewInstanceFloatBasic` will create an :class:`vtkm::cont::ArrayHandle` of type :type:`vtkm::Vec3f`.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::NewInstanceFloatBasic
.. load-example:: UnknownArrayHandleFloatInstance
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Creating a new array instance with floating point values.
Finally, it may be the case where you are finished using a :class:`vtkm::cont::UnknownArrayHandle`.
If you want to free up memory on the device, which may have limited memory, you can do so with :func:`vtkm::cont::UnknownArrayHandle::ReleaseResourcesExecution`, which will free any memory on the device but preserve the data on the host.
If the data will never be used again, all memory can be freed with :func:`vtkm::cont::UnknownArrayHandle::ReleaseResources`
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::ReleaseResourcesExecution
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::ReleaseResources
------------------------------
Casting to Known Types
------------------------------
.. index::
single: unknown array handle; cast
single: unknown array handle; as array handle
Data pointed to by an :class:`vtkm::cont::UnknownArrayHandle` is not directly
accessible.
To access the data, you need to retrieve the data as an :class:`vtkm::cont::ArrayHandle`.
If you happen to know (or can guess) the type, you can use the :func:`vtkm::cont::UnknownArrayHandle::AsArrayHandle` method to retrieve the array as a specific type.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::AsArrayHandle(vtkm::cont::ArrayHandle<T, S>&) const
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::AsArrayHandle() const
.. load-example:: UnknownArrayHandleAsArrayHandle1
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Retrieving an array of a known type from :class:`vtkm::cont::UnknownArrayHandle`.
:func:`vtkm::cont::UnknownArrayHandle::AsArrayHandle` actually has two forms.
The first form, shown in the previous example, has no arguments and returns the :class:`vtkm::cont::ArrayHandle`.
This form requires you to specify the type of array as a template parameter.
The alternate form has you pass a reference to a concrete :class:`vtkm::cont::ArrayHandle` as an argument as shown in the following example.
This form can imply the template parameter from the argument.
.. load-example:: UnknownArrayHandleAsArrayHandle2
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Alternate form for retrieving an array of a known type from :class:`vtkm::cont::UnknownArrayHandle`.
:func:`vtkm::cont::UnknownArrayHandle::AsArrayHandle` treats :class:`vtkm::cont::ArrayHandleCast` and :class:`vtkm::cont::ArrayHandleMultiplexer` special.
If the special :class:`vtkm::cont::ArrayHandle` can hold the actual array stored, then :func:`vtkm::cont::UnknownArrayHandle::AsArrayHandle` will return successfully.
In the following example, :func:`vtkm::cont::UnknownArrayHandle::AsArrayHandle` returns an array of type :type:`vtkm::Float32` as an :class:`vtkm::cont::ArrayHandleCast` that converts the values to :type:`vtkm::Float64`.
.. load-example:: UnknownArrayHandleAsCastArray
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Getting a cast array handle from an :class:`vtkm::cont::ArrayHandleCast`.
.. didyouknow::
The inverse retrieval works as well.
If you create an :class:`vtkm::cont::UnknownArrayHandle` with an :class:`vtkm::cont::ArrayHandleCast` or :class:`vtkm::cont::ArrayHandleMultiplexer`, you can get the underlying array with :func:`vtkm::cont::UnknownArrayHandle::AsArrayHandle`.
These relationships also work recursively (e.g. an array placed in a cast array that is placed in a multiplexer).
.. index:: unknown array handle; query type
If the :class:`vtkm::cont::UnknownArrayHandle` cannot store its array in the type given to :func:`vtkm::cont::UnknownArrayHandle::AsArrayHandle`, it will throw an exception.
Thus, you should not use :func:`vtkm::cont::UnknownArrayHandle::AsArrayHandle` with types that you are not sure about.
Use the :func:`vtkm::cont::UnknownArrayHandle::CanConvert` method to determine if a given :class:`vtkm::cont::ArrayHandle` type will work with :func:`vtkm::cont::UnknownArrayHandle::AsArrayHandle`.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::CanConvert
.. load-example:: UnknownArrayHandleCanConvert
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Querying whether a given :class:`vtkm::cont::ArrayHandle` can be retrieved from a :class:`vtkm::cont::UnknownArrayHandle`.
By design, :func:`vtkm::cont::UnknownArrayHandle::CanConvert` will return true for types that are not actually stored in the :class:`vtkm::cont::UnknownArrayHandle` but can be retrieved.
If you need to know specifically what type is stored in the :class:`vtkm::cont::UnknownArrayHandle`, you can use the :func:`vtkm::cont::UnknownArrayHandle::IsType` method instead.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::IsType
If you need to query either the value type or the storage, you can use :func:`vtkm::cont::UnknownArrayHandle::IsValueType` and :func:`vtkm::cont::UnknownArrayHandle::IsStorageType`, respectively.
:class:`vtkm::cont::UnknownArrayHandle` also provides :func:`vtkm::cont::UnknownArrayHandle::GetValueTypeName`, :func:`vtkm::cont::UnknownArrayHandle::GetStorageTypeName`, and :func:`vtkm::cont::UnknownArrayHandle::GetArrayTypeName` for debugging purposes.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::IsValueType
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::IsStorageType
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::GetValueTypeName
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::GetStorageTypeName
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::GetArrayTypeName
.. commonerrors::
:func:`vtkm::cont::UnknownArrayHandle::CanConvert` is almost always safer to use than :func:`vtkm::cont::UnknownArrayHandle::IsType` or its similar methods.
Even though :func:`vtkm::cont::UnknownArrayHandle::IsType` reflects the actual array type, :func:`vtkm::cont::UnknownArrayHandle::CanConvert` better describes how :class:`vtkm::cont::UnknownArrayHandle` will behave.
If you do not know the exact type of the array contained in an :class:`vtkm::cont::UnknownArrayHandle`, a brute force method to get the data out is to copy it to an array of a known type.
This can be done with the :func:`vtkm::cont::UnknownArrayHandle::DeepCopyFrom` method, which will copy the contents of a target array into an existing array of a (potentially) different type.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::DeepCopyFrom(const vtkm::cont::UnknownArrayHandle&)
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::DeepCopyFrom(const vtkm::cont::UnknownArrayHandle&) const
.. load-example:: UnknownArrayHandleDeepCopy
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Deep copy arrays of unknown types.
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.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::CopyShallowIfPossible(const vtkm::cont::UnknownArrayHandle&)
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::CopyShallowIfPossible(const vtkm::cont::UnknownArrayHandle&) const
.. load-example:: UnknownArrayHandleShallowCopy
: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
----------------------------------------
.. index:: unknown array handle; cast
Using :func:`vtkm::cont::UnknownArrayHandle::AsArrayHandle` is fine as long as the correct types are known, but often times they are not.
For this use case :class:`vtkm::cont::UnknownArrayHandle` has a method named :func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypes` that attempts to cast the array to some set of types.
The :func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypes` method accepts a functor to run on the appropriately cast array.
The functor must have an overloaded const parentheses operator that accepts an :class:`vtkm::cont::ArrayHandle` of the appropriate type.
You also have to specify two template parameters that specify a :class:`vtkm::List` of value types to try and a :class:`vtkm::List` of storage types to try, respectively.
The macros :c:macro:`VTKM_DEFAULT_TYPE_LIST` and :c:macro:`VTKM_DEFAULT_STORAGE_LIST` are often used when nothing more specific is known.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::CastAndCallForTypes
.. load-example:: UsingCastAndCallForTypes
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Operating on an :class:`vtkm::cont::UnknownArrayHandle` with :func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypes`.
.. didyouknow::
The first (required) argument to :func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypes` is the functor to call with the array.
You can supply any number of optional arguments after that.
Those arguments will be passed directly to the functor.
This makes it easy to pass state to the functor.
.. didyouknow::
When an :class:`vtkm::cont::UnknownArrayHandle` is used in place of an :class:`vtkm::cont::ArrayHandle` as an argument to a worklet invocation, it will internally use :func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypes` to attempt to call the worklet with an :class:`vtkm::cont::ArrayHandle` of the correct type.
:class:`vtkm::cont::UnknownArrayHandle` has a simple subclass named :class:`vtkm::cont::UncertainArrayHandle` for use when you can narrow the array to a finite set of types.
:class:`vtkm::cont::UncertainArrayHandle` has two template parameters that must be specified: a :class:`vtkm::List` of value types and a :class:`vtkm::List` of storage types.
.. doxygenclass:: vtkm::cont::UncertainArrayHandle
:class:`vtkm::cont::UncertainArrayHandle` has a method named :func:`vtkm::cont::UncertainArrayHandle::CastAndCall` that behaves the same as :func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypes` except that you do not have to specify the types to try.
Instead, the types are taken from the template parameters of the :class:`vtkm::cont::UncertainArrayHandle` itself.
.. doxygenfunction:: vtkm::cont::UncertainArrayHandle::CastAndCall
.. load-example:: UncertainArrayHandle
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Using :class:`vtkm::cont::UncertainArrayHandle` to cast and call a functor.
.. didyouknow::
Like with :class:`vtkm::cont::UnknownArrayHandle`, if an :class:`vtkm::cont::UncertainArrayHandle` is used in a worklet invocation, it will internally use :func:`vtkm::cont::UncertainArrayHandle::CastAndCall`.
This provides a convenient way to specify what array types the invoker should try.
Both :class:`vtkm::cont::UnknownArrayHandle` and :class:`vtkm::cont::UncertainArrayHandle` provide a method named :func:`vtkm::cont::UnknownArrayHandle::ResetTypes` to redefine the types to try.
:func:`vtkm::cont::UncertainArrayHandle::ResetTypes` has two template parameters that are the :class:`vtkm::List`'s of value and storage types.
:func:`vtkm::cont::UnknownArrayHandle::ResetTypes` returns a new :class:`vtkm::cont::UncertainArrayHandle` with the given types.
This is a convenient way to pass these types to functions.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::ResetTypes
:class:`vtkm::cont::UncertainArrayHandle` additionally has methods named :func:`vtkm::cont::UncertainArrayHandle::ResetValueTypes` and :func:`vtkm::cont::UncertainArrayHandle::ResetStorageTypes` to reset the value types and storage types, respectively, without modifying the other.
.. doxygenfunction:: vtkm::cont::UncertainArrayHandle::ResetValueTypes
.. doxygenfunction:: vtkm::cont::UncertainArrayHandle::ResetStorageTypes
.. load-example:: UnknownArrayResetTypes
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Resetting the types of an :class:`vtkm::cont::UnknownArrayHandle`.
.. commonerrors::
Because it returns an :class:`vtkm::cont::UncertainArrayHandle`, you need to include :file:`vtkm/cont/UncertainArrayHandle.h` if you use :func:`vtkm::cont::UnknownArrayHandle::ResetTypes`.
This is true even if you do not directly use the returned object.
------------------------------
Accessing Truly Unknown Arrays
------------------------------
So far in :secref:`unknown-array-handle:Casting to Known Types` and :secref:`unknown-array-handle:Casting to a List of Potential Types` we explored how to access the data in an :class:`vtkm::cont::UnknownArrayHandle` when you actually know the array type or can narrow down the array type to some finite number of candidates.
But what happens if you cannot practically narrow down the types in the :class:`vtkm::cont::UnknownArrayHandle`?
For this case, :class:`vtkm::cont::UnknownArrayHandle` provides mechanisms for extracting data knowing little or nothing about the types.
Cast with Floating Point Fallback
========================================
.. index:: unknown array handle; fallback
The problem with :func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypes` and :func:`vtkm::cont::UncertainArrayHandle::CastAndCall` is that you can only list a finite amount of value types and storage types to try.
If you encounter an :class:`vtkm::cont::UnknownArrayHandle` containing a different :class:`vtkm::cont::ArrayHandle` type, the cast and call will simply fail.
Since the compiler must create a code path for each possible :class:`vtkm::cont::ArrayHandle` type, it may not even be feasible to list all known types.
:func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback` works around this problem by providing a fallback in case the contained :class:`vtkm::cont::ArrayHandle` does not match any of the types tried.
If none of the types match, then :func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback` will copy the data to a :class:`vtkm::cont::ArrayHandle` with :type:`vtkm::FloatDefault` values (or some compatible :class:`vtkm::Vec` with :type:`vtkm::FloatDefault` components) and basic storage.
It will then attempt to match again with this copied array.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback
.. load-example:: CastAndCallForTypesWithFloatFallback
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Cast and call a functor from an :class:`vtkm::cont::UnknownArrayHandle` with a float fallback.
In this case, we do not have to list every possible type because the array will be copied to a known type if nothing matches.
Note that when using :func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback`, you still need to include an appropriate type based on :type:`vtkm::FloatDefault` in the value type list and :class:`vtkm::cont::StorageTagBasic` in the storage list so that the copied array can match.
:class:`vtkm::cont::UncertainArrayHandle` has a matching method named :func:`vtkm::cont::UncertainArrayHandle::CastAndCallWithFloatFallback` that does the same operation using the types specified in the :class:`vtkm::cont::UncertainArrayHandle`.
.. doxygenfunction:: vtkm::cont::UncertainArrayHandle::CastAndCallWithFloatFallback
.. load-example:: CastAndCallWithFloatFallback
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Cast and call a functor from an :class:`vtkm::cont::UncertainArrayHandle` with a float fallback.
Extracting Components
==============================
Using a floating point fallback allows you to use arrays of unknown types in most circumstances, but it does have a few drawbacks.
First, and most obvious, is that you may not operate on the data in its native format.
If you want to preserve the integer format of data, this may not be the method.
Second, the fallback requires a copy of the data.
If :func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback` does not match the type of the array, it copies the array to a new type that (hopefully) can be matched.
Third, :func:`vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback` still needs to match the number of components in each array value.
If the contained :class:`vtkm::cont::ArrayHandle` contains values that are :class:`vtkm::Vec`'s of length 2, then the data will be copied to an array of :type:`vtkm::Vec2f`'s.
If :type:`vtkm::Vec2f` is not included in the types to try, the cast and call will still fail.
.. index:: unknown array handle; extract component
A way to get around these problems is to extract a single component from the array.
You can use the :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` method to return an :class:`vtkm::cont::ArrayHandle` with the values for a given component for each value in the array.
The type of the returned :class:`vtkm::cont::ArrayHandle` will be the same regardless of the actual array type stored in the :class:`vtkm::cont::UnknownArrayHandle`.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::ExtractComponent
:func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` must be given a template argument for the base component type.
The following example extracts the first component of all :class:`vtkm::Vec` values in an :class:`vtkm::cont::UnknownArrayHandle` assuming that the component is of type :type:`vtkm::FloatDefault` (:exlineref:`ex:UnknownArrayExtractComponent:Call`).
.. load-example:: UnknownArrayExtractComponent
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Extracting the first component of every value in an :class:`vtkm::cont::UnknownArrayHandle`.
The code in :numref:`ex:UnknownArrayExtractComponent` works with any array with values based on the default floating point type.
If the :class:`vtkm::cont::UnknownArrayHandle` has an array containing :type:`vtkm::FloatDefault`, then the returned array has all the same values.
If the :class:`vtkm::cont::UnknownArrayHandle` contains values of type :type:`vtkm::Vec3f`, then each value in the returned array will be the first component of this array.
If the :class:`vtkm::cont::UnknownArrayHandle` really contains an array with incompatible value types (such as ``vtkm::cont::ArrayHandle<vtkm::Id>``), then an :class:`vtkm::cont::ErrorBadType` will be thrown.
To check if the :class:`vtkm::cont::UnknownArrayHandle` contains an array of a compatible type, use the :func:`vtkm::cont::UnknownArrayHandle::IsBaseComponentType` method to check the component type being used as the template argument to :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent`.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::IsBaseComponentType
.. load-example:: UnknownArrayBaseComponentType
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Checking the base component type in an :class:`vtkm::cont::UnknownArrayHandle`.
it is also possible to get a name for the base component type (mostly for debugging purposes) with :func:`vtkm::cont::UnknownArrayHandle::GetBaseComponentTypeName`.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::GetBaseComponentTypeName
You will often need to query the number of components that can be extracted from the array.
This can be queried with :func:`vtkm::cont::UnknownArrayHandle::GetNumberOfComponentsFlat`.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::GetNumberOfComponentsFlat
This section started with the motivation of getting data from an :class:`vtkm::cont::UnknownArrayHandle` without knowing anything about the type, yet :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` still requires a type parameter.
However, by limiting the type needed to the base component type, you only need to check the base C types (standard integers and floating points) available in C++.
You do not need to know whether these components are arranged in :class:`vtkm::Vec`'s or the size of the :class:`vtkm::Vec`.
A general implementation of an algorithm might have to deal with scalars as well as :class:`vtkm::Vec`'s of size 2, 3, and 4.
If we consider operations on tensors, :class:`vtkm::Vec`'s of size 6 and 9 can be common as well.
But when using :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent`, a single condition can handle any potential :class:`vtkm::Vec` size.
Another advantage of :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` is that the type of storage does not need to be specified.
:func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` works with any type of :class:`vtkm::cont::ArrayHandle` storage (with some caveats).
So, :numref:`ex:UnknownArrayExtractComponent` works equally as well with :class:`vtkm::cont::ArrayHandleBasic`, :class:`vtkm::cont::ArrayHandleSOA`, :class:`vtkm::cont::ArrayHandleUniformPointCoordinates`, :class:`vtkm::cont::ArrayHandleCartesianProduct`, and many others.
Trying to capture all reasonable types of arrays could easily require hundreds of conditions, all of which and more can be captured with :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` and the roughly 12 basic C data types.
In practice, you often only really have to worry about floating point components, which further reduces the cases down to (usually) 2.
:func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` works by returning an :class:`vtkm::cont::ArrayHandleStride`.
This is a special :class:`vtkm::cont::ArrayHandle` that can access data buffers by skipping values at regular intervals.
This allows it to access data packed in different ways such as :class:`vtkm::cont::ArrayHandleBasic`, :class:`vtkm::cont::ArrayHandleSOA`, and many others.
That said, :class:`vtkm::cont::ArrayHandleStride` is not magic, so if it cannot directly access memory, some or all of it may be copied.
If you are attempting to use the array from :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` as an output array, pass :enumerator:`vtkm::CopyFlag::Off` as a second argument.
This will ensure that data are not copied so that any data written will go to the original array (or throw an exception if this cannot be done).
.. commonerrors::
Although :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` will technically work with any :class:`vtkm::cont::ArrayHandle` (of simple :class:`vtkm::Vec` types), it may require a very inefficient memory copy.
Pay attention if :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` issues a warning about an inefficient memory copy.
This is likely a serious performance issue, and the data should be retrieved in a different way (or better yet stored in a different way).
Extracting All Components
==============================
:numref:`ex:UnknownArrayExtractComponent` accesses the first component of each :class:`vtkm::Vec` in an array.
But in practice you usually want to operate on all components stored in the array.
A simple solution is to iterate over each component.
.. load-example:: UnknownArrayExtractComponentsMultiple
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Extracting each component from an :class:`vtkm::cont::UnknownArrayHandle`.
To ensure that the type of the extracted component is a basic C type, the :class:`vtkm::Vec` values are "flattened."
That is, they are treated as if they are a single level :class:`vtkm::Vec`.
For example, if you have a value type of ``vtkm::Vec<vtkm::Id3, 2>``, :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` treats this type as ``vtkm::Vec<vtkm::Id, 6>``.
This allows you to extract the components as type :type:`vtkm::Id` rather than having a special case for :type:`vtkm::Id3`.
Although iterating over components works fine, it can be inconvenient.
An alternate mechanism is to use :func:`vtkm::cont::UnknownArrayHandle::ExtractArrayFromComponents` to get all the components at once.
:func:`vtkm::cont::UnknownArrayHandle::ExtractArrayFromComponents` works like :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` except that instead of returning an :class:`vtkm::cont::ArrayHandleStride`, it returns a special :class:`vtkm::cont::ArrayHandleRecombineVec` that behaves like an :class:`vtkm::cont::ArrayHandle` to reference all component arrays at once.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::ExtractArrayFromComponents
.. load-example:: UnknownArrayExtractArrayFromComponents
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Extracting all components from an :class:`vtkm::cont::UnknownArrayHandle` at once.
.. commonerrors::
Although it has the same interface as other :class:`vtkm::cont::ArrayHandle`'s, :class:`vtkm::cont::ArrayHandleRecombineVec` has a special value type that breaks some conventions.
For example, when used in a worklet, the value type passed from this array to the worklet cannot be replicated.
That is, you cannot create a temporary stack value of the same type.
Because you still need to specify a base component type, you will likely still need to check several types to safely extract data from an :class:`vtkm::cont::UnknownArrayHandle` by component.
To do this automatically, you can use the :func:`vtkm::cont::UnknownArrayHandle::CastAndCallWithExtractedArray`.
This method behaves similarly to :func:`vtkm::cont::UncertainArrayHandle::CastAndCall` except that it internally uses :func:`vtkm::cont::UnknownArrayHandle::ExtractArrayFromComponents`.
.. doxygenfunction:: vtkm::cont::UnknownArrayHandle::CastAndCallWithExtractedArray
.. load-example:: UnknownArrayCallWithExtractedArray
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Calling a functor for nearly any type of array stored in an :class:`vtkm::cont::UnknownArrayHandle`.
------------------------------
Mutability
------------------------------
.. index:: unknown array handle; const
One subtle feature of :class:`vtkm::cont::UnknownArrayHandle` is that the class is, in principle, a pointer to an array pointer.
This means that the data in an :class:`vtkm::cont::UnknownArrayHandle` is always mutable even if the class is declared ``const``.
The upshot is that you can pass output arrays as constant :class:`vtkm::cont::UnknownArrayHandle` references.
.. load-example:: UnknownArrayConstOutput
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Using a ``const`` :class:`vtkm::cont::UnknownArrayHandle` for a function output.
Although it seems strange, there is a good reason to allow an output :class:`vtkm::cont::UnknownArrayHandle` to be ``const``.
It allows a typed :class:`vtkm::cont::ArrayHandle` to be used as the argument to the function.
In this case, the compiler will automatically convert the :class:`vtkm::cont::ArrayHandle` to a :class:`vtkm::cont::UnknownArrayHandle`.
When C++ creates objects like this, they can only be passed a constant reference, an Rvalue reference, or by value.
So, declaring the output parameter as ``const`` :class:`vtkm::cont::UnknownArrayHandle` allows it to be used for code like this.
.. load-example:: UseUnknownArrayConstOutput
:file: GuideExampleUnknownArrayHandle.cxx
:caption: Passing an :class:`vtkm::cont::ArrayHandle` as an output :class:`vtkm::cont::UnknownArrayHandle`.
Of course, you could also declare the output by value instead of by reference, but this has the same semantics with extra internal pointer management.
.. didyouknow::
When possible, it is better to pass a :class:`vtkm::cont::UnknownArrayHandle` as a constant reference (or by value) rather than a mutable reference, even if the array contents are going to be modified.
This allows the function to support automatic conversion of an output :class:`vtkm::cont::ArrayHandle`.
So if a constant :class:`vtkm::cont::UnknownArrayHandle` can have its contents modified, what is the difference between a constant reference and a non-constant reference?
The difference is that the constant reference can change the array's content, but not the array itself.
If you want to do operations like doing a shallow copy or changing the underlying type of the array, a non-constant reference is needed.

@ -669,6 +669,7 @@ int main(int argc, char* argv[])
if (computeHierarchicalVolumetricBranchDecomposition)
{
vtkm::filter::scalar_topology::DistributedBranchDecompositionFilter bd_filter;
bd_filter.SetTimingsLogLevel(timingsLogLevel);
bd_result = bd_filter.Execute(result);
}
currTime = totalTime.GetElapsedTime();
@ -681,8 +682,13 @@ int main(int argc, char* argv[])
{
vtkm::filter::scalar_topology::SelectTopVolumeContoursFilter tp_filter;
tp_filter.SetSavedBranches(numBranches);
tp_filter.SetMarchingCubes(useMarchingCubes);
tp_filter.SetTimingsLogLevel(timingsLogLevel);
tp_result = tp_filter.Execute(bd_result);
}
currTime = totalTime.GetElapsedTime();
vtkm::Float64 topVolBranchTime = currTime - prevTime;
prevTime = currTime;
// Save output
if (saveOutputData)
@ -740,14 +746,19 @@ int main(int argc, char* argv[])
for (vtkm::Id ds_no = 0; print_to_files && ds_no < max_blocks_to_print; ++ds_no)
{
auto ds = tp_result.GetPartition(ds_no);
std::string topVolumeBranchFileName = std::string("TopVolumeBranch_Rank_") +
std::to_string(static_cast<int>(rank)) + std::string("_Block_") +
std::to_string(static_cast<int>(ds_no)) + std::string(".txt");
std::ofstream topVolumeBranchStream(topVolumeBranchFileName.c_str());
auto topVolBranchGRId = ds.GetField("TopVolumeBranchGlobalRegularIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto topVolBranchUpperEnd = ds.GetField("TopVolumeBranchUpperEnd")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto topVolBranchLowerEnd = ds.GetField("TopVolumeBranchLowerEnd")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto topVolBranchVolume = ds.GetField("TopVolumeBranchVolume")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
@ -761,10 +772,11 @@ int main(int argc, char* argv[])
.AsArrayHandle<vtkm::cont::ArrayHandle<ValueType>>()
.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)
@ -785,6 +797,111 @@ int main(int argc, char* argv[])
isoValuesStream << std::endl;
}
for (vtkm::Id ds_no = 0; ds_no < tp_result.GetNumberOfPartitions(); ++ds_no)
{
auto bd_ds = bd_result.GetPartition(ds_no);
auto ds = tp_result.GetPartition(ds_no);
std::string branchDecompositionVolumeFileName =
std::string("BranchDecompositionVolume_Rank_") +
std::to_string(static_cast<int>(rank)) + std::string("_Block_") +
std::to_string(static_cast<int>(ds_no)) + std::string(".txt");
std::ofstream bdVolStream(branchDecompositionVolumeFileName.c_str());
auto upperEndGRId = bd_ds.GetField("UpperEndGlobalRegularIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto lowerEndGRId = bd_ds.GetField("LowerEndGlobalRegularIds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto branchSaddleEpsilon = ds.GetField("BranchSaddleEpsilon")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto branchVolume = ds.GetField("BranchVolume")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.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;
}
std::string isosurfaceFileName = std::string("Isosurface_Rank_") +
std::to_string(static_cast<int>(rank)) + std::string("_Block_") +
std::to_string(static_cast<int>(ds_no)) + std::string(".txt");
std::ofstream isosurfaceStream(isosurfaceFileName.c_str());
auto isosurfaceEdgesFrom = ds.GetField("IsosurfaceEdgesFrom")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Vec3f_64>>()
.ReadPortal();
auto isosurfaceEdgesTo = ds.GetField("IsosurfaceEdgesTo")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Vec3f_64>>()
.ReadPortal();
auto isosurfaceEdgesLabels = ds.GetField("IsosurfaceEdgesLabels")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto isosurfaceEdgesOrders = ds.GetField("IsosurfaceEdgesOrders")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto isosurfaceEdgesOffset = ds.GetField("IsosurfaceEdgesOffset")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>()
.ReadPortal();
auto isosurfaceIsoValue = ds.GetField("IsosurfaceIsoValue")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<ValueType>>()
.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;
}
}
}
}
}
else
@ -892,6 +1009,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"

@ -587,19 +587,19 @@ public:
} // namespace internal
/// \brief A grouping of `ArrayHandleStride`s into an `ArrayHandle` of `Vec`s.
/// @brief A grouping of `ArrayHandleStride`s into an `ArrayHandle` of `vtkm::Vec`s.
///
/// The main intention of `ArrayHandleStride` is to pull out a component of an
/// `ArrayHandle` without knowing there `ArrayHandle`'s storage or `Vec` shape.
/// `ArrayHandle` without knowing there `ArrayHandle`'s storage or `vtkm::Vec` shape.
/// However, usually you want to do an operation on all the components together.
/// `ArrayHandleRecombineVec` implements the functionality to easily take a
/// group of extracted components and treat them as a single `ArrayHandle` of
/// `Vec` values.
/// `vtkm::Vec` values.
///
/// Note that caution should be used with `ArrayHandleRecombineVec` because the
/// size of the `Vec` values is not known at compile time. Thus, the value
/// size of the `vtkm::Vec` values is not known at compile time. Thus, the value
/// type of this array is forced to a special `RecombineVec` class that can cause
/// surprises if treated as a `Vec`. In particular, the static `NUM_COMPONENTS`
/// surprises if treated as a `vtkm::Vec`. In particular, the static `NUM_COMPONENTS`
/// expression does not exist. Furthermore, new variables of type `RecombineVec`
/// cannot be created. This means that simple operators like `+` will not work
/// because they require an intermediate object to be created. (Equal operators
@ -618,17 +618,33 @@ public:
(vtkm::cont::ArrayHandle<internal::detail::RecombinedValueType<ComponentType>,
vtkm::cont::internal::StorageTagRecombineVec>));
/// @brief Return the number of components in each value of the array.
///
/// This is also equal to the number of component arrays referenced by this
/// fancy array.
///
/// `ArrayHandleRecombineVec` always stores flat Vec values. As such, this number
/// of components is the same as the number of base components.
vtkm::IdComponent GetNumberOfComponents() const
{
return StorageType::GetNumberOfComponents(this->GetBuffers());
}
/// @brief Get the array storing the values for a particular component.
///
/// The returned array is a `vtkm::cont::ArrayHandleStride`. It is possible
/// that the returned arrays from different components reference the same area
/// of physical memory (usually referencing values interleaved with each other).
vtkm::cont::ArrayHandleStride<ComponentType> GetComponentArray(
vtkm::IdComponent componentIndex) const
{
return StorageType::ArrayForComponent(this->GetBuffers(), componentIndex);
}
/// @brief Add a component array.
///
/// `AppendComponentArray()` provides an easy way to build an `ArrayHandleRecombineVec`
/// by iteratively adding the component arrays.
void AppendComponentArray(
const vtkm::cont::ArrayHandle<ComponentType, vtkm::cont::StorageTagStride>& array)
{

@ -109,10 +109,10 @@ public:
/// \brief Call a functor using the underlying array type with a float cast fallback.
///
/// `CastAndCallWithFloatFallback` attempts to cast the held array to a specific value type,
/// `CastAndCallWithFloatFallback()` attempts to cast the held array to a specific value type,
/// and then calls the given functor with the cast array. If the underlying array
/// does not match any of the requested array types, the array is copied to a new
/// `ArrayHandleBasic` with `FloatDefault` components in its value and attempts to
/// `ArrayHandleBasic` with `vtkm::FloatDefault` components in its value and attempts to
/// cast to those types.
///
template <typename Functor, typename... Args>

@ -397,7 +397,7 @@ inline std::shared_ptr<UnknownAHContainer> MakeUnknownAHContainerFunctor::operat
template <typename ValueTypeList, typename StorageTypeList>
class UncertainArrayHandle;
/// \brief An ArrayHandle of an unknown value type and storage.
/// @brief An ArrayHandle of an unknown value type and storage.
///
/// `UnknownArrayHandle` holds an `ArrayHandle` object using runtime polymorphism
/// to manage different value and storage types rather than compile-time templates.
@ -408,14 +408,14 @@ class UncertainArrayHandle;
/// types.
///
/// To interface between the runtime polymorphism and the templated algorithms
/// in VTK-m, `UnknownArrayHandle` contains a method named `CastAndCallForTypes`
/// in VTK-m, `UnknownArrayHandle` contains a method named `CastAndCallForTypes()`
/// that determines the correct type from some known list of value types and
/// storage. This mechanism is used internally by VTK-m's worklet invocation
/// mechanism to determine the type when running algorithms.
///
/// If the `UnknownArrayHandle` is used in a context where the possible array
/// types can be whittled down to a finite list (or you have to), you can
/// specify lists of value types and storage using the `ResetTypesAndStorage`
/// specify lists of value types and storage using the `ResetTypesAndStorage()`
/// method. This will convert this object to an `UncertainArrayHandle` of the
/// given types. In cases where a finite set of types need to specified but
/// there is no known subset, `VTKM_DEFAULT_TYPE_LIST` and
@ -444,7 +444,7 @@ public:
{
}
/// \brief Returns whether an array is stored in this `UnknownArrayHandle`.
/// @brief Returns whether an array is stored in this `UnknownArrayHandle`.
///
/// If the `UnknownArrayHandle` is constructed without an `ArrayHandle`, it
/// will not have an underlying type, and therefore the operations will be
@ -452,7 +452,7 @@ public:
/// `ArrayHandle`.
VTKM_CONT bool IsValid() const;
/// \brief Create a new array of the same type as this array.
/// @brief Create a new array of the same type as this array.
///
/// This method creates a new array that is the same type as this one and
/// returns a new `UnknownArrayHandle` for it. This method is convenient when
@ -460,7 +460,7 @@ public:
///
VTKM_CONT UnknownArrayHandle NewInstance() const;
/// \brief Create a new `ArrayHandleBasic` with the same `ValueType` as this array.
/// @brief Create a new `ArrayHandleBasic` with the same `ValueType` as this array.
///
/// This method creates a new `ArrayHandleBasic` that has the same `ValueType` as the
/// array held by this one and returns a new `UnknownArrayHandle` for it. This method
@ -469,7 +469,7 @@ public:
///
VTKM_CONT UnknownArrayHandle NewInstanceBasic() const;
/// \brief Create a new `ArrayHandleBasic` with the base component of `FloatDefault`
/// @brief Create a new `ArrayHandleBasic` with the base component of `vtkm::FloatDefault`
///
/// This method creates a new `ArrayHandleBasic` that has a `ValueType` that is similar
/// to the array held by this one except that the base component type is replaced with
@ -487,22 +487,22 @@ public:
///
VTKM_CONT UnknownArrayHandle NewInstanceFloatBasic() const;
/// \brief Returns the name of the value type stored in the array.
/// @brief Returns the name of the value type stored in the array.
///
/// Returns an empty string if no array is stored.
VTKM_CONT std::string GetValueTypeName() const;
/// \brief Returns the name of the base component of the value type stored in the array.
/// @brief Returns the name of the base component of the value type stored in the array.
///
/// Returns an empty string if no array is stored.
VTKM_CONT std::string GetBaseComponentTypeName() const;
/// \brief Returns the name of the storage tag for the array.
/// @brief Returns the name of the storage tag for the array.
///
/// Returns an empty string if no array is stored.
VTKM_CONT std::string GetStorageTypeName() const;
/// \brief Returns a string representation of the underlying data type.
/// @brief Returns a string representation of the underlying data type.
///
/// The returned string will be of the form `vtkm::cont::ArrayHandle<T, S>` rather than the name
/// of an actual subclass. If no array is stored, an empty string is returned.
@ -525,7 +525,7 @@ public:
return this->IsStorageTypeImpl(typeid(StorageType));
}
/// \brief Returns true if this array's `ValueType` has the provided base component type.
/// @brief Returns true if this array's `ValueType` has the provided base component type.
///
/// The base component type is the recursive component type of any `Vec`-like object. So
/// if the array's `ValueType` is `vtkm::Vec<vtkm::Float32, 3>`, then the base component
@ -542,17 +542,17 @@ public:
return this->IsBaseComponentTypeImpl(detail::UnknownAHComponentInfo::Make<BaseComponentType>());
}
/// \brief Returns true if this array matches the ArrayHandleType template argument.
/// @brief Returns true if this array matches the ArrayHandleType template argument.
///
/// Note that `UnknownArrayHandle` has some special handling for `ArrayHandleCast` and
/// `ArrayHandleMultiplexer`. If you stored an array of one of these types into an
/// `UnknownArrayHandle`, the type of the underlying array will change and `IsType`
/// `UnknownArrayHandle`, the type of the underlying array will change and `IsType()`
/// will fail. However, you can still get the array back out as that type using
/// `AsArrayHandle`.
///
/// Use the `CanConvert` method instead to determine if the `UnknownArrayHandle`
/// Use the `CanConvert()` method instead to determine if the `UnknownArrayHandle`
/// contains an array that "matches" the array of a given type. Under most
/// circumstances, you should prefer `CanConvert` over `IsType`.
/// circumstances, you should prefer `CanConvert()` over `IsType()`.
///
template <typename ArrayHandleType>
VTKM_CONT bool IsType() const
@ -562,7 +562,7 @@ public:
this->IsStorageType<typename ArrayHandleType::StorageTag>());
}
/// \brief Assigns potential value and storage types.
/// @brief Assigns potential value and storage types.
///
/// Calling this method will return an `UncertainArrayHandle` with the provided
/// value and storage type lists. The returned object will hold the same
@ -575,11 +575,11 @@ public:
NewValueTypeList = NewValueTypeList{},
NewStorageTypeList = NewStorageTypeList{}) const;
/// \brief Returns the number of values in the array.
/// @brief Returns the number of values in the array.
///
VTKM_CONT vtkm::Id GetNumberOfValues() const;
/// \brief Returns the number of components for each value in the array.
/// @brief Returns the number of components for each value in the array.
///
/// If the array holds `vtkm::Vec` objects, this will return the number of components
/// in each value. If the array holds a basic C type (such as `float`), this will return 1.
@ -588,7 +588,7 @@ public:
///
VTKM_CONT vtkm::IdComponent GetNumberOfComponents() const;
/// \brief Returns the total number of components for each value in the array.
/// @brief Returns the total number of components for each value in the array.
///
/// If the array holds `vtkm::Vec` objects, this will return the total number of components
/// in each value assuming the object is flattened out to one level of `Vec` objects.
@ -606,21 +606,19 @@ public:
///
VTKM_CONT vtkm::IdComponent GetNumberOfComponentsFlat() const;
/// \brief Reallocate the data in the array.
/// @brief Reallocate the data in the array.
///
/// The allocation works the same as the `Allocate` method of `vtkm::cont::ArrayHandle`.
///
/// @{
/// The allocation works the same as the `Allocate()` method of `vtkm::cont::ArrayHandle`.
VTKM_CONT void Allocate(vtkm::Id numValues,
vtkm::CopyFlag preserve,
vtkm::cont::Token& token) const;
/// @copydoc Allocate
VTKM_CONT void Allocate(vtkm::Id numValues, vtkm::CopyFlag preserve = vtkm::CopyFlag::Off) const;
/// @}
/// \brief Determine if the contained array can be passed to the given array type.
/// @brief Determine if the contained array can be passed to the given array type.
///
/// This method will return true if calling `AsArrayHandle` of the given type will
/// succeed. The result is similar to `IsType`, and if `IsType` returns true, then
/// This method will return true if calling `AsArrayHandle()` of the given type will
/// succeed. The result is similar to `IsType()`, and if `IsType()` returns true, then
/// this will return true. However, this method will also return true for other
/// types such as an `ArrayHandleMultiplexer` that can contain the array.
///
@ -651,24 +649,23 @@ private:
}
public:
///@{
/// Returns this array cast appropriately and stored in the given `ArrayHandle` type.
/// Throws an `ErrorBadType` if the stored array cannot be stored in the given array type.
/// Use the `CanConvert` method to determine if the array can be returned with the given type.
///
/// Throws a `vtkm::cont::ErrorBadType` if the stored array cannot be stored in the given
/// array type. Use the `CanConvert()` method to determine if the array can be returned
/// with the given type.
template <typename T, typename S>
VTKM_CONT void AsArrayHandle(vtkm::cont::ArrayHandle<T, S>& array) const
{
this->BaseAsArrayHandle(array);
}
/// @copydoc AsArrayHandle
template <typename T>
VTKM_CONT void AsArrayHandle(vtkm::cont::ArrayHandle<T>& array) const;
/// @copydoc AsArrayHandle
template <typename T, typename... Ss>
VTKM_CONT void AsArrayHandle(
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagMultiplexer<Ss...>>& array) const;
/// @copydoc AsArrayHandle
template <typename TargetT, typename SourceT, typename SourceS>
VTKM_CONT void AsArrayHandle(
vtkm::cont::ArrayHandle<TargetT, vtkm::cont::StorageTagCast<SourceT, SourceS>>& array) const
@ -677,7 +674,7 @@ public:
array = vtkm::cont::ArrayHandleCast<TargetT, ContainedArrayType>(
this->AsArrayHandle<ContainedArrayType>());
}
/// @copydoc AsArrayHandle
template <typename T>
VTKM_CONT void AsArrayHandle(
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagRuntimeVec>& array) const
@ -697,7 +694,7 @@ public:
this->BaseAsArrayHandle(array);
}
}
/// @copydoc AsArrayHandle
template <typename ArrayType>
VTKM_CONT ArrayType AsArrayHandle() const
{
@ -706,12 +703,12 @@ public:
this->AsArrayHandle(array);
return array;
}
///@}
#ifdef VTKM_MSVC
VTKM_DEPRECATED_SUPPRESS_END
#endif
/// \brief Deep copies data from another `UnknownArrayHandle`.
/// @brief Deep copies data from another `UnknownArrayHandle`.
///
/// This method takes an `UnknownArrayHandle` and deep copies data from it.
///
@ -720,60 +717,60 @@ public:
///
void DeepCopyFrom(const vtkm::cont::UnknownArrayHandle& source);
/// \brief Deep copies data from another `UnknownArrayHandle`.
/// @brief Deep copies data from another `UnknownArrayHandle`.
///
/// This method takes an `UnknownArrayHandle` and deep copies data from it.
///
/// If this object does not point to an existing `ArrayHandle`, this const version
/// of `DeepCopyFrom` throws an exception.
/// of `DeepCopyFrom()` throws an exception.
///
void DeepCopyFrom(const vtkm::cont::UnknownArrayHandle& source) const;
/// \brief Attempts a shallow copy of an array or a deep copy if that is not possible.
/// @brief Attempts a shallow copy of an array or a deep copy if that is not possible.
///
/// This method takes an `UnknownArrayHandle` and attempts to perform a shallow copy.
/// This shallow copy occurs if this object points to an `ArrayHandle` of the same type
/// or does not point to any `ArrayHandle` at all. If this is not possible, then
/// the array is deep copied.
///
/// This method is roughly equivalent to the `ArrayCopyShallowIfPossible` function
/// 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 `ArrayCopyShallowIfPossible` does require
/// having to use a device compiler (whereas `vtkm::cont::ArrayCopyShallowIfPossible()` does require
/// a device device compiler).
///
void CopyShallowIfPossible(const vtkm::cont::UnknownArrayHandle& source);
/// \brief Attempts a shallow copy of an array or a deep copy if that is not possible.
/// @brief Attempts a shallow copy of an array or a deep copy if that is not possible.
///
/// This method takes an `UnknownArrayHandle` and attempts to perform a shallow copy.
/// This shallow copy occurs if this object points to an `ArrayHandle` of the same type.
/// If the types are incompatible, then the array is deep copied.
///
/// If this object does not point to an existing `ArrayHandle`, this const version
/// of `CopyShallowIfPossible` throws an exception.
/// of `CopyShallowIfPossible()` throws an exception.
///
/// This method is roughly equivalent to the `ArrayCopyShallowIfPossible` function
/// 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 `ArrayCopyShallowIfPossible` does require
/// having to use a device compiler (whereas `vtkm::cont::ArrayCopyShallowIfPossible()` does require
/// a device device compiler).
///
void CopyShallowIfPossible(const vtkm::cont::UnknownArrayHandle& source) const;
/// \brief Extract a component of the array.
/// @brief Extract a component of the array.
///
/// This method returns an array that holds the data for a given flat component of the data.
/// The `BaseComponentType` has to be specified and must match the contained array (i.e.
/// the result of `IsBaseComponentType` must succeed for the given type).
/// the result of `IsBaseComponentType()` must succeed for the given type).
///
/// This method treats each value in the array as a flat `Vec` even if it is a `Vec` of
/// `Vec`s. For example, if the array actually holds values of type `Vec<Vec<T, 3>, 2>`,
/// it is treated as if it holds a `Vec<T, 6>`. See `vtkm::VecFlat` for details on how
/// vectors are flattened.
/// This method treats each value in the array as a flat `vtkm::Vec` even if it is a
/// `vtkm::Vec` of `Vec`s. For example, if the array actually holds values of type
/// `vtkm::Vec<vtkm::Vec<T, 3>, 2>`, it is treated as if it holds a `Vec<T, 6>`. See
/// `vtkm::VecFlat` for details on how vectors are flattened.
///
/// The point of using `ExtractComponent` over `AsArrayHandle` is that it drastically reduces
/// the amount of types you have to try. Most of the type the base component type is one of
/// The point of using `ExtractComponent()` over `AsArrayHandle()` is that it drastically reduces
/// the amount of types you have to try. Most of the time the base component type is one of
/// the basic C types (i.e. `int`, `long`, `float`, etc.). You do not need to know what shape
/// the containing `Vec` is in, nor do you need to know the actual storage of the array.
/// the containing `vtkm::Vec` is in, nor do you need to know the actual storage of the array.
///
/// Note that the type of the array returned is `ArrayHandleStride`. Using this type of
/// array handle has a slight overhead over basic arrays like `ArrayHandleBasic` and
@ -804,38 +801,39 @@ public:
return ComponentArrayType(buffers);
}
/// \brief Extract the array knowing only the component type of the array.
/// @brief Extract the array knowing only the component type of the array.
///
/// This method returns an `ArrayHandle` that points to the data in the array. This method
/// differs from `AsArrayHandle` because you do not need to know the exact `ValueType` and
/// differs from `AsArrayHandle()` because you do not need to know the exact `ValueType` and
/// `StorageTag` of the array. Instead, you only need to know the base component type.
///
/// `ExtractArrayFromComponents` works by calling the `ExtractComponent` method and then
/// `ExtractArrayFromComponents()` works by calling the `ExtractComponent()` method and then
/// combining them together in a fancy `ArrayHandle`. This allows you to ignore the storage
/// type of the underlying array as well as any `Vec` structure of the value type. However,
/// it also places some limitations on how the data can be pulled from the data.
///
/// First, you have to specify the base component type. This must match the data in the
/// underlying array (as reported by `IsBaseComponentType`).
/// underlying array (as reported by `IsBaseComponentType()`).
///
/// Second, the array returned will have the `Vec`s flattened. For example, if the underlying
/// array has a `ValueType` of `Vec<Vec<T, 3>, 3>`, then this method will tread the data as
/// if it was `Vec<T, 9>`. There is no way to get an array with `Vec` of `Vec` values.
/// array has a `ValueType` of `vtkm::Vec<vtkm::Vec<T, 3>, 3>`, then this method will treat
/// the data as if it was `vtkm::Vec<T, 9>`. There is no way to get an array with `vtkm::Vec`
/// of `vtkm::Vec` values.
///
/// Third, because the `Vec` length of the values in the returned `ArrayHandle` must be
/// determined at runtime, that can break many assumptions of using `Vec` objects. The
/// type is not going to be a `Vec<T,N>` type but rather an internal class that is intended
/// to behave like that. The type should behave mostly like a `Vec`, but will have some
/// determined at runtime, that can break many assumptions of using `vtkm::Vec` objects. The
/// type is not going to be a `vtkm::Vec<T,N>` type but rather an internal class that is intended
/// to behave like that. The type should behave mostly like a `vtkm::Vec`, but will have some
/// differences that can lead to unexpected behavior. For example, this `Vec`-like object
/// will not have a `NUM_COMPONENTS` constant static expression because it is not known
/// at compile time. (Use the `GetNumberOfComponents` method instead.) And for the same
/// at compile time. (Use the `GetNumberOfComponents()` method instead.) And for the same
/// reason you will not be able to pass these objects to classes overloaded or templated
/// on the `Vec` type. Also, these `Vec`-like objects cannot be created as new instances.
/// Thus, you will likely have to iterate over all components rather than do operations on
/// the whole `Vec`.
///
/// Fourth, because `ExtractArrayFromComponents` uses `ExtractComponent` to pull data from
/// the array (which in turn uses `ArrayExtractComponent`), there are some `ArrayHandle` types
/// Fourth, because `ExtractArrayFromComponents()` uses `ExtractComponent()` to pull data from
/// the array (which in turn uses `ArrayExtractComponent()`), there are some `ArrayHandle` types
/// that will require copying data to a new array. This could be problematic in cases where
/// you want to write to the array. To prevent data from being copied, set the optional
/// `allowCopy` to `vtkm::CopyFlag::Off`. This will cause an exception to be thrown if
@ -858,9 +856,9 @@ public:
return result;
}
/// \brief Call a functor using the underlying array type.
/// @brief Call a functor using the underlying array type.
///
/// `CastAndCallForTypes` attempts to cast the held array to a specific value type,
/// `CastAndCallForTypes()` attempts to cast the held array to a specific value type,
/// and then calls the given functor with the cast array. You must specify
/// the `TypeList` and `StorageList` as template arguments.
///
@ -870,9 +868,9 @@ public:
template <typename TypeList, typename StorageList, typename Functor, typename... Args>
VTKM_CONT void CastAndCallForTypes(Functor&& functor, Args&&... args) const;
/// \brief Call a functor using the underlying array type with a float cast fallback.
/// @brief Call a functor using the underlying array type with a float cast fallback.
///
/// `CastAndCallForTypesWithFloatFallback` attempts to cast the held array to a specific
/// `CastAndCallForTypesWithFloatFallback()` attempts to cast the held array to a specific
/// value type, and then calls the given functor with the cast array. You must specify
/// the `TypeList` and `StorageList` as template arguments.
///
@ -880,28 +878,28 @@ public:
/// passed to the functor after the converted `ArrayHandle`.
///
/// If the underlying array does not match any of the requested array types, the
/// array is copied to a new `ArrayHandleBasic` with `FloatDefault` components
/// array is copied to a new `ArrayHandleBasic` with `vtkm::FloatDefault` components
/// in its value and attempts to cast to those types.
///
template <typename TypeList, typename StorageList, typename Functor, typename... Args>
VTKM_CONT void CastAndCallForTypesWithFloatFallback(Functor&& functor, Args&&... args) const;
/// \brief Call a functor on an array extracted from the components.
/// @brief Call a functor on an array extracted from the components.
///
/// `CastAndCallWithExtractedArray` behaves similarly to `CastAndCallForTypes`.
/// `CastAndCallWithExtractedArray()` behaves similarly to `CastAndCallForTypes()`.
/// It converts the contained data to an `ArrayHandle` and calls a functor with
/// that `ArrayHandle` (and any number of optionally specified arguments).
///
/// The advantage of `CastAndCallWithExtractedArray` is that you do not need to
/// The advantage of `CastAndCallWithExtractedArray()` is that you do not need to
/// specify any `TypeList` or `StorageList`. Instead, it internally uses
/// `ExtractArrayFromComponents` to work with most `ArrayHandle` types with only
/// about 10 instances of the functor. In contrast, calling `CastAndCallForTypes`
/// `ExtractArrayFromComponents()` to work with most `ArrayHandle` types with only
/// about 10 instances of the functor. In contrast, calling `CastAndCallForTypes()`
/// with, for example, `VTKM_DEFAULT_TYPE_LIST` and `VTKM_DEFAULT_STORAGE_LIST`
/// results in many more instances of the functor but handling many fewer types
/// of `ArrayHandle`.
///
/// There are, however, costs to using this method. Details of these costs are
/// documented for the `ExtractArrayFromComponents` method, but briefly they
/// documented for the `ExtractArrayFromComponents()` method, but briefly they
/// are that `Vec` types get flattened, the resulting array has a strange `Vec`-like
/// value type that has many limitations on its use, there is an overhead for
/// retrieving each value from the array, and there is a potential that data
@ -919,6 +917,7 @@ public:
///
VTKM_CONT void ReleaseResources() const;
/// Prints a summary of the array's type, size, and contents.
VTKM_CONT void PrintSummary(std::ostream& out, bool full = false) const;
};

@ -21,6 +21,7 @@ set(scalar_topology_sources
internal/ComputeBlockIndices.cxx
internal/ComputeDistributedBranchDecompositionFunctor.cxx
internal/SelectTopVolumeContoursFunctor.cxx
internal/ParentBranchExtremaFunctor.cxx
internal/ExchangeBranchEndsFunctor.cxx
ContourTreeUniform.cxx
ContourTreeUniformAugmented.cxx

@ -1138,11 +1138,17 @@ VTKM_CONT void ContourTreeUniformDistributed::DoPostExecute(
// ******** 3. Augment the hierarchical tree if requested ********
if (this->AugmentHierarchicalTree)
{
master.foreach (
[](DistributedContourTreeBlockData* blockData, const vtkmdiy::Master::ProxyWithLink&) {
blockData->HierarchicalAugmenter.Initialize(
blockData->GlobalBlockId, &blockData->HierarchicalTree, &blockData->AugmentedTree);
});
master.foreach ([globalPointDimensions](DistributedContourTreeBlockData* blockData,
const vtkmdiy::Master::ProxyWithLink&) {
blockData->HierarchicalAugmenter.Initialize(
blockData->GlobalBlockId,
&blockData->HierarchicalTree,
&blockData->AugmentedTree,
blockData->BlockOrigin, // Origin of the data block
blockData->BlockSize, // Extends of the data block
globalPointDimensions // global point dimensions
);
});
timingsStream << " " << std::setw(38) << std::left << "Initalize Hierarchical Trees"
<< ": " << timer.GetElapsedTime() << " seconds" << std::endl;

@ -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();
@ -237,7 +239,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 +317,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<vtkm::cont::DataSet> outputDataSets(input.GetNumberOfPartitions());
// Copy input data set to output
@ -347,6 +357,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 +402,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);
@ -391,13 +410,74 @@ VTKM_CONT vtkm::cont::PartitionedDataSet DistributedBranchDecompositionFilter::D
vtkm::cont::Field::Association::WholeDataSet,
b->VolumetricBranchDecomposer.BranchRootGRId);
outputDataSets[b->LocalBlockNo].AddField(BranchRootGRId);
/* To select top volume branches,
we need to carry over the input fields below to the output*/
using vtkm::worklet::contourtree_augmented::IdArrayType;
const vtkm::cont::DataSet& ds = input.GetPartition(b->LocalBlockNo);
// RegularNodeGlobalIds
vtkm::cont::Field RegularNodeGlobalIds("RegularNodeGlobalIds",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("RegularNodeGlobalIds").GetData());
outputDataSets[b->LocalBlockNo].AddField(RegularNodeGlobalIds);
// DataValues
vtkm::cont::Field DataValues("DataValues",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("DataValues").GetData());
outputDataSets[b->LocalBlockNo].AddField(DataValues);
// Superparents
vtkm::cont::Field Superparents("Superparents",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Superparents").GetData());
outputDataSets[b->LocalBlockNo].AddField(Superparents);
// Supernodes
vtkm::cont::Field Supernodes("Supernodes",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Supernodes").GetData());
outputDataSets[b->LocalBlockNo].AddField(Supernodes);
// Superarcs
vtkm::cont::Field Superarcs("Superarcs",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Superarcs").GetData());
outputDataSets[b->LocalBlockNo].AddField(Superarcs);
// Superchildren
vtkm::cont::Field Superchildren("Superchildren",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Superchildren").GetData());
outputDataSets[b->LocalBlockNo].AddField(Superchildren);
// WhichRound
vtkm::cont::Field WhichRound("WhichRound",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("WhichRound").GetData());
outputDataSets[b->LocalBlockNo].AddField(WhichRound);
// WhichIteration
vtkm::cont::Field WhichIteration("WhichIteration",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("WhichIteration").GetData());
outputDataSets[b->LocalBlockNo].AddField(WhichIteration);
// Hyperparents
vtkm::cont::Field Hyperparents("Hyperparents",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Hyperparents").GetData());
outputDataSets[b->LocalBlockNo].AddField(Hyperparents);
// Hypernodes
vtkm::cont::Field Hypernodes("Hypernodes",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Hypernodes").GetData());
outputDataSets[b->LocalBlockNo].AddField(Hypernodes);
// Hyperarcs
vtkm::cont::Field Hyperarcs("Hyperarcs",
vtkm::cont::Field::Association::WholeDataSet,
ds.GetField("Hyperarcs").GetData());
outputDataSets[b->LocalBlockNo].AddField(Hyperarcs);
});
timingsStream << " " << std::setw(38) << std::left
<< "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());

@ -63,11 +63,18 @@ public:
const vtkm::cont::ArrayHandle<vtkm::Id3>&,
const vtkm::cont::ArrayHandle<vtkm::Id3>&,
const vtkm::cont::ArrayHandle<vtkm::Id3>&);
VTKM_CONT void SetTimingsLogLevel(vtkm::cont::LogLevel timingsLogLevel)
{
this->TimingsLogLevel = 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;
/// 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

File diff suppressed because it is too large Load Diff

@ -44,7 +44,6 @@
#include <vtkm/filter/Filter.h>
#include <vtkm/filter/scalar_topology/vtkm_filter_scalar_topology_export.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/Types.h>
namespace vtkm
{
@ -52,28 +51,47 @@ namespace filter
{
namespace scalar_topology
{
/// \brief Compute branch decompostion from distributed contour tree
/// \brief Compute branch decompostion from distributed contour tree
class VTKM_FILTER_SCALAR_TOPOLOGY_EXPORT SelectTopVolumeContoursFilter : public vtkm::filter::Filter
{
public:
VTKM_CONT SelectTopVolumeContoursFilter() = default;
VTKM_CONT void SetTimingsLogLevel(vtkm::cont::LogLevel timingsLogLevel)
{
this->TimingsLogLevel = timingsLogLevel;
}
VTKM_CONT void SetSavedBranches(const vtkm::Id& numBranches)
{
this->nSavedBranches = numBranches;
}
VTKM_CONT void SetMarchingCubes(const bool& marchingCubes)
{
this->isMarchingCubes = marchingCubes;
}
private:
VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet&) override;
VTKM_CONT vtkm::cont::PartitionedDataSet DoExecutePartitions(
const vtkm::cont::PartitionedDataSet& inData) override;
bool isMarchingCubes = false;
vtkm::Id nSavedBranches;
vtkm::worklet::contourtree_augmented::IdArrayType BranchVolume;
vtkm::worklet::contourtree_augmented::IdArrayType BranchSaddleEpsilon;
vtkm::worklet::contourtree_augmented::IdArrayType SortedBranchByVolume;
vtkm::cont::ArrayHandle<vtkm::Id> BranchVolume;
vtkm::cont::ArrayHandle<vtkm::Id> BranchSaddleEpsilon;
vtkm::cont::ArrayHandle<vtkm::Id> 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<vtkm::Id> 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

@ -13,6 +13,7 @@ set(headers
SelectTopVolumeContoursBlock.h
ComputeBlockIndices.h
ComputeDistributedBranchDecompositionFunctor.h
ParentBranchExtremaFunctor.h
SelectTopVolumeContoursFunctor.h
ExchangeBranchEndsFunctor.h
)

@ -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<vtkm::Id> 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

@ -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

@ -54,6 +54,7 @@
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/BranchEndGlobalUpdateWorklet.h>
#include <vtkm/Types.h>
#include <vtkm/cont/Logging.h>
#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);

@ -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

@ -0,0 +1,355 @@
//============================================================================
// 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 <vtkm/cont/EnvironmentTracker.h>
#include <vtkm/filter/scalar_topology/internal/ParentBranchExtremaFunctor.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/ArrayTransforms.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/GetBranchHierarchyWorklet.h>
#include <vtkm/Types.h>
#include <iomanip>
#ifdef DEBUG_PRINT
#define DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/PrintVectors.h>
#endif
namespace vtkm
{
namespace filter
{
namespace scalar_topology
{
namespace internal
{
void ParentBranchIsoValueFunctor::operator()(SelectTopVolumeContoursBlock* b,
const vtkmdiy::ReduceProxy& rp // communication proxy
) const
{
// Get our rank and
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_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
auto extraMaximaBranchOrderPortal = b->ExtraMaximaBranchOrder.ReadPortal();
vtkm::Id nExtraMaxBranches = extraMaximaBranchOrderPortal.GetNumberOfValues();
rp.enqueue(target, nExtraMaxBranches);
auto resolveMaxArray = [&](const auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
// the global order of the branch can be the identity (unique for each branch)
auto extraMaxBranchIsoValuePortal = inArray.ReadPortal();
for (vtkm::Id branch = 0; branch < nExtraMaxBranches; ++branch)
rp.enqueue(target, extraMaximaBranchOrderPortal.Get(branch));
for (vtkm::Id branch = 0; branch < nExtraMaxBranches; ++branch)
rp.enqueue<ValueType>(target, extraMaxBranchIsoValuePortal.Get(branch));
};
if (b->ExtraMaximaBranchIsoValue.GetNumberOfValues())
b->ExtraMaximaBranchIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(
resolveMaxArray);
// rp.enqueue(target, b->ExtraMaximaBranchOrder);
// rp.enqueue(target, b->ExtraMaximaBranchIsoValue);
auto extraMinimaBranchOrderPortal = b->ExtraMinimaBranchOrder.ReadPortal();
vtkm::Id nExtraMinBranches = extraMinimaBranchOrderPortal.GetNumberOfValues();
rp.enqueue(target, nExtraMinBranches);
auto resolveMinArray = [&](const auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
// the global order of the branch can be the identity (unique for each branch)
auto extraMinBranchIsoValuePortal = inArray.ReadPortal();
for (vtkm::Id branch = 0; branch < nExtraMinBranches; ++branch)
rp.enqueue(target, extraMinimaBranchOrderPortal.Get(branch));
for (vtkm::Id branch = 0; branch < nExtraMinBranches; ++branch)
rp.enqueue<ValueType>(target, extraMinBranchIsoValuePortal.Get(branch));
};
if (b->ExtraMinimaBranchIsoValue.GetNumberOfValues())
b->ExtraMinimaBranchIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(
resolveMinArray);
// rp.enqueue(target, b->ExtraMinimaBranchOrder);
// rp.enqueue(target, b->ExtraMinimaBranchIsoValue);
}
}
}
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_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->ExtraMaximaBranchOrder.GetNumberOfValues();
vtkm::Id nSelfMinBranch = b->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
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
vtkm::Id nIncomingMaxBranch;
rp.dequeue(ingid, nIncomingMaxBranch);
auto resolveMaxArray = [&](auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
IdArrayType incomingMaxBranchOrder;
incomingMaxBranchOrder.Allocate(nIncomingMaxBranch);
auto incomingMaxBranchOrderPortal = incomingMaxBranchOrder.WritePortal();
for (vtkm::Id branch = 0; branch < nIncomingMaxBranch; ++branch)
{
vtkm::Id incomingTmpBranchOrder;
rp.dequeue(ingid, incomingTmpBranchOrder);
incomingMaxBranchOrderPortal.Set(branch, incomingTmpBranchOrder);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue(ingid, incomingMaxBranchOrder);
InArrayHandleType incomingMaxBranchIsoValue;
incomingMaxBranchIsoValue.Allocate(nIncomingMaxBranch);
auto incomingMaxBranchIsoValuePortal = incomingMaxBranchIsoValue.WritePortal();
for (vtkm::Id branch = 0; branch < nIncomingMaxBranch; ++branch)
{
ValueType incomingBranchValue;
rp.dequeue<ValueType>(ingid, incomingBranchValue);
incomingMaxBranchIsoValuePortal.Set(branch, incomingBranchValue);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue<InArrayHandleType>(ingid, incomingMaxBranchIsoValue);
#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(nIncomingMaxBranch, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingMaxBranchOrder", incomingMaxBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"incomingMaxBranchVal", incomingMaxBranchIsoValue, -1, rs);
vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMaxBranch, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfMaxBranchOrder", b->ExtraMaximaBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"selfMaxBranchVal", inArray, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
vtkm::cont::Algorithm::SortByKey(incomingMaxBranchOrder, incomingMaxBranchIsoValue);
vtkm::worklet::scalar_topology::select_top_volume_contours::UpdateOuterSaddle<true>
updateValueOnMaxBranch;
invoke(updateValueOnMaxBranch,
b->ExtraMaximaBranchOrder,
inArray,
incomingMaxBranchOrder,
incomingMaxBranchIsoValue);
#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->ExtraMaximaBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"selfMaxBranchVal", inArray, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
};
if (nSelfMaxBranch > 0 && nIncomingMaxBranch > 0)
b->ExtraMaximaBranchIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(
resolveMaxArray);
/* Apply the same pipeline for branches with minima to extract */
vtkm::Id nIncomingMinBranch;
rp.dequeue(ingid, nIncomingMinBranch);
auto resolveMinArray = [&](auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
IdArrayType incomingMinBranchOrder;
incomingMinBranchOrder.Allocate(nIncomingMinBranch);
auto incomingMinBranchOrderPortal = incomingMinBranchOrder.WritePortal();
for (vtkm::Id branch = 0; branch < nIncomingMinBranch; ++branch)
{
vtkm::Id incomingTmpBranchOrder;
rp.dequeue(ingid, incomingTmpBranchOrder);
incomingMinBranchOrderPortal.Set(branch, incomingTmpBranchOrder);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue(ingid, incomingMinBranchOrder);
InArrayHandleType incomingMinBranchIsoValue;
incomingMinBranchIsoValue.Allocate(nIncomingMinBranch);
auto incomingMinBranchIsoValuePortal = incomingMinBranchIsoValue.WritePortal();
for (vtkm::Id branch = 0; branch < nIncomingMinBranch; ++branch)
{
ValueType incomingBranchValue;
rp.dequeue<ValueType>(ingid, incomingBranchValue);
incomingMinBranchIsoValuePortal.Set(branch, incomingBranchValue);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue<InArrayHandleType>(ingid, incomingMinBranchIsoValue);
#ifdef DEBUG_PRINT_UPDATE_PARENT_BRANCH_ISOVALUE
{
std::stringstream rs;
vtkm::worklet::contourtree_augmented::PrintHeader(nIncomingMinBranch, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"incomingMinBranchOrder", incomingMinBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"incomingMinBranchVal", incomingMinBranchIsoValue, -1, rs);
vtkm::worklet::contourtree_augmented::PrintHeader(nSelfMinBranch, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfMinBranchOrder", b->ExtraMinimaBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"selfMinBranchVal", inArray, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
vtkm::cont::Algorithm::SortByKey(incomingMinBranchOrder, incomingMinBranchIsoValue);
vtkm::worklet::scalar_topology::select_top_volume_contours::UpdateOuterSaddle<false>
updateValueOnMinBranch;
invoke(updateValueOnMinBranch,
b->ExtraMinimaBranchOrder,
inArray,
incomingMinBranchOrder,
incomingMinBranchIsoValue);
#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->ExtraMinimaBranchOrder, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"selfMinBranchVal", inArray, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
};
if (nSelfMinBranch > 0 && nIncomingMinBranch > 0)
b->ExtraMinimaBranchIsoValue
.CastAndCallForTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(
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());
}
}
}
} // namespace internal
} // namespace scalar_topology
} // namespace filter
} // namespace vtkm

@ -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.
//============================================================================
// 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_ParentBranchExtremaFunctor_h
#define vtk_m_filter_scalar_topology_internal_ParentBranchExtremaFunctor_h
#include <vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursBlock.h>
// clang-format off
VTKM_THIRDPARTY_PRE_INCLUDE
#include <vtkm/thirdparty/diy/diy.h>
VTKM_THIRDPARTY_POST_INCLUDE
// clang-format on
namespace vtkm
{
namespace filter
{
namespace scalar_topology
{
namespace internal
{
struct ParentBranchIsoValueFunctor
{
ParentBranchIsoValueFunctor(const vtkm::cont::LogLevel& timingsLogLevel)
: TimingsLogLevel(timingsLogLevel)
{
}
void operator()(SelectTopVolumeContoursBlock* b,
const vtkmdiy::ReduceProxy& rp // communication proxy
) const;
const vtkm::cont::LogLevel TimingsLogLevel;
};
} // namespace internal
} // namespace scalar_topology
} // namespace filter
} // namespace vtkm
#endif

@ -69,6 +69,12 @@ namespace scalar_topology
namespace internal
{
struct IsNegativeDebug
{
VTKM_EXEC_CONT IsNegativeDebug() {}
VTKM_EXEC_CONT bool operator()(vtkm::Id x) const { return x < 0; }
};
SelectTopVolumeContoursBlock::SelectTopVolumeContoursBlock(vtkm::Id localBlockNo, int globalBlockId)
: LocalBlockNo(localBlockNo)
, GlobalBlockId(globalBlockId)
@ -103,13 +109,14 @@ void SelectTopVolumeContoursBlock::SortBranchByVolume(
auto lowerEndDependentVolume = hierarchicalTreeDataSet.GetField("LowerEndDependentVolume")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto lowerEndSuperarcId = hierarchicalTreeDataSet.GetField("LowerEndSuperarcId")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto upperEndSuperarcId = hierarchicalTreeDataSet.GetField("UpperEndSuperarcId")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
auto branchRoot = hierarchicalTreeDataSet.GetField("BranchRoot")
auto branchRoot = hierarchicalTreeDataSet.GetField("BranchRootByBranch")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();

@ -76,16 +76,58 @@ struct SelectTopVolumeContoursBlock
vtkm::Id LocalBlockNo;
int GlobalBlockId; // TODO/FIXME: Check whether really needed. Possibly only during debugging
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::ArrayHandle<bool> IsParentBranch;
vtkm::cont::UnknownArrayHandle BranchSaddleIsoValue;
// Output Datasets.
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchRoot;
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchRootGRId;
vtkm::worklet::contourtree_augmented::IdArrayType TopVolumeBranchVolume;
// the parent branch of top volume 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;
// the 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;
// the branch information index (known by the block) of the top volume branch
// size: nTopVolBranchKnownByBlock
// value range: [0, nBranches - 1]
vtkm::worklet::contourtree_augmented::IdArrayType TopVolBranchInfoActualIndex;
// information to extract extra isosurface for extrema
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchUpperEnd;
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchLowerEnd;
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMaximaBranchOrder;
vtkm::cont::UnknownArrayHandle ExtraMaximaBranchIsoValue;
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchUpperEnd;
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchLowerEnd;
vtkm::worklet::contourtree_augmented::IdArrayType ExtraMinimaBranchOrder;
vtkm::cont::UnknownArrayHandle ExtraMinimaBranchIsoValue;
// Isosurface output
vtkm::cont::ArrayHandle<vtkm::Vec3f_64> IsosurfaceEdgesFrom;
vtkm::cont::ArrayHandle<vtkm::Vec3f_64> IsosurfaceEdgesTo;
vtkm::worklet::contourtree_augmented::IdArrayType IsosurfaceEdgesOffset;
vtkm::worklet::contourtree_augmented::IdArrayType IsosurfaceEdgesLabels;
vtkm::worklet::contourtree_augmented::IdArrayType IsosurfaceEdgesOrders;
vtkm::cont::UnknownArrayHandle IsosurfaceIsoValue;
// Destroy function allowing DIY to own blocks and clean them up after use
static void Destroy(void* b) { delete static_cast<SelectTopVolumeContoursBlock*>(b); }

@ -49,7 +49,7 @@
// Hamish Carr (University of Leeds), Gunther H. Weber (LBNL), and
// Oliver Ruebel (LBNL)
//==============================================================================
#include <vtkm/cont/EnvironmentTracker.h>
#include <vtkm/filter/scalar_topology/internal/SelectTopVolumeContoursFunctor.h>
#include <vtkm/filter/scalar_topology/worklet/branch_decomposition/hierarchical_volumetric_branch_decomposer/GetOuterEndWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/ArrayTransforms.h>
@ -57,6 +57,8 @@
#include <vtkm/Types.h>
#include <iomanip>
#ifdef DEBUG_PRINT
#define DEBUG_PRINT_COMBINED_HIGH_VOLUME_BRANCH
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/PrintVectors.h>
@ -79,7 +81,7 @@ void SelectTopVolumeContoursFunctor::operator()(
if (this->nSavedBranches < 1)
return;
// 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
@ -102,6 +104,8 @@ void SelectTopVolumeContoursFunctor::operator()(
auto topVolBranchRootGRIdPortal = b->TopVolumeBranchRootGRId.ReadPortal();
auto topVolBranchVolumePortal = b->TopVolumeBranchVolume.ReadPortal();
auto topVolBranchSaddleEpsilonPortal = b->TopVolumeBranchSaddleEpsilon.ReadPortal();
auto topVolBranchUpperEndPortal = b->TopVolumeBranchUpperEndGRId.ReadPortal();
auto topVolBranchLowerEndPortal = b->TopVolumeBranchLowerEndGRId.ReadPortal();
vtkm::Id nBranches = topVolBranchRootGRIdPortal.GetNumberOfValues();
@ -112,6 +116,10 @@ void SelectTopVolumeContoursFunctor::operator()(
rp.enqueue(target, topVolBranchVolumePortal.Get(branch));
for (vtkm::Id branch = 0; branch < nBranches; ++branch)
rp.enqueue(target, topVolBranchSaddleEpsilonPortal.Get(branch));
for (vtkm::Id branch = 0; branch < nBranches; ++branch)
rp.enqueue(target, topVolBranchUpperEndPortal.Get(branch));
for (vtkm::Id branch = 0; branch < nBranches; ++branch)
rp.enqueue(target, topVolBranchLowerEndPortal.Get(branch));
auto resolveArray = [&](const auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
@ -199,6 +207,34 @@ void SelectTopVolumeContoursFunctor::operator()(
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue(ingid, incomingTopVolBranchSaddleEpsilon);
IdArrayType incomingTopVolBranchUpperEnd;
incomingTopVolBranchUpperEnd.Allocate(nIncoming);
auto incomingTopVolBranchUpperEndPortal = incomingTopVolBranchUpperEnd.WritePortal();
for (vtkm::Id branch = 0; branch < nIncoming; ++branch)
{
vtkm::Id incomingTmpBranchUpperEnd;
rp.dequeue(ingid, incomingTmpBranchUpperEnd);
incomingTopVolBranchUpperEndPortal.Set(branch, incomingTmpBranchUpperEnd);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue(ingid, incomingTopVolBranchUpperEnd);
IdArrayType incomingTopVolBranchLowerEnd;
incomingTopVolBranchLowerEnd.Allocate(nIncoming);
auto incomingTopVolBranchLowerEndPortal = incomingTopVolBranchLowerEnd.WritePortal();
for (vtkm::Id branch = 0; branch < nIncoming; ++branch)
{
vtkm::Id incomingTmpBranchLowerEnd;
rp.dequeue(ingid, incomingTmpBranchLowerEnd);
incomingTopVolBranchLowerEndPortal.Set(branch, incomingTmpBranchLowerEnd);
}
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue(ingid, incomingTopVolBranchLowerEnd);
auto resolveArray = [&](auto& inArray) {
using InArrayHandleType = std::decay_t<decltype(inArray)>;
using ValueType = typename InArrayHandleType::ValueType;
@ -213,6 +249,20 @@ void SelectTopVolumeContoursFunctor::operator()(
incomingTopVolBranchSaddleIsoValuePortal.Set(branch, incomingSaddleValue);
}
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());
// TODO/FIXME: This is a workaround for a bug in DIY/vtk-m.
// Replace with dequeuing ArrayHandles once bug is fixed.
// rp.dequeue<InArrayHandleType>(ingid, incomingTopVolBranchSaddleIsoValue);
@ -231,6 +281,10 @@ void SelectTopVolumeContoursFunctor::operator()(
"incomingTopBranchVol", incomingTopVolBranchVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"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(
@ -239,6 +293,10 @@ void SelectTopVolumeContoursFunctor::operator()(
"selfTopBranchVol", b->TopVolumeBranchVolume, -1, rs);
vtkm::worklet::contourtree_augmented::PrintValues<ValueType>(
"selfTopSaddleVal", inArray, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfTopBranchUpperEnd", b->TopVolumeBranchUpperEndGRId, -1, rs);
vtkm::worklet::contourtree_augmented::PrintIndices(
"selfTopBranchLowerEnd", b->TopVolumeBranchLowerEndGRId, -1, rs);
VTKM_LOG_S(vtkm::cont::LogLevel::Info, rs.str());
}
#endif
@ -246,10 +304,14 @@ void SelectTopVolumeContoursFunctor::operator()(
IdArrayType mergedTopVolBranchGRId;
IdArrayType mergedTopVolBranchVolume;
IdArrayType mergedTopVolBranchSaddleEpsilon;
IdArrayType mergedTopVolBranchUpperEnd;
IdArrayType mergedTopVolBranchLowerEnd;
InArrayHandleType mergedTopVolBranchSaddleIsoValue;
mergedTopVolBranchGRId.Allocate(nIncoming + nSelf);
mergedTopVolBranchVolume.Allocate(nIncoming + nSelf);
mergedTopVolBranchSaddleEpsilon.Allocate(nIncoming + nSelf);
mergedTopVolBranchUpperEnd.Allocate(nIncoming + nSelf);
mergedTopVolBranchLowerEnd.Allocate(nIncoming + nSelf);
mergedTopVolBranchSaddleIsoValue.Allocate(nIncoming + nSelf);
vtkm::cont::Algorithm::CopySubRange(
@ -258,6 +320,10 @@ void SelectTopVolumeContoursFunctor::operator()(
incomingTopVolBranchVolume, 0, nIncoming, mergedTopVolBranchVolume, 0);
vtkm::cont::Algorithm::CopySubRange(
incomingTopVolBranchSaddleEpsilon, 0, nIncoming, mergedTopVolBranchSaddleEpsilon, 0);
vtkm::cont::Algorithm::CopySubRange(
incomingTopVolBranchUpperEnd, 0, nIncoming, mergedTopVolBranchUpperEnd, 0);
vtkm::cont::Algorithm::CopySubRange(
incomingTopVolBranchLowerEnd, 0, nIncoming, mergedTopVolBranchLowerEnd, 0);
vtkm::cont::Algorithm::CopySubRange<ValueType, ValueType>(
incomingTopVolBranchSaddleIsoValue, 0, nIncoming, mergedTopVolBranchSaddleIsoValue, 0);
vtkm::cont::Algorithm::CopySubRange(
@ -266,6 +332,10 @@ void SelectTopVolumeContoursFunctor::operator()(
b->TopVolumeBranchVolume, 0, nSelf, mergedTopVolBranchVolume, nIncoming);
vtkm::cont::Algorithm::CopySubRange(
b->TopVolumeBranchSaddleEpsilon, 0, nSelf, mergedTopVolBranchSaddleEpsilon, nIncoming);
vtkm::cont::Algorithm::CopySubRange(
b->TopVolumeBranchUpperEndGRId, 0, nSelf, mergedTopVolBranchUpperEnd, nIncoming);
vtkm::cont::Algorithm::CopySubRange(
b->TopVolumeBranchLowerEndGRId, 0, nSelf, mergedTopVolBranchLowerEnd, nIncoming);
vtkm::cont::Algorithm::CopySubRange<ValueType, ValueType>(
inArray, 0, nSelf, mergedTopVolBranchSaddleIsoValue, nIncoming);
@ -289,6 +359,12 @@ void SelectTopVolumeContoursFunctor::operator()(
IdArrayType permutedTopVolBranchSaddleEpsilon;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
mergedTopVolBranchSaddleEpsilon, sortedBranchId, permutedTopVolBranchSaddleEpsilon);
IdArrayType permutedTopVolBranchUpperEnd;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
mergedTopVolBranchUpperEnd, sortedBranchId, permutedTopVolBranchUpperEnd);
IdArrayType permutedTopVolBranchLowerEnd;
vtkm::worklet::contourtree_augmented::PermuteArrayWithMaskedIndex<vtkm::Id, IdArrayType>(
mergedTopVolBranchLowerEnd, sortedBranchId, permutedTopVolBranchLowerEnd);
InArrayHandleType permutedTopVolBranchSaddleIsoValue;
vtkm::worklet::contourtree_augmented::PermuteArrayWithRawIndex<InArrayHandleType>(
mergedTopVolBranchSaddleIsoValue, sortedBranchId, permutedTopVolBranchSaddleIsoValue);
@ -301,6 +377,10 @@ void SelectTopVolumeContoursFunctor::operator()(
"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<ValueType>(
"permutedTopSaddleVal", permutedTopVolBranchSaddleIsoValue, -1, rs);
}
@ -318,6 +398,8 @@ void SelectTopVolumeContoursFunctor::operator()(
IdArrayType mergedUniqueBranchGRId;
IdArrayType mergedUniqueBranchVolume;
IdArrayType mergedUniqueBranchSaddleEpsilon;
IdArrayType mergedUniqueBranchUpperEnd;
IdArrayType mergedUniqueBranchLowerEnd;
InArrayHandleType mergedUniqueBranchSaddleIsoValue;
vtkm::cont::Algorithm::CopyIf(
@ -326,6 +408,10 @@ void SelectTopVolumeContoursFunctor::operator()(
permutedTopVolBranchVolume, oneIfUniqueBranch, mergedUniqueBranchVolume);
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchSaddleEpsilon, oneIfUniqueBranch, mergedUniqueBranchSaddleEpsilon);
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchUpperEnd, oneIfUniqueBranch, mergedUniqueBranchUpperEnd);
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchLowerEnd, oneIfUniqueBranch, mergedUniqueBranchLowerEnd);
vtkm::cont::Algorithm::CopyIf(
permutedTopVolBranchSaddleIsoValue, oneIfUniqueBranch, mergedUniqueBranchSaddleIsoValue);
@ -339,6 +425,10 @@ void SelectTopVolumeContoursFunctor::operator()(
"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<ValueType>(
"mergedUniqueSaddleVal", mergedUniqueBranchSaddleIsoValue, -1, rs);
}
@ -356,6 +446,10 @@ void SelectTopVolumeContoursFunctor::operator()(
0,
this->nSavedBranches,
b->TopVolumeBranchSaddleEpsilon);
vtkm::cont::Algorithm::CopySubRange(
mergedUniqueBranchUpperEnd, 0, this->nSavedBranches, b->TopVolumeBranchUpperEndGRId);
vtkm::cont::Algorithm::CopySubRange(
mergedUniqueBranchLowerEnd, 0, this->nSavedBranches, b->TopVolumeBranchLowerEndGRId);
// InArrayHandleType subRangeUniqueBranchSaddleIsoValue;
inArray.Allocate(this->nSavedBranches);
vtkm::cont::Algorithm::CopySubRange(
@ -368,6 +462,8 @@ void SelectTopVolumeContoursFunctor::operator()(
vtkm::cont::Algorithm::Copy(mergedUniqueBranchVolume, b->TopVolumeBranchVolume);
vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleEpsilon,
b->TopVolumeBranchSaddleEpsilon);
vtkm::cont::Algorithm::Copy(mergedUniqueBranchUpperEnd, b->TopVolumeBranchUpperEndGRId);
vtkm::cont::Algorithm::Copy(mergedUniqueBranchLowerEnd, b->TopVolumeBranchLowerEndGRId);
inArray.Allocate(nMergedUnique);
vtkm::cont::Algorithm::Copy(mergedUniqueBranchSaddleIsoValue, inArray);
}

@ -73,8 +73,9 @@ namespace internal
struct SelectTopVolumeContoursFunctor
{
SelectTopVolumeContoursFunctor(const vtkm::Id& nSB)
SelectTopVolumeContoursFunctor(const vtkm::Id& nSB, const vtkm::cont::LogLevel& timingsLogLevel)
: nSavedBranches(nSB)
, TimingsLogLevel(timingsLogLevel)
{
}
@ -83,6 +84,7 @@ struct SelectTopVolumeContoursFunctor
) const;
const vtkm::Id nSavedBranches;
const vtkm::cont::LogLevel TimingsLogLevel;
};
} // namespace internal

@ -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:
@ -507,11 +512,19 @@ inline void HierarchicalVolumetricBranchDecomposer::CollapseBranches(
hierarchicalTreeSuperarcs
};
// Get the number of rounds
auto numRoundsArray = hierarchicalTreeDataSet.GetField("NumRounds")
.GetData()
.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Id>>();
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 +783,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 +832,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 +873,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(

@ -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 <typename ExecObjectType1,
@ -88,6 +92,7 @@ public:
const vtkm::Id& supernode, // iteration index
const vtkm::Id& bestUpSupernodeId, // bestUpSupernode[supernode]
const vtkm::Id& bestDownSupernodeId, // bestDownSupernode[supernode]
const vtkm::Id& superarcsId, // hierarchicalTree.superarcs[supernode]
const ExecObjectType1& findRegularByGlobal, // Execution object to call FindRegularByGlobal
const ExecObjectType2&
findSuperArcBetweenNodes, // Execution object to call FindSuperArcBetweenNodes
@ -104,6 +109,18 @@ public:
// If it does exist and is an upwards superarc, then the current supernode must have an ascending arc to it, and we're done
// Also do the same for the best down, then for each supernode, point the higher numbered at the lower
// ADDED 19/07/2023
// If there are any attachment points left in the hierarchical tree, there is an extra edge case we need to deal with.
// It occurs when a supernode is simultaneously the target of an ascending superarc and a descending one
// What we do is to test for this here: if we are an attachment point, we omit connecting the best up and down
// ADDED 19/07/2023
// test for attachment points
if ((hierarchicalTreeWhichRoundPortal.Get(supernode) != this->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

@ -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;

@ -102,6 +102,7 @@
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/AttachmentSuperparentAndIndexComparator.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CopyBaseRegularStructureWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/CreateSuperarcsWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FillEmptyIterationWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/FindSuperparentForNecessaryNodesWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/HierarchicalAugmenterInOutData.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_augmenter/IsAscendingDecorator.h>
@ -127,6 +128,11 @@ template <typename FieldType>
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<FieldType>* BaseTree;
/// the tree that it is building
@ -198,7 +204,10 @@ public:
void Initialize(
vtkm::Id blockId,
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* inBaseTree,
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* inAugmentedTree);
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* inAugmentedTree,
vtkm::Id3 meshBlockOrigin,
vtkm::Id3 meshBockSize,
vtkm::Id3 meshGlobalSize);
/// routine to prepare the set of attachment points to transfer
void PrepareOutAttachmentPoints(vtkm::Id round);
@ -225,7 +234,7 @@ public:
void CopyBaseRegularStructure();
// subroutines for CopySuperstructure
/// gets a list of all the old supernodes to transfer at this level (ie except attachment points
/// gets a list of all the old supernodes to transfer at this level (i.e., except attachment points
void RetrieveOldSupernodes(vtkm::Id roundNumber);
/// resizes the arrays for the level
void ResizeArrays(vtkm::Id roundNumber);
@ -249,12 +258,18 @@ template <typename FieldType>
void HierarchicalAugmenter<FieldType>::Initialize(
vtkm::Id blockId,
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* baseTree,
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* augmentedTree)
vtkm::worklet::contourtree_distributed::HierarchicalContourTree<FieldType>* augmentedTree,
vtkm::Id3 meshBlockOrigin,
vtkm::Id3 meshBockSize,
vtkm::Id3 meshGlobalSize)
{ // Initialize()
// copy the parameters for use
this->BlockId = blockId;
this->BaseTree = baseTree;
this->AugmentedTree = augmentedTree;
this->MeshBlockOrigin = meshBlockOrigin;
this->MeshBlockSize = meshBockSize;
this->MeshGlobalSize = meshGlobalSize;
// now construct a list of all attachment points on the block
// to do this, we construct an index array with all supernode ID's that satisfy:
@ -728,12 +743,13 @@ void HierarchicalAugmenter<FieldType>::CopyBaseRegularStructure()
vtkm::worklet::contourtree_augmented::IdArrayType tempRegularNodesNeeded;
// create the worklet
vtkm::worklet::contourtree_distributed::hierarchical_augmenter::
FindSuperparentForNecessaryNodesWorklet findSuperparentForNecessaryNodesWorklet;
FindSuperparentForNecessaryNodesWorklet findSuperparentForNecessaryNodesWorklet(
this->MeshBlockOrigin, this->MeshBlockSize, this->MeshGlobalSize);
// Get a FindRegularByGlobal and FindSuperArcForUnknownNode execution object for our worklet
auto findRegularByGlobal = this->AugmentedTree->GetFindRegularByGlobal();
auto findSuperArcForUnknownNode = this->AugmentedTree->GetFindSuperArcForUnknownNode();
// excute the worklet
// execute the worklet
this->Invoke(findSuperparentForNecessaryNodesWorklet, // the worklet to call
// inputs
this->BaseTree->RegularNodeGlobalIds, // input domain
@ -831,6 +847,11 @@ void HierarchicalAugmenter<FieldType>::CopyBaseRegularStructure()
);
}
// Reset the number of regular nodes in round 0
vtkm::Id regularNodesInRound0 =
numTotalRegular - this->AugmentedTree->NumRegularNodesInRound.ReadPortal().Get(1);
this->AugmentedTree->NumRegularNodesInRound.WritePortal().Set(0, regularNodesInRound0);
// Finally, we resort the regular node sort order
{
vtkm::worklet::contourtree_distributed::PermuteComparator // hierarchical_contour_tree::
@ -1218,6 +1239,7 @@ void HierarchicalAugmenter<FieldType>::CreateSuperarcs(vtkm::Id roundNumber)
vtkm::cont::make_ArrayHandleView(this->AugmentedTree->Super2Hypernode,
numSupernodesAlready,
this->SupernodeSorter.GetNumberOfValues());
// invoke the worklet
this->Invoke(createSuperarcsWorklet, // the worklet
this->SupernodeSorter, // input domain
@ -1297,6 +1319,19 @@ void HierarchicalAugmenter<FieldType>::CreateSuperarcs(vtkm::Id roundNumber)
// But there might be *NO* supernodes in the round, so we check first
vtkm::Id iterationArraySize =
vtkm::cont::ArrayGetValue(roundNumber, this->AugmentedTree->NumIterations);
// 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]);
if (iterationArraySize > 0)
{ // at least one iteration
vtkm::Id lastSupernodeThisLevel = this->AugmentedTree->Supernodes.GetNumberOfValues() - 1;

@ -110,6 +110,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();
}
}

@ -81,6 +81,7 @@
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/FindSuperArcForUnknownNode.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/InitalizeSuperchildrenWorklet.h>
#include <vtkm/filter/scalar_topology/worklet/contourtree_distributed/hierarchical_contour_tree/PermuteComparator.h>
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/LocalIsosurfaceExtractWorklet.h>
namespace vtkm
{
@ -1024,9 +1025,18 @@ void HierarchicalContourTree<FieldType>::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 +1064,13 @@ void HierarchicalContourTree<FieldType>::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<vtkm::Id> 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

@ -200,7 +200,6 @@ private:
}; // class HierarchicalHyperSweeper
template <typename SweepValueType, typename ContourTreeFieldType>
HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::HierarchicalHyperSweeper(
vtkm::Id blockId,
@ -360,6 +359,7 @@ void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::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);
@ -424,7 +424,6 @@ void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::
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 +441,6 @@ void HierarchicalHyperSweeper<SweepValueType, ContourTreeFieldType>::
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

@ -22,6 +22,7 @@ set(headers
SetSuperparentSetDecorator.h
AttachmentAndSupernodeComparator.h
ResizeArraysBuildNewSupernodeIdsWorklet.h
FillEmptyIterationWorklet.h
CreateSuperarcsWorklet.h
HierarchicalAugmenterInOutData.h
)

@ -175,7 +175,6 @@ public:
// 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).
@ -237,8 +236,12 @@ public:
(superarcAscends ? vtkm::worklet::contourtree_augmented::IS_ASCENDING : 0x00);
} // not last in the segment
// The following single line sets the first supernode for the iteration for each round
// Why does it execute separately for each supernode? This will kill speed with all the write collisions
// Commented out so that the location of the error is apparent
// It looks like this was set separately in ResizeArrays(), so it's also redundant!!!!
// set the first supernode in the first iteration to the beginning of the round
augmentedTreeFirstSupernodePerIterationPortal.Set(0, this->NumSupernodesAlready);
// augmentedTreeFirstSupernodePerIterationPortal.Set(0, this->NumSupernodesAlready);
// NOTE: This part has been moved out of the worklet and is performed using standard vtkm copy constructs

@ -0,0 +1,95 @@
//============================================================================
// 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_fill_empty_iteration_worklet_h
#define vtk_m_worklet_contourtree_distributed_hierarchical_augmenter_fill_empty_iteration_worklet_h
#include <vtkm/filter/scalar_topology/worklet/contourtree_augmented/Types.h>
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace worklet
{
namespace contourtree_distributed
{
namespace hierarchical_augmenter
{
// Worklet for a rare case where an iteration has no supernode
// need to update the FirstSupernodePerIteration array to avoid crash
class FillEmptyIterationWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
WholeArrayInOut augmentedTreeFirstSupernodePerIteration // input/output
);
using ExecutionSignature = void(InputIndex, _1);
using InputDomain = _1;
VTKM_EXEC_CONT
FillEmptyIterationWorklet() {}
/// operator() of the worklet
template <typename InOutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& inputIndex,
InOutFieldPortalType& firstSupernodePerIteration) const
{
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 worklet
} // namespace vtkm
#endif

@ -83,7 +83,14 @@ public:
/// 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 <typename InFieldPortalType,
@ -111,6 +118,17 @@ public:
// first check to see if it is already present (newRegularId set on input)
vtkm::Id newRegularId = findRegularByGlobal.FindRegularByGlobal(globalRegularId);
// WARNING: Mingzhe: the code commented out below comes from one of Oliver's fixes
// However, the code fails to work with isosurface extraction code.
// Many regular vertices are missing from the array with the code below
// Explicitly check whether the vertex belongs to the base block. If it doesn't, we ignore it
/* if (!this->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;
} else */
// if it fails this test, then it's already in tree
if (vtkm::worklet::contourtree_augmented::NoSuchElement(newRegularId))
{ // not yet in tree
@ -200,7 +218,79 @@ public:
*/
} // operator()()
}; // FindSuperparentForNecessaryNodesWorklet
private:
// Mesh data
vtkm::Id3 MeshBlockOrigin;
vtkm::Id3 MeshBlockSize;
vtkm::Id3 MeshGlobalSize;
VTKM_EXEC
bool IsInMesh(vtkm::Id globalId) const
{ // IsInMesh()
if (this->MeshGlobalSize[2] > 1) // 3D
{
// convert from global ID to global coords
vtkm::Id globalSliceSize = this->MeshGlobalSize[0] * this->MeshGlobalSize[1];
vtkm::Id globalSlice = globalId / globalSliceSize;
vtkm::Id globalRow = globalId / this->MeshGlobalSize[0];
vtkm::Id globalCol = globalId % this->MeshGlobalSize[0];
// test validity
if (globalSlice < this->MeshBlockOrigin[2])
{
return false;
}
if (globalSlice >= this->MeshBlockOrigin[2] + this->MeshBlockSize[2])
{
return false;
}
if (globalRow < this->MeshBlockOrigin[1])
{
return false;
}
if (globalRow >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1])
{
return false;
}
if (globalCol < this->MeshBlockOrigin[0])
{
return false;
}
if (globalCol >= this->MeshBlockOrigin[0] + this->MeshBlockSize[0])
{
return false;
}
// it's in the block - return true
return true;
} // end if 3D
else // 2D mesh
{
// convert from global ID to global coords
vtkm::Id globalRow = globalId / this->MeshGlobalSize[0];
vtkm::Id globalCol = globalId % this->MeshGlobalSize[0];
// test validity
if (globalRow < this->MeshBlockOrigin[1])
{
return false;
}
if (globalRow >= this->MeshBlockOrigin[1] + this->MeshBlockSize[1])
{
return false;
}
if (globalCol < this->MeshBlockOrigin[0])
{
return false;
}
if (globalCol >= this->MeshBlockOrigin[0] + this->MeshBlockSize[0])
{
return false;
}
// it's in the block - return true
return true;
}
} // IsInMesh()
}; // FindSuperparentForNecessaryNodesWorklet
} // namespace hierarchical_augmenter
} // namespace contourtree_distributed

@ -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);

@ -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_contours_BranchParentComparator_h
#define vtk_m_filter_scalar_topology_worklet_branch_decomposition_select_top_volume_contours_BranchParentComparator_h
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
namespace select_top_volume_contours
{
using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType;
// Implementation of BranchParentComparator
template <typename ValueType>
class BranchParentComparatorImpl
{
public:
using ValueArrayType = typename vtkm::cont::ArrayHandle<ValueType>;
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
/// <summary>
/// Comparator of branch parent. Lower parent comes first
/// </summary>
template <typename ValueType>
class BranchParentComparator : public vtkm::cont::ExecutionObjectBase
{
using ValueArrayType = typename vtkm::cont::ArrayHandle<ValueType>;
public:
// constructor
VTKM_CONT
BranchParentComparator(const IdArrayType& branchParent,
const ValueArrayType& saddleIsoValue,
const IdArrayType& branchRootGRId)
: BranchParent(branchParent)
, SaddleIsoValue(saddleIsoValue)
, BranchRootGRId(branchRootGRId)
{
}
VTKM_CONT BranchParentComparatorImpl<ValueType> PrepareForExecution(
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token) const
{
return BranchParentComparatorImpl<ValueType>(
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)
return true;
if (isNullJ)
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
/// <summary>
/// Comparator of superarc target. The NULL superarc always comes first.
/// </summary>
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_contours
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -9,10 +9,15 @@
##============================================================================
set(headers
Predicates.h
ClarifyBranchEndSupernodeTypeWorklet.h
UpdateInfoByBranchDirectionWorklet.h
GetBranchHierarchyWorklet.h
GetBranchVolumeWorklet.h
BranchParentComparator.h
BranchVolumeComparator.h
MarchingCubesDataTables.h
LocalIsosurfaceExtractWorklet.h
)
#-----------------------------------------------------------------------------

@ -0,0 +1,401 @@
//============================================================================
// 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_contours_GetBranchHierarchyWorklet_h
#define vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_GetBranchHierarchyWorklet_h
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
namespace select_top_volume_contours
{
constexpr vtkm::IdComponent MAX_CONNECTIVITY_3D = static_cast<vtkm::IdComponent>(14);
using IdArrayType = vtkm::worklet::contourtree_augmented::IdArrayType;
/// <summary>
/// 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
/// </summary>
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 minimum-saddle branch
if (branchSaddleEps > 0)
return lowerEndGRId == lowerLocalEndGRId
? lowerLocalEnd
: vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
// if the branch is a maximum-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
/// <summary>
/// worklet to compute the parent branch of branches
/// </summary>
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 <typename IdArrayPortalType>
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 <typename IdArrayPortalType>
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;
}
// should always find the branch root index
// if not, report error
VTKM_ASSERT(false && "Cannot find the branch root known by the block!");
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
}
template <typename IdArrayPortalType>
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);
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++)
{
// NOTE: we ALWAYS exclude the virtual superarc, which does not belong to any branch
if (candidateBranchRoot[branchRoot] == permutedBranchRoots.Get(0))
continue;
const vtkm::Id branchIdx =
GetBranchRootIdx(candidateBranchRoot[branchRoot], branchRootByBranch);
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
/// <summary>
/// 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
/// </summary>
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 <typename ValueType, typename ValueArrayPortalType>
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
/// <summary>
/// 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
/// </summary>
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
/// <summary>
/// worklet to update the value of outer saddles for parent branches
/// </summary>
template <bool isMaximum>
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
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
);
using ExecutionSignature = void(_1, _2, _3, _4);
using InputDomain = _1;
using IdArrayPortalType = typename IdArrayType::ReadPortalType;
/// Constructor
VTKM_EXEC_CONT
UpdateOuterSaddle() {}
template <typename ValueType, typename ValuePortalType>
VTKM_EXEC void operator()(const vtkm::Id& branchOrder,
ValueType& branchValue,
const IdArrayPortalType& incomingOrders,
const ValuePortalType& incomingValues) 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);
if (isMaximum && midValue > branchValue)
branchValue = midValue;
else if (!isMaximum && midValue < branchValue)
branchValue = midValue;
return;
}
else if (midOrder > branchOrder)
tail = mid - 1;
else
head = mid + 1;
}
}
}; // UpdateOuterSaddle
} // namespace select_top_volume_contours
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -108,18 +108,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;

@ -0,0 +1,751 @@
//============================================================================
// 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_contours_local_isosurface_extract_worklet_h
#define vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_local_isosurface_extract_worklet_h
#include <vtkm/filter/scalar_topology/worklet/select_top_volume_contours/MarchingCubesDataTables.h>
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
namespace select_top_volume_contours
{
constexpr vtkm::IdComponent MAX_MARCHING_CUBE_TRIANGLES = static_cast<vtkm::IdComponent>(5);
constexpr vtkm::IdComponent MAX_LINEAR_INTERPOLATION_TRIANGLES = static_cast<vtkm::IdComponent>(12);
/// Worklet to check whether the global regular id in inside the block
/// The global regular Ids in block should be sorted beforehand
class IdxIfWithinBlockWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(
FieldIn regularId, // (input) global regular id
WholeArrayIn idsInBlock, // (array input) all global regular ids within the local block
FieldOut inBlockIndicator, // (output) 1 if the regularId is inside the block
FieldOut inBlockIdx // (output) the index of regularId in idsInBlock
);
using ExecutionSignature = void(_1, _2, _3, _4);
using InputDomain = _1;
/// Constructor
VTKM_EXEC_CONT
IdxIfWithinBlockWorklet() {}
/// The functor uses binary search to locate regularId in idsInBlock
/// if the search fails, return NO_SUCH_ELEMENT; otherwise, return the location
/// idsInBlock should be sorted
template <typename InIdPortalType>
VTKM_EXEC void operator()(const vtkm::Id& regularId,
const InIdPortalType& idsInBlock,
vtkm::Id& inBlockIndicator,
vtkm::Id& inBlockIdx) const
{
vtkm::Id head = 0;
vtkm::Id tail = idsInBlock.GetNumberOfValues() - 1;
inBlockIndicator = 0;
inBlockIdx = vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
while (head <= tail)
{
vtkm::Id mid = (head + tail) >> 1;
vtkm::Id midValue = idsInBlock.Get(mid);
if (regularId == midValue)
{
inBlockIdx = mid;
inBlockIndicator = 1;
return;
}
else if (regularId > midValue)
head = mid + 1;
else
tail = mid - 1;
}
}
}; // IdxIfWithinBlockWorklet
/// Worklet for debug, upon remove when release
class DebugSearchWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature =
void(FieldIn regularId, // (input) global regular id
WholeArrayIn idsInBlock, // (array input) all global regular ids within the local block
FieldOut foundId);
using ExecutionSignature = _3(InputIndex, _1, _2);
using InputDomain = _1;
/// Constructor
VTKM_EXEC_CONT
DebugSearchWorklet(const vtkm::Id& superarcSize)
: SuperarcSize(superarcSize)
{
}
template <typename InIdPortalType>
VTKM_EXEC vtkm::Id operator()(const vtkm::Id& inputIndex,
const vtkm::Id& regularId,
const InIdPortalType& idsInBlock) const
{
vtkm::Id head = 0;
vtkm::Id tail = idsInBlock.GetNumberOfValues() - 1;
while (head <= tail)
{
vtkm::Id mid = (head + tail) >> 1;
vtkm::Id midValue = idsInBlock.Get(mid);
if (regularId == midValue)
{
if (inputIndex < SuperarcSize)
return inputIndex;
break;
}
else if (regularId > midValue)
head = mid + 1;
else
tail = mid - 1;
}
return vtkm::worklet::contourtree_augmented::NO_SUCH_ELEMENT;
}
private:
const vtkm::Id SuperarcSize;
}; // DebugSearchWorklet
/// 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 <typename ValueType>
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 vertexOffset, // (array input) vertex offset look-up table
FieldOut caseCell // (output) the polarity case (in binary) of the cell
);
using ExecutionSignature = _4(_1, _2, _3);
using InputDomain = _1;
using IdArrayPortalType = typename vtkm::cont::ArrayHandle<vtkm::Id>::ReadPortalType;
using ValueArrayPortalType = typename vtkm::cont::ArrayHandle<ValueType>::ReadPortalType;
/// <summary>
/// Constructor
/// </summary>
/// <param name="ptDimensions">dimension of points in the grid</param>
/// <param name="branchSaddleEpsilon">the direction for tiebreaking when comparing values</param>
/// <param name="isoValue">isovalue for the isosurface to extract</param>
/// <returns></returns>
VTKM_EXEC_CONT
GetCellCasesWorklet(const vtkm::Id3 ptDimensions,
const vtkm::Id branchSaddleEpsilon,
const ValueType isoValue)
: PointDimensions(ptDimensions)
, BranchSaddleEpsilon(branchSaddleEpsilon)
, IsoValue(isoValue)
{
CellDimensions[0] = ptDimensions[0] - 1;
CellDimensions[1] = ptDimensions[1] - 1;
CellDimensions[2] = ptDimensions[2] - 1;
}
/// <summary>
/// Computes the polarity case of cells
/// </summary>
/// <param name="localIndex">the local index of the point in the local grid</param>
/// <param name="dataValuesPortal">all data values on the local grid points</param>
/// <returns>integer indicating the polarity case of the cell originating at the input point</returns>
VTKM_EXEC vtkm::Id operator()(const vtkm::Id localIndex,
const ValueArrayPortalType& dataValuesPortal,
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
if (dataValuesPortal.Get(currPt) > IsoValue ||
(dataValuesPortal.Get(currPt) == IsoValue && BranchSaddleEpsilon < 0))
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
if (dataValuesPortal.Get(currPt) > IsoValue ||
(dataValuesPortal.Get(currPt) == IsoValue && BranchSaddleEpsilon < 0))
caseCell |= vtkm::Id(1) << i;
}
return caseCell;
}
}
private:
vtkm::Id3 PointDimensions;
vtkm::Id BranchSaddleEpsilon;
ValueType IsoValue;
vtkm::Id3 CellDimensions;
}; // GetCellCasesWorklet
/// 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 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 = _5(_1, _2, _3, _4, _6);
using InputDomain = _1;
/// <summary>
/// Constructor
/// </summary>
/// <param name="totNumPoints">total number of points within the local grid</param>
/// <hint>We only need a number that is larger than any grid index</hint>
VTKM_EXEC_CONT
GetSuperarcByIsoValueWorklet(const vtkm::Id totNumPoints)
: TotalNumPoints(totNumPoints)
{
}
/// <summary>
/// Implementation of GetSuperarcByIsoValueWorklet.
/// Check vtkm::worklet::contourtree_distributed::FindSuperArcForUnknownNode
/// for the execution object description.
/// </summary>
/// <typeparam name="ValueType">data value type</typeparam>
/// <typeparam name="findSuperarcType">execution object type of findSuperarc</typeparam>
/// <param name="upperEndLocalId">local id of the upper end vertex of the branch</param>
/// <param name="lowerEndLocalId">local id of the lower end vertex of the branch</param>
/// <param name="isoValue">isovalue</param>
/// <param name="branchSaddleEpsilon">the direction for tiebreaking when comparing values</param>
/// <param name="findSuperarc">execution object</param>
/// <returns></returns>
template <typename ValueType, typename findSuperarcType>
VTKM_EXEC vtkm::Id operator()(const vtkm::Id upperEndLocalId,
const vtkm::Id lowerEndLocalId,
const ValueType isoValue,
const vtkm::Id branchSaddleEpsilon,
const findSuperarcType& findSuperarc) const
{
VTKM_ASSERT(branchSaddleEpsilon != 0);
if (branchSaddleEpsilon < 0)
return findSuperarc.FindSuperArcForUnknownNode(
-1, isoValue, upperEndLocalId, lowerEndLocalId);
return findSuperarc.FindSuperArcForUnknownNode(
TotalNumPoints, isoValue, upperEndLocalId, lowerEndLocalId);
}
private:
vtkm::Id TotalNumPoints;
}; // GetSuperarcByIsoValueWorklet
/// Worklet for calculating the edges to be drawn in the cell
/// NOTE: this worklet can only work on 2D and 3D data
template <typename ValueType>
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 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);
using InputDomain = _1;
using IdArrayReadPortalType = typename vtkm::cont::ArrayHandle<vtkm::Id>::ReadPortalType;
using IdArrayWritePortalType = typename vtkm::cont::ArrayHandle<vtkm::Id>::WritePortalType;
using ValueArrayPortalType = typename vtkm::cont::ArrayHandle<ValueType>::ReadPortalType;
using EdgePointArrayPortalType =
typename vtkm::cont::ArrayHandle<vtkm::Vec3f_64>::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 branchSuperarc,
const vtkm::Id branchSaddleEpsilon,
const vtkm::Id totNumPoints,
const bool marchingCubes)
: PointDimensions(ptDimensions)
, GlobalPointIndexStart(globalPointIndexStart)
, IsoValue(isoValue)
, BranchSuperarc(branchSuperarc)
, BranchSaddleEpsilon(branchSaddleEpsilon)
, TotalNumPoints(totNumPoints)
, isMarchingCubes(marchingCubes)
{
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<vtkm::Float64>(vertOffset.Get(cellIndex * 2) + localPt[0]),
static_cast<vtkm::Float64>(vertOffset.Get(cellIndex * 2 + 1) + localPt[1]),
static_cast<vtkm::Float64>(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<vtkm::Float64>(vertOffset.Get(cellIndex * 3) + localPt[0]),
static_cast<vtkm::Float64>(vertOffset.Get(cellIndex * 3 + 1) + localPt[1]),
static_cast<vtkm::Float64>(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 <typename FindSuperarcExecType>
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& 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);
// 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);
// 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+
VTKM_ASSERT(BranchSaddleEpsilon != 0);
// lower end of branch is leaf
// the actual isovalue should be IsoValue-eps
// eps is 0+ (infinitely small)
if (BranchSaddleEpsilon < 0)
{
lineFromSuperarc =
findSuperarcForNode.FindSuperArcForUnknownNode(-1, IsoValue, highVertFrom, lowVertFrom);
lineToSuperarc =
findSuperarcForNode.FindSuperArcForUnknownNode(-1, IsoValue, highVertTo, lowVertTo);
}
// upper end of branch is leaf
// the actual isovalue should be IsoValue+eps
// eps is 0+ (infinitely small)
else if (BranchSaddleEpsilon > 0)
{
lineFromSuperarc = findSuperarcForNode.FindSuperArcForUnknownNode(
TotalNumPoints, IsoValue, highVertFrom, lowVertFrom);
lineToSuperarc = findSuperarcForNode.FindSuperArcForUnknownNode(
TotalNumPoints, 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);
vtkm::Float64 fromRatio =
vtkm::Float64(IsoValue - lineFromVert0Value) / (lineFromVert1Value - lineFromVert0Value);
vtkm::Float64 toRatio =
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 by 0+
VTKM_ASSERT(BranchSaddleEpsilon != 0);
// lower end of branch is leaf
// the actual isovalue should be IsoValue-(0+)
if (BranchSaddleEpsilon < 0)
{
labelEdgeSuperarc =
findSuperarcForNode.FindSuperArcForUnknownNode(-1, IsoValue, highVert, lowVert);
}
// upper end of branch is leaf
// the actual isovalue should be IsoValue+(0+)
else if (BranchSaddleEpsilon > 0)
{
labelEdgeSuperarc = findSuperarcForNode.FindSuperArcForUnknownNode(
TotalNumPoints, 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 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);
vtkm::Float64 fromRatio = vtkm::Float64(IsoValue - lineFromVert0Value) /
(lineFromVert1Value - lineFromVert0Value);
vtkm::Float64 toRatio =
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 BranchSuperarc;
vtkm::Id BranchSaddleEpsilon;
vtkm::Id TotalNumPoints;
bool isMarchingCubes;
vtkm::Id3 CellDimensions;
}; // GetEdgesInCellWorklet
} // namespace select_top_volume_contours
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,945 @@
//============================================================================
// 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_contours_marching_cubes_data_tables_h
#define vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_marching_cubes_data_tables_h
#include <vtkm/Types.h>
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
namespace select_top_volume_contours
{
// Edges include the diagonal of the triangulation of the mesh
const vtkm::Id nVertices2d = 4;
const vtkm::Id nEdges2d = 5;
const vtkm::Id nCases2d = 16;
const vtkm::Id nLineTableElemSize2d = 8;
const vtkm::cont::ArrayHandle<vtkm::Id> vertexOffset2d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({ 0, 0, 1, 0, 1, 1, 0, 1 });
const vtkm::cont::ArrayHandle<vtkm::Id> edgeTable2d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({ 0, 1, 1, 2, 3, 2, 0, 3, 0, 2 });
const vtkm::cont::ArrayHandle<vtkm::Id> numLinesTable2d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({ 0, 2, 1, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 1, 2, 0 });
// size: nCase2d * nLineTableElemSize2d
const vtkm::cont::ArrayHandle<vtkm::Id> lineTable2d = vtkm::cont::make_ArrayHandle<vtkm::Id>({
#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
});
const vtkm::Id nVertices3d = 8;
const vtkm::Id nEdgesMC3d = 12;
const vtkm::Id nEdgesLT3d = 19;
const vtkm::Id nCasesMC3d = 256;
const vtkm::Id nCasesLT3d = 256;
// size: nVertices3d * nDims (3)
const vtkm::cont::ArrayHandle<vtkm::Id> vertexOffset3d = vtkm::cont::make_ArrayHandle<vtkm::Id>(
{ 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1 });
// size: nEdgesMC3d * 2 (vertices per edge)
const vtkm::cont::ArrayHandle<vtkm::Id> edgeTableMC3d = vtkm::cont::make_ArrayHandle<vtkm::Id>(
{ 0, 1, 1, 2, 3, 2, 0, 3, 4, 5, 5, 6, 7, 6, 4, 7, 0, 4, 1, 5, 2, 6, 3, 7 });
// size: nEdgesLT3d * 2
const vtkm::cont::ArrayHandle<vtkm::Id> edgeTableLT3d = vtkm::cont::make_ArrayHandle<vtkm::Id>(
{ 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 });
// size: nCasesMC3d
const vtkm::cont::ArrayHandle<vtkm::Id> numTrianglesTableMC3d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({
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,
});
// size: nCasesMC3d
const vtkm::cont::ArrayHandle<vtkm::Id> numTrianglesTableLT3d =
vtkm::cont::make_ArrayHandle<vtkm::Id>(
{ 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 });
const vtkm::Id nTriTableMC3dElemSize = 16; // (at most 5 triangles, each with 3 vertices)
const vtkm::Id nTriTableLT3dElemSize = 37; // (at most 12 triangles, each with 3 vertices)
// size: nCasesMC3d * nTriTableMCElemSize
const vtkm::cont::ArrayHandle<vtkm::Id> triTableMC3d = vtkm::cont::make_ArrayHandle<vtkm::Id>({
#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
});
// size: nCasesLT3d * nTriTableLT3dElemSize
const vtkm::cont::ArrayHandle<vtkm::Id> triTableLT3d = vtkm::cont::make_ArrayHandle<vtkm::Id>({
#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
});
const vtkm::Id nLabelEdgeTableMC3dElemSize = 9; // (at most 4 label edges)
const vtkm::Id nLabelEdgeTableLT3dElemSize = 13; // (at most 6 tetrahedra)
// size: nCasesMC3d * nLabelEdgeTableMC3dElemSize
const vtkm::cont::ArrayHandle<vtkm::Id> labelEdgeTableMC3d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({
#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
});
// size: nCasesLT3d * nLabelEdgeTableLT3dElemSize
// (TODO/FIXME: it can be improved to be shortened a little bit)
const vtkm::cont::ArrayHandle<vtkm::Id> labelEdgeTableLT3d =
vtkm::cont::make_ArrayHandle<vtkm::Id>({
#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
});
} // namespace select_top_volume_contours
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif

@ -0,0 +1,103 @@
//============================================================================
// 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_contours_predicates_h
#define vtk_m_filter_scalar_topology_worklet_select_top_volume_contours_predicates_h
#include <vtkm/Types.h>
namespace vtkm
{
namespace worklet
{
namespace scalar_topology
{
namespace select_top_volume_contours
{
constexpr vtkm::Id BRANCH_SADDLE = static_cast<vtkm::Id>(1); // 1 << 0
constexpr vtkm::Id BRANCH_COVER = static_cast<vtkm::Id>(2); // 1 << 1
constexpr vtkm::Id MAXIMA_CONTOUR = static_cast<vtkm::Id>(4); // 1 << 2
struct IsNonNegative
{
VTKM_EXEC_CONT
IsNonNegative() {}
VTKM_EXEC_CONT
bool operator()(const vtkm::Id x) const { return x >= 0; }
};
struct IsExtraMinima
{
VTKM_EXEC_CONT
IsExtraMinima() {}
VTKM_EXEC_CONT
bool operator()(const vtkm::Id x) const { return (x & vtkm::Id(1)) != 0; }
};
struct IsExtraMaxima
{
VTKM_EXEC_CONT
IsExtraMaxima() {}
VTKM_EXEC_CONT
bool operator()(const vtkm::Id x) const { return (x & vtkm::Id(2)) != 0; }
};
} // namespace select_top_volume_contours
} // namespace scalar_topology
} // namespace worklet
} // namespace vtkm
#endif