ROOT Version: master Platform: Linux 5.4 (Manjaro) Compiler: GCC 9.3
I can create a TTree with a branch holding boolean values like this -
#include "TFile.h"
#include "TTree.h"
#include "TBranch.h"
void tleafo() {
TFile *f;
f = new TFile("tleafo.root", "RECREATE");
f->SetCompressionLevel(0);
TTree *t = new TTree("sample", "");
Bool_t b;
t->Branch("b", &b, "b/O");
for (int i = 0; i<5; i++) {
b = 1;
t->Fill();
}
t->Write();
f->Close();
}
I want to read the data out using PyROOT.
This is how I am trying to do that -
import ROOT
import numpy
ROOT.gInterpreter.Declare("""
void readnewbasket(bool* arr, char* filename) {
Bool_t x;
TFile f(filename);
auto tree = f.Get<TTree>("t");
auto branch = tree->GetBranch("booleanBranch");
branch->SetAddress(&x);
for (int i=0; i<tree->GetEntries(); i++) {
tree->GetEvent(i);
arr[i] = x;
}
}""")
initializer = numpy.array([0, 0, 0, 0, 0], dtype=">?")
ROOT.readnewbasket(initializer, "tleafo.root")
for i in range(5):
print(initializer[i])
But when I try to execute the above Python code, I get an error (on both pyroot_experimental ON and OFF) -
TypeError: void ::readnewbasket(bool* arr, char* filename) =>
TypeError: could not convert argument 1 (could not convert argument to buffer or nullptr)
I would really appreciate any help with this. Thanks!
Not directly related but might be useful information - I think this used to work in the past when I had originally written the above Python script (maybe for ROOT 6.16… I am not sure). But this also works on the conda-forge build of ROOT.
Indeed, I think the automatic conversion from a boolean buffer to a bool* is broken. We will investigate! However, to fix your application, you can do the following:
import ROOT
import numpy
ROOT.gInterpreter.Declare("""
void tleafo() {
TFile *f;
f = new TFile("tleafo.root", "RECREATE");
f->SetCompressionLevel(0);
TTree *t = new TTree("sample", "");
Bool_t b;
t->Branch("b", &b, "b/O");
for (int i = 0; i<5; i++) {
b = 1;
t->Fill();
}
t->Write();
f->Close();
}
void readnewbasket(void* arr, char* filename) {
Bool_t x;
auto arr2 = (bool*) arr;
TFile f(filename);
auto tree = f.Get<TTree>("sample");
auto branch = tree->GetBranch("b");
branch->SetAddress(&x);
for (int i=0; i<tree->GetEntries(); i++) {
tree->GetEvent(i);
arr2[i] = x;
}
f.Close();
}""")
ROOT.tleafo()
initializer = numpy.array([0, 0, 0, 0, 0], dtype=numpy.bool)
ROOT.readnewbasket(initializer, "tleafo.root")
for i in range(5):
print(initializer[i])
So in short: Take a void* in the signature in C++ and then simply cast it to a bool* inside. This fixes your issue with the PyROOT layer as long we investigate further.
Side note, you can achieve your conversion to numpy also in a single line like this
b = ROOT.RDataFrame('sample', 'tleafo.root').AsNumpy(['b'])['b']
Though, if the buffer is small, your implementation is more efficient.
Actually fully correct! If you don’t care on the python side about that detail (normally numpy abstracts this detail for you very nicely), your code should do the same thing. On the C++ side, both of them have 1 byte and we are fine. Also, the Python type code b is actually what we recognize as a bool in PyROOT for conversion.