Doubts separating tree entries into different trees

I need to separate entries in a TTree and put them in other two (identical) trees, so I use Tree::CloneTree:

void do_ROOT_job(int ch, double x0, double y0, double r, double l) {

    // input file
    TFile inFile(Form("../gedet/surf/ver/ver-gedet-surf-%i.root", ch));
    // output file
    TFile nFile(Form("../gedet/nplus/ver/ver-gedet-nplus-%i.root", ch), "RECREATE");
    auto tree = dynamic_cast<TTree*>(inFile.Get("GSSTree"));

    // set up for reading
    TTreeReader treereader;
    TTreeReaderValue<Double_t> x(treereader, "x_cm.x");
    TTreeReaderValue<Double_t> y(treereader, "y_cm.y");
    TTreeReaderValue<Double_t> z(treereader, "z_cm.z");
    treereader.SetTree(tree);

    // clone structure of original tree
    auto pTree = tree->CloneTree(0);
    auto nTree = tree->CloneTree(0);

    // select events
    while (treereader.Next()) {
        if ( (x0-*x)*(x0-*x)+(y0-*y)*(y0-*y) < r*r and *z < l ) pTree->Fill();
        else                                                    nTree->Fill();
    }

    // write on file
    pTree->Write("p");
    nTree->Write("n");
}

Then, opening the output file:

$ root ver-gedet-nplus-1.root
root [0]
Attaching file ver-gedet-nplus-1.root as _file0...
(TFile *) 0x7fa27a7dfbc0
root [1] .ls
TFile**		ver-gedet-nplus-1.root
 TFile*		ver-gedet-nplus-1.root
  KEY: TTree	GSSTree;2	General Surface Sampler Coordinate Tree
  KEY: TTree	p;1	General Surface Sampler Coordinate Tree
  KEY: TTree	n;1	General Surface Sampler Coordinate Tree

I notice that also a GSSTree tree is present, equal to n. Why? I couldn’t find help on the docs. Generally speaking, is this a good approach?

Thanks

When you cloned the tree, you created a tree with the same name. So in one file you should have the entries from the if part, and on the other the entries for the else part, but in both cases the name of the trees is the same as the original tree. Does that answer your question?

Mmh no, that’s not what I have here. Let’s take this updated version:

void do_ROOT_job(bool upside_down, int ch, double x0, double y0, double r, double l) {

    TFile inFile("../../gedet/surf/ver/ver-gedet-surf-0.root");
    TFile nFile("../../gedet/nplus/ver/ver-gedet-nplus-0.root", "RECREATE");
    TFile pFile("../../gedet/pplus/ver/ver-gedet-pplus-0.root", "RECREATE");

    auto tree = dynamic_cast<TTree*>(inFile.Get("GSSTree"));
    auto pTree = tree->CloneTree(0);
    auto nTree = tree->CloneTree(0);

    double x, y, z;
    tree->SetBranchAddress("x_cm", &x);
    tree->SetBranchAddress("y_cm", &y);
    tree->SetBranchAddress("z_cm", &z);

    int N = tree->GetEntries();
    if (upside_down) {
        for (int i = 0; i < N; ++i) {
            tree->GetEntry(i);
            if ( (x0-x)*(x0-x)+(y0-y)*(y0-y) <= r*r and z > l ) pTree->Fill();
            else                                                nTree->Fill();
        }
    }

    else {
        for (int i = 0; i < N; ++i) {
            tree->GetEntry(i);
            if ( (x0-x)*(x0-x)+(y0-y)*(y0-y) <= r*r and z < l ) pTree->Fill();
            else                                                nTree->Fill();
        }
    }

    pFile.WriteTObject(pTree);
    nFile.WriteTObject(nTree);
}

Now I’m trying to save the two cloned trees into different files. The result is the following:

$ root ver-gedet-pplus-0.root
root [0]
Attaching file ver-gedet-pplus-0.root as _file0...
(TFile *) 0x7fa96fdb1ef0
root [1] .ls
TFile**		ver-gedet-pplus-0.root
 TFile*		ver-gedet-pplus-0.root
  KEY: TTree	GSSTree;3	General Surface Sampler Coordinate Tree
  KEY: TTree	GSSTree;2	General Surface Sampler Coordinate Tree
$ root ver-gedet-nplus-0.root
root [0]
Attaching file ver-gedet-nplus-0.root as _file0...
(TFile *) 0x7f98a90e3430
root [1] .ls
TFile**		ver-gedet-nplus-0.root
 TFile*		ver-gedet-nplus-0.root
  KEY: TTree	GSSTree;1	General Surface Sampler Coordinate Tree

The doubling of trees in the first file is actually what I don’t understand.

I see. When you see an object with Name;1, Name;2, etc, that means it has “cycle” 1, 2, etc. This happens when you save the same object multiple times in a file. All versions of the object are kept, and the highest cycle is the latest version. So if you call Get() on the tree, ROOT will give you the latest version of this object that was saved into the file. You can also get a specific version by specifying the full name with the cycle included. You can use that to check what is in each tree and see if there is any difference, but what is probably happening is that gDirectory is set to your pFile file and the tree is auto-saved when the file is closed, creating a second version of it in the output file. If you create one file, then the tree that goes in it, then the second file and its respective tree, you can call just pTree->Write(); nTree->Write() at the end, and only one copy will appear in the output file.

I modified my code as follows:

void do_ROOT_job(bool upside_down, int ch, double x0, double y0, double r, double l) {

    TFile inFile("../../gedet/surf/ver/ver-gedet-surf-0.root");
    auto tree = dynamic_cast<TTree*>(inFile.Get("GSSTree"));

    TFile pFile("../../gedet/pplus/ver/ver-gedet-pplus-0.root", "RECREATE");
    auto pTree = tree->CloneTree(0);
    TFile nFile("../../gedet/nplus/ver/ver-gedet-nplus-0.root", "RECREATE");
    auto nTree = tree->CloneTree(0);

    double x, y, z;
    tree->SetBranchAddress("x_cm", &x);
    tree->SetBranchAddress("y_cm", &y);
    tree->SetBranchAddress("z_cm", &z);

    int N = tree->GetEntries();
    if (upside_down) {
        for (int i = 0; i < N; ++i) {
            tree->GetEntry(i);
            if ( (x0-x)*(x0-x)+(y0-y)*(y0-y) <= r*r and z > l ) pTree->Fill();
            else                                                nTree->Fill();
        }
    }

    else {
        for (int i = 0; i < N; ++i) {
            tree->GetEntry(i);
            if ( (x0-x)*(x0-x)+(y0-y)*(y0-y) <= r*r and z < l ) pTree->Fill();
            else                                                nTree->Fill();
        }
    }

    pFile.WriteTObject(pTree);
    nFile.WriteTObject(nTree);
}

But I get still the same behaviour…

Please replace

pFile.WriteTObject(pTree);
nFile.WriteTObject(nTree);

with

pTree->Write();
nTree->Write();

and try again. I think that will give you the behaviour you want.

With that modification everything is written into pFile and nFile remains empty, but what I actually need is to save nTree into nFile and pTree into pFile

I see. Then you have to explicitly change gDirectory to where you want to write things out, something like:

pFile.cd(); pTree->Write();
nFile.cd(); nTree->Write();

I tried also that, again the result is identical to the situation described in the third post: there’s an additional, unwanted, tree saved into pFile.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.