TChain::MakeClass including friend chains

Dear all,

I have a set of root files, each of them cointaining the 3 trees. I open all the data with a TChain and some friends TChain:

allT1 = new TChain("T1");
allT2 = new TChain("T2");
allT3 = new TChain("T3");

allT1->AddFriend( allT2, "T2");
allT1->AddFriend( allT3, "T3");

for (.. every file .. ) {
  allT1->Add(filepath);
  allT2->Add(filepath);
  allT3->Add(filepath);
}

Now I want to use TChain::MakeClass() to have a skeleton code for all my data. I noticed that with allT1->MakeClass(“myclass”) only the variables of the “T1” tree are included in the skeleton… have I missed an option to specify?
Otherwise, is there any workaround?
For example, I’m planning to generate 3 separate skeleton classes and to write a derived class that multiply-inherit from all of them… can it work?

I tried indeed the following:

/* MyAnalysis.h */

#include T1skeleton.h
[..etc..]

class MyAnalysis
  : public T1skeleton, T2skeleton, T3skeleton
{
public:
  MyAnalysis( );
  virtual ~MyAnalysis();
public:
  void ClearRunList();
  int AddRunList( std::string& filepath );
public:
  TChain* allT1;
  TChain* allT2;
  TChain* allT3;
}

/* MyAnalysis.C */

MyAnalysis::MyAnalysis( )
{
  allT1 = new TChain("T1");
  allT2 = new TChain("T2");
  allT3 = new TChain("T3");
  allT1->AddFriend( allT2, "T2");
  allT1->AddFriend( allT3, "T3");

  //The base classes constructors are not called, but rather:
  T1skeleton::Init(allT1);
  T2skeleton::Init(allT2);
  T3skeleton::Init(allT3);
}

MyAnalysis::~MyAnalysis()
{
  // the skeleton classes destructors can mess up with their
  // "delete fChain->GetCurrentFile();". I try as
  // workaround to "disconnect" the chain object from them
  T1skeleton::fChain = 0;
  T2skeleton::fChain = 0;
  T3skeleton::fChain = 0;

  allT1->GetListOfFriends()->Clear();
  delete allT3;
  delete allT2;
  delete allT1;
}

int MyAnalysis::AddRunList( std::string& filepath )
{
  allT1->Add(filepath.c_str());
  allT2->Add(filepath.c_str());
  allT3->Add(filepath.c_str());
}

void MyAnalysis::ClearRunList()
{
  allT1->GetListOfFiles()->Clear();
  allT2->GetListOfFiles()->Clear();
  allT3->GetListOfFiles()->Clear();
}

EDIT: Please skip the first question (in quote now)… it was my mistake, a typo when creating the three skeletons was giving me twice the same skeleton…

[size=85][quote]I made as first fast test:

The plot seems correct, but I got the following error messages:

Error in <TTree::SetBranchStatus>: unknown branch -> EventId Error in <TTree::SetBranchStatus>: unknown branch -> TimeSec
…etc… for each variable of THE FIRST chain (I mean I DON’T get such messages for the FRIEND chains) and every time the chain is loading the next time.
I verified the last statement calling explicitely:

myanalysis.T1skeleton::LoadTree(0) ; //first time: I got error messages for all variables in allT1 myanalysis.T1skeleton::LoadTree(1) ; //same file: no error message myanalysis.T1skeleton::LoadTree(1000000) ; //new file is loaded: again error messages for all allT1 variables myanalysis.T3skeleton::LoadTree(0) ; //back to first file: I got again error messages for all variables in allT1, even if I called a method of the T3 skeleton

Can you explain the meaning of such messages and where is my mistake?
[/quote][/size]

Moreover I’m still quite puzzled on some aspects of the produced skeletons and how to manage this “chains friendship”. In particular I have the following questions:

  1. If the user does not customise the “Notify()” method of the skeleton, is it just doing nothing?
  2. I’m adding MyAnalysis::LoadTree() and MyAnalysis::GetEntry() methods in order to hide the same methods in the single skeletons… but when I “LoadTree” or “GetEntry” a chain with friends, should I call such methods for all chains, or calling on the first chain is enough because the friendship propagates the request?
  3. If I want to loop only on the events in a specific TEventList, should I “SetEventList()” only on the first chain and the friendship propagate the request, or I should explicitely do that also on the friend chain?
    Moreover is “GetEntry()” taking into account event lists? I mean: may I GetEntry() an event that is out of the currently set event list?

Thanks a lot in advance and I apologize for the very long post

Hi,

[quote]1) If the user does not customise the “Notify()” method of the skeleton, is it just doing nothing?[/quote]Indeed as no action is needed at each file transition except for the one already done by the TChain itself (namely open the TFile, loading the TTree and setting the branch pointer passed as the 3rd arguments to SetBranchAddress).

[quote]2) I’m adding MyAnalysis::LoadTree() and MyAnalysis::GetEntry() methods in order to hide the same methods in the single skeletons… but when I “LoadTree” or “GetEntry” a chain with friends, should I call such methods for all chains, or calling on the first chain is enough because the friendship propagates the request?[/quote]Yes, the 2nd and 3rd chain are friend of the first one, you should call LoadTree only on the first chain which will takes care of the other two (in particular if they have a TTreeIndex).

[quote]3) If I want to loop only on the events in a specific TEventList, should I “SetEventList()” only on the first chain and the friendship propagate the request, or I should explicitely do that also on the friend chain?
Moreover is “GetEntry()” taking into account event lists? I mean: may I GetEntry() an event that is out of the currently set event list?[/quote]This is correct, set the EventList only on the first chain. However MakeClass as is does not respect the event list (MakeSelector and using Process would). In order, to use is you need to use something like fChain->LoadTree(fChain->GetEntryNumber(index));

Cheers,
Philippe.

Dear Philippe,

thanks for the replies, things are clear now. Just a further question: as I always know the current TEventList (I embedded a copy in my class), the following code seems more efficient:

//Look only in the current eventlist
  Long64_t nevlentries = evlnow->GetN();
  
  for (Long64_t evlentry = 0; evlentry < nevlentries; evlentry++)
    {
      Long64_t jentry = evlnow->GetList()[evlentry];

      Long64_t ientry = LoadTree(jentry);
      if (ientry < 0) break;

      .. do some stuff ..
   }

also because, looking at the source code of TChain::GetEntryNumber(), the method sum up all the TTree offsets (looping with Loadtree if never done) to obtain the global entry, an information which I already have in the TEventList().

I save at least the cpu time of few addition, times the number of entries of my analysis… Do you think I can use this shortcut?

Thanks again,
Matteo

Hi,

Yes you can use this shortcut as long as you TEntryList always refer to the whole chain and the files are always in the same order in the chain.

Cheers,
Philippe.