Problem writing TObjArray of TTrees?

Hi,

I am trying to write a TObjArray of TTrees in ROOT 6.02.03 (x86_64-slc6-gcc48-opt). Starting from a TFile with several trees and running the code below, some of vectors are not readable / empty when doing a Scan. Am I doing something wrong?

In previous versions, the code that created this TObjArray (instead of writing the trees individually) used to work fine. The files can be found on /afs/cern.ch/user/b/blenzi/public/MVACalib.

Thanks in advance,
Bruno

import ROOT
f = ROOT.TFile('MVACalib_electron.weights.root')
fout = ROOT.TFile('MVACalib_electron.weights1.root', 'recreate')
opt = ROOT.TObject.kSingleKey

o = ROOT.TObjArray()
for i in range(102):
  tree = f.Get('BDT%s' % i)
  _ = o.Add( tree.CloneTree() )

for key in f.GetListOfKeys():
  obj = f.Get( key.GetName() )
  _ = fout.cd()
  # do not write the TTrees again
  if isinstance(obj, ROOT.TTree):
    continue
  # copy other TObjArrays
  elif isinstance(obj, ROOT.TObjArray):
    _ = obj.Clone().Write(key.GetName(), opt)
  else:
    _ = obj.Clone().Write()

fout.cd()
_ = o.Write('trees', opt)
fout.Close()

Hi,

What is the purpose of storing the TTree object as part of a TObjArray. When being created (during TTree::CloneTree) they are automatically added to the list of object of the TFile and thus already part of a collection.

Why are you calling Clone in _ = obj.Clone().Write(key.GetName(), opt)

Cheers,
Philippe.

Hi,

This is just an example to get the structure:

 TFile*		MVACalib_electron.weights1.root
  KEY: TObjArray	variables;1	An array of objects
  KEY: TObjArray	formulae;1	An array of objects
  KEY: TObjArray	shifts;1	An array of objects
  KEY: TH2Poly	hPoly;1	NoTitle
  KEY: TObjArray	trees;1	An array of objects

where trees, shifts and variables have the same number of entries and are aligned.

Thanks again,
Bruno

Hi Bruno,

The issue is that your use case is in between two supported use case. One is the most common one where the TTree ‘belongs to’ (is attached to) a TDirectoryFile (for example the TFile itself) and one, less common, where the TTree is not attached to a TDirectoryFile is keeps all its data in memory. [As is in your example the output TTree are actually insert in both the TObjArray and the TFile and if you make a call to fout->Write() you would end up with (correct) copy of the TTree header in the TFile list of keys.

To properly handle you case you need to add several tweaks to work around the normal assumptions.[code]import ROOT
f = ROOT.TFile(‘MVACalib_electron.weights.root’)
fout = ROOT.TFile(‘MVACalib_electron.weights2.root’, ‘recreate’)
opt = ROOT.TObject.kSingleKey

o = ROOT.TObjArray()
for i in range(102):
tree = f.Get(‘BDT%s’ % i)
otree = tree.CloneTree()
otree.FlushBaskets() # make sure all baskets are stored individually
fout.Remove(otree) # avoid double ownership
_ = o.Add( otree )

for key in f.GetListOfKeys():
obj = f.Get( key.GetName() )
_ = fout.cd()

do not write the TTrees again

if isinstance(obj, ROOT.TTree):
continue

copy other TObjArrays

elif isinstance(obj, ROOT.TObjArray):
_ = obj.Write(key.GetName(), opt)
else:
_ = obj.Write()

fout.cd()
_ = o.Write(‘trees’, opt)
fout.Close()[/code]
and when reading

      t = arr.At(i);
      t.SetDirectory(fout); // Make sure the TTree can find its baskets.
      fout->Remove(t);      // Avoid double ownership.

Cheers,
Philippe.

PS. Technically the ‘problem’ is that the branches can not find their baskets during reading.