I wrote a program that fills a ROOT TTree with the events from a Monte Carlo simulation. Assume the tree contains the following variables:Short_t n
Float_t x[n]
Float_t y[n]
I’m not interested in the whole tree, but only in a small subset of events. The events are identified by some condition on the tree variables, e.g.sqrt(x)<=1./ywhich however is user-defined, i.e. known only at run time. I’d like to be able to fill the tree only with the events, or parts of events, that fulfil the user-defined condition.
Ideally I’d like to do something like
// assign tree branches to variables n, x, y
n = 3;
x[0] = 1.0; x[1] = 1.0; x[2] = 1.0;
y[0] = 10.0; y[1] = 0.1; y[2] = 0.01;
TString condition;
std::cin >> condition;
tree->Fill(condition);and that would fill the tree with the following event:n=2
x[0]=1.0 y[0]=0.1
x[1]=1.0 y[1]=0.01Or, if this is impossible, I could fill the tree only if any of the array items satisfies the relevant condition.
Is there any way to realize that? Perhaps I could fill a temporary tree and copy over only the relevant bits, but I’m not sure how to do that.
I partially solved this by using a “buffer” tree.// define tree and tree branches here
TTree *bufferTree = tree->CloneTree();When I want to add a row, I add it to bufferTree:bufferTree->Fill();From time to time, I copy the relevant events over to tree' and I resetbufferTree’:TTree *tempTree = bufferTree->CopyTree(condition);
tree->CopyEntries(tempTree);
delete tempTree;
bufferTree->Reset();At the end of the simulation, I only write `tree’ to the output file.
This works, but it seems unnecessarily cumbersome. One could avoid creating a temporary tree if TTree::CopyEntries allowed to specify some selection, like TTree::CopyTree does.
I am interested on this too. I think it would have been very helpful to have the possibility of introducing a cut as an argument of the TTree::Fill() function, so that the tree gets filled only if the condition is satisfied. Another way of doing this could be adding:
c_tree.Fill();
TTreeFormula form("tform", cut, &c_tree);
auto value = form.EvalInstance(i_entry);
//**************************************
if (value)
tree.Fill();
[/code]
before filling the tree. However this means that one would have to make the branches all over again, normally tens of them, so probably the solution above is better.
I tried to obtain c_tree as a clone of tree, but it seems that the branches of the clone are not linked to the variables a and b but some version of them inside tree, so that everytime I do c_tree.Fill() I fill with the wrong number.
Hi, assuming you have your tree in a file on disk, since v6.10 you can solve your problem with TDataFrame, as pcanal suggested. The following code is not tested but should work about fine for branches containing a single float:
ROOT::Experimental::TDataFrame d("myTree", "file.root"); // instantiate TDF
d.Filter("sqrt(x) <= 1./y") // only consider events that fulfill this condition
.Snapshot("skimmedTree", "output.root", {"n", "x", "y"}); // write to disk
For more complicated filter expressions you can use c++11 lambdas (or normal c++ functions). Here is a filter applied to branches x and y containing arrays of floats:
using arr_view_f = std::array_view<Float_t>;
auto myCut = [](const arr_view_f& x, const arr_view_f& y, int n) {
/* your logic goes here, e.g. */
auto xysum = 0;
for(auto i = 0; i < n; ++i) xysum += x[i] + y[i];
return xysum > 0;
};
d.Filter(myCut, {"x", "y", "n"}).Snapshot(/*as before*/);