How to create sub-sub branch manually


Please read tips for efficient and successful posting and posting code

ROOT Version: 6.36.000
Platform: MacOS 26
Compiler: clang


Hi,

I was developing the ROOTIO.jl package, which is a Julia interface for ROOT. We want to support creating nested structures from Julia, and to do that, we need to add sub-branches, sub-sub-branches and so on.

I came across the this post that talks about creating sub-branches. However, the final example doesn’t work with the TBrowser. Also, creating a sub-sub-branch gives the error missing StreamerInfo.

Code:

#include <TFile.h>
#include <TTree.h>
#include <TBranch.h>
#include <TBranchElement.h>
#include <TLeafElement.h>
#include <TObject.h>
#include <TList.h>

struct TBranchElementModifier : public TBranchElement {
   void SetID (int id) { fID = id; }
};

int tst ()
{
  TFile * outFile = new TFile ( "Trees/TreeFile.root", "RECREATE" );
  TTree * outTree = new TTree ( "MyTree", "MyTree" );

  Int_t buffer0[2] = { -1, -1 };       // dummy top-level buffer
  Int_t buffer1[2] = { -1, -1 };   // buffer for sub-branch 1
  Int_t buffer2[2] = { -1, -1 };       // buffer for sub-branch 2
  Int_t buffer3[2] = { -1 };       // buffer for sub-sub-branch of sub-branch 1

  // define the top branch
  TBranch * topBranch = new TBranchElement ( );
  topBranch->SetName ( "MainBr" );
  topBranch->SetTree ( outTree );
  outTree->GetListOfBranches()->Add ( topBranch );

  // --> correction needed of branch ID
  ((TBranchElementModifier*)topBranch)->SetID(-1);

  // --> an address is required also... (most likely dummy)
  topBranch->SetAddress ( &buffer0[0] );
  //        it also works with:
  //            topBranch->SetAddress ( topBranch );

  // --> a dummy leaf must be added
  TLeaf * leaf = new TLeafElement ( topBranch, "", -1, 0 );
  topBranch->GetListOfLeaves()->Add(leaf);

  // define the sub-branches

  TBranch * subBranch1 = new TBranchElement ( );
  subBranch1->SetName ( "Sub1" );
  subBranch1->SetTree ( outTree );
  ((TBranchElementModifier*)subBranch1)->SetID(-1);
  subBranch1->SetAddress ( &buffer1[0] );
  TLeaf * leaf2 = new TLeafElement ( subBranch1, "", -1, 0 );
  subBranch1->GetListOfLeaves()->Add(leaf2);

  TBranch * subBranch2 = new TBranch ( outTree, "Sub2", &buffer2[0], "VarA2/I:VarB2/I" );

  TBranch * subSubBranch11 = new TBranch ( outTree, "SubSub11", &buffer3[0], "VarAA1/I" );

  topBranch->GetListOfBranches()->Add ( subBranch1 );
  topBranch->GetListOfBranches()->Add ( subBranch2 );

  subBranch1->GetListOfBranches()->Add( subSubBranch11 );

  //------------------------------------------------------------
  // Fill the TTree with outTree->Fill()
  for (int i = 0; i < 10; ++i) {
    buffer1[0] = i;
    buffer1[1] = i + 10;

    buffer2[0] = i * 2;
    buffer2[1] = i * 3;

    buffer3[0] = 100 * i;

    outTree->Fill();
  }
  //------------------------------------------------------------

  outTree->Write();

  outFile->Close();
  delete outFile;

  return ( 0 );
}

void readtst() {
    TFile *f = TFile::Open("Trees/TreeFile.root");
    TTree *t = (TTree*)f->Get("MyTree");
    t->Scan();
    t->Print();
}

Best,
Yash

Hi @yashnator, @pcanal might be able to help you with this.

@pcanal, any help would be great- thanks in advance!

The example accomplish ‘nominally’ what your described but is not completely implementing the way TTree and TBranchElement split an object type into subbranches … which is … complicated.

Unfortunately it is also not formally documented. The closest is (again unfortunately) the code of TBranchElement in particular the series of TBranchElement::Init functions and TBranchElement::Unroll.

In term of the example shown above. A couple of clear issues. TBranchElement expects/assumes that all the element of the list of branches are TBranchElement themselves. The tools (TBrowser, etc.) are likely to expect that the class name and fID of each branches to be set to match a corresponding TStreamerInfo. The reading will also need the fCheckSum or fClassVersion to be set accurately so that the TStreamerInfo can be found back. For self-description/schema evolution, those TStreamerInfo need to be stored in the TFile list of StreamerInfos.

Hi,
Sorry for the late answer.
I reply by e-mail, this the question is not added to the ROOT forum.

I went back to this question, and made a small standalone program
(attached archive file) that I could run without problem (rocky linux,
ROOT 6.26/10
).
Actually there are 2 programs, but very similar ones.
The programs create a TTree file, with nested branched, then re-read it
and print the content of the entries.

I could also read the TTree in the TBrowser as shown in this screenshot:
(note that the branches with only sub-branches - no leaf - also contain
a “dummy” leaf that does not appear on this display).

I join the code as an archive file that contains:

  • TestSubBranch.cxx: program creating and reading a simple (only
    Int_t values) TTree with nested branches
  • TestSubBranch2.cxx: program creating and reading another TTree
    (with mixed types values) with nested branches
    (note: both programs write the same output file that is overwritten)
  • makefile: simple compilation script
  • TestReadTree.C: a ROOT macro to read the created TTree file.

I hope that this will help.

Regards.
Jérôme

(attachments)

SubBranches_2025-07-09_14h22.tgz (22.7 KB)