KaliVeda
Toolkit for HIC analysis
Development with KaliVeda

Writing code with KaliVeda

Automatic class source code generator

KaliVeda provides a class which can write source code for other classes: KVClassFactory. At its simplest, only a class name and short description are required in order to generate a fully ROOT-compatible class from the KaliVeda command line. Giving a base class to derive your new class from will copy and implement all constructors from the base class:

kaliveda [0] KVClassFactory::MakeClass("MyClass", "A new class", "TNamed")
<KVClassFactory::WriteClassHeader> : File MyClass.h generated.
<KVClassFactory::WriteClassImp> : File MyClass.cpp generated.
void WriteClassHeader()
Write the class header file.
static void MakeClass(const Char_t *classname, const Char_t *classdesc, const Char_t *base_class="", Bool_t withTemplate=kFALSE, const Char_t *templateFile="")

will generate the following MyClass.h and MyClass.cpp files which includes a template doxygen comment block for documenting your class (not shown):

#ifndef __MYCLASS_H
#define __MYCLASS_H
#include "TNamed.h"
class MyClass : public TNamed
{
public:
MyClass()
: TNamed()
{
}
MyClass(const char* name, const char* title)
: TNamed(name, title)
{
}
MyClass(const TString& name, const TString& title)
: TNamed(name, title)
{
}
virtual ~MyClass()
{
}
ClassDef(MyClass,1)//A new class
};
#endif
#define ClassDef(name, id)
char name[80]
#include "MyClass.h"
ClassImp(MyClass)
ClassImp(TPyArg)

You can also add methods to your class before generating the code, or use template files to define complicated methods which can be reused in many classes. See the KVClassFactory documentation and associated examples.

Useful C++ Pre-processor Symbols

The header file KVConfig.h which is configured and generated at build time contains several symbols which may be of help when writing your own code using KaliVeda. KVConfig.h is #included by most class headers in the toolkit; in case of doubt, just add #include "KVConfig.h" in your code.

The following symbols are mainly to ensure portability of code:

Symbol Meaning/Function
External software
WITH_GEMINI GEMINI++ interface KVGemini exists (see Optional software)
WITH_ZMQ compiled with ZeroMQ support (KVZMQMessage)
WITH_MFM can read GANIL MFM format data (KVMFMDataFileReader)
WITH_BOOST compiled with boost library support
WITH_MESYTEC can read Mesytec acquisition data
WITH_PROTOBUF can read Google Proto Buffer data (KVProtobufDataReader)
WITH_RSQLITE has SQLite interfaces (KVSQLite::database, KVSQLROOTFile)
WITH_BUILTIN_GRU can read legacy GANIL EBYEDAT format data (KVGANILDataReader)
Hardware
WITH_MULTICORE_CPU integer value equal to number of processors/cores
C++ standard
WITH_CPP11 C++11 language can be used
WITH_CPP14 C++14 & C++11 language can be used
WITH_CPP17 C++17,C++14 & C++11 can be used
WITH_CPP20 C++20, C++17,C++14 & C++11 can be used

Code compilation outside the interpreter (simple example)

If you want/need to compile code using the toolkit in stand-alone mode, you can use the kaliveda-config tool in order to obtain the necessary installation-dependent paths to header files and libraries. Given the following code:

// file: MyCode.cpp
#include "KVNucleus.h"
int main()
{
KVNucleus xe("129Xe",49.9);
KVNucleus sn("119Sn");
KVNucleus CN = xe + sn;
CN.Print();
}
Description of properties and kinematics of atomic nuclei.
Definition: KVNucleus.h:126
virtual void Print(Option_t *t="") const
Display nucleus parameters.
Definition: KVNucleus.cpp:761
int main(int argc, char **argv)

this can be compiled and linked into an executable using the following command:

$ g++ MyCode.cpp `kaliveda-config --cflags --linklibs`

(we assume the g++ compiler; in reality you should use whatever compiler was used to compile ROOT, i.e. the result of root-config --cxx). Running the executable produces:

$ ./a.out
KVNucleus Z=104 A=248 E*=2734.43
KVParticle mass=233847 Theta=0 Phi=0 KE=3368.85 Vpar=5.03451

Note that the above commands include also the required paths and libraries from ROOT, and are equivalent to

-I`kaliveda-config --incdir` `root-config --cflags` -L`kaliveda-config --libdir --libs` `root-config --glibs`

CMake support

The compilation method given just above is unwieldy for anything more than a simple executable. It cannot be used to make shared libraries containing your own classes, for which you would also need to generate the associated ROOT dictionaries, etc.. Therefore we provide modules which enable KaliVeda to be used with the CMake build system.

Simple executable example

