TTree DrawScript fails to compile / find shared library

Dear ROOT experts,

I’ve been trying to use user defined macro in order to use TTree::Draw method to fill histograms.
I have a very simple macro and .h which just imports #include "TLorentzVector.h"

TH1D* h = new TH1D("mjj_max", "mjj_max", 10, 0, 3000);

int mjj_max() {

    TLorentzVector p1(pxj1, pyj1, pzj1, Ej1);
    TLorentzVector p2(pxj2, pyj2, pzj2, Ej2);
    TLorentzVector p3(pxj3, pyj3, pzj3, Ej3);
    TLorentzVector p4(pxj4, pyj4, pzj4, Ej4);

    std::vector<TLorentzVector> pjs = {p1, p2, p3, p4};

    std::pair<int, int> max_mjj{0,1};
    double max = 0;

    for (int j=1; j < 3; ++j){
        for (int i=0; i < j; ++i){
            double tmp_max = (pjs[j] + pjs[i]).M();
            if (tmp_max  > max){
                max = tmp_max;
                max_mjj.first = j;
                max_mjj.second = i;

            }      
        }
    }

    h->Fill(max, w);

    return 1;
}

With a simple script i can successfully create the shared library and reload it multiple times:

import ROOT
import os

for i in range(5):
    f = ROOT.TFile("file.root")
    t = f.Get("tree")
    t.GetPlayer().DrawScript("libs/selName2",os.getcwd()+ "/macros/mjj_max.cxx", os.getcwd()+ "/macros/cutfile.cxx", "", t.GetEntries(), 0)
    h = ROOT.gDirectory.Get("mjj_max")


c = ROOT.TCanvas("c", "c", 1000, 1000)

h.Draw()
c.Draw()
c.SaveAs("prova.png")

Now i want to do the same thing inside a class which inherits from multiprocess. An object of this class is created by a main script and then the file reading is issued. All the files (main file, object file, and the simple example above) are all in the same directory so the DrawScript line is exactly the same.

However this time the output is quite different as it creates successfully the shared library the first time but then it always tries to re-create it (instead of just loading) without success:

 ---------- @ @ @ FIRST @ @ @ @ ---------- 
TTreePlayer::DrawScript:0: RuntimeWarning: TTreeProxy does not work in interpreted mode yet. The script will be compiled.
Info in <TTreePlayer::DrawScript>: Will process tree/chain using libs/selName2.h+
Info in <TUnixSystem::ACLiC>: creating shared library /afs/cern.ch/user/g/gboldrin/public/CMSSW_10_6_4/src/D6EFTStudies/D6tomkDatacard/./libs/selName2_h.so
Info in <TCanvas::MakeDefCanvas>:  created default TCanvas with name c1      
 ---------- @ @ @ SECOND @ @ @ @ ---------- 
TTreePlayer::DrawScript:0: RuntimeWarning: TTreeProxy does not work in interpreted mode yet. The script will be compiled.
Info in <TTreePlayer::DrawScript>: Will process tree/chain using libs/selName2.h+
Info in <ACLiC>: modified script has already been compiled and loaded
Info in <ACLiC>: it will be regenerated and reloaded!
Info in <TUnixSystem::ACLiC>: creating shared library /afs/cern.ch/user/g/gboldrin/public/CMSSW_10_6_4/src/D6EFTStudies/D6tomkDatacard/./libs/selName2_h.so
sh: -c: line 0: unexpected EOF while looking for matching `"'
sh: -c: line 1: syntax error: unexpected end of file
Error in <ACLiC>: Compilation failed!

[And then the same all over again]

What is going wrong?

Best,
Giacomo


ROOT Version: ROOT 6.25/01
Platform: macOS BigSur 11.6.4


Hi @GiacomoBoldrini,

thanks for the question and the code snippets!

Now i want to do the same thing inside a class which inherits from multiprocess.

It would help tremendously if you could post this Python code too. Like that we can easily reproduce the exact crash you have and debug the problem.

Thanks!

I hope this counts as a minimal working example:

main file:

import ROOT
import os
from WorkerLeader import WorkerLeader

f = ROOT.TFile("ntuple_emV_cHj1_LI.root")
t = f.Get("emV_cHj1_LI")

t.GetPlayer().DrawScript("libs/selName2", os.getcwd() + "/macros/max_mjj.cxx", os.getcwd() + "/macros/cutfile.cxx", "", t.GetEntries(), 0)
h = ROOT.gDirectory.Get("maxmjj")
        
wl = WorkerLeader()
wl.initializeWorkers()
wl.startWorkers()

print("---> INFO: FINISHED FILLING")

Second file:

from worker import Worker
import time
import sys
from multiprocessing import Manager

class WorkerLeader():
    def __init__(self):
        print("Hello")
        
    def initializeWorkers(self):
        self.w = Worker()
        
    def startWorkers(self):
        p = self.w.start()
        self.w.join()

Last file:

import threading 
#import numpy as np
import ROOT
import sys 
import math as mt
from copy import deepcopy
import multiprocessing
from multiprocessing import Manager
import os

class Worker(multiprocessing.Process):

    def __init__(self):

        super(Worker, self).__init__()

        self.run_event = threading.Event()
        
        print("hi")
        
    def run(self):

        print("Starting ...")
        f = ROOT.TFile("ntuple_emV_cHj1_LI.root")
        t = f.Get("emV_cHj1_LI")
        
        t.GetPlayer().DrawScript("libs/selName2", os.getcwd() + "/macros/max_mjj.cxx", os.getcwd() + "/macros/cutfile.cxx", "", t.GetEntries(), 0)
        h = ROOT.gDirectory.Get("maxmjj")
        
        print("Starting ...")
        f = ROOT.TFile("ntuple_emV_cHj1_LI.root")
        t = f.Get("emV_cHj1_LI")
        
        t.GetPlayer().DrawScript("libs/selName2", os.getcwd() + "/macros/max_mjj.cxx", os.getcwd() + "/macros/cutfile.cxx", "", t.GetEntries(), 0)
        h = ROOT.gDirectory.Get("maxmjj")
        

Apprarently if you remove the join on the processes, everything runs smoothly

Thanks! I still can’t easily reproduce the problem because there are some input files and scripts missing. You can also attach files to your post instead of copy-pasting in code. Optimal would be a file archive which we can unpack, run one single script inside, and see the problem.

Otherwise, I don’t think anyone can help you easily with your problem, which is quite specific and relates to an outdated component of ROOT. And what you are doing is an interesting but very fragile workaround to what the new RDataFrame does for you in a few lines. You would get the multithreading with one line (ROOT::EnableImplicitMT()) and have all the functionality to define new custom variables based on the TTree branches and fill histograms in parallel.

Could this be an alternative?

Cheers!
Jonas

Dear jonas,

RDataframe does not seem to be an option for my architecture unfortunately (already tried them for newer versions but they seems to yield subleading performances for my problem).

So it seems to me that the problem stems from the fact that the shared library is compiled with one tree and then called from another one. If the library is loaded from same file/tree everything runs fine but if one attempts to load it from another file/tree root will try to recompile it and fail for some reason.

I’ll attach another working example as you suggested.
Is this supposed to happen? Can i disentangle the compilation of the shared library from the ttree Draw while still loading the necessary user-defined variables?

Cheers,
Giacomo
mwe.tar.gz (64.8 KB)

To avoid some warning message try:

t.GetPlayer().DrawScript("libs/selName2",os.getcwd()+ "/macros/mjj_max.cxx+", os.getcwd()+ "/macros/cutfile.cxx+", "", t.GetEntries(), 0)