ROOT dictionary for writing ouput TTree with RDataFrame::Snapshot

Hi all,
I use this environment:

  • cmake version 3.22.2
  • gcc version 9.3.0 (GCC)
  • ROOT Version: 6.22/08 (for linuxx8664gcc from tags/v6-22-08@v6-22-08)
  • Python 3.9.2

Basically, I first build (build.sh) a library with a function (make_vector) and a dictionary for a stl collection of TVector3 with the following CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)
project(TVector3VecDictPrj CXX)

find_package(ROOT)
include(${ROOT_USE_FILE})

add_library(MyLib SHARED src/myCode.cpp)
target_include_directories(MyLib PUBLIC include)
target_link_libraries(MyLib ${ROOT_LIBRARIES})

ROOT_GENERATE_DICTIONARY(TVector3VecDict MODULE MyLib TVector3.h LINKDEF LinkDef.h)

install(TARGETS MyLib LIBRARY DESTINATION ${PROJECT_SOURCE_DIR}/lib)

install(
    DIRECTORY "${CMAKE_BINARY_DIR}/."
    DESTINATION "${PROJECT_SOURCE_DIR}/lib"
    FILES_MATCHING PATTERN "*.pcm"
    PATTERN ".cmake" EXCLUDE
    PATTERN "CMakeFiles" EXCLUDE)

The source is:

#include "myCode.h"

ROOT::VecOps::RVec<TVector3> make_vector(ROOT::VecOps::RVec<double> x, ROOT::VecOps::RVec<double> y, ROOT::VecOps::RVec<double> z) {
    ROOT::VecOps::RVec<TVector3> vs;
    for(unsigned int i = 0; i < x.size(); i++)
    {
        vs.push_back({x[i], y[i], z[i]});
    }
    return vs;
}

The header is:

#ifndef __MYCODE_H__
#define __MYCODE_H__

#include <TVector3.h>
#include <ROOT/RVec.hxx>

ROOT::VecOps::RVec<TVector3> make_vector(ROOT::VecOps::RVec<double> x, ROOT::VecOps::RVec<double> y, ROOT::VecOps::RVec<double> z);

#endif

Then, in the script (process.py) a TTree is created and passed to a RDataFrame. A new column of the RDataFrame is created with the function make_vector and finally the RDataFrame is written into an output file. Here the script:

#!/usr/bin/env python3

import ROOT as r

r.gSystem.Load("lib/libMyLib.so")
r.gInterpreter.AddIncludePath("include")
r.gInterpreter.ProcessLine('#include "myCode.h"')

vx = r.std.vector["double"]()
vy = r.std.vector["double"]()
vz = r.std.vector["double"]()

for i in range(3):
    vx.push_back(float(i))
    vy.push_back(float(i))
    vz.push_back(float(i))

t = r.TTree("myTree","myTree")
t.Branch("x", vx)
t.Branch("y", vy)
t.Branch("z", vz)

for i in range(3):
    t.Fill()

df = r.RDataFrame(t)

df = df.Define("v","make_vector(x,y,z)")

df.Snapshot("myTree","myFile.root")

What I get, when I run the script, is:

Error in <TTree::Branch>: The class requested (vector<TVector3,ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >) for the branch "v" is an instance of an stl collection and does not have a compiled CollectionProxy. Please generate the dictionary for this collection (vector<TVector3,ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >) to avoid to write corrupted data.

But naively checking the library, I get:

bash-4.2$ nm -C lib/libMyLib.so | grep "vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >"
000000000000bdec W __gnu_cxx::__normal_iterator<TVector3*, std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> > >::__normal_iterator(TVector3* const&)
000000000000bdec W __gnu_cxx::__normal_iterator<TVector3*, std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> > >::__normal_iterator(TVector3* const&)
000000000000befd W __gnu_cxx::__normal_iterator<TVector3*, std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> > >::difference_type __gnu_cxx::operator-<TVector3*, std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> > >(__gnu_cxx::__normal_iterator<TVector3*, std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> > > const&, __gnu_cxx::__normal_iterator<TVector3*, std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> > > const&)
000000000000bf82 W __gnu_cxx::__normal_iterator<TVector3*, std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> > >::base() const
000000000000c030 W __gnu_cxx::__normal_iterator<TVector3*, std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> > >::operator*() const
000000000000bfe4 W __gnu_cxx::__normal_iterator<TVector3*, std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> > >::operator-(long) const
000000000000be0a W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::_M_check_len(unsigned long, char const*) const
000000000000c104 W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::size() const
000000000000c0e2 W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::max_size() const
000000000000c28f W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::_S_max_size(ROOT::Detail::VecOps::RAdoptAllocator<TVector3> const&)
000000000000b920 W TVector3& std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::emplace_back<TVector3>(TVector3&&)
000000000000ba8c W void std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::_M_realloc_insert<TVector3>(__gnu_cxx::__normal_iterator<TVector3*, std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> > >, TVector3&&)
000000000000ba62 W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::end()
000000000000bcfc W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::back()
000000000000bed4 W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::begin()
000000000000b81c W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::push_back(TVector3&&)
000000000000b62e W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::vector()
000000000000b62e W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::vector()
000000000000b666 W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::~vector()
000000000000b666 W std::vector<TVector3, ROOT::Detail::VecOps::RAdoptAllocator<TVector3> >::~vector()

suggesting that the dictionary is there, so the question is what is wrong with this code or compilation?

code.tar.gz (1.4 KB)

Hi,
It seems to me the library contains the symbols but you need also the dictionary for I/O.
You ned to add the line

#pragma link C++ class .......

as explained here: Fill Branch from Vector of Vectors - #2 by moneta

Lorenzo

Thank you @moneta for the prompt reply.
I am still a bit confused because I used that line
in LinkDef.h

#ifdef __CINT__
#pragma link C++ class std::vector<TVector3>+;
#endif

so I expected the dictionary to be generated.
What is wrong with my code?
Matteo

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.

You need to load the generated library in order to load the dictionary