I am seeing some odd behaviour when trying to befriend two TChains, and I am not sure if this is a bug or if I misunderstand how SetBranchAddress works with TChain. Basically the variable used to set the branch address to stops updating after certain methods are called, but only if the TChain contains more than one TTree.
The most simple scenario where I observe this behaviour is the following: I have N files (let’s say two for simplicity), each with two TTrees A and B, which I want to concatenate across files. A has a branch called Avar, B has a branch called Bvar. Then I try the following:
TChain A("A");
A.Add("file1.root");
A.Add("file2.root");
TChain B("B");
B.Add("file1.root");
B.Add("file2.root");
A.AddFriend(&B);
Float_t Avar, Bvar;
A.SetBranchAddress("Avar", &Avar);
A.SetBranchAddress("Bvar", &Bvar);
A.GetEntry(0);
cout << Avar << " " << Bvar << endl; // Here both Avar and Bvar updated properly
A.GetEntries(); //This causes something to happen behind the scenes
A.GetEntry(1);
cout << Avar << " " << Bvar << endl; // Here only Avar updated properly, Bvar still has the previous value
“GetEntries()” is not the only method that somehow “unsets” SetBranchAddress for the variable in the friend TChain. I have noticed the same when doing for example A.Draw(">>EvtEntryList", "somecut", "entrylist"). And after the branch gets “unset”, it cannot be set again, i.e. redoing A.SetBranchAddress("Bvar", &Bvar) does not fix the issue. Note also that I only see this if the TChain actually has more than one TTree, otherwise everything behaves as expected.
I don’t see that in my tests. Do you have a reproducer (trees with data for that code) that shows the issue? Maybe the problem is in your trees; also, note that if you are getting entries 0 and 1, they are most likely still within the same tree in the chain (unless the first tree only has 1 entry); have you made sure that those entries don’t have the values you are seeing printed (i.e. Bvar is the same for entries 0 and 1)?
Here is an example (“aa.C”) with your code:
Thank you for doing this test! Then it does seem like there is something going on with my input files then. I am sharing a link so you can download, they add up to ~500MB (let me know if you have a different preferred file transfer system, this link is valid for 7 days):
I have not tried with your data, but for now: did my example work fine for you?
Can you also try (your code and data) with a more recent ROOT (I was testing with the latest, 6.36.00)? maybe it’s a version issue, as @ferhue suggested. I just tried my example on the same version you have, 6.32.02, and it works ok too, but maybe with larger or more complex trees you get the issue.
Checking your trees, n_gen is not a branch, but a leaf inside the branch sum_mc_evt.
If you just open one of your files you should get warnings about it:
$ root Qlow1_000XXXXX_nu_v9.0.root
------------------------------------------------------------------
| Welcome to ROOT 6.36.000 https://root.cern |
| (c) 1995-2025, The ROOT Team; conception: R. Brun, F. Rademakers |
| Built for linuxx8664gcc on May 25 2025, 16:47:38 |
| From tags/v6-36-00@v6-36-00 |
| With c++ (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 |
| Try '.help'/'.?', '.demo', '.license', '.credits', '.quit'/'.q' |
------------------------------------------------------------------
root [0]
Attaching file Qlow1_000XXXXX_nu_v9.0.root as _file0...
Warning in <TClass::Init>: no dictionary for class Evt is available
Warning in <TClass::Init>: no dictionary for class AAObject is available
Warning in <TClass::Init>: no dictionary for class Hit is available
Warning in <TClass::Init>: no dictionary for class Vec is available
Warning in <TClass::Init>: no dictionary for class Trk is available
Warning in <TClass::Init>: no dictionary for class MC_trks_summary is available
Warning in <TClass::Init>: no dictionary for class hits_summary is available
Warning in <TClass::Init>: no dictionary for class nu_summary is available
Warning in <TClass::Init>: no dictionary for class MC_evts_summary is available
Warning in <TClass::Init>: no dictionary for class Celestial_coordinates is available
Warning in <TClass::Init>: no dictionary for class crkv_hits is available
Warning in <TClass::Init>: no dictionary for class ORCA_Neutrino2020 is available
Warning in <TClass::Init>: no dictionary for class rec_trks_summary is available
Warning in <TClass::Init>: no dictionary for class jppshower_summary is available
(TFile *) 0x5eab1618bde0
root [1]
@dastudillo : I confirm that the example you wrote works as expected for me too.
And that I still see the same issue with ROOT 6.36.00 (with source /cvmfs/sft.cern.ch/lcg/app/releases/ROOT/6.36.00/x86_64-almalinux9.5-gcc115-opt/bin/thisroot.sh), even with the updated path to the branch, i.e. “sum_mc_evt.n_gen” (thank you for pointing that out though!). Are you saying you cannot reproduce the error with my input files and the correct branch name?
@ferhue: Besides the error I describe here, I do indeed see odd behaviour when the TChain changes TTrees - I assume it’s happening when I call GetEntries(). I don’t know for sure, but the bug report you linked to does indeed sound similar to what I see.
With that change on my laptop, using ROOT 6.36.00 (didn’t test with the older version), I got 648 for n_gen in the first and last printed lines (instead of 22100 on the last one).
Thank you for the test. That is very strange. We have the same inputs, the same ROOT version, and we run the same commands, but we see different outputs.
How are you running the code? I am doing it directly from the ROOT interpreter, are you doing it differently?
Update/correction:
I tried again on another laptop and did not work (already deleted your files from the previous one), but then remembered that I had also added a GetEntries() before setting the branch addresses (probably at any point before the first GetEntry works) and indeed, it works this way:
{
TChain sel("sel");
TChain T("T");
sel.Add("Qlow1_000XXXXX_nu_v9.0.root");
T.Add("Qlow1_000XXXXX_nu_v9.0.root");
sel.Add("bestQ_000XXXXX_nu_v9.0.root");
T.Add("bestQ_000XXXXX_nu_v9.0.root");
sel.AddFriend(&T);
cout << "Number of entries: " << sel.GetEntries() << endl;
Float_t muonscore; //This var is in sel
Int_t n_gen; //This var is in T
sel.SetBranchAddress("muonscore", &muonscore);
//sel.SetBranchAddress("n_gen", &n_gen);
sel.SetBranchAddress("sum_mc_evt.n_gen", &n_gen);
sel.GetEntry(0);
cout << "muonscore(0): " << muonscore << ", n_gen(0): " << n_gen << endl;
sel.GetEntry(100);
cout << "muonscore(100): " << muonscore << ", n_gen(100): " << n_gen << endl;
sel.GetEntry(0);
cout << "muonscore(0): " << muonscore << ", n_gen(0): " << n_gen << endl;
}
even with the original sel.SetBranchAddress("n_gen", &n_gen);; in both cases I get:
root [0] .x a.C
Number of entries: Warning in <TClass::Init>: no dictionary for class Evt is available
Warning in <TClass::Init>: no dictionary for class AAObject is available
Warning in <TClass::Init>: no dictionary for class Hit is available
Warning in <TClass::Init>: no dictionary for class Vec is available
Warning in <TClass::Init>: no dictionary for class Trk is available
Warning in <TClass::Init>: no dictionary for class MC_trks_summary is available
Warning in <TClass::Init>: no dictionary for class hits_summary is available
Warning in <TClass::Init>: no dictionary for class nu_summary is available
Warning in <TClass::Init>: no dictionary for class MC_evts_summary is available
Warning in <TClass::Init>: no dictionary for class Celestial_coordinates is available
Warning in <TClass::Init>: no dictionary for class crkv_hits is available
Warning in <TClass::Init>: no dictionary for class ORCA_Neutrino2020 is available
Warning in <TClass::Init>: no dictionary for class rec_trks_summary is available
Warning in <TClass::Init>: no dictionary for class jppshower_summary is available
395824
muonscore(0): 0.000750723, n_gen(0): 648
muonscore(100): 0.0123782, n_gen(100): 22100
muonscore(0): 0.000750723, n_gen(0): 648
root [1]
Anyway, you should address the warnings too. Maybe generate a skeleton to avoid issues.
Agreed about the warnings, it’s indeed somewhere on my TODO list. I will look into the skeleton option, thanks!
I noticed that putting GetEntries() early on “fixes” the problem, but it’s not the only method that messes things up. For example, even if we add GetEntries early on, but we call, e.g. cout << "Run a cut: " << sel.Draw(">>entrylist", "E.run_id>14800", "entrylist") << endl; where the original GetEntries() was, then the branch stops getting updated too. It is too constraining, especially since I don’t know until after the fact if I broke it or not.