Given the MyCode.cpp file above, the same executable can be built using cmake with the following configuration file CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)
project(MyProject)
#------- locate KaliVeda installation
find_package(KaliVeda REQUIRED)
include(${KALIVEDA_USE_FILE})
#------- locate ROOT installation
find_package(ROOT REQUIRED)
include(SetUpROOTBuild)
add_executable(XeSn MyCode.cpp)
target_link_libraries(XeSn ${KALIVEDA_LIBRARIES})

Configuration of the build then proceeds like this (note that we strongly discourage in-source builds, the following is just a simplified example):

$ cmake .
-- The C compiler identification is GNU 12.2.0
-- The CXX compiler identification is GNU 12.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found KaliVeda: [...]/dev/lib (found version 1.13.01)
-- Found ROOT version 6.26.08
-- SetUpROOTBuild : CMAKE_CXX_FLAGS = -std=c++17 -pipe -fsigned-char -pthread
-- Configuring done
-- Generating done
-- Build files have been written to: [...]

After which your code can be compiled like so (note we are assuming a Unix Makefile build generator):

$ make
Scanning dependencies of target XeSn
[ 50%] Building CXX object CMakeFiles/XeSn.dir/MyCode.cpp.o
[100%] Linking CXX executable bin/XeSn
[100%] Built target XeSn

Building a shared library with a ROOT dictionary

Assuming you have a directory with class source files and a LinkDef.h file like this:

myProj/
    class1.h
    class1.cpp
    class2.h
    class2.cpp
    LinkDef.h

the minimum cmake-based project to build and install the shared library and other necessary files will look like this:

[myProj/CMakeLists.txt:]

cmake_minimum_required (VERSION 3.5)

project(myProj)

#------- locate KaliVeda installation
find_package(KaliVeda REQUIRED)
include(${KALIVEDA_USE_FILE})

#------- locate ROOT installation
find_package(ROOT REQUIRED)
include(SetUpROOTBuild)

#------- set up standard GNU installation directories
include(GNUInstallDirs)

#------- use KaliVeda-provided CMake module for ROOT dictionary & library generation
include(GenerateRootLibrary)
GENERATE_ROOT_LIBRARY(myProj DEPENDENCIES ${KALIVEDA_LIBRARIES} ${ROOT_LIBRARIES})

Note that find_package(KaliVeda) is called before find_package(ROOT): this is so that the KaliVeda modules are used to find ROOT on the system, instead of whatever default modules were installed by ROOT. This ensures transparent operation with either ROOT 5 or ROOT 6. If you inverse the order, it might work with certain versions of ROOT but not all.

To build and install this project, make a fresh build directory somewhere outside the source tree and do:

cmake [path to myProj]/myProj -DCMAKE_INSTALL_PREFIX=[where you want to install]
make [-jN] install

with -jN for parallel building on an N-core machine.

The resulting installation will look like this:

[installation prefix]/
    lib/
       libmyProj.so
       libmyProj.rootmap
       libmyProj_rdict.pcm
    include/
       class1.h
       class2.h

What if I get linker errors concerning ROOT classes?

You may need to explicitly require that certain ROOT libraries are available in order for CMake to link them with your library, e.g.: if you get errors like undefined reference to typeinfo for TChain and similar ones for TH1 and TFile, then you need to specify the corresponding libraries in the call to find_package(ROOT) like so:

find_package(ROOT REQUIRED Tree Hist RIO)

using the name of each lib[Name].so which contains the undefined classes: Tree for libTree.so (TChain), Hist for libHist.so (TH1) and RIO for libRIO.so (TFile).

What if my library's classes are not in the top-level source directory?

You could even have several directories corresponding to different libraries. No problem, do it like this:

myProj/
   CMakeLists.txt
   myLib1/
       class1.h
       class1.cpp
       class2.h
       class2.cpp
       CMakeLists.txt
       LinkDef.h

In the top-level CMakeLists.txt:

[myProj/CMakeLists.txt:]
cmake_minimum_required (VERSION 3.5)
project(myProj)
#------- locate KaliVeda installation
find_package(KaliVeda REQUIRED)
include(${KALIVEDA_USE_FILE})
#------- locate ROOT installation
find_package(ROOT REQUIRED)
include(SetUpROOTBuild)
#------- set up standard GNU installation directories
include(GNUInstallDirs)
#------- build library myLib1
add_subdirectory(myLib1)

And in the library directory:

[myProj/myLib1/CMakeLists.txt:]
include(GenerateRootLibrary)
GENERATE_ROOT_LIBRARY(myLib1 DEPENDENCIES ${KALIVEDA_LIBRARIES} ${ROOT_LIBRARIES})

Provided CMake Modules

Locating KaliVeda package

Usage:

find_package(KaliVeda
    [version] [EXACT]                    # Minimum or EXACT version e.g. 1.13.01
    [REQUIRED]                           # Fail with error if KaliVeda not found
    [[COMPONENTS] <modules/libs>...]     # Required specific components,
                                           either modules or libraries (see below)
    [OPTIONAL_COMPONENTS <modules/libs>] # Optional component libraries or modules (see below)
)

Search for an installation of KaliVeda on the system, optionally specifying the version and/or specific components. The components can be either one of the main subprojects which are enabled or disabled using the -DUSE_<comp> switches (see [[CMake options|Build-system#cmake-options]]: e.g. INDRA, FAZIA, BACKTRACK, etc.), or the name of a specific library target (see [[library targets|Build-system#library-targets]]: e.g. KVMultiDetbase, MicroStatmontecarlo, etc.)

Results of the search are reported in the following variables:

KaliVeda_FOUND                      - True if a compatible version with all required components was found
KaliVeda_<comp>_FOUND               - True if specific component was found

KALIVEDA_USE_FILE                   - Path to file to be included by top-level project
KALIVEDA_INCLUDE_DIR                - Path to installed header files
KALIVEDA_LIB_DIR                    - Path to installed libraries
KALIVEDA_BIN_DIR                    - Path to installed executables
KALIVEDA_CMAKEPKG_DIR               - Path to CMake modules installed by KaliVeda
KALIVEDA_LIBRARIES                  - List of all installed library targets for linking

In most cases it is sufficient to include the file given by KALIVEDA_USE_FILE in order to compile and link your code with the KaliVeda libraries, and use the other CMake modules we provide (see below):

find_package(KaliVeda REQUIRED)
include(${KALIVEDA_USE_FILE})

add_executable(toto [your sources])
target_link_libraries(toto ${KALIVEDA_LIBRARIES} ${ROOT_LIBRARIES})

Note that ${ROOT_LIBRARIES} contains only the core ROOT libraries if no ROOT components are explicitly requested in the call to find_package(ROOT). Any other required libraries should be explicitly requested (e.g. find_package(ROOT REQUIRED Hist) in order to link with libHisto.so). Another solution to the problem may be to just link with ${KALIVEDA_LIBRARIES}, as the KaliVeda libraries are themselves linked with many more than just the core ROOT libraries.

  • GenerateRootLibrary.cmake: build a shared library, dictionary, rootmap file etc. with ROOT5 or ROOT6

Usage:

GENERATE_ROOT_LIBRARY(<libname>
    [DICT_EXCLUDE Class1.h Class2.h ...]   # header files of classes to exclude from the dictionary
    [LIB_EXCLUDE Class1 Class2 ...]        # classes which should not be compiled
    [DEPENDENCIES lib1 lib2 ...]           # other libraries this library depends on
)

This macro can generate a ROOT dictionary (using rootcint or rootcling depending on ROOT version 5 or 6), shared library and rootmap file (plus the pcm file for ROOT6). The CMakeLists.txt file invoking this macro should be in the same directory as the source files, plus the LinkDef.h, i.e. a layout such as:

some_dir/
    CMakeLists.txt
    Class1.h
    Class1.cpp
    Class2.h
    Class2.cpp
    LinkDef.h

The CMakeLists.txt file should contain at least the following:

include(GenerateRootLibrary)
GENERATE_ROOT_LIBRARY(MyLib)

where we have assumed that some top-level CMakeLists.txt contains the necessary calls to find_package(... etc. in order to set up the necessary CMake module search paths (see above).

The resulting library libMyLib.so and its rootmap file libMyLib.rootmap (plus the libMyLib_rdict.pcm file if ROOT6 is used) will be installed in paths defined by ${CMAKE_INSTALL_LIBDIR}, while the class headers will be installed in paths defined by ${CMAKE_INSTALL_INCLUDEDIR}. These variables are not defined by default, you must make sure they are defined before calling GenerateRootLibrary, for example

set(CMAKE_INSTALL_LIBDIR lib)
set(CMAKE_INSTALL_INCLUDEDIR include/MyLib)
...
GENERATE_ROOT_LIBRARY(MyLib)

then the final installation locations will be ${CMAKE_INSTALL_PREFIX}/lib and ${CMAKE_INSTALL_PREFIX}/include/MyLib.

Another way to set up the installation paths is to use the standard CMake module GNUInstallDirs ([[see here|https://cmake.org/cmake/help/v2.8.11/cmake.html::module:GNUInstallDirs]]). This will define standard GNU-type values for all the CMAKE_INSTALL_* variables.

Don't forget that with ROOT6 you will need to add the path to the header files to environment variable ROOT_INCLUDE_PATH at runtime (i.e. before trying to use the shared library in a ROOT session).