Skip to content

Guidelines for Developing a New TTK Module

Julien Tierny edited this page Jul 1, 2019 · 38 revisions

Thanks for creating a new module for TTK!

Please take the time to review our guidelines below, which will hopefully ease your work.

1. Module structure

As documented in TTK's companion paper, each TTK module is organized in 3 layers:

  • Base layer core/base/: This directory contains the implementations of the core algorithms.
  • VTK layer core/vtk: This directory provides wrappers for the VTK API.
  • ParaView layer paraview/: This directory provides XML specifications for the ParaView support. We provide a set of scripts which will help you create automatically all the necessary files. To create a blank new module, enter the following command from the top of TTK's source tree: scripts/createTTKmodule.sh <Name, first letter in uppercase, no space. for example: HelloWorld>

Assuming your module name is HelloWorldThe above command will create the following directories:

  • core/base/helloWorld
  • core/vtk/ttkHelloWorld
  • core/paraview/HelloWorld
  • standalone/HelloWorld The last directory (standalone/HelloWorld) contains 2 standalone programs using your new module (one in command line, the other one with a basic VTK-based GUI). If you do now wish to provide/maintain these standalone programs, simply remove the directory standalone/HelloWorld.

At this point, if you build and install TTK, your new module should be readily available and executable in ParaView.

If you wish to delete a module (for instance the above HelloWorld example), enter the following command: $ scripts/deleteTTKmodule.sh HelloWorld

You can also create a new module by cloning an already-existing module (which is particularly useful to copy a specific configuration of the VTK layer, in terms of inputs and outputs): scripts/cloneTTKmodule.sh

If you created your module with the createTTKmodule.sh script, you can start to adjust the module to your needs by typically updating the input and output specification at the VTK/ParaView layers. For this, follow the TODO comments, in increasing order, from the file core/vtk/ttkHelloWorld/ttkHelloWorld.h. Note that modifications made to the VTK layer will have to be reported to the ParaView and standalone components.

Once you have adjusted the inputs and outputs of your TTK module, you can proceed with the implementation of the core algorithm in the base layer. For this, follow the TODO comments, in increasing order, from the file core/base/helloWorld/HelloWorld.h. Note that this file contains many other comments providing useful recommendations (in particular regarding interactions with TTK's internal triangulation data structure).

Note that by default, new modules will use TTK's internal triangulation data structure for efficient mesh traversals. Please review this class documentation before using it, as it uses a pre-conditioning mechanism.

Now, before diving in the implementation of your module, we invite you to read carefully the following recommendations.

2. Implementation recommendations

  • Base layer:

    • The base TTK classes (implementing the core of your module) should be implemented as templatized functors (as initially declared with the above scripts). This means that your base class should not store data. It should only be passed a pointer to an input buffer to read, and a pointer to an output buffer to fill. It is recommend to templatize the core functions of your class to be generic to the type of data considered on the input and the output (float, double, char, etc., as initially declared with the above scripts).
    • By default (as initially declared with the above scripts), each TTK base class inherits from the ttk::Debug class, which holds some information about:
      • the current debug level (debugLevel_, setup by the calling program with the function setDebugLevel())
      • the current thread number (threadNumber_, setup by the calling program with the function setThreadNumber(), default value: number of cores on the system).
      • a pointer to the calling wrapper (used by the VTK layer)
    • To print some messages on the standard outputs, please do not use std::cout or std::cerr. Please use the dMsg() function of your TTK class instead (see the doxygen documentation of the ttk::Debug class)
    • TTK provides a number of general-purpose classes that may make your life easier in the development of the base classes of your module:
      • To measure time in your code, please use the ttk::Timer class.
      • To perform some geometrical computation (dot products, cross products, barycentric coordinates, etc), please checkout the doxygen documentation of the ttk::Geometry class. It is very likely that what you need is already implemented there. If it is not, this class is the right place to implement new, low-level, geometry related functions.
      • The ttk::CommandLineParser class provides a full featured command line parser (used by the standalone components of your module).
      • Several other classes from the base layer can be useful for the development of your own module (see for instance: ttk::Dijkstra class, ttk::DimensionReduction class, ttk::Laplacian class, ttk::UnionFind class). Please have a quick look at the list of available classes before implementing a new algorithm your module may need. Note that if your module need a specific functionality, it may be useful to implement it as a separate base layer class (for instance the ttk::HarmonicField class uses the ttk::Laplacian class, which uses itself the ttk::Geometry class).
      • If you use an already-existing base layer class in the base class of your own module, please update your base class CMakeLists.txt file accordingly to make sure that your class links against that already-existing class (see for instance core/base/scalarFieldCriticalPoints/CMakeLists.txt, where the class ttk::ScalarFieldCriticalPoints links against the ttk::UnionFind class in addition to the default ttk::Triangulation one).
    • We recommend to use OpenMP as soon as possible. Please wrap any OpenMP call around #ifdef TTK_ENABLE_OPENMP and #endif statements.
    • At the beginning of each function, wrap the check of the validity of its parameters around #ifdef TTK_ENABLE_KAMIKAZE and #endif statements (see core/base/blank/Blank.h for an example). When built in KAMIKAZE mode, TTK will skip these sanity checks for faster performances.
  • Third-party dependencies:

    • Third-party dependencies should be handled at the base layer.
    • If a third-party dependency is not found, your module should still build and run. At run time, an error message can be reported about the missing dependency.
    • To add a third-party dependency in your code, follow the example of the inclusion of ZLIB:
      • Add the necessary find_package instructions in core/CMakeLists.txt and position a CMake option TTK_ENABLE_ with a description
      • Add a status message regarding the presence of your third-party library in functions.cmake
      • Add the necessary compile instructions in core/functions.cmake
      • Add the necessary compile instructions in core/base/functions.cmake
      • Add the necessary dependency handling instructions in core/base/TTKBaseConfig.cmake.in
      • In your base class, protect the usage of the calls to the third-party library and return an error message if it has not been installed. See the files core/base/topologicalCompression/TopologicalCompression.h and core/base/topologicalCompression/TopologicalCompression.cpp for an example (applied to ZLIB).
  • TODO VTK layer:

    • pointer to VTK documentation
    • by default shallowcopy, deepcopy only for data which needs to be modified (smoother version of an input field)
    • GetVoidPointer
    • SetVoidPointer
  • TODO ParaView layer: make sure to assign your module to the right category.

  • ** TODO** General recommendations:

    • Never use new (or related functions, such as VTK's ::New()).

3. Documentation

3. Authorship and references