Large TChain into RooDataSet

I want to load a large TChain (millions of entries) into a RooDataSet in 4 different observables. When I try, however, I get:

Error in <TBufferFile::WriteByteCount>: bytecount too large (more than 1073741822)

and when I try to import it:

importing dataset...
Error in <TBufferFile::CheckByteCount>: object of class vector<RooVectorDataStore::RealVector*> read too many bytes: 1283909525 instead of 210167701
Warning in <TBufferFile::CheckByteCount>: vector<RooVectorDataStore::RealVector*>::Streamer() not in sync with data on file path/to/file.root, fix Streamer()
Error in <TBufferFile::ReadVersion>: Could not find the StreamerInfo with a checksum of 0xdf5740a1 for the class "vector<RooVectorDataStore::RealFullVector*>" in FitOutput/data/\
path/to/file.root.
Error in <TBufferFile::CheckByteCount>: object of class vector<RooVectorDataStore::RealFullVector*> read too few bytes: 10 instead of 10605960

 *** Break *** segmentation violation

Is there a way around this? Snippet of my current approach:

    obs = RooArgSet(<RooRealVars>)
    obslist = <list version of obs>
    t.SetBranchStatus('*', 0)
    for bnm in neededbranchnames:
        t.SetBranchStatus(bnm, 1)
    # Roo stuff
    wsout = ROOT.RooWorkspace('myWS')
    data = ROOT.RooDataSet('data', 'data', obs)
    # fill dataset
    for evt in t:
        vals = []
        for i, v in enumerate(obslist):
            vals.append(
                eval(evalstring[i].format('evt'))
            )
        if any(v < o.getMin() or v > o.getMax() for v, o in zip(vals, obslist)):
            continue
        
        for v, o in zip(vals, obslist):
            o.setVal(v)
        
        data.add(obs)
    t.Delete()  # save memory space
    
    getattr(wsout, 'import')(data)
    fout = ROOT.TFile.Open(outfilename, 'RECREATE')
    wsout.Write()
    fout.Close()

Three of my observables are only there to make selections on later, so I could just import the observables I plan to fit and use the others just for selections, but it would be convenient to have it all stored in one data set instead of many.

That looks like an I/O error. To ask the simple things first:
Does reading the bare file work? Like opening it and drawing a branch?

Yes. I was able to write this chain into a RooDataSet just fine with only one variable, and I noticed this behavior after adding additional ones to the data set. I just verified that I am still able to do GetEntries on the chain (40658753) and can call Draw on at least two of the branches without issue.

It would be best to send us the file(s) with a short macro to read them. It’s not unlikely that it’s an I/O problem that needs to be fixed on the ROOT side.

I have sent you a link to the files uploaded to CERNbox in a private message. To read them (from the for_ROOT_team directory):

from glob import glob
import ROOT
ch = ROOT.TChain('Lc/DecayTree')
for filelocation in glob('./*/*root'):
    ch.Add(filelocation)

I should perhaps also mention a (probably unrelated) issue I have had with these files. They were created using a friend tree; that is, I had a base tree that I wanted to make some selections on using values in a friend tree:

BaseTree.AddFriend(FriendTree)
NewTree = BaseTree.CopyTree('somevalueinfriendtree > value')

When I try to access these files after copying them somewhere else (or using a FUSE mount), I will get messages like:

Error in <TFile::TFile>: file <path/to/file/containing/friend/tree.root> does not exist

You will probably see something similar when you go to open the files.

Does the file with the given path exist?

Not at that path when I see:

Error in <TFile::TFile>: file <path/to/file/containing/friend/tree.root> does not exist

, no. (It only comes up when I’m using a FUSE mount, thus changing the path.) Those files are not included with what I shared; I can add them if you like, but the path will of course still be different.

I see.
Maybe the following will solved the problem:

NewTree = BaseTree.CopyTree('somevalueinfriendtree > value')
NewTree->GetListOfFriends()->Delete(); // Remove the contains of the list of friends.

Cheers,
Philippe.

Thank you. I will give it a try. I do not think it will solve the main issue of this thread, however, as those files are available when I get:

Error in <TBufferFile::WriteByteCount>: bytecount too large (more than 1073741822)

One of the object is larger than 1GB (limit for a single streaming operation). Do you know which I/O operation is triggering this message?

You can see the relevant section of my code in my initial message.

try

    fout = ROOT.TFile.Open(outfilename, 'RECREATE')
    wtree = ROOT.TTree('myWS','')
    wtree.Branch('myWS',ROOT.AddressOf(wsout)) # humm not sure of the python syntax :(
    wtree.Fill()
    fout.Write()
    fout.Close()

I believe that is the wrong syntax.

    getattr(wsout, 'import')(data)
    fout = ROOT.TFile.Open(outfilename, 'RECREATE')
    wtree = ROOT.TTree('myWS', '')
    wtree.Branch('myWS', ROOT.AddressOf(wsout))
    wtree.Fill()
    fout.Write()
    # wsout.Write()                                                                                                                                           
    fout.Close()
    import sys; sys.exit()

leads to

Traceback (most recent call last):
  File "fit_.py", line 403, in <module>
    Fit(mode, args.recreate, args.shortfit)
  File "fit_.py", line 387, in Fit
    shortfit,
  File "fit_.py", line 334, in RunFit
    SaveDataSet(wspace, infile, dataoutfilename, coordbranches)
  File "fit_.py", line 96, in SaveDataSet
    wtree.Branch('myWS', ROOT.AddressOf(wsout))
TypeError: none of the 8 overloaded methods succeeded. Full details:
  int TTree::Branch(TCollection* list, int bufsize = 32000, int splitlevel = 99, const char* name = "") =>
    could not convert argument 1
  int TTree::Branch(TList* list, int bufsize = 32000, int splitlevel = 99) =>
    could not convert argument 1
  int TTree::Branch(const char* folder, int bufsize = 32000, int splitlevel = 99) =>
    could not convert argument 2 (int/long converion expects an integer object)
  TBranch* TTree::Branch(const char* name, char* address, const char* leaflist, int bufsize = 32000) =>
    takes at least 3 arguments (2 given)
  TBranch* TTree::Branch(const char* name, long address, const char* leaflist, int bufsize = 32000) =>
    takes at least 3 arguments (2 given)
  TBranch* TTree::Branch(const char* name, int address, const char* leaflist, int bufsize = 32000) =>
    takes at least 3 arguments (2 given)
  TBranch* TTree::Branch(const char* name, void* address, const char* leaflist, int bufsize = 32000) =>
    takes at least 3 arguments (2 given)
  TBranch* TTree::Branch(const char* name, const char* classname, void* addobj, int bufsize = 32000, int splitlevel = 99) =>
    takes at least 3 arguments (2 given)
*** glibc detected *** python: free(): invalid pointer: 0x0000000005f4f34c ***
======= Backtrace: =========
/lib64/libc.so.6[0x32c8875e76]

etc.
It seems to want some sort of leaflist…?

Try

 wtree.Branch('myWS', 'RooWorkspace', ROOT.AddressOf(wsout))

Same result:

Error in <TBufferFile::WriteByteCount>: bytecount too large (more than 1073741822)

etc.

@StephanH Any ideas on how to reduce the size of the workspace in this case?

Aside:

RE: Errors about missing TFile with friend tree:

Your solution worked with one caveat: To avoid getting errors like:

pure virtual method called
terminate called without an active exception

one must make sure to close the input file manually (this is not necessary if one does not delete the list of friends):

BaseTree.AddFriend(FriendTree)
OutFile.cd()
NewTree = BaseTree.CopyTree('SomeValueInFriendTree > value')
NewTree.GetListOfFriends.Delete()
NewTree.Write()
InFileWithBaseTree.Close()

Hi @mwilkins and @pcanal,

I just came back from travelling. Do I see correctly that we are still facing the problem that the dataset is too large to be written in a single streamer operation?

For @pcanal:
Internally, a dataset is a couple of std::vector<double>, so I don’t see why having one vector with millions of entries is ok, but multiple vectors is not. Are they serialised in a single operation?

For @mwilkins:
Next to storing as std::vector in memory, RooFit also supports storing data in a tree, therefore lifting the size limit when writing to disk. An (untested, just from reading code) way to do this:

#Trying to reach the enumerator RooAbsData::StorageType
ROOT.RooAbsData.setDefaultStorageType(ROOT.RooAbsData.Tree)
data = ROOT.RooDataSet('data', 'data', obs)

Sorry for the delay getting back to you; it took some time for me to be able to test your script. Changing the default storage type seems to have worked, although I do not really understand the consequences of the change, aside from allowing me to store more data.

Thank you for your help.