Last significant change: 2005/07/07
An Ntuple stores data of basic data types in an easy to access format in which data variables may be manipulated, plotted and fitted. An example of an application of an ntuple is to store frequently accessed reconstruction variables, which taken together summarize the results of a reconstruction job.
The purpose of this chapter is to describe how to create and use ntuples within the MINOS framework.
Standard Ntuples are those ntuples produced during production running as an end product of Standard Reconstruction. Each Standard Ntuple file contains one or more ntuples, each in the form of a ROOT TTree.
The ntuple trees produced during production up until R1.14 were:
These 3 ntuples were replaced in production running as of R1.14 with a single ntuple TTree: NtpSt. The NtpSt tree contains all data previously stored in the 3 separate ntuples. The 3 separate ntuples are maintained, and although not produced in production running, can still be produced in user's individual jobs.
The standard ntuples are produced through storage of NtpXXRecord objects in a root TTree of name NtpXX. The best place to look for definition of data members stored in the standard ntuples is to look at the NtpXXRecord.h and helper class header files. These files are stored in the following packages:
For example, the definition of the cosmic ray data members stored in the NtpSR tree can be found in the CandNtupleSR/NtpSRCosmicRay.h file as:
Float_t zenith; // zenith angle (degrees) Float_t azimuth; // azimuthal angle measured easterly from north (degrees) Float_t ra; // right ascension (degrees) Float_t rahourangle; // right ascension (hours) Float_t dec; // declination (degrees) Double_t juliandate; // julian date (days since Jan 1, 4713 BC 12h UT) Float_t locsiderialtime; // local sidereal time (hours)
Ntuple trees may be analyzed mutiple ways:
Taking the example a step further, the mc.iaction associated with the first slice of a given snarl is mc[thslc[0].neumc].iaction. To select slices which satisfy the selection of best match neutrino has mc.iaction equal to 0 (neutral current), use the selection string "mc[thslc.neumc].iaction==0". The entries in thslc are implicitly looped over.
To Draw members of the slc array which satisfies this selection cut, do this:
root[0] TFile* file = new TFile("overlayfile.root","READ");
root[1] TTree* ntpsr = (TTree*)(file -> Get("NtpSR"));
root[2] TTree* ntpmc = (TTree*)(file -> Get("NtpMC"));
root[3] TTree* ntpth = (TTree*)(file -> Get("NtpTH"));
root[4] ntpmc -> AddFriend(ntpsr);
root[5] ntpmc -> AddFriend(ntpth);
root[6] ntpmc -> Draw("slc.index","mc[thslc.neumc].iaction==0");
Since slc and thslc share a common set of indices (i.e. there's a 1:1 relationship between them), this command will plot the slc indices corresponding to those with mc.iaction==0.
{
// usage: root .x scriptname.C
// The output is two files NtpSR.h and NtpSR.C
f = new TFile("F00017566_0000.sntp.root","READ");
t = (TTree*)(f->Get("NtpSR"));
t -> MakeClass();
}
The execution of this example script results in two files: NtpSR.h and NtpSR.C.
NtpSR.h is a header file for a new class NtpSR which reproduces the
structure of the data members stored on the NtpSR tree. NtpSR.C implements
the Loop method of the NtpSR class as a demonstration of how one might
loop over the entries in the tree using this NtpSR class. The user
may customize the Loop method implementation in NtpSR.C to suit his or her
needs.
The following examples illustrate the use of the NtpSR.C script.
root[0].L NtpSR.C root[1] NtpSR ntpsr; // instantiate the NtpSR object using its default ctor root[2] ntpsr.Loop();
root[0]TChain* chain = new TChain("NtpSR");
root[1]chain -> Add("ntuple1.root");
root[2]chain -> Add("ntuple2.root");
root[3].L NtpSR.C
root[4]NtpSR ntpsr(chain); //instantiate the NtpSR object to use the new chain
root[5]ntpsr.Loop();
{
// usage: loon sriptname.C
//
gSystem -> Load("libCandNtupleSR.so");
f = new TFile("F00017566_0000.sntp.root","READ");
t = (TTree*)(f->Get("NtpSR"));
NtpSRRecord* record = 0;
t -> SetBranchAddress("NtpSRRecord",&record);
// Loop over all entries in tree
for ( int ient = 0; ient < t -> GetEntries(); ient++ ) {
int nbytes = t->GetEntry(ient);
cout << "Read entry, nbytes = " << ient << "," << nbytes << endl;
// Example of Accessing header data
cout << "snarl " << (record -> GetHeader()).GetSnarl() << endl;
// or
RecCandHeader& header = record -> GetHeader();
cout << "snarl " << header.GetSnarl() << endl;
cout << "run " << header.GetRun() << endl;
// Example of accessing event summary data
cout << "crhdr.azimuth " << record -> crhdr.azimuth << endl;
// Example of accessing TClonesArray (stp,trk,etc.) data
TClonesArray& striparray = *(record->stp);
for ( int istp = 0; istp < striparray.GetEntries(); istp++ ) {
NtpSRStrip* ntpstrip = dynamic_cast<NtpSRStrip*>striparray[istp];
cout << "strip index " << ntpstrip->index << endl;
}
record -> Clear(); // release allocated memory, record will be reused
}
delete record; record = 0;
}
Analyzing data from two or more ntuples in parallel in a root session requires making use of the TTree::AddFriend mechanism.
In the simplest use case in which the ntuple trees are aligned such that there is a 1:1 relationship between entries in the separate trees, the TTree::AddFriend method may be used directly. The following example illustrates how to do this:
{
TFile* file = new TFile("ntuple.root","READ");
TTree* mctree = (TTree*)(file -> Get("NtpMC"));
TTree* srtree = (TTree*)(file -> Get("NtpSR"));
// Make the srtree a friend of the mctree
mctree->AddFriend(srtree);
// It's now possible to draw data members of either tree
// using the mctree pointer, for example:
// mctree->Draw("evt.vtx.z"); // branch on NtpSR tree
// mctree->Draw("mc.vtxz"); // branch on NtpMC tree
mctree->Draw("evt.vtx.z:mc.vtxz","","box"); // plotting SR vtx vs MC vtx
}
In some cases, however, the relationship between entries in different ntuple trees is not 1:1. An example of this is the writing of non-triggering truth records to the NtpMC tree, which do not result in the writing of a corresponding entry to the NtpSR tree. In this case, the 1:1 relationship between tree entries is lost.
This loss of 1:1 relationship is not a problem within our framework, which will use the VldContext stored with the records to resynchronize the entries across the two trees when reading the NtpMC and NtpSR streams in during a subsequent loon job. But the loss of 1:1 relationship is a problem when analyzing the trees in a root session using TTree::AddFriend, since this method only matches records across two or more trees by tree index.
A future version of root should support the feature of using TTree::AddFriend to synchronize by ``key value'' (e.g. VldContext). In the meantime, a script is provided by the MINOS framework that can be used to restore the functionality of TTree::AddFriend with non-aligned trees. The script synchronizes trees by "key value", so that they can be used with the TTree::AddFriend mechanism. The script, and a second script to demonstrate its use, can be found in Record/macros as AddFriendByKey.C and AlignTreesByKey.C.