Home | News | Documentation | Download

Copy tree to new file and rename it

I am trying to do something that i think should be extremely simple.

The big picture is that i have multiple (20+) files that each contain multiple trees (for different systematic variations) each with two cycles due to their size (these files are provided to me by someone else). Some of these files are for my signal and the rest is for my background.

What i need in the end is one file that contains two trees. One sould be the same of all the nominal trees of the background samples and the other should be the sum of the nominal trees of the signal sample.

So my idea what to just go through each file, copy the nominal tree to a new one and rename it there to Sig or Bkg depending on signal or background. and then hadd all of these files together to get what i need.

However i am already not managing to do the first step.

This is what one of the input files looks like
YaoInput

And this is the relevant part of the macro that i am running over it.

  for (Int_t i = 0; i < n; i++) {
    std::cout << filename[i] << std::endl;
    TFile oldfile(filename[i]);
    TTree *oldtree;
    oldfile.GetObject("nominal", oldtree);
    TFile newfile(newname[i], "recreate");
    newfile.cd();
    auto newtree = oldtree->CloneTree();
    newtree->SetObject("PolSig","PolSig");
    newfile.Write("PolSig");
    newfile.Close();
  }

and this is what the output file looks like

Modified

Those two are exactly the things i wanted to copy but one of them is not renamed properly. Does anyone know how to do this whole thing?

Is your 2nd picture showing what you have in “newfile” ?
If that’s the case I do not understand why you have nominal and PolSig in it and not only PolSig as it is the only one you wrote in “newfile” …
Our I/O expert, @pcanal , may have an idea about your problem.

Yes, the second picture is the new file i am producing with my code and is exactly what is confusing me.

You might be also interested by the command line tools. rootcp in your case.

With that i get:

Same name objects aren’t supported: ‘nominal’ of ‘allYears_501793_MGPy8EG_W0Z0_lvll_lvllj_LO_emu_PtZge150GeV_myOutput.root’ won’t be processed

Which command did you typed ?

rootcp ‘allYears_501793_MGPy8EG_W0Z0_lvll_lvllj_LO_emu_PtZge150GeV_myOutput.root:nominal’ ModallYears_501793_MGPy8EG_W0Z0_lvll_lvllj_LO_emu_PtZge150GeV_myOutput.root

you do not need the simple quotes. Ex:

% rootls -l hsimple.root
TProfile        May 16 10:50 2019 hprof   "Profile of pz versus px"
TH1F            May 16 10:50 2019 hpx     "This is the px distribution"
TH2F            May 16 10:50 2019 hpxpy   "py vs px"
TNtuple         May 16 10:50 2019 ntuple  "Demo ntuple"
TDirectoryFile  Feb 12 15:14 2020 subdir  "subdir"
% rootcp hsimple.root:ntuple h2.root
% rootls -l h2.root                 
TNtuple  Mar 17 13:23 2021 ntuple  "Demo ntuple"
% 

rootls -l allYears_501793_MGPy8EG_W0Z0_lvll_lvllj_LO_emu_PtZge150GeV_myOutput.root
TTree  Dec 21 08:32 2020 MUON_SCALE__1up                                     "MUON_SCALE__1up"
TTree  Dec 21 08:31 2020 MUON_SCALE__1up                                     "MUON_SCALE__1up"
TTree  Dec 21 08:32 2020 nominal                                             "nominal"
TTree  Dec 21 08:31 2020 nominal                                             "nominal"
TTree  Dec 21 08:32 2020 PRW_DATASF__1down                                   "PRW_DATASF__1down"
TTree  Dec 21 08:31 2020 PRW_DATASF__1down                                   "PRW_DATASF__1down"
TTree  Dec 21 08:32 2020 PRW_DATASF__1up                                     "PRW_DATASF__1up"
TTree  Dec 21 08:31 2020 PRW_DATASF__1up                                     "PRW_DATASF__1up"
TTree  Dec 21 08:32 2020 Truth                                               "Truth"
TTree  Dec 21 08:31 2020 Truth                                               "Truth"

 rootcp allYears_501793_MGPy8EG_W0Z0_lvll_lvllj_LO_emu_PtZge150GeV_myOutput.root:nominal test.root
WARNING: Same name objects aren't supported: 'nominal' of 'allYears_501793_MGPy8EG_W0Z0_lvll_lvllj_LO_emu_PtZge150GeV_myOutput.root' won't be processed

May try to put the cycle number:

 rootcp allYears_501793_MGPy8EG_W0Z0_lvll_lvllj_LO_emu_PtZge150GeV_myOutput.root:nominal;1 test.root

with that i get

usage: rootcp [-h] [-c COMPRESS] [--recreate] [-r] [--replace]
              SOURCE [SOURCE ...] DEST
rootcp: error: too few arguments
-bash: 1: command not found

and if i put quotation marks around the first argument i get

 rootcp "allYears_501793_MGPy8EG_W0Z0_lvll_lvllj_LO_emu_PtZge150GeV_myOutput.root:nominal;1" test.root
WARNING: can't find nominal;1 in allYears_501793_MGPy8EG_W0Z0_lvll_lvllj_LO_emu_PtZge150GeV_myOutput.root

Can you provide access to this root file if it is not too big ?

@pcanal To me, the original post here demonstrates a bug that appears when the original object (tree) has more than one “cycle” in the original file.

UPDATE: As stated in one of the posts below, this is NOT a “bug” but a “feature” (expected behavior).

A brutal fix:

    oldtree->SetNameTitle("PolSig", "PolSig");
    auto newtree = oldtree->CloneTree();
    newfile.Write();

@Wile_E_Coyote I am not sure why it would be related the number of cycles in the original file.

My guess is that in the code:

    auto newtree = oldtree->CloneTree();
    newtree->SetNameTitle("PolSig", "PolSig");
    newfile.Write();

CloneTree does an autosave and/or write of the TTree with its "then current" name (i.e. nominal) so it works ‘as intended’.

The work-around that you provided is ‘correct’ (change the name before cloning it).

If one does not want to change the original tree name, then you can split the operationing:

auto newtree = oldtree->CloneTree(0); // copy just the structure.
newtree->SetObject("PolSig", "PolSig"); // Somehow SetNameTitle is not yet overloded in TTree :( ...
newtree->CopyEntries(oldtree, -1, "fast"); // "fast" is not the default in CopyEntries not in CloneTree :(

Cheers,
Philippe

@pcanal I did a small test with the “hsmiple.root”. As soon as there are two “cycles” of the “ntuple” (note: you need to increase its number of entries; I tried with 2500000), the problem with “SetObject” appears.

{ // hsimple_newfile.cxx
  TFile oldfile("hsimple.root");
  TTree *oldtree;
  oldfile.GetObject("ntuple", oldtree);
  TFile newfile("hsimple_newfile.root", "recreate");
  // newfile.cd();
#if 0 /* 0 or 1 */
  oldtree->SetNameTitle("PolSig","PolSig");
  auto newtree = oldtree->CloneTree();
#else /* 0 or 1 */
  auto newtree = oldtree->CloneTree();
  newtree->SetObject("PolSig","PolSig");
#endif /* 0 or 1 */
  newfile.Write();
  oldfile.ls(); std::cout << std::endl;
  newfile.ls();
  newfile.Close();
}

With your script (changing the old file name) and the file:

mac-135395:master.module pcanal$ root.exe -b -l  hs3.root -e 'ntuple->Print()'
root [0] 
Attaching file hs3.root as _file0...
(TFile *) 0x7fae38409660
******************************************************************************
*Tree    :ntuple    : Demo ntuple                                            *
*Entries :    25001 : Total =          504554 bytes  File  Size =     401247 *
*        :          : Tree compression factor =   1.25                       *

I get:

Processing wile.C...
TFile**		hs3.root	Demo ROOT file with histograms
 TFile*		hs3.root	Demo ROOT file with histograms
  OBJ: TNtuple	ntuple	Demo ntuple : 0 at: 0x7fb4fecd4070
  KEY: TH1F	hpx;1	This is the px distribution
  KEY: TH2F	hpxpy;1	py vs px
  KEY: TProfile	hprof;1	Profile of pz versus px
  KEY: TNtuple	ntuple;2	Demo ntuple [current cycle]
  KEY: TNtuple	ntuple;1	Demo ntuple [backup cycle]

TFile**		hsimple_newfile.root	
 TFile*		hsimple_newfile.root	
  OBJ: TNtuple	PolSig	PolSig : 0 at: 0x7fb4fee1bc80
  KEY: TNtuple	PolSig;1	PolSig

ROOT 6.22/08 on a Ubuntu 20.04 / x86_64, gcc 9.3.0:

[...]$ root -q hsimple_newfile.cxx 

Processing hsimple_newfile.cxx...
TFile**		hsimple.root	Demo ROOT file with histograms
 TFile*		hsimple.root	Demo ROOT file with histograms
  OBJ: TNtuple	ntuple	Demo ntuple : 0 at: 0x562993478980
  KEY: TNtuple	ntuple;2	Demo ntuple
  KEY: TNtuple	ntuple;1	Demo ntuple
  KEY: TH1F	hpx;1	This is the px distribution
  KEY: TH2F	hpxpy;1	py vs px
  KEY: TProfile	hprof;1	Profile of pz versus px

TFile**		hsimple_newfile.root	
 TFile*		hsimple_newfile.root	
  OBJ: TNtuple	PolSig	PolSig : 0 at: 0x562993926b00
  KEY: TNtuple	ntuple;1	Demo ntuple
  KEY: TNtuple	PolSig;1	PolSig

I’ll try 6.22 but it looks like I already fixed it :slight_smile: … my attempt were with tip of the main branch.

We got a mystery on our hand … I just tried with v6.22/08 and got the same (good) result I got before …
One noticeable difference between your output and mine (in both master and v6.22/08) is the order of the keys in the input file, you get:

  KEY: TNtuple	ntuple;2	Demo ntuple
  KEY: TNtuple	ntuple;1	Demo ntuple
  KEY: TH1F	hpx;1	This is the px distribution
  KEY: TH2F	hpxpy;1	py vs px
  KEY: TProfile	hprof;1	Profile of pz versus px

while I get:

  KEY: TH1F     hpx;1   This is the px distribution
  KEY: TH2F     hpxpy;1 py vs px
  KEY: TProfile hprof;1 Profile of pz versus px
  KEY: TNtuple  ntuple;2        Demo ntuple
  KEY: TNtuple  ntuple;1        Demo ntuple

So there is something different between your hsimple.root and mine. Could you send me yours?

Mine is created with:

bash: cp hsimple.root hs2.root
bash: root.exe -b -l
root [0] f = new TFile("hs2.root", "UPDATE");
root [1] ntuple->Fill(1.0);
root [2] f->Write();
root [3] delete f;
root [4] .q

For test purposes, I took the “${ROOTSYS}/tutorials/hsimple.C” macro and modified two lines:

   const Int_t kUPDATE = 100000;
   for (Int_t i = 0; i < 2500000; i++) {

Then:

rm -f hsimple.root # not really needed, of course
root -q hsimple.C # the modified macro which (re)creates "hsimple.root"
root -q hsimple_newfile.cxx # my test macro (output in my previous post)

@pcanal Let me know if you cannot reproduce this problem (I will then send you the “hsimple.root” file that I produced).