More importantly this conversion is unnecessary as you can pass instances `DeviceAdapterTags` into methods or functions
that want `vtkm::cont::DeviceAdapterId` as they are that type!
Previously if you wanted to see if a DeviceAdapter was enabled you would the following:
```cpp
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceTag>;
constexpr auto isValid = std::integral_constant<bool,Traits::Valid>();
```
Now you would do:
```cpp
constexpr auto isValid = std::integral_constant<bool,DeviceTag::IsEnabled>();
```
So why did VTK-m make these changes?
That is a good question, and the answer for that is two fold. The VTK-m project is working better support for ArraysHandles that leverage runtime polymorphism (aka virtuals), and the ability to construct `vtkm::worklet::Dispatchers` without specifying
the explicit device they should run on. Both of these designs push more of the VTK-m logic to operate at runtime rather than compile time. This changes are designed to allow for consistent object usage between runtime and compile time instead of having
to convert between compile time and runtime types.
## New geometry classes
There are now some additional structures available both
the control and execution environments for representing
geometric entities (mostly of dimensions 2 and 3).
These new structures are now in `vtkm/Geometry.h` and
demonstrated/tested in `vtkm/testing/TestingGeometry.h`:
+ `Ray<CoordType, Dimension, IsTwoSided>`.
Instances of this struct represent a semi-infinite line
segment in a 2-D plane or in a 3-D space, depending on the
integer dimension specified as a template parameter.
Its state is the point at the start of the ray (`Origin`)
plus the ray's `Direction`, a unit-length vector.
If the third template parameter (IsTwoSided) is true, then
the ray serves as an infinite line. Otherwise, the ray will
only report intersections in its positive halfspace.
+ `LineSegment<CoordType, Dimension>`.
Instances of this struct represent a finite line segment
in a 2-D plane or in a 3-D space, depending on the integer
dimension specified as a template parameter.
Its state is the coordinates of its `Endpoints`.
+ `Plane<CoordType>`.
Instances of this struct represent a plane in 3-D.
Its state is the coordinates of a base point (`Origin`) and
a unit-length normal vector (`Normal`).
+ `Sphere<CoordType, Dimension>`.
Instances of this struct represent a *d*-dimensional sphere.
Its state is the coordinates of its center plus a radius.
It is also aliased with a `using` statement to `Circle<CoordType>`
for the specific case of 2-D.
These structures provide useful queries and generally
interact with one another.
For instance, it is possible to intersect lines and planes
and compute distances.
For ease of use, there are also several `using` statements
that alias these geometric structures to names that specialize
them for a particular dimension or other template parameter.
As an example, `Ray<CoordType, Dimension, true>` is aliased
to `Line<CoordType, Dimension>` and `Ray<CoordType, 3, true>`
is aliased to `Line3<CoordType>` and `Ray<FloatDefault, 3, true>`
is aliased to `Line3d`.
### Design patterns
If you plan to add a new geometric entity type,
please adopt these conventions:
+ Each geometric entity may be default-constructed.
The default constructor will initialize the state to some
valid unit-length entity, usually with some part of
its state at the origin of the coordinate system.
+ Entities may always be constructed by passing in values
for their internal state.
Alternate construction methods are declared as free functions
such as `make_CircleFrom3Points()`
+ Use template metaprogramming to make methods available
only when the template dimension gives them semantic meaning.
For example, a 2-D line segment's perpendicular bisector
is another line segment, but a 3-D line segment's perpendicular
line segment is a plane.
Note how this is accomplished and apply this pattern to
new geometric entities or new methods on existing entities.
+ Some entities may have invalid state.
If this is possible, the entity will have an `IsValid()` method.
For example, a sphere may be invalid because the user or some
construction technique specified a zero or negative radius.
+ When signed distance is semantically meaningful, provide it
in favor of or in addition to unsigned distance.
+ Accept a tolerance parameter when appropriate,
but provide a sensible default value.
You may want to perform exact arithmetic versions of tests,
but please provide fast, tolerance-based versions as well.
## Logging support via loguru.
The loguru project has been integrated with VTK-m to provide runtime logging
facilities. A sample of the log output can be found at
https://gitlab.kitware.com/snippets/427.
Logging is enabled by setting the CMake variable VTKm_ENABLE_LOGGING. When
this flag is enabled, any messages logged to the Info, Warn, Error, and
Fatal levels are printed to stderr by default.
Additional logging features are enabled by calling vtkm::cont::InitLogging
in an executable. This will:
- Set human-readable names for the log levels in the output.
- Allow the stderr logging level to be set at runtime by passing a
'-v [level]' argument to the executable.
- Name the main thread.
- Print a preamble with details of the program's startup (args, etc).
- Install signal handlers to automatically print stacktraces and error
contexts (linux only) on crashes.
The main logging entry points are the macros VTKM_LOG_S and VTKM_LOG_F,
which using C++ stream and printf syntax, repectively. Other variants exist,
including conditional logging and special-purpose logs for writing specific
events, such as DynamicObject cast results and TryExecute failures.
The logging backend supports the concept of "Scopes". By creating a new
scope with the macros VTKM_LOG_SCOPE or VTKM_LOG_SCOPE_FUNCTION, a new
"logging scope" is opened within the C++ scope the macro is called from. New
messages will be indented in the log until the scope ends, at which point
a message is logged with the elapsed time that the scope was active. Scopes
may be nested to arbitrary depths.
The logging implementation is thread-safe. When working in a multithreaded
environment, each thread may be assigned a human-readable name using
vtkm::cont::SetThreadName. This will appear in the log output so that
per-thread messages can be easily tracked.
By default, only Info, Warn, Error, and Fatal messages are printed to
stderr. This can be changed at runtime by passing the '-v' flag to an
executable that calls vtkm::cont::InitLogging. Alternatively, the
application can explicitly call vtkm::cont::SetStderrLogLevel to change the
verbosity. When specifying a verbosity, all log levels with enum values
less-than-or-equal-to the requested level are printed.
vtkm::cont::LogLevel::Off (or "-v Off") may be used to silence the log
completely.
The helper functions vtkm::cont::GetHumanReadableSize and
vtkm::cont::GetSizeString assist in formating byte sizes to a more readable
format. Similarly, the vtkm::cont::TypeName template functions provide RTTI
based type-name information. When logging is enabled, these use the logging
backend to demangle symbol names on supported platforms.
The more verbose VTK-m log levels are:
- Perf: Logs performance information, using the scopes feature to track
execution time of filters, worklets, and device algorithms with
microsecond resolution.
- MemCont / MemExec: These levels log memory allocations in the control and
execution environments, respectively.
- MemTransfer: This level logs memory transfers between the control and host
environments.
- Cast: Logs details of dynamic object resolution.
The log may be shared and extended by applications that use VTK-m. There
are two log level ranges left available for applications: User and
UserVerbose. The User levels may be enabled without showing any of the
verbose VTK-m levels, while UserVerbose levels will also enable all VTK-m
levels.
# ArrayHandle
## `ArrayHandle` can now take ownership of a user allocated memory location
Previously memory that was allocated outside of VTK-m was impossible to transfer to
VTK-m as we didn't know how to free it. By extending the ArrayHandle constructors
to support a Storage object that is being moved, we can clearly express that
the ArrayHandle now owns memory it didn't allocate.
Here is an example of how this is done:
```cpp
T* buffer = new T[100];
auto user_free_function = [](void* ptr) { delete[] static_cast<T*>(ptr); };
## User defined execution objects now usable with runtime selection of device adapter
Changed how Execution objects are created and passed from the `cont` environment to the `exec` environment. Instead we will now fill out a class and call `PrepareForExecution()` and create the execution object for the `exec` environment from this function. This way we do not have to template the class that extends `vtkm::cont::ExecutionObjectBase` on the device.
Example of new execution object:
```cpp
template <typenameDevice>
struct ExecutionObject
{
vtkm::Int32 Number;
};
struct TestExecutionObject : public vtkm::cont::ExecutionObjectBase
By passing TypeList and StorageList type into FieldRangeGlobalCompute,
upstream users(VTK) can pass custom types into the histogram filter.
## Cell measure functions, worklet, and filter
VTK-m now provides free functions, a worklet, and a filter for computing
the integral measure of a cell (i.e., its arc length, area, or volume).
The free functions are located in `vtkm/exec/CellMeasure.h` and share the
same signature:
```c++
template<typenameOutType,typenamePointVecType>
OutType CellMeasure(
const vtkm::IdComponent& numPts,
const PointCoordVecType& pts,
CellShapeTag,
const vtkm::exec::FunctorBase& worklet);
```
The number of points argument is provided for cell-types such as lines,
which allow an arbitrary number of points per cell.
See the worklet for examples of their use.
The worklet is named `vtkm::worklet::CellMeasure` and takes a template
parameter that is a tag list of measures to include.
Cells that are not selected by the tag list return a measure of 0.
Some convenient tag lists are predefined for you:
+ `vtkm::ArcLength` will only compute the measure of cells with a 1-dimensional parameter-space.
+ `vtkm::Area` will only compute the measure of cells with a 2-dimensional parameter-space.
+ `vtkm::Volume` will only compute the measure of cells with a 3-dimensional parameter-space.
+ `vtkm::AllMeasures` will compute all of the above.
The filter version, named `vtkm::filter::CellMeasures`– plural since
it produces a cell-centered array of measures — takes the same template
parameter and tag lists as the worklet.
By default, the output array of measure values is named "measure" but
the filter accepts other names via the `SetCellMeasureName()` method.
The only cell type that is not supported is the polygon;
you must triangulate polygons before running this filter.
See the unit tests for examples of how to use the worklet and filter.
The cell measures are all signed: negative measures indicate that the cell is inverted.
Simplicial cells (points, lines, triangles, tetrahedra) cannot not be inverted
by definition and thus always return values above or equal to 0.0.
Negative values indicate either the order in which vertices appear in its connectivity
array is improper or the relative locations of the vertices in world coordinates
result in a cell with a negative Jacobian somewhere in its interior.
Finally, note that cell measures may return invalid (NaN) or infinite (Inf, -Inf)
values if the cell is poorly defined, e.g., has coincident vertices
or a parametric dimension larger than the space spanned by its world-coordinate
vertices.
The verdict mesh quality library was used as the source of the methods
for approximating the cell measures.
## Add a `WaveletGenerator` worklet (e.g. vtkRTAnalyticSource)
Add a VTK-m implementation of VTK's `vtkRTAnalyticSource`, or "Wavelet" source
as it is known in ParaView. This is a customizable dataset with properties
that make it useful for testing and benchmarking various algorithms.
## Add a filter to support Lagrangian analysis capabilities
Lagrangian analysis operates in two phases - phase one involes the extraction of flow field information. Phase two involves calculating new particle trajectories using the saved information.
The lagrangian filter can be used to extract flow field information given a time-varying vector fields. The extracted information is in the form of particle trajectories.
The filter operates by first being set up with some information regarding step size, the interval at which information should be saved (write frequency), the number of seeds to be placed in the domain (specified as a reduction factor along each axis of the original dimensions).
The step size should be equivalent to the time between vector field data input.
The write frequency corresponds to the number of cycles between saves.
Filter execution is called for each cycle of the simulation data. Each filter execution call requires a velocity field to advect particles forward.
The extracted particle trajectories - referred to as basis flows exist in the domain for the specified interval (write frequency). Particles are then reset along a uniform grid and new particle trajectories are calculated.
An example of using the Lagrangian filter is at vtk-m/examples/lagrangian
The basis flows are saved into a folder named output which needs to be created in the directory in which the program is being executed.
The basis flows can be interpolated using barycentric coordinate interpolation or a form of linear interpolation to calculate new particle trajectories post hoc.
An example of using basis flows generated by the Lagrangian filter is at vtk-m/examples/posthocinterpolation. The folder contains a script which specifies parameters which need to be provided to use the example.
## Add a warp vector worklet and filter
This commit adds a worklet that modifies point coordinates by moving points
along point normals by the scalar amount. It's a simplified version of the
vtkWarpScalar in VTK. Additionally the filter doesn't modify the point coordinates,
but creates a new point coordinates that have been warped.
Useful for showing flow profiles or mechanical deformation.
## Add a warp scalar worklet and filter
This commit adds a worklet as well as a filter that modify point coordinates by moving points
along point normals by the scalar amount times the scalar factor.
It's a simplified version of the vtkWarpScalar class in VTK. Additionally the filter doesn't
modify the point coordinates, but creates a new point coordinates that have been warped.
## Add a split sharp edge filter
It's a filter that splits sharp manifold edges where the feature angle
between the adjacent surfaces are larger than the threshold value.
When an edge is split, it would add a new point to the coordinates
and update the connectivity of an adjacent surface.
Ex. There are two adjacent triangles(0,1,2) and (2,1,3). Edge (1,2) needs
to be split. Two new points 4(duplication of point 1) an 5(duplication of point 2)
would be added and the later triangle's connectivity would be changed
to (5,4,3).
By default, all old point's fields would be copied to the new point.
Use with caution.
## Time-varying "oscillator" filter and example
The oscillator is a simple analytical source of time-varying data.
It provides a function value at each point of a uniform grid that
is computed as a sum of Gaussian kernels — each with a specified
position, amplitude, frequency, and phase.
The example (in `examples/oscillator`) generates volumetric Cinema
datasets that can be viewed in a web browser with [ArcticViewer][].