Merge topic 'NewFilter'

ba2a710e7 Update NewFitlerInterface.md
1469d34b4 rename DoExecutePartitions
74905a05c use new header file
161391656 Add ChangeLog
0e3cc8076 restore deprecated filter::GenerateIds
130d0d9df Updated Doxygen comments
8cf02d363 New Filter Interface Design

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !2641
This commit is contained in:
Li-Ta Lo 2021-12-13 21:32:40 +00:00 committed by Kitware Robot
commit 06428e2bc7
13 changed files with 714 additions and 103 deletions

@ -0,0 +1,20 @@
## New Filter Interface Design ##
An overhaul of the Filter interface is undergoing. This refactoring effort will
address many problems we faced in the old design. The most important one is to
remove the requirement to compile every single Filter users with a Device Compiler.
This is addressed by removing C++ template (and CRTP) from Filter and is subclasses.
A new non-templated NewFilter class is added with many old templated public interface
removed.
This new design also made Filter implementations thread-safe by default. Filter
implementations are encouraged to take advantage of the new design and removing
shared metatable states from their `DoExecute`, see Doxygen documentation in
NewFilter.h
Filter implementations are also re-organized into submodules, with each submodule
in its own `vtkm/filter` subdirectory. User should update their code to include
the new header files, for example, `vtkm/filter/field_transform/GenerateIds.h`and
link to submodule library file, for example, `libvtkm_filter_field_transform.so`.
To maintain backward compatability, old `vtkm/filter/FooFilter.h` header files
can still be used but will be deprecated in release 2.0.

@ -191,7 +191,6 @@ set(extra_sources_device
${ClipWithFieldInstantiations}
${ClipWithImplicitFunctionInstantiations}
ExternalFaces.cxx
GenerateIds.cxx
VectorMagnitude.cxx
particleadvection/Messenger.cxx
particleadvection/ParticleMessenger.cxx
@ -234,6 +233,18 @@ set(gradient_sources_device
vtkm_pyexpander_generated_file(ClipWithFieldExternInstantiations.h)
vtkm_pyexpander_generated_file(ClipWithImplicitFunctionExternInstantiations.h)
set(core_headers
NewFilter.h)
set(core_sources_device
NewFilter.cxx)
vtkm_library(
NAME vtkm_filter_core
HEADERS ${core_headers}
DEVICE_SOURCES ${core_sources_device}
USE_VTKM_JOB_POOL
)
add_library(vtkm_filter INTERFACE)
vtkm_library(
@ -270,6 +281,7 @@ vtkm_library(
set_target_properties(
vtkm_filter_common
vtkm_filter_core
vtkm_filter_extra
vtkm_filter_contour
vtkm_filter_gradient
@ -279,6 +291,7 @@ set_target_properties(
)
target_link_libraries(vtkm_filter_common PUBLIC vtkm_worklet)
target_link_libraries(vtkm_filter_core PUBLIC vtkm_cont)
target_link_libraries(vtkm_filter_extra PUBLIC vtkm_filter_common)
target_link_libraries(vtkm_filter_contour PUBLIC vtkm_filter_common)
target_link_libraries(vtkm_filter_gradient PUBLIC vtkm_filter_common)
@ -293,6 +306,7 @@ target_link_libraries(vtkm_filter PUBLIC INTERFACE
vtkm_filter_contour
vtkm_filter_gradient
vtkm_filter_common
vtkm_filter_core
)
install(TARGETS vtkm_filter EXPORT ${VTKm_EXPORT_NAME})
@ -300,6 +314,7 @@ install(TARGETS vtkm_filter EXPORT ${VTKm_EXPORT_NAME})
add_subdirectory(internal)
add_subdirectory(particleadvection)
add_subdirectory(field_transform)
#-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
if (VTKm_ENABLE_TESTING)

@ -10,95 +10,31 @@
#ifndef vtk_m_filter_GenerateIds_h
#define vtk_m_filter_GenerateIds_h
#include <vtkm/filter/Filter.h>
#include <vtkm/filter/vtkm_filter_extra_export.h>
#include <vtkm/Deprecated.h>
#include <vtkm/filter/field_transform/GenerateIds.h>
namespace vtkm
{
namespace filter
{
/// \brief Adds fields to a `DataSet` that give the ids for the points and cells.
///
/// This filter will add (by default) a point field named `pointids` that gives the
/// index of the associated point and likewise a cell field named `cellids` for the
/// associated cell indices. These fields are useful for tracking the provenance of
/// the elements of a `DataSet` as it gets manipulated by filters. It is also
/// convenient for adding indices to operations designed for fields and generally
/// creating test data.
///
class VTKM_FILTER_EXTRA_EXPORT GenerateIds : public vtkm::filter::Filter<GenerateIds>
VTKM_DEPRECATED(
1.8,
"Use vtkm/filter/field_transform/GenerateIds.h instead of vtkm/filter/GenerateIds.h.")
inline void GenerateIds_deprecated() {}
inline void GenerateIds_deprecated_warning()
{
std::string PointFieldName = "pointids";
std::string CellFieldName = "cellids";
bool GeneratePointIds = true;
bool GenerateCellIds = true;
bool UseFloat = false;
GenerateIds_deprecated();
}
public:
GenerateIds() = default;
~GenerateIds() = default;
/// \{
/// \brief The name given to the generated point field.
///
/// By default, the name is `pointids`.
///
const std::string& GetPointFieldName() const { return this->PointFieldName; }
void SetPointFieldName(const std::string& name) { this->PointFieldName = name; }
/// \}
/// \{
/// \brief The name given to the generated cell field.
///
/// By default, the name is `cellids`.
///
const std::string& GetCellFieldName() const { return this->CellFieldName; }
void SetCellFieldName(const std::string& name) { this->CellFieldName = name; }
/// \}
/// \{
/// \brief Specify whether the point id field is generated.
///
/// When `GeneratePointIds` is `true` (the default), a field echoing the point
/// indices is generated. When set to `false`, this output is not created.
///
bool GetGeneratePointIds() const { return this->GeneratePointIds; }
void SetGeneratePointIds(bool flag) { this->GeneratePointIds = flag; }
/// \}
/// \{
/// \brief Specify whether the cell id field is generated.
///
/// When `GenerateCellIds` is `true` (the default), a field echoing the cell
/// indices is generated. When set to `false`, this output is not created.
///
bool GetGenerateCellIds() const { return this->GenerateCellIds; }
void SetGenerateCellIds(bool flag) { this->GenerateCellIds = flag; }
/// \}
/// \{
/// \brief Specify whether the generated fields should be integer or float.
///
/// When `UseFloat` is `false` (the default), then the fields generated will have
/// type `vtkm::Id`. If it is set to `true`, then the fields will be generated
/// with type `vtkm::FloatDefault`.
///
bool GetUseFloat() const { return this->UseFloat; }
void SetUseFloat(bool flag) { this->UseFloat = flag; }
vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& input) const;
template <typename DerivedPolicy>
vtkm::cont::DataSet PrepareForExecution(const vtkm::cont::DataSet& input,
vtkm::filter::PolicyBase<DerivedPolicy>) const
{
return this->DoExecute(input);
}
class VTKM_DEPRECATED(1.8, "Use vtkm::filter::field_transform::GenerateIds.") GenerateIds
: public vtkm::filter::field_transform::GenerateIds
{
using field_transform::GenerateIds::GenerateIds;
};
}
} // namespace vtkm::filter
} // namespace vtkm
#endif //vtk_m_filter_GenerateIds_h

136
vtkm/filter/NewFilter.cxx Normal file

@ -0,0 +1,136 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
#include <vtkm/filter/NewFilter.h>
#include <vtkm/filter/TaskQueue.h>
#include <future>
namespace vtkm
{
namespace filter
{
namespace
{
void RunFilter(NewFilter* self,
vtkm::filter::DataSetQueue& input,
vtkm::filter::DataSetQueue& output)
{
std::pair<vtkm::Id, vtkm::cont::DataSet> task;
while (input.GetTask(task))
{
auto outDS = self->Execute(task.second);
output.Push(std::make_pair(task.first, std::move(outDS)));
}
vtkm::cont::Algorithm::Synchronize();
}
} // anonymous namespace
NewFilter::~NewFilter() = default;
bool NewFilter::CanThread() const
{
return true;
}
//----------------------------------------------------------------------------
vtkm::cont::PartitionedDataSet NewFilter::DoExecutePartitions(
const vtkm::cont::PartitionedDataSet& input)
{
vtkm::cont::PartitionedDataSet output;
if (this->GetRunMultiThreadedFilter())
{
vtkm::filter::DataSetQueue inputQueue(input);
vtkm::filter::DataSetQueue outputQueue;
vtkm::Id numThreads = this->DetermineNumberOfThreads(input);
//Run 'numThreads' filters.
std::vector<std::future<void>> futures(static_cast<std::size_t>(numThreads));
for (std::size_t i = 0; i < static_cast<std::size_t>(numThreads); i++)
{
auto f = std::async(
std::launch::async, RunFilter, this, std::ref(inputQueue), std::ref(outputQueue));
futures[i] = std::move(f);
}
for (auto& f : futures)
f.get();
//Get results from the outputQueue.
output = outputQueue.Get();
}
else
{
for (const auto& inBlock : input)
{
vtkm::cont::DataSet outBlock = this->Execute(inBlock);
output.AppendPartition(outBlock);
}
}
return output;
}
vtkm::cont::DataSet NewFilter::Execute(const vtkm::cont::DataSet& input)
{
return this->DoExecute(input);
}
vtkm::cont::PartitionedDataSet NewFilter::Execute(const vtkm::cont::PartitionedDataSet& input)
{
VTKM_LOG_SCOPE(vtkm::cont::LogLevel::Perf,
"NewFilter (%d partitions): '%s'",
(int)input.GetNumberOfPartitions(),
vtkm::cont::TypeToString<decltype(*this)>().c_str());
vtkm::cont::PartitionedDataSet output = this->DoExecutePartitions(input);
return output;
}
vtkm::Id NewFilter::DetermineNumberOfThreads(const vtkm::cont::PartitionedDataSet& input)
{
vtkm::Id numDS = input.GetNumberOfPartitions();
//Aribitrary constants.
const vtkm::Id threadsPerGPU = 8;
const vtkm::Id threadsPerCPU = 4;
vtkm::Id availThreads = 1;
auto& tracker = vtkm::cont::GetRuntimeDeviceTracker();
if (tracker.CanRunOn(vtkm::cont::DeviceAdapterTagCuda{}))
availThreads = threadsPerGPU;
else if (tracker.CanRunOn(vtkm::cont::DeviceAdapterTagKokkos{}))
{
//Kokkos doesn't support threading on the CPU.
#ifdef VTKM_KOKKOS_CUDA
availThreads = threadsPerGPU;
#else
availThreads = 1;
#endif
}
else if (tracker.CanRunOn(vtkm::cont::DeviceAdapterTagSerial{}))
availThreads = 1;
else
availThreads = threadsPerCPU;
vtkm::Id numThreads = std::min<vtkm::Id>(numDS, availThreads);
return numThreads;
}
} // namespace filter
} // namespace vtkm

362
vtkm/filter/NewFilter.h Normal file

@ -0,0 +1,362 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#ifndef vtk_m_filter_NewFilter_h
#define vtk_m_filter_NewFilter_h
#include <vtkm/cont/DataSet.h>
#include <vtkm/cont/Field.h>
#include <vtkm/cont/Invoker.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/cont/PartitionedDataSet.h>
#include <vtkm/filter/FieldSelection.h>
#include <vtkm/filter/vtkm_filter_core_export.h>
namespace vtkm
{
namespace filter
{
/// \brief base class for all filters.
///
/// This is the base class for all filters. To add a new filter, one can subclass this (or any of
/// the existing subclasses e.g. FilterField, FilterParticleAdvection, etc.) and implement relevant
/// methods.
///
/// \section FilterUsage Usage
///
/// To execute a filter, one typically calls the `auto result = filter.Execute(input)`. Typical
/// usage is as follows:
///
/// \code{cpp}
///
/// // create the concrete subclass (e.g. Contour).
/// vtkm::filter::contour::Contour contour;
///
/// // select fields to map to the output, if different from default which is to map all input
/// // fields.
/// contour.SetFieldToPass({"var1", "var2"});
///
/// // execute the filter on vtkm::cont::DataSet.
/// vtkm::cont::DataSet dsInput = ...
/// auto outputDS = contour.Execute(dsInput);
///
/// // or, execute on a vtkm::cont::PartitionedDataSet
/// vtkm::cont::PartitionedDataSet mbInput = ...
/// auto outputMB = contour.Execute(mbInput);
/// \endcode
///
/// `Execute` methods take in the input DataSet or PartitionedDataSet to process and return the
/// result. The type of the result is same as the input type, thus `Execute(DataSet&)` returns
/// a DataSet while `Execute(PartitionedDataSet&)` returns a PartitionedDataSet.
///
/// `Execute` simply calls the pure virtual function `DoExecute(DataSet&)` which is the main
/// extension point of the Filter interface. Filter developer needs to override
/// `DoExecute(DataSet)` to implement the business logic of filtering operations on a single
/// DataSet.
///
/// The default implementation of `Execute(PartitionedDataSet&)` is merely provided for
/// convenience. Internally, it calls `DoExecutePartitions(PartitionedDataSet)` to iterate DataSets
/// of a PartitionedDataSet and pass each individual DataSets to `DoExecute(DataSet&)`,
/// possibly in a multi-threaded setting. Developer of `DoExecute(DataSet&)` needs to indicate
/// the thread-safeness of `DoExecute(DataSet&)` by overriding the `CanThread()` virtual method
/// which by default returns `true`.
///
/// In the case that filtering on a PartitionedDataSet can not be simply implemented as a
/// for-each loop on the component DataSets, filter implementor needs to override the
/// `DoExecutePartitions(PartitionedDataSet&)`. See the implementation of
/// `FilterParticleAdvection::Execute(PartitionedDataSet&)` for an example.
///
/// \section FilterSubclassing Subclassing
///
/// In many uses cases, one subclasses one of the immediate subclasses of this class such as
/// FilterField, FilterParticleAdvection, etc. Those may impose additional constraints on the
/// methods to implement in the subclasses. Here, we describes the things to consider when directly
/// subclassing vtkm::filter::NewFilter.
///
/// \subsection FilterExecution Execute
///
/// A concrete subclass of Filter must provide `DoExecute` implementation that provides the meat
/// for the filter i.e. the implementation for the filter's data processing logic. There are two
/// signatures available; which one to implement depends on the nature of the filter.
///
/// Let's consider simple filters that do not need to do anything special to handle
/// PartitionedDataSet e.g. clip, contour, etc. These are the filters where executing the filter
/// on a PartitionedDataSet simply means executing the filter on one partition at a time and
/// packing the output for each iteration info the result PartitionedDataSet. For such filters,
/// one must implement the following signature.
///
/// \code{cpp}
///
/// vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& input);
///
/// \endcode
///
/// The role of this method is to execute on the input dataset and generate the result and return
/// it. If there are any errors, the subclass must throw an exception
/// (e.g. `vtkm::cont::ErrorFilterExecution`).
///
/// In this simple case, the NewFilter superclass handles iterating over multiple partitions in the
/// input PartitionedDataSet and calling `DoExecute(DataSet&)` iteratively.
///
/// The aforementioned approach is also suitable for filters that need special handling for
/// PartitionedDataSets that requires certain cross DataSet operations (usually scatter/gather
/// and reduction on DataSets) before and/or after the per DataSet operation. This can be done by
/// overriding `DoExecutePartitions(PartitionedDataSet&)` while calling to the base class
/// `DoExecutePartitions(PartitionedDataSet&) as helper function for iteration on DataSets.
///
/// \code{cpp}
/// vtkm::cont::PartitionedDataSet FooFilter::DoExecutePartitions(
/// const vtkm::cont::PartitionedDataSet& input)
/// {
/// // Do pre execute stuff, e.g. scattering to each DataSet
/// auto output = this->NewFilter::DoExecutePartitions(input);
/// // Do post execute stuff, e.g gather/reduce from DataSets
/// return output;
/// }
/// \endcode
///
/// For more complex filters, like streamlines, particle tracking, where the processing of
/// PartitionedDataSets cannot be modelled as mapping and reduction operation on DataSet, one
/// needs fully implement `DoExecutePartitions(PartitionedDataSet&)`. Now the subclass is given
/// full control over the execution, including any mapping of fields to output (described in next
/// sub-section).
///
/// \subsection FilterMappingFields MapFieldsOntoOutput
///
/// For subclasses that map input fields into output fields, the implementation of its
/// `DoExecute(DataSet&)` should call `NewFilter::MapFieldsOntoOutput` with a properly defined
/// `Mapper`, before returning the output DataSet. For example:
///
/// \code{cpp}
/// VTKM_CONT DataSet SomeFilter::DoExecute(const vtkm::cont::DataSet& input)
/// {
/// vtkm::cont::DataSet output;
/// output = ... // Generation of the new DataSet
///
/// // Mapper is a callable object (function object, lambda, etc.) that takes an input Field
/// // and maps it to an output Field and then add the output Field to the output DataSet
/// auto mapper = [](auto& outputDs, const auto& inputField) {
/// auto outputField = ... // Business logic for mapping input field to output field
/// output.AddField(outputField);
/// };
/// MapFieldsOntoOutput(input, output, mapper);
///
/// return output;
/// }
/// \endcode
///
/// `MapFieldsOntoOutput` iterates through each `FieldToPass` in the input DataSet and calls the
/// Mapper to map the input Field to output Field. For simple filters that just pass on input
/// fields to the output DataSet without any computation, an overload of
/// `MapFieldsOntoOutput(const vtkm::cont::DataSet& input, vtkm::cont::DataSet& output)` is also
/// provided as a convenience that uses the default mapper which trivially adds input Field to
/// output DaaSet (via a shallow copy).
///
/// \subsection FilterThreadSafety CanThread
///
/// By default, the implementation of `DoExecute(DataSet&)` should model a *pure function*, i.e. it
/// does not have any mutable shared state. This makes it thread-safe by default and allows
/// the default implementation of `DoExecutePartitions(PartitionedDataSet&)` to be simply a parallel
/// for-each, thus facilitates multi-threaded execution without any lock.
///
/// Many legacy (VTKm 1.x) filter implementations needed to store states between the mesh generation
/// phase and field mapping phase of filter execution, for example, parameters for field
/// interpolation. The shared mutable states were mostly stored as mutable data members of the
/// filter class (either in terms of ArrayHandle or some kind of Worket). The new filter interface,
/// by combining the two phases into a single call to `DoExecute(DataSet&)`, we have eliminated most
/// of the cases that require such shared mutable states. New implementations of filters that
/// require passing information between these two phases can now use local variables within the
/// `DoExecute(DataSet&)`. For example:
///
/// \code{cpp}
/// struct SharedState; // shared states between mesh generation and field mapping.
/// VTKM_CONT DataSet ThreadSafeFilter::DoExecute(const vtkm::cont::DataSet& input)
/// {
/// // Mutable states that was a data member of the filter is now a local variable.
/// // Each invocation of Execute(DataSet) in the multi-threaded execution of
/// // Execute(PartitionedDataSet&) will have a copy of `states` on each thread's stack
/// // thus making it thread-safe.
/// SharedStates states;
///
/// vtkm::cont::DataSet output;
/// output = ... // Generation of the new DataSet and store interpolation parameters in `states`
///
/// // Lambda capture of `states`, effectively passing the shared states to the Mapper.
/// auto mapper = [&states](auto& outputDs, const auto& inputField) {
/// auto outputField = ... // Use `states` for mapping input field to output field
/// output.AddField(outputField);
/// };
/// MapFieldsOntoOutput(input, output, mapper);
///
/// return output;
/// }
/// \endcode
///
/// In the rare cases that filter implementation can not be made thread-safe, the implementation
/// needs to override the `CanThread()` virtual method to return `false`. The default
/// `Execute(PartitionedDataSet&)` implementation will fallback to a serial for loop execution.
///
/// \subsection FilterThreadScheduling DoExecute
/// The default multi-threaded execution of `Execute(PartitionedDataSet&)` uses a simple FIFO queue
/// of DataSet and pool of *worker* threads. Implementation of Filter subclass can override the
/// `DoExecutePartitions(PartitionedDataSet)` virtual method to provide implementation specific
/// scheduling policy. The default number of *worker* threads in the pool are determined by the
/// `DetermineNumberOfThreads()` virtual method using several backend dependent heuristic.
/// Implementations of Filter subclass can also override
/// `DetermineNumberOfThreads()` to provide implementation specific heuristic.
///
class VTKM_FILTER_CORE_EXPORT NewFilter
{
public:
VTKM_CONT
virtual ~NewFilter();
VTKM_CONT
virtual bool CanThread() const;
VTKM_CONT
bool GetRunMultiThreadedFilter() const
{
return this->CanThread() && this->RunFilterWithMultipleThreads;
}
VTKM_CONT
void SetRunMultiThreadedFilter(bool val)
{
if (this->CanThread())
this->RunFilterWithMultipleThreads = val;
else
{
std::string msg =
"Multi threaded filter not supported for " + std::string(typeid(*this).name());
VTKM_LOG_S(vtkm::cont::LogLevel::Info, msg);
}
}
/// \brief Specify which subset of types a filter supports.
///
/// A filter is able to state what subset of types it supports.
using SupportedTypes = VTKM_DEFAULT_TYPE_LIST;
//@{
/// \brief Specify which fields get passed from input to output.
///
/// After a filter successfully executes and returns a new data set, fields are mapped from
/// input to output. Depending on what operation the filter does, this could be a simple shallow
/// copy of an array, or it could be a computed operation. You can control which fields are
/// passed (and equivalently which are not) with this parameter.
///
/// By default, all fields are passed during execution.
///
VTKM_CONT
void SetFieldsToPass(const vtkm::filter::FieldSelection& fieldsToPass)
{
this->FieldsToPass = fieldsToPass;
}
VTKM_CONT
void SetFieldsToPass(const vtkm::filter::FieldSelection& fieldsToPass,
vtkm::filter::FieldSelection::ModeEnum mode)
{
this->FieldsToPass = fieldsToPass;
this->FieldsToPass.SetMode(mode);
}
VTKM_CONT
void SetFieldsToPass(
const std::string& fieldname,
vtkm::cont::Field::Association association,
vtkm::filter::FieldSelection::ModeEnum mode = vtkm::filter::FieldSelection::MODE_SELECT)
{
this->SetFieldsToPass({ fieldname, association }, mode);
}
VTKM_CONT
const vtkm::filter::FieldSelection& GetFieldsToPass() const { return this->FieldsToPass; }
VTKM_CONT
vtkm::filter::FieldSelection& GetFieldsToPass() { return this->FieldsToPass; }
//@}
//@{
/// Select the coordinate system index to make active to use when processing the input
/// DataSet. This is used primarily by the Filter to select the coordinate system
/// to use as a field when \c UseCoordinateSystemAsField is true.
VTKM_CONT
void SetActiveCoordinateSystem(vtkm::Id index) { this->CoordinateSystemIndex = index; }
VTKM_CONT
vtkm::Id GetActiveCoordinateSystemIndex() const { return this->CoordinateSystemIndex; }
//@}
//@{
/// Executes the filter on the input and produces a result dataset.
///
/// On success, this the dataset produced. On error, vtkm::cont::ErrorExecution will be thrown.
VTKM_CONT vtkm::cont::DataSet Execute(const vtkm::cont::DataSet& input);
//@}
//@{
/// Executes the filter on the input PartitionedDataSet and produces a result PartitionedDataSet.
///
/// On success, this the dataset produced. On error, vtkm::cont::ErrorExecution will be thrown.
VTKM_CONT vtkm::cont::PartitionedDataSet Execute(const vtkm::cont::PartitionedDataSet& input);
//@}
// FIXME: Is this actually materialize? Are there different kinds of Invoker?
/// Specify the vtkm::cont::Invoker to be used to execute worklets by
/// this filter instance. Overriding the default allows callers to control
/// which device adapters a filter uses.
void SetInvoker(vtkm::cont::Invoker inv) { this->Invoke = inv; }
protected:
vtkm::cont::Invoker Invoke;
vtkm::Id CoordinateSystemIndex = 0;
template <typename Mapper>
VTKM_CONT void MapFieldsOntoOutput(const vtkm::cont::DataSet& input,
vtkm::cont::DataSet& output,
Mapper&& mapper)
{
for (vtkm::IdComponent cc = 0; cc < input.GetNumberOfFields(); ++cc)
{
auto field = input.GetField(cc);
if (this->GetFieldsToPass().IsFieldSelected(field))
{
mapper(output, field);
}
}
}
VTKM_CONT void MapFieldsOntoOutput(const vtkm::cont::DataSet& input, vtkm::cont::DataSet& output)
{
MapFieldsOntoOutput(input, output, defaultMapper);
}
private:
VTKM_CONT
virtual vtkm::Id DetermineNumberOfThreads(const vtkm::cont::PartitionedDataSet& input);
// Note: In C++, subclasses can override private methods of superclass.
VTKM_CONT virtual vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& inData) = 0;
VTKM_CONT virtual vtkm::cont::PartitionedDataSet DoExecutePartitions(
const vtkm::cont::PartitionedDataSet& inData);
static void defaultMapper(vtkm::cont::DataSet& output, const vtkm::cont::Field& field)
{
output.AddField(field);
};
vtkm::filter::FieldSelection FieldsToPass = vtkm::filter::FieldSelection::MODE_ALL;
bool RunFilterWithMultipleThreads = false;
};
}
} // namespace vtkm::filter
#endif

@ -0,0 +1,28 @@
##============================================================================
## Copyright (c) Kitware, Inc.
## All rights reserved.
## See LICENSE.txt for details.
##
## This software is distributed WITHOUT ANY WARRANTY; without even
## the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
## PURPOSE. See the above copyright notice for more information.
##============================================================================
set(field_transform_headers
GenerateIds.h)
set(field_transform_sources_device
GenerateIds.cxx)
vtkm_library(
NAME vtkm_filter_field_transform
HEADERS ${field_transform_headers}
DEVICE_SOURCES ${field_transform_sources_device}
USE_VTKM_JOB_POOL
)
target_link_libraries(vtkm_filter_field_transform PUBLIC vtkm_worklet vtkm_filter_core)
target_link_libraries(vtkm_filter PUBLIC INTERFACE vtkm_filter_field_transform)
#-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
if (VTKm_ENABLE_TESTING)
add_subdirectory(testing)
endif ()

@ -7,17 +7,15 @@
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/filter/GenerateIds.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ArrayHandleCast.h>
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/filter/field_transform/GenerateIds.h>
namespace
{
vtkm::cont::UnknownArrayHandle GenerateArray(const vtkm::filter::GenerateIds& self, vtkm::Id size)
vtkm::cont::UnknownArrayHandle GenerateArray(const vtkm::filter::field_transform::GenerateIds& self,
vtkm::Id size)
{
vtkm::cont::ArrayHandleIndex indexArray(size);
@ -41,8 +39,9 @@ namespace vtkm
{
namespace filter
{
vtkm::cont::DataSet GenerateIds::DoExecute(const vtkm::cont::DataSet& input) const
namespace field_transform
{
vtkm::cont::DataSet GenerateIds::DoExecute(const vtkm::cont::DataSet& input)
{
vtkm::cont::DataSet output = input;
@ -57,8 +56,10 @@ vtkm::cont::DataSet GenerateIds::DoExecute(const vtkm::cont::DataSet& input) con
output.AddCellField(this->GetCellFieldName(), GenerateArray(*this, input.GetNumberOfCells()));
}
MapFieldsOntoOutput(input, output);
return output;
}
} // namespace field_transform
} // namespace vtkm::filter
} // namespace vtkm

@ -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.
//============================================================================
#ifndef vtk_m_filter_field_transform_GenerateIds_h
#define vtk_m_filter_field_transform_GenerateIds_h
#include <vtkm/filter/NewFilter.h>
#include <vtkm/filter/field_transform/vtkm_filter_field_transform_export.h>
namespace vtkm
{
namespace filter
{
namespace field_transform
{
/// \brief Adds fields to a `DataSet` that give the ids for the points and cells.
///
/// This filter will add (by default) a point field named `pointids` that gives the
/// index of the associated point and likewise a cell field named `cellids` for the
/// associated cell indices. These fields are useful for tracking the provenance of
/// the elements of a `DataSet` as it gets manipulated by filters. It is also
/// convenient for adding indices to operations designed for fields and generally
/// creating test data.
///
class VTKM_FILTER_FIELD_TRANSFORM_EXPORT GenerateIds : public vtkm::filter::NewFilter
{
std::string PointFieldName = "pointids";
std::string CellFieldName = "cellids";
bool GeneratePointIds = true;
bool GenerateCellIds = true;
bool UseFloat = false;
public:
/// \{
/// \brief The name given to the generated point field.
///
/// By default, the name is `pointids`.
///
const std::string& GetPointFieldName() const { return this->PointFieldName; }
void SetPointFieldName(const std::string& name) { this->PointFieldName = name; }
/// \}
/// \{
/// \brief The name given to the generated cell field.
///
/// By default, the name is `cellids`.
///
const std::string& GetCellFieldName() const { return this->CellFieldName; }
void SetCellFieldName(const std::string& name) { this->CellFieldName = name; }
/// \}
/// \{
/// \brief Specify whether the point id field is generated.
///
/// When `GeneratePointIds` is `true` (the default), a field echoing the point
/// indices is generated. When set to `false`, this output is not created.
///
bool GetGeneratePointIds() const { return this->GeneratePointIds; }
void SetGeneratePointIds(bool flag) { this->GeneratePointIds = flag; }
/// \}
/// \{
/// \brief Specify whether the cell id field is generated.
///
/// When `GenerateCellIds` is `true` (the default), a field echoing the cell
/// indices is generated. When set to `false`, this output is not created.
///
bool GetGenerateCellIds() const { return this->GenerateCellIds; }
void SetGenerateCellIds(bool flag) { this->GenerateCellIds = flag; }
/// \}
/// \{
/// \brief Specify whether the generated fields should be integer or float.
///
/// When `UseFloat` is `false` (the default), then the fields generated will have
/// type `vtkm::Id`. If it is set to `true`, then the fields will be generated
/// with type `vtkm::FloatDefault`.
///
bool GetUseFloat() const { return this->UseFloat; }
void SetUseFloat(bool flag) { this->UseFloat = flag; }
private:
vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& input) override;
};
} // namespace field_transform
} // namespace vtkm::filter
} // namespace vtkm
#endif //vtk_m_filter_field_transform_GenerateIds_h

@ -0,0 +1,22 @@
##============================================================================
## Copyright (c) Kitware, Inc.
## All rights reserved.
## See LICENSE.txt for details.
##
## This software is distributed WITHOUT ANY WARRANTY; without even
## the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
## PURPOSE. See the above copyright notice for more information.
##============================================================================
set(unit_tests
UnitTestGenerateIds.cxx)
set(libraries
vtkm_filter_field_transform
vtkm_source)
vtkm_unit_tests(
SOURCES ${unit_tests}
LIBRARIES ${libraries}
USE_VTKM_JOB_POOL
)

@ -7,14 +7,10 @@
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/filter/GenerateIds.h>
#include <vtkm/source/Tangle.h>
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/field_transform/GenerateIds.h>
#include <vtkm/source/Tangle.h>
namespace
{
@ -38,8 +34,9 @@ void CheckField(const vtkm::cont::UnknownArrayHandle& array, vtkm::Id expectedSi
}
}
void TryGenerateIds(vtkm::filter::GenerateIds& filter, // Why is Filter::Execute not const?
const vtkm::cont::DataSet& input)
void TryGenerateIds(
vtkm::filter::field_transform::GenerateIds& filter, // Why is Filter::Execute not const?
const vtkm::cont::DataSet& input)
{
vtkm::cont::DataSet output = filter.Execute(input);
VTKM_TEST_ASSERT(output.GetNumberOfPoints() == input.GetNumberOfPoints());
@ -81,7 +78,7 @@ void TryGenerateIds(vtkm::filter::GenerateIds& filter, // Why is Filter::Execute
void TestGenerateIds()
{
vtkm::cont::DataSet input = vtkm::source::Tangle{ vtkm::Id3(8) }.Execute();
vtkm::filter::GenerateIds filter;
vtkm::filter::field_transform::GenerateIds filter;
TryGenerateIds(filter, input);

@ -37,7 +37,6 @@ set(unit_tests
UnitTestFieldToColors.cxx
UnitTestGradientExplicit.cxx
UnitTestGradientUniform.cxx
UnitTestGenerateIds.cxx
UnitTestGhostCellClassify.cxx
UnitTestGhostCellRemove.cxx
UnitTestHistogramFilter.cxx

@ -15,7 +15,7 @@
#include <vtkm/filter/CleanGrid.h>
#include <vtkm/filter/Contour.h>
#include <vtkm/filter/GenerateIds.h>
#include <vtkm/filter/field_transform/GenerateIds.h>
#include <vtkm/io/VTKDataSetReader.h>
#include <vtkm/source/Tangle.h>
@ -32,7 +32,7 @@ public:
vtkm::Id3 dims(4, 4, 4);
vtkm::source::Tangle tangle(dims);
vtkm::filter::GenerateIds genIds;
vtkm::filter::field_transform::GenerateIds genIds;
genIds.SetUseFloat(true);
genIds.SetGeneratePointIds(false);
genIds.SetCellFieldName("cellvar");

@ -16,7 +16,7 @@
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/ClipWithImplicitFunction.h>
#include <vtkm/filter/GenerateIds.h>
#include <vtkm/filter/field_transform/GenerateIds.h>
#include <vtkm/source/Tangle.h>
#include <vtkm/worklet/Contour.h>
@ -186,7 +186,7 @@ void TestContourUniformGrid()
vtkm::Id3 dims(4, 4, 4);
vtkm::source::Tangle tangle(dims);
vtkm::filter::GenerateIds genIds;
vtkm::filter::field_transform::GenerateIds genIds;
genIds.SetUseFloat(true);
genIds.SetGeneratePointIds(false);
genIds.SetCellFieldName("cellvar");
@ -365,7 +365,7 @@ void TestContourClipped()
vtkm::Id3 dims(4, 4, 4);
vtkm::source::Tangle tangle(dims);
vtkm::filter::GenerateIds genIds;
vtkm::filter::field_transform::GenerateIds genIds;
genIds.SetUseFloat(true);
genIds.SetGeneratePointIds(false);
genIds.SetCellFieldName("cellvar");