Back to home page

EIC code displayed by LXR

 
 

    


Warning, /tutorial-analysis/_extras/exercise_scripts.md is written in an unsupported language. File is not indexed.

0001 ---
0002 title: "Exercise Scripts"
0003 ---
0004 
0005 Included below is a selection of scripts for the exercises in part 3 of this tutorial. Shortly after the tutorial, I will also include "complete" examples for future reference.
0006 
0007 You should be able to copy the code text directly into a new file. The name of the file is included as the title of each script section and in the accompanying descriptive text.
0008 
0009 ## ROOT TTreeReader Scripts
0010 
0011 ### EfficiencyAnalysis.C
0012 
0013 Create a file called `EfficiencyAnalysis.C` and copy in the code below to get started on the efficiency analysis exercise. Note that you will need to correctly specifiy your input file path in the first line.
0014 
0015 ```c++
0016 void EfficiencyAnalysis(TString infile="PATH_TO_INPUT_FILE"){
0017   // Set output file for the histograms
0018   TFile *ofile = TFile::Open("EfficiencyAnalysis_Out.root","RECREATE");
0019   
0020   // Set up input file chain
0021   TChain *mychain = new TChain("events");
0022   mychain->Add(infile);
0023   
0024   // Initialize reader
0025   TTreeReader tree_reader(mychain);
0026   
0027   // Get Particle Information
0028   TTreeReaderArray<int> partGenStat(tree_reader, "MCParticles.generatorStatus");
0029   TTreeReaderArray<float> partMomX(tree_reader, "MCParticles.momentum.x");
0030   TTreeReaderArray<float> partMomY(tree_reader, "MCParticles.momentum.y");
0031   TTreeReaderArray<float> partMomZ(tree_reader, "MCParticles.momentum.z");
0032   TTreeReaderArray<int> partPdg(tree_reader, "MCParticles.PDG");
0033   
0034   // Get Reconstructed Track Information
0035   TTreeReaderArray<float> trackMomX(tree_reader, "ReconstructedChargedParticles.momentum.x");
0036   TTreeReaderArray<float> trackMomY(tree_reader, "ReconstructedChargedParticles.momentum.y");
0037   TTreeReaderArray<float> trackMomZ(tree_reader, "ReconstructedChargedParticles.momentum.z");
0038   
0039   // Get Associations Between MCParticles and ReconstructedChargedParticles
0040   TTreeReaderArray<unsigned int> recoAssoc(tree_reader, "ReconstructedChargedParticleAssociations.recID");
0041   TTreeReaderArray<unsigned int> simuAssoc(tree_reader, "ReconstructedChargedParticleAssociations.simID");
0042       
0043   // Define Histograms
0044   TH1D *partEta = new TH1D("partEta","Eta of Thrown Charged Particles;Eta",100,-5.,5.);
0045   TH1D *matchedPartEta = new TH1D("matchedPartEta","Eta of Thrown Charged Particles That Have Matching Track",100,-5.,5.);
0046   TH1D *matchedPartTrackDeltaR = new TH1D("matchedPartTrackDeltaR","Delta R Between Matching Thrown and Reconstructed Charged Particle",5000,0.,5.);
0047 
0048   while(tree_reader.Next()) { // Loop over events
0049     for(unsigned int i=0; i<partGenStat.GetSize(); i++){ // Loop over thrown particles
0050         if(partGenStat[i] == 1){ // Select stable thrown particles
0051             int pdg = TMath::Abs(partPdg[i]);
0052             if(pdg == 11 || pdg == 13 || pdg == 211 || pdg == 321 || pdg == 2212){ // Look at charged particles (electrons, muons, pions, kaons, protons)
0053                 TVector3 trueMom(partMomX[i],partMomY[i],partMomZ[i]);
0054 
0055                 float trueEta = trueMom.PseudoRapidity();
0056                 float truePhi = trueMom.Phi();
0057             
0058                 partEta->Fill(trueEta);
0059 
0060                 for(unsigned int j=0; j<simuAssoc.GetSize(); j++){ // Loop over associations to find matching ReconstructedChargedParticle
0061                     if(simuAssoc[j] == i){ // Find association index matching the index of the thrown particle we are looking at
0062                         TVector3 recMom(trackMomX[recoAssoc[j]],trackMomY[recoAssoc[j]],trackMomZ[recoAssoc[j]]); // recoAssoc[j] is the index of the matched ReconstructedChargedParticle
0063 
0064                         // Check the distance between the thrown and reconstructed particle
0065                         float deltaEta = trueEta - recMom.PseudoRapidity();
0066                         float deltaPhi = TVector2::Phi_mpi_pi(truePhi - recMom.Phi());
0067                         float deltaR = TMath::Sqrt(deltaEta*deltaEta + deltaPhi*deltaPhi);
0068 
0069                         matchedPartTrackDeltaR->Fill(deltaR);
0070 
0071                         matchedPartEta->Fill(trueEta); // Plot the thrown eta if a matched ReconstructedChargedParticle was found
0072                     }
0073                 } // End loop over associations 
0074             } // End PDG check          
0075         } // End stable particles condition  
0076     } // End loop over thrown particles
0077   } // End loop over events 
0078   ofile->Write(); // Write histograms to file
0079   ofile->Close(); // Close output file
0080 }
0081 ```
0082 <!--
0083 A "solution" version of the script for the exercise is included below -
0084 
0085 ```c++
0086 void EfficiencyAnalysis_Exercise(TString infile="PATH_TO_FILE"){
0087   
0088   // Set output file for the histograms
0089   TFile *ofile = TFile::Open("EfficiencyAnalysis_Exercise_Out.root","RECREATE");
0090 
0091   // Analysis code will go here
0092   // Set up input file chain
0093   TChain *mychain = new TChain("events");
0094   mychain->Add(infile);
0095 
0096   // Initialize reader
0097   TTreeReader tree_reader(mychain);
0098 
0099   // Get Particle Information
0100   TTreeReaderArray<int> partGenStat(tree_reader, "MCParticles.generatorStatus");
0101   TTreeReaderArray<float> partMomX(tree_reader, "MCParticles.momentum.x");
0102   TTreeReaderArray<float> partMomY(tree_reader, "MCParticles.momentum.y");
0103   TTreeReaderArray<float> partMomZ(tree_reader, "MCParticles.momentum.z");
0104   TTreeReaderArray<int> partPdg(tree_reader, "MCParticles.PDG");
0105 
0106   // Get Reconstructed Track Information
0107   TTreeReaderArray<float> trackMomX(tree_reader, "ReconstructedChargedParticles.momentum.x");
0108   TTreeReaderArray<float> trackMomY(tree_reader, "ReconstructedChargedParticles.momentum.y");
0109   TTreeReaderArray<float> trackMomZ(tree_reader, "ReconstructedChargedParticles.momentum.z");
0110 
0111   // Get Associations Between MCParticles and ReconstructedChargedParticles
0112   TTreeReaderArray<unsigned int> recoAssoc(tree_reader, "ReconstructedChargedParticleAssociations.recID");
0113   TTreeReaderArray<unsigned int> simuAssoc(tree_reader, "ReconstructedChargedParticleAssociations.simID");
0114     
0115   // Define Histograms
0116   TH1D *partEta = new TH1D("partEta","#eta of Thrown Charged Particles; #eta", 120, -6, 6);
0117   TH1D *matchedPartEta = new TH1D("matchedPartEta","#eta of Thrown Charged Particles That Have Matching Track; #eta", 120, -6, 6);
0118   TH1D* partMom = new TH1D("partMom", "Momentum of Thrown Charged Particles (truth); P(GeV/c)", 150, 0, 150);
0119   TH1D* matchedPartMom = new TH1D("matchedPartMom", "Momentum of Thrown Charged Particles (truth), with matching track; P(GeV/c)", 150, 0, 150);
0120   TH1D* partPhi = new TH1D("partPhi", "#phi of Thrown Charged Particles (truth); #phi(rad)", 320, -3.2, 3.2);
0121   TH1D* matchedPartPhi = new TH1D("matchedPartPhi", "#phi of Thrown Charged Particles (truth), with matching track; #phi(rad)", 320, -3.2, 3.2);
0122 
0123   TH2D* partPEta = new TH2D("partPEta", "P vs #eta of Thrown Charged Particles; P(GeV/c); #eta", 150, 0, 150, 120, -6, 6);
0124   TH2D* matchedPartPEta = new TH2D("matchedPartPEta", "P vs #eta of Thrown Charged Particles, with matching track; P(GeV/c); #eta", 150, 0, 150, 120, -6, 6);
0125   TH2D* partPhiEta = new TH2D("partPhiEta", "#phi vs #eta of Thrown Charged Particles; #phi(rad); #eta", 160, -3.2, 3.2, 120, -6, 6);
0126   TH2D* matchedPartPhiEta = new TH2D("matchedPartPhiEta", "#phi vs #eta of Thrown Charged Particles; #phi(rad); #eta", 160, -3.2, 3.2, 120, -6, 6);
0127     
0128   TH1D *matchedPartTrackDeltaEta = new TH1D("matchedPartTrackDeltaEta","#Delta#eta Between Matching Thrown and Reconstructed Charged Particle; #Delta#eta", 100, -0.25, 0.25);
0129   TH1D *matchedPartTrackDeltaPhi = new TH1D("matchedPartTrackDeltaPhi","#Detla #phi Between Matching Thrown and Reconstructed Charged Particle; #Delta#phi", 200, -0.2, 0.2);
0130   TH1D *matchedPartTrackDeltaR = new TH1D("matchedPartTrackDeltaR","#Delta R Between Matching Thrown and Reconstructed Charged Particle; #Delta R", 300, 0, 0.3);
0131   TH1D *matchedPartTrackDeltaMom = new TH1D("matchedPartTrackDeltaMom","#Delta P Between Matching Thrown and Reconstructed Charged Particle; #Delta P", 200, -10, 10);
0132     
0133   // Define some histograms for our efficiencies
0134   TH1D *TrackEff_Eta = new TH1D("TrackEff_Eta", "Tracking efficiency as fn of #eta; #eta; Eff(%)", 120, -6, 6); 
0135   TH1D *TrackEff_Mom = new TH1D("TrackEff_Mom", "Tracking efficiency as fn of P; P(GeV/c); Eff(%)", 150, 0, 150); 
0136   TH1D *TrackEff_Phi = new TH1D("TrackEff_Phi", "Tracking efficiency as fn of #phi; #phi(rad); Eff(%)", 320, -3.2, 3.2);
0137 
0138   // 2D Efficiencies
0139   TH2D* TrackEff_PEta = new TH2D("TrackEff_PEta", "Tracking efficiency as fn of P and #eta; P(GeV/c); #eta", 150, 0, 150, 120, -6, 6);
0140   TH2D* TrackEff_PhiEta = new TH2D("TrackEff_PhiEta", "Tracking efficiency as fn of #phi and #eta; #phi(rad); #eta", 160, -3.2, 3.2, 120, -6, 6);
0141 
0142   // All charged particle histos
0143   TH1D *ChargedEta = new TH1D("ChargedEta", "#eta of all charged particles; #eta", 120, -6, 6);
0144   TH1D *ChargedPhi = new TH1D("ChargedPhi", "#phi of all charged particles; #phi (rad)", 120, -3.2, 3.2);
0145   TH1D *ChargedP = new TH1D("ChargedP", "P of all charged particles; P(GeV/c)", 150, 0, 150);
0146   
0147   while(tree_reader.Next()) { // Loop over events
0148 
0149     for(unsigned int i=0; i<partGenStat.GetSize(); i++) // Loop over thrown particles
0150       {
0151         if(partGenStat[i] == 1) // Select stable thrown particles
0152           {
0153             int pdg = TMath::Abs(partPdg[i]);
0154 
0155             if(pdg == 11 || pdg == 13 || pdg == 211 || pdg == 321 || pdg == 2212) // Look at charged particles (electrons, muons, pions, kaons, protons)
0156               {
0157                 TVector3 trueMom(partMomX[i],partMomY[i],partMomZ[i]);
0158 
0159                 float trueEta = trueMom.PseudoRapidity();
0160                 float truePhi = trueMom.Phi();
0161             
0162                 partEta->Fill(trueEta);
0163                 partPhi->Fill(truePhi);
0164                 partMom->Fill(trueMom.Mag());
0165                 partPEta->Fill(trueMom.Mag(), trueEta);
0166                 partPhiEta->Fill(truePhi, trueEta);
0167 
0168                 // Loop over associations to find matching ReconstructedChargedParticle
0169                 for(unsigned int j=0; j<simuAssoc.GetSize(); j++)
0170                   {
0171                     if(simuAssoc[j] == i) // Find association index matching the index of the thrown particle we are looking at
0172                       {
0173                         TVector3 recMom(trackMomX[recoAssoc[j]],trackMomY[recoAssoc[j]],trackMomZ[recoAssoc[j]]); // recoAssoc[j] is the index of the matched ReconstructedChargedParticle
0174 
0175                         // Check the distance between the thrown and reconstructed particle
0176                         float deltaEta = trueEta - recMom.PseudoRapidity();
0177                         float deltaPhi = TVector2::Phi_mpi_pi(truePhi - recMom.Phi());
0178                         float deltaR = TMath::Sqrt(deltaEta*deltaEta + deltaPhi*deltaPhi);
0179                         float deltaMom = ((trueMom.Mag()) - (recMom.Mag()));
0180 
0181                         matchedPartTrackDeltaEta->Fill(deltaEta);
0182                         matchedPartTrackDeltaPhi->Fill(deltaPhi);
0183                         matchedPartTrackDeltaR->Fill(deltaR);
0184                         matchedPartTrackDeltaMom->Fill(deltaMom);
0185 
0186                         matchedPartEta->Fill(trueEta); // Plot the thrown eta if a matched ReconstructedChargedParticle was found
0187                         matchedPartPhi->Fill(truePhi);
0188                         matchedPartMom->Fill(trueMom.Mag());
0189 
0190                         matchedPartPEta->Fill(trueMom.Mag(), trueEta);
0191                         matchedPartPhiEta->Fill(truePhi, trueEta);
0192         
0193                       }
0194                   }// End loop over associations
0195               } // End PDG check
0196           } // End stable particles condition
0197       } // End loop over thrown particles
0198     // Loop over all charged particles and fill some histograms of kinematics quantities
0199     for(unsigned int k=0; k<trackMomX.GetSize(); k++){ // Loop over all charged particles, thrown or not
0200       
0201       TVector3 CPartMom(trackMomX[k], trackMomY[k], trackMomZ[k]);
0202 
0203       float CPartEta = CPartMom.PseudoRapidity();
0204       float CPartPhi = CPartMom.Phi();
0205 
0206       ChargedEta->Fill(CPartEta);
0207       ChargedPhi->Fill(CPartPhi);
0208       ChargedP->Fill(CPartMom.Mag());
0209       
0210     } // End loop over all charged particles
0211   } // End loop over events
0212 
0213   // Take the ratio of the histograms above to get our efficiency plots
0214   TrackEff_Eta->Divide(matchedPartEta, partEta, 1, 1, "b");
0215   TrackEff_Mom->Divide(matchedPartMom, partMom, 1, 1, "b");
0216   TrackEff_Phi->Divide(matchedPartPhi, partPhi, 1, 1, "b");
0217   TrackEff_PEta->Divide(matchedPartPEta, partPEta, 1, 1, "b");
0218   TrackEff_PhiEta->Divide(matchedPartPhiEta, partPhiEta, 1, 1, "b");
0219   
0220   ofile->Write(); // Write histograms to file
0221   ofile->Close(); // Close output file
0222 }
0223 ```
0224 Insert your input file path and execute as the example code above.
0225 -->
0226 
0227 ### ResolutionAnalysis.C
0228 
0229 Create a file called `ResolutionAnalysis.C` and copy in the code below to get started on the resolution analysis exercise. Note that you will need to correctly specifiy your input file path in the first line.
0230 
0231 ```c++
0232 void ResolutionAnalysis(TString infile="PATH_TO_INPUT_FILE"){
0233   // Set output file for the histograms
0234   TFile *ofile = TFile::Open("ResolutionAnalysis_Out.root","RECREATE");
0235 
0236   // Analysis code will go here
0237   // Set up input file chain
0238   TChain *mychain = new TChain("events");
0239   mychain->Add(infile);
0240 
0241   // Initialize reader
0242   TTreeReader tree_reader(mychain);
0243 
0244   // Get Particle Information
0245   TTreeReaderArray<int> partGenStat(tree_reader, "MCParticles.generatorStatus");
0246   TTreeReaderArray<float> partMomX(tree_reader, "MCParticles.momentum.x");
0247   TTreeReaderArray<float> partMomY(tree_reader, "MCParticles.momentum.y");
0248   TTreeReaderArray<float> partMomZ(tree_reader, "MCParticles.momentum.z");
0249   TTreeReaderArray<int> partPdg(tree_reader, "MCParticles.PDG");
0250 
0251   // Get Reconstructed Track Information
0252   TTreeReaderArray<float> trackMomX(tree_reader, "ReconstructedChargedParticles.momentum.x");
0253   TTreeReaderArray<float> trackMomY(tree_reader, "ReconstructedChargedParticles.momentum.y");
0254   TTreeReaderArray<float> trackMomZ(tree_reader, "ReconstructedChargedParticles.momentum.z");
0255 
0256   // Get Associations Between MCParticles and ReconstructedChargedParticles
0257   TTreeReaderArray<unsigned int> recoAssoc(tree_reader, "ReconstructedChargedParticleAssociations.recID");
0258   TTreeReaderArray<unsigned int> simuAssoc(tree_reader, "ReconstructedChargedParticleAssociations.simID");
0259     
0260   // Define Histograms
0261   TH1D *trackMomentumRes = new TH1D("trackMomentumRes","Track Momentum Resolution", 400, -2, 2);
0262  
0263   TH1D *matchedPartTrackDeltaEta = new TH1D("matchedPartTrackDeltaEta","#Delta#eta Between Matching Thrown and Reconstructed Charged Particle; #Delta#eta", 100, -0.25, 0.25);
0264   TH1D *matchedPartTrackDeltaPhi = new TH1D("matchedPartTrackDeltaPhi","#Detla #phi Between Matching Thrown and Reconstructed Charged Particle; #Delta#phi", 200, -0.2, 0.2);
0265   TH1D *matchedPartTrackDeltaR = new TH1D("matchedPartTrackDeltaR","#Delta R Between Matching Thrown and Reconstructed Charged Particle; #Delta R", 300, 0, 0.3);
0266   TH1D *matchedPartTrackDeltaMom = new TH1D("matchedPartTrackDeltaMom","#Delta P Between Matching Thrown and Reconstructed Charged Particle; #Delta P", 200, -10, 10);
0267   while(tree_reader.Next()) { // Loop over events
0268     for(unsigned int i=0; i<partGenStat.GetSize(); i++){ // Loop over thrown particles
0269         if(partGenStat[i] == 1){ // Select stable thrown particles
0270             int pdg = TMath::Abs(partPdg[i]);
0271             if(pdg == 11 || pdg == 13 || pdg == 211 || pdg == 321 || pdg == 2212){ // Look at charged particles (electrons, muons, pions, kaons, protons)
0272                 TVector3 trueMom(partMomX[i],partMomY[i],partMomZ[i]);
0273 
0274                 float trueEta = trueMom.PseudoRapidity();
0275                 float truePhi = trueMom.Phi();
0276             
0277                 for(unsigned int j=0; j<simuAssoc.GetSize(); j++){ // Loop over associations to find matching ReconstructedChargedParticle
0278                     if(simuAssoc[j] == i){ // Find association index matching the index of the thrown particle we are looking at
0279                         TVector3 recMom(trackMomX[recoAssoc[j]],trackMomY[recoAssoc[j]],trackMomZ[recoAssoc[j]]); // recoAssoc[j] is the index of the matched ReconstructedChargedParticle
0280 
0281                         // Check the distance between the thrown and reconstructed particle
0282                         float deltaEta = trueEta - recMom.PseudoRapidity();
0283                         float deltaPhi = TVector2::Phi_mpi_pi(truePhi - recMom.Phi());
0284                         float deltaR = TMath::Sqrt(deltaEta*deltaEta + deltaPhi*deltaPhi);
0285                         float deltaMom = ((trueMom.Mag()) - (recMom.Mag()));
0286                         double momRes = (recMom.Mag()- trueMom.Mag())/trueMom.Mag();
0287       
0288                         trackMomentumRes->Fill(momRes);
0289 
0290                         matchedPartTrackDeltaEta->Fill(deltaEta);
0291                         matchedPartTrackDeltaPhi->Fill(deltaPhi);
0292                         matchedPartTrackDeltaR->Fill(deltaR);
0293                         matchedPartTrackDeltaMom->Fill(deltaMom);
0294                     }
0295                 } // End loop over associations 
0296             } // End PDG check          
0297         } // End stable particles condition  
0298     } // End loop over thrown particles
0299   } // End loop over events 
0300   ofile->Write(); // Write histograms to file
0301   ofile->Close(); // Close output file
0302 }
0303 ```
0304 <!--
0305 A "solution" version of the script for the exercise is included below -
0306 
0307 ```c++
0308 void ResolutionAnalysis_Exercise(TString infile="PATH_TO_FILE"){
0309   // Set output file for the histograms
0310   TFile *ofile = TFile::Open("ResolutionAnalysis_Exercise_Out.root","RECREATE");
0311 
0312   // Analysis code will go here
0313   // Set up input file chain
0314   TChain *mychain = new TChain("events");
0315   mychain->Add(infile);
0316 
0317   // Initialize reader
0318   TTreeReader tree_reader(mychain);
0319 
0320   // Get Particle Information
0321   TTreeReaderArray<int> partGenStat(tree_reader, "MCParticles.generatorStatus");
0322   TTreeReaderArray<float> partMomX(tree_reader, "MCParticles.momentum.x");
0323   TTreeReaderArray<float> partMomY(tree_reader, "MCParticles.momentum.y");
0324   TTreeReaderArray<float> partMomZ(tree_reader, "MCParticles.momentum.z");
0325   TTreeReaderArray<int> partPdg(tree_reader, "MCParticles.PDG");
0326 
0327   // Get Reconstructed Track Information
0328   TTreeReaderArray<float> trackMomX(tree_reader, "ReconstructedChargedParticles.momentum.x");
0329   TTreeReaderArray<float> trackMomY(tree_reader, "ReconstructedChargedParticles.momentum.y");
0330   TTreeReaderArray<float> trackMomZ(tree_reader, "ReconstructedChargedParticles.momentum.z");
0331 
0332   // Get Associations Between MCParticles and ReconstructedChargedParticles
0333   TTreeReaderArray<unsigned int> recoAssoc(tree_reader, "ReconstructedChargedParticleAssociations.recID");
0334   TTreeReaderArray<unsigned int> simuAssoc(tree_reader, "ReconstructedChargedParticleAssociations.simID");
0335     
0336   // Define Histograms
0337   TH1D *trackMomentumRes = new TH1D("trackMomentumRes","Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0338   TH2D* trackMomResP = new TH2D("trackMomResP", "Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 150);
0339   TH2D* trackMomResEta = new TH2D("trackMomResEta", "Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0340 
0341   TH1D *trackMomentumRes_e = new TH1D("trackMomentumRes_e","e^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0342   TH2D* trackMomResP_e = new TH2D("trackMomResP_e", "e^{#pm} Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 25);
0343   TH2D* trackMomResEta_e = new TH2D("trackMomResEta_e", "e^{#pm} Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0344 
0345   TH1D *trackMomentumRes_mu = new TH1D("trackMomentumRes_mu","#mu^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0346   TH2D* trackMomResP_mu = new TH2D("trackMomResP_mu", "#mu^{#pm} Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 25);
0347   TH2D* trackMomResEta_mu = new TH2D("trackMomResEta_mu", "#mu^{#pm} Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0348 
0349   TH1D *trackMomentumRes_pi = new TH1D("trackMomentumRes_pi","#pi^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0350   TH2D* trackMomResP_pi = new TH2D("trackMomResP_pi", "#pi^{#pm} Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 150);
0351   TH2D* trackMomResEta_pi = new TH2D("trackMomResEta_pi", "#pi^{#pm} Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0352 
0353   TH1D *trackMomentumRes_K = new TH1D("trackMomentumRes_K","K^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0354   TH2D* trackMomResP_K = new TH2D("trackMomResP_K", "K^{#pm} Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 150);
0355   TH2D* trackMomResEta_K = new TH2D("trackMomResEta_K", "K^{#pm} Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0356 
0357   TH1D *trackMomentumRes_p = new TH1D("trackMomentumRes_p","p Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0358   TH2D* trackMomResP_p = new TH2D("trackMomResP_p", "p Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 150);
0359   TH2D* trackMomResEta_p = new TH2D("trackMomResEta_p", "p Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0360   
0361   TH1D *matchedPartTrackDeltaEta = new TH1D("matchedPartTrackDeltaEta","#Delta#eta Between Matching Thrown and Reconstructed Charged Particle; #Delta#eta", 100, -0.25, 0.25);
0362   TH1D *matchedPartTrackDeltaPhi = new TH1D("matchedPartTrackDeltaPhi","#Detla #phi Between Matching Thrown and Reconstructed Charged Particle; #Delta#phi", 200, -0.2, 0.2);
0363   TH1D *matchedPartTrackDeltaR = new TH1D("matchedPartTrackDeltaR","#Delta R Between Matching Thrown and Reconstructed Charged Particle; #Delta R", 300, 0, 0.3);
0364   TH1D *matchedPartTrackDeltaMom = new TH1D("matchedPartTrackDeltaMom","#Delta P Between Matching Thrown and Reconstructed Charged Particle; #Delta P", 200, -10, 10);
0365 
0366   while(tree_reader.Next()) { // Loop over events
0367 
0368     for(unsigned int i=0; i<partGenStat.GetSize(); i++) // Loop over thrown particles
0369       {
0370         if(partGenStat[i] == 1) // Select stable thrown particles
0371           {
0372             int pdg = TMath::Abs(partPdg[i]);
0373 
0374             if(pdg == 11 || pdg == 13 || pdg == 211 || pdg == 321 || pdg == 2212) // Look at charged particles (electrons, muons, pions, kaons, protons)
0375               {
0376                 TVector3 trueMom(partMomX[i],partMomY[i],partMomZ[i]);
0377 
0378                 float trueEta = trueMom.PseudoRapidity();
0379                 float truePhi = trueMom.Phi();
0380 
0381                 // Loop over associations to find matching ReconstructedChargedParticle
0382                 for(unsigned int j=0; j<simuAssoc.GetSize(); j++)
0383                   {
0384                     if(simuAssoc[j] == i) // Find association index matching the index of the thrown particle we are looking at
0385                       {
0386                         TVector3 recMom(trackMomX[recoAssoc[j]],trackMomY[recoAssoc[j]],trackMomZ[recoAssoc[j]]); // recoAssoc[j] is the index of the matched ReconstructedChargedParticle
0387 
0388                         // Check the distance between the thrown and reconstructed particle
0389                         float deltaEta = trueEta - recMom.PseudoRapidity();
0390                         float deltaPhi = TVector2::Phi_mpi_pi(truePhi - recMom.Phi());
0391                         float deltaR = TMath::Sqrt(deltaEta*deltaEta + deltaPhi*deltaPhi);
0392                         float deltaMom = ((trueMom.Mag()) - (recMom.Mag()));
0393 
0394                         double momRes = (recMom.Mag() - trueMom.Mag())/trueMom.Mag();
0395         
0396                         trackMomentumRes->Fill(momRes); // Could also multiply by 100 and express as a percentage instead
0397                         trackMomResP->Fill(momRes, trueMom.Mag());
0398                         trackMomResEta->Fill(momRes, trueEta);
0399 
0400                         if( pdg == 11){
0401                           trackMomentumRes_e->Fill(momRes);
0402                           trackMomResP_e->Fill(momRes, trueMom.Mag());
0403                           trackMomResEta_e->Fill(momRes, trueEta);
0404                         }
0405                         else if( pdg == 13){
0406                           trackMomentumRes_mu->Fill(momRes);
0407                           trackMomResP_mu->Fill(momRes, trueMom.Mag());
0408                           trackMomResEta_mu->Fill(momRes, trueEta);
0409                         }
0410                         else if( pdg == 211){
0411                           trackMomentumRes_pi->Fill(momRes);
0412                           trackMomResP_pi->Fill(momRes, trueMom.Mag());
0413                           trackMomResEta_pi->Fill(momRes, trueEta);
0414                         }
0415                         else if( pdg == 321){
0416                           trackMomentumRes_K->Fill(momRes);
0417                           trackMomResP_K->Fill(momRes, trueMom.Mag());
0418                           trackMomResEta_K->Fill(momRes, trueEta);
0419                         }
0420                         else if( pdg == 2212){
0421                           trackMomentumRes_p->Fill(momRes);
0422                           trackMomResP_p->Fill(momRes, trueMom.Mag());
0423                           trackMomResEta_p->Fill(momRes, trueEta);
0424                         }
0425                           
0426                         matchedPartTrackDeltaEta->Fill(deltaEta);
0427                         matchedPartTrackDeltaPhi->Fill(deltaPhi);
0428                         matchedPartTrackDeltaR->Fill(deltaR);
0429                         matchedPartTrackDeltaMom->Fill(deltaMom);
0430                         
0431                       }
0432                   }// End loop over associations
0433               } // End PDG check
0434           } // End stable particles condition
0435       } // End loop over thrown particles
0436   } // End loop over events
0437 
0438   ofile->Write(); // Write histograms to file
0439   ofile->Close(); // Close output file
0440 }
0441 ```
0442 
0443 Insert your input file path and execute as the example code above.
0444 -->
0445 
0446 ### Compiled ROOT Scripts 
0447 
0448 As brought up in the tutorial, you may wish to compile your ROOT based scripts for faster processing. Included below are some scripts and a short example of a compiled ROOT macro provided by Kolja Kauder.
0449 
0450 Each file is uploaded invidually, but your directory should be structured as follows -
0451 
0452 - helloroot
0453     - README.md
0454     - CMakeLists.txt
0455     - include
0456       - helloroot
0457         - helloroot.hh
0458     - src
0459         - helloexec.cxx
0460         - helloroot.cxx
0461      
0462 Note that any entry in the above without a file extension is a directory.
0463 
0464 The contents of README.md are -
0465 
0466 ```
0467 To build using cmake, create a build directory, navigate to it and run cmake. e.g.:
0468 
0469 \```
0470 mkdir build
0471 cd build
0472 cmake .. 
0473 make 
0474 \```
0475 You can specify a number of parallel build threads with the -j flag, e.g.
0476 \```
0477 make -j4
0478 \```
0479 
0480 You can specify an install directory to cmake with
0481 -DCMAKE_INSTALL_PREFIX=<path>
0482 then, after building, 
0483 \```
0484 make install
0485 \```
0486 to install the headers and libraries under that location.
0487 There is no "make uninstall" but (on Unix-like systems)
0488 you can do
0489 xargs rm < install_manifest.txt
0490 from the cmake build directory.
0491 ```
0492 
0493 Note that you should delete the \ characters in this block.
0494 
0495 The contents of CMakeLists.txt are -
0496 
0497 ```c++
0498 # CMakeLists.txt for helloroot.
0499 # More complicated than needed but demonstrates making and linking your own libraries
0500 # cf. https://cliutils.gitlab.io/modern-cmake/chapters/packages/ROOT.html
0501 # https://root.cern/manual/integrate_root_into_my_cmake_project/
0502 
0503 cmake_minimum_required(VERSION 3.10)
0504 project(helloroot VERSION 1.0 LANGUAGES CXX ) # not needed
0505 
0506  # Find ROOT. Use at least 6.20 for smoother cmake support
0507  find_package(ROOT 6.20 REQUIRED )
0508 
0509  message ( " ROOT Libraries = " ${ROOT_LIBRARIES} )
0510 
0511  ##############################################################################################################
0512 
0513 # Main target is the libhelloroot library
0514 add_library(
0515   # You can use wildcards but it's cleaner to list the files explicitly
0516    helloroot
0517    SHARED
0518    src/helloroot.cxx
0519    )
0520 ## The particular syntax here is a bit annoying because you have to list all the sub-modules you need
0521 ## but it picks up automatically all the compile options needed for root, e.g. the c++ std version
0522 ## Find all available ROOT modules with `root-config --libs`
0523 target_link_libraries(helloroot PUBLIC ROOT::Core ROOT::RIO ROOT::Rint ROOT::Tree ROOT::EG ROOT::Physics )
0524 
0525 ## The above _should_ be true, and it is on most systems. If it's not, uncoment one of the following lines
0526 # target_compile_features(helloroot PUBLIC cxx_std_17)
0527 # target_compile_features(helloroot PUBLIC cxx_std_20)
0528 
0529 # include directories - this is also overkill but useful if you want to create dictionaries
0530 # Contact kkauder@gmail.com for that - it's too much for this example
0531 target_include_directories(helloroot 
0532 PUBLIC 
0533 $<INSTALL_INTERFACE:include>
0534 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
0535  )
0536 
0537 # Can add addtional options here
0538 target_compile_options(helloroot PRIVATE -Wall -Wextra -pedantic -g)  
0539 
0540 ##############################################################################################################
0541 
0542 ## Build executables
0543 add_executable(helloexec src/helloexec.cxx)
0544 # target_compile_options(helloexec PRIVATE -Wall -Wextra -pedantic -g)
0545 target_link_libraries(helloexec helloroot )
0546 target_include_directories(helloexec
0547   PRIVATE
0548   ${ROOT_INCLUDE_DIRS}
0549   )
0550 
0551 install(TARGETS helloexec DESTINATION bin)
0552 
0553 
0554 ##############################################################################################################
0555 
0556 ## Install library
0557 # Could also use include(GNUInstallDirs)
0558 # and then destinations of the form ${CMAKE_INSTALL_INCLUDEDIR}
0559 install(TARGETS helloroot
0560   EXPORT helloroot-export
0561   LIBRARY DESTINATION lib
0562   ARCHIVE DESTINATION lib
0563   )
0564 
0565 ## Install headers
0566 install (DIRECTORY ${CMAKE_SOURCE_DIR}/include/helloroot
0567   DESTINATION  include/helloroot
0568   )
0569 
0570 ## Generate configuration file - this allows you to use cmake in another project 
0571 ## to find and link the installed helloroot library
0572 install(EXPORT helloroot-export
0573   FILE
0574   hellorootConfig.cmake
0575   NAMESPACE
0576     helloroot::
0577   DESTINATION
0578   cmake
0579   )
0580 
0581 ## Final message
0582 message( " Done!")
0583 ```
0584 
0585 The contents of helloroot.hh are -
0586 
0587 ```c++
0588 #ifndef HELLO_ROOT_H
0589 #define HELLO_ROOT_H
0590 
0591 void HelloRoot();
0592 
0593 #endif // HELLO_ROOT_H
0594 ```
0595 
0596 The contents of helloexec.cxx are -
0597 
0598 ```c++
0599 #include<helloroot/helloroot.hh>
0600 
0601 #include<iostream>
0602 #include<string>
0603 
0604 int main()
0605 {
0606     std::cout << "Hello from main " << std::endl;
0607     HelloRoot(); 
0608     
0609     return 0;
0610 }
0611 ```
0612 
0613 And finally, the contents of helloroot.cxx are -
0614 
0615 ```c++
0616 #include<helloroot/helloroot.hh>
0617 
0618 #include<iostream>
0619 #include<string>
0620 
0621 #include<TH1D.h>
0622 #include<TPad.h>
0623 
0624 void HelloRoot()
0625 {
0626   std::cout << "Hello from HelloRoot" << std::endl;
0627 
0628   // do something with root
0629   TH1D h("h", "h", 100, -5, 5);
0630   h.FillRandom("gaus", 1000);
0631   h.Draw();
0632   gPad->SaveAs("hello.png");
0633 
0634   return;
0635 }
0636 ```
0637 Please consult the README and script comments for further instructions.
0638 
0639 ## Python Uproot Scripts
0640 
0641 ### EfficiencyAnalysis.py
0642 
0643 Create a file called `EfficiencyAnalysis.py` and copy in the code below to get started on the resolution analysis exercise. Note that you will need to correctly specifiy your input file path in the variable `infile`.
0644 
0645 ```python
0646 #! /usr/bin/python
0647 
0648 #Import relevant packages
0649 import ROOT, math, array
0650 from ROOT import TH1F, TH2F, TMath, TTree, TVector3, TVector2
0651 import uproot as up
0652 
0653 #Define and open files
0654 infile="PATH_TO_INPUT_FILE"
0655 ofile=ROOT.TFile.Open("EfficiencyAnalysis_OutPy.root", "RECREATE")
0656 
0657 # Open input file and define branches we want to look at with uproot
0658 events_tree = up.open(infile)["events"]
0659 
0660 # Get particle information
0661 partGenStat = events_tree["MCParticles.generatorStatus"].array()
0662 partMomX = events_tree["MCParticles.momentum.x"].array()
0663 partMomY = events_tree["MCParticles.momentum.y"].array()
0664 partMomZ = events_tree["MCParticles.momentum.z"].array()
0665 partPdg = events_tree["MCParticles.PDG"].array()
0666 
0667 # Get reconstructed track information
0668 trackMomX = events_tree["ReconstructedChargedParticles.momentum.x"].array()
0669 trackMomY = events_tree["ReconstructedChargedParticles.momentum.y"].array()
0670 trackMomZ = events_tree["ReconstructedChargedParticles.momentum.z"].array()
0671 
0672 # Get assocations between MCParticles and ReconstructedChargedParticles
0673 recoAssoc = events_tree["ReconstructedChargedParticleAssociations.recID"].array()
0674 simuAssoc = events_tree["ReconstructedChargedParticleAssociations.simID"].array()
0675 
0676 # Define histograms below
0677 partEta = ROOT.TH1D("partEta","Eta of Thrown Charged Particles;Eta",100, -5 ,5 )
0678 matchedPartEta = ROOT.TH1D("matchedPartEta","Eta of Thrown Charged Particles That Have Matching Track", 100, -5 ,5);
0679 matchedPartTrackDeltaR = ROOT.TH1D("matchedPartTrackDeltaR","Delta R Between Matching Thrown and Reconstructed Charge Particle", 5000, 0, 5);
0680 
0681 # Add main analysis loop(s) below
0682 for i in range(0, len(events_tree)): # Loop over all events
0683     for j in range(0, len(partGenStat[i])): # Loop over all thrown particles
0684         if partGenStat[i][j] == 1: # Select stable particles
0685             pdg = abs(partPdg[i][j]) # Get PDG for each stable particle
0686             if(pdg == 11 or pdg == 13 or pdg == 211 or pdg == 321 or pdg == 2212):
0687                 trueMom = ROOT.TVector3(partMomX[i][j], partMomY[i][j], partMomZ[i][j])
0688                 trueEta = trueMom.PseudoRapidity()
0689                 truePhi = trueMom.Phi()
0690                 
0691                 partEta.Fill(trueEta)
0692                 for k in range(0,len(simuAssoc[i])): # Loop over associations to find matching ReconstructedChargedParticle
0693                     if (simuAssoc[i][k] == j):
0694                         recMom = ROOT.TVector3(trackMomX[i][recoAssoc[i][k]], trackMomY[i][recoAssoc[i][k]], trackMomZ[i][recoAssoc[i][k]])
0695                         deltaEta = trueEta - recMom.PseudoRapidity()
0696                         deltaPhi = TVector2. Phi_mpi_pi(truePhi - recMom.Phi())
0697                         deltaR = math.sqrt((deltaEta*deltaEta) + (deltaPhi*deltaPhi))
0698 
0699                         matchedPartEta.Fill(trueEta)
0700                         matchedPartTrackDeltaR.Fill(deltaR)
0701                         
0702 # Write output histograms to file below
0703 partEta.Write()
0704 matchedPartEta.Write()
0705 matchedPartTrackDeltaR.Write()
0706 
0707 # Close files
0708 ofile.Close()
0709 ```
0710 <!--
0711 
0712 A "solution" version of the script for the exercise is included below -
0713 
0714 ```python
0715 #! /usr/bin/python
0716 
0717 #Import relevant packages
0718 import ROOT, math, array
0719 from ROOT import TCanvas, TColor, TGaxis, TH1F, TH2F, TPad, TStyle, gStyle, gPad, TGaxis, TLine, TMath, TPaveText, TTree, TVector3, TVector2
0720 import uproot as up
0721 
0722 #Define and open files
0723 infile="PATH_TO_FILE"
0724 ofile=ROOT.TFile.Open("EfficiencyAnalysis_Exercise_OutPy.root", "RECREATE")
0725 
0726 # Open input file and define branches we want to look at with uproot
0727 events_tree = up.open(infile)["events"]
0728 
0729 # Get particle information
0730 partGenStat = events_tree["MCParticles.generatorStatus"].array()
0731 partMomX = events_tree["MCParticles.momentum.x"].array()
0732 partMomY = events_tree["MCParticles.momentum.y"].array()
0733 partMomZ = events_tree["MCParticles.momentum.z"].array()
0734 partPdg = events_tree["MCParticles.PDG"].array()
0735 
0736 # Get reconstructed track information
0737 trackMomX = events_tree["ReconstructedChargedParticles.momentum.x"].array()
0738 trackMomY = events_tree["ReconstructedChargedParticles.momentum.y"].array()
0739 trackMomZ = events_tree["ReconstructedChargedParticles.momentum.z"].array()
0740 
0741 # Get assocations between MCParticles and ReconstructedChargedParticles
0742 recoAssoc = events_tree["ReconstructedChargedParticleAssociations.recID"].array()
0743 simuAssoc = events_tree["ReconstructedChargedParticleAssociations.simID"].array()
0744 
0745 # Define histograms below
0746 partEta = ROOT.TH1D("partEta","#eta of Thrown Charged Particles; #eta", 120, -6, 6)
0747 matchedPartEta = ROOT.TH1D("matchedPartEta","#eta of Thrown Charged Particles That Have Matching Track; #eta", 120, -6, 6)
0748 partMom = ROOT.TH1D("partMom", "Momentum of Thrown Charged Particles (truth); P(GeV/c)", 150, 0, 150)
0749 matchedPartMom = ROOT.TH1D("matchedPartMom", "Momentum of Thrown Charged Particles (truth), with matching track; P(GeV/c)", 150, 0, 150)
0750 partPhi = ROOT.TH1D("partPhi", "#phi of Thrown Charged Particles (truth); #phi(rad)", 320, -3.2, 3.2)
0751 matchedPartPhi = ROOT.TH1D("matchedPartPhi", "#phi of Thrown Charged Particles (truth), with matching track; #phi(rad)", 320, -3.2, 3.2)
0752 
0753 partPEta = ROOT.TH2D("partPEta", "P vs #eta of Thrown Charged Particles; P(GeV/c); #eta", 150, 0, 150, 120, -6, 6)
0754 matchedPartPEta = ROOT.TH2D("matchedPartPEta", "P vs #eta of Thrown Charged Particles, with matching track; P(GeV/C); #eta", 150, 0, 150, 120, -6, 6)
0755 partPhiEta = ROOT.TH2D("partPhiEta", "#phi vs #eta of Thrown Charged Particles; #phi(rad); #eta", 160, -3.2, 3.2, 120, -6, 6)
0756 matchedPartPhiEta = ROOT.TH2D("matchedPartPhiEta", "#phi vs #eta of Thrown Charged Particles; #phi(rad); #eta", 160, -3.2, 3.2, 120, -6, 6)
0757 
0758 matchedPartTrackDeltaEta = ROOT.TH1D("matchedPartTrackDeltaEta","#Delta#eta Between Matching Thrown and Reconstructe Charged Particle; #Delta#eta", 100, -0.25, 0.25)
0759 matchedPartTrackDeltaPhi = ROOT.TH1D("matchedPartTrackDeltaPhi","#Detla #phi Between Matching Thrown and Reconstructed Charged Particle; #Delta#phi", 200, -0.2, 0.2)
0760 matchedPartTrackDeltaR = ROOT.TH1D("matchedPartTrackDeltaR","#Delta R Between Matching Thrown and Reconstructed Charged Particle; #Delta R", 300, 0, 0.3)
0761 matchedPartTrackDeltaMom = ROOT.TH1D("matchedPartTrackDeltaMom","#Delta P Between Matching Thrown and Reconstructed Charged Particle; #Delta P", 200, -10, 10)
0762 
0763 # Define some histograms for our efficiencies
0764 TrackEff_Eta = ROOT.TH1D("TrackEff_Eta", "Tracking efficiency as fn of #eta; #eta; Eff(%)", 120, -6, 6)
0765 TrackEffMom = ROOT.TH1D("TrackEff_Mom", "Tracking efficiency as fn of P; P(GeV/c); Eff(%)", 150, 0, 150)
0766 TrackEffPhi = ROOT.TH1D("TrackEff_Phi", "Tracking efficiency as fn of #phi; #phi(rad); Eff(%)", 320, -3.2, 3.2)
0767 
0768 # 2D Efficiencies
0769 TrackEff_PEta = ROOT.TH2D("TrackEff_PEta", "Tracking efficiency as fn of P and #eta; P(GeV/c); #eta", 150, 0, 150, 120, -6, 6)
0770 TrackEff_PhiEta = ROOT.TH2D("TrackEff_PhiEta", "Tracking efficiency as fn of #phi and #eta; #phi(rad); #eta", 160, -3.2, 3.2, 120, -6, 6)
0771 
0772 # All charged particle histos
0773 ChargedEta = ROOT.TH1D("ChargedEta", "#eta of all charged particles; #eta", 120, -6, 6)
0774 ChargedPhi = ROOT.TH1D("ChargedPhi", "#phi of all charged particles; #phi (rad)", 120, -3.2, 3.2)
0775 ChargedP = ROOT.TH1D("ChargedP", "P of all charged particles; P(GeV/c)", 150, 0, 150)
0776 
0777 # Add main analysis loop(s) below
0778 for i in range(0, len(events_tree)): # Loop over all events
0779     for j in range(0, len(partGenStat[i])): # Loop over all thrown particles
0780         if partGenStat[i][j] == 1: # Select stable particles
0781             pdg = abs(partPdg[i][j]) # Get PDG for each stable particle
0782             if(pdg == 11 or pdg == 13 or pdg == 211 or pdg == 321 or pdg == 2212):
0783                 trueMom = ROOT.TVector3(partMomX[i][j], partMomY[i][j], partMomZ[i][j])
0784                 trueEta = trueMom.PseudoRapidity()
0785                 truePhi = trueMom.Phi()
0786                 
0787                 partEta.Fill(trueEta)
0788                 partPhi.Fill(truePhi)
0789                 partMom.Fill(trueMom.Mag())
0790                 partPEta.Fill(trueMom.Mag(), trueEta)
0791                 partPhiEta.Fill(truePhi, trueEta)
0792                 for k in range(0,len(simuAssoc[i])): # Loop over associations to find matching ReconstructedChargedParticle
0793                     if (simuAssoc[i][k] == j):
0794                         recMom = ROOT.TVector3(trackMomX[i][recoAssoc[i][k]], trackMomY[i][recoAssoc[i][k]], trackMomZ[i][recoAssoc[i][k]])
0795                         deltaEta = trueEta - recMom.PseudoRapidity()
0796                         deltaPhi = TVector2. Phi_mpi_pi(truePhi - recMom.Phi())
0797                         deltaR = math.sqrt((deltaEta*deltaEta) + (deltaPhi*deltaPhi))
0798                         deltaMom = ((trueMom.Mag()) - (recMom.Mag()))
0799 
0800                         matchedPartTrackDeltaEta.Fill(deltaEta)
0801                         matchedPartTrackDeltaPhi.Fill(deltaPhi)
0802                         matchedPartTrackDeltaR.Fill(deltaR)
0803                         matchedPartTrackDeltaMom.Fill(deltaMom)
0804                         
0805                         matchedPartEta.Fill(trueEta)
0806                         matchedPartPhi.Fill(truePhi)
0807                         matchedPartMom.Fill(trueMom.Mag())
0808                         matchedPartPEta.Fill(trueMom.Mag(), trueEta)
0809                         matchedPartPhiEta.Fill(truePhi, trueEta)
0810     for x in range (0, len(trackMomX[i])): # Loop over all charged particles, thrown or not
0811         CPartMom = ROOT.TVector3(trackMomX[i][x], trackMomY[i][x], trackMomZ[i][x])
0812         CPartEta = CPartMom.PseudoRapidity()
0813         CPartPhi = CPartMom.Phi()
0814 
0815         ChargedEta.Fill(CPartEta)
0816         ChargedPhi.Fill(CPartPhi)
0817         ChargedP.Fill(CPartMom.Mag())
0818         
0819 # Write output histograms to file below
0820 partEta.Write()
0821 matchedPartEta.Write()
0822 partMom.Write()
0823 matchedPartMom.Write()
0824 partPhi.Write()
0825 matchedPartPhi.Write()
0826 partPEta.Write()
0827 matchedPartPEta.Write()
0828 partPhiEta.Write()
0829 matchedPartPhiEta.Write()
0830 matchedPartTrackDeltaEta.Write()
0831 matchedPartTrackDeltaPhi.Write()
0832 matchedPartTrackDeltaR.Write()
0833 matchedPartTrackDeltaMom.Write()
0834 ChargedEta.Write()
0835 ChargedPhi.Write()
0836 ChargedP.Write()
0837 
0838 TrackEff_Eta.Divide(matchedPartEta, partEta, 1, 1, "b")
0839 TrackEffMom.Divide(matchedPartMom, partMom, 1, 1, "b")
0840 TrackEffPhi.Divide(matchedPartPhi, partPhi, 1, 1, "b")
0841 TrackEff_PEta.Divide(matchedPartPEta, partPEta, 1, 1, "b")
0842 TrackEff_PhiEta.Divide(matchedPartPhiEta, partPhiEta, 1, 1, "b")
0843 
0844 TrackEff_Eta.Write()
0845 TrackEffMom.Write()
0846 TrackEffPhi.Write()
0847 TrackEff_PEta.Write()
0848 TrackEff_PhiEta.Write()
0849 
0850 # Close files
0851 ofile.Close()
0852 ```
0853 Insert your input file path and execute as the example code above.
0854 -->
0855 ### ResolutionAnalysis.py
0856 
0857 Create a file called `ResolutionAnalysis.py` and copy in the code below to get started on the resolution analysis exercise. Note that you will need to correctly specifiy your input file path in the variable `infile`.
0858 
0859 ```python
0860 #! /usr/bin/python
0861 
0862 #Import relevant packages
0863 import ROOT, math, array
0864 from ROOT import TH1F, TH2F, TMath, TTree, TVector3, TVector2
0865 import uproot as up
0866 
0867 #Define and open files
0868 infile="PATH_TO_INPUT_FILE"
0869 ofile=ROOT.TFile.Open("ResolutionAnalysis_OutPy.root", "RECREATE")
0870 
0871 # Open input file and define branches we want to look at with uproot
0872 events_tree = up.open(infile)["events"]
0873 
0874 # Get particle information
0875 partGenStat = events_tree["MCParticles.generatorStatus"].array()
0876 partMomX = events_tree["MCParticles.momentum.x"].array()
0877 partMomY = events_tree["MCParticles.momentum.y"].array()
0878 partMomZ = events_tree["MCParticles.momentum.z"].array()
0879 partPdg = events_tree["MCParticles.PDG"].array()
0880 
0881 # Get reconstructed track information
0882 trackMomX = events_tree["ReconstructedChargedParticles.momentum.x"].array()
0883 trackMomY = events_tree["ReconstructedChargedParticles.momentum.y"].array()
0884 trackMomZ = events_tree["ReconstructedChargedParticles.momentum.z"].array()
0885 
0886 # Get assocations between MCParticles and ReconstructedChargedParticles
0887 recoAssoc = events_tree["ReconstructedChargedParticleAssociations.recID"].array()
0888 simuAssoc = events_tree["ReconstructedChargedParticleAssociations.simID"].array()
0889 
0890 # Define histograms below
0891 trackMomentumRes = ROOT.TH1D("trackMomentumRes","Track Momentum Resolution", 400, -2, 2)
0892 
0893 matchedPartTrackDeltaEta = ROOT.TH1D("matchedPartTrackDeltaEta","#Delta#eta Between Matching Thrown and Reconstructe Charged Particle; #Delta#eta", 100, -0.25, 0.25)
0894 matchedPartTrackDeltaPhi = ROOT.TH1D("matchedPartTrackDeltaPhi","#Detla #phi Between Matching Thrown and Reconstructed Charged Particle; #Delta#phi", 200, -0.2, 0.2)
0895 matchedPartTrackDeltaR = ROOT.TH1D("matchedPartTrackDeltaR","#Delta R Between Matching Thrown and Reconstructed Charged Particle; #Delta R", 300, 0, 0.3)
0896 matchedPartTrackDeltaMom = ROOT.TH1D("matchedPartTrackDeltaMom","#Delta P Between Matching Thrown and Reconstructed Charged Particle; #Delta P", 200, -10, 10)
0897 
0898 # Add main analysis loop(s) below
0899 for i in range(0, len(events_tree)): # Loop over all events
0900     for j in range(0, len(partGenStat[i])): # Loop over all thrown particles
0901         if partGenStat[i][j] == 1: # Select stable particles
0902             pdg = abs(partPdg[i][j]) # Get PDG for each stable particle
0903             if(pdg == 11 or pdg == 13 or pdg == 211 or pdg == 321 or pdg == 2212):
0904                 trueMom = ROOT.TVector3(partMomX[i][j], partMomY[i][j], partMomZ[i][j])
0905                 trueEta = trueMom.PseudoRapidity()
0906                 truePhi = trueMom.Phi()
0907                 
0908                 for k in range(0,len(simuAssoc[i])): # Loop over associations to find matching ReconstructedChargedParticle
0909                     if (simuAssoc[i][k] == j):
0910                         recMom = ROOT.TVector3(trackMomX[i][recoAssoc[i][k]], trackMomY[i][recoAssoc[i][k]], trackMomZ[i][recoAssoc[i][k]])
0911                         deltaEta = trueEta - recMom.PseudoRapidity()
0912                         deltaPhi = TVector2. Phi_mpi_pi(truePhi - recMom.Phi())
0913                         deltaR = math.sqrt((deltaEta*deltaEta) + (deltaPhi*deltaPhi))
0914                         deltaMom = ((trueMom.Mag()) - (recMom.Mag()))
0915 
0916                         momRes = (recMom.Mag() - trueMom.Mag())/trueMom.Mag()
0917                         
0918                         matchedPartTrackDeltaEta.Fill(deltaEta)
0919                         matchedPartTrackDeltaPhi.Fill(deltaPhi)
0920                         matchedPartTrackDeltaR.Fill(deltaR)
0921                         matchedPartTrackDeltaMom.Fill(deltaMom)                        
0922 
0923                         trackMomentumRes.Fill(momRes)
0924                         
0925 # Write output histograms to file below
0926 trackMomentumRes.Write()
0927 matchedPartTrackDeltaEta.Write()
0928 matchedPartTrackDeltaPhi.Write()
0929 matchedPartTrackDeltaR.Write()
0930 matchedPartTrackDeltaMom.Write()
0931 
0932 # Close files
0933 ofile.Close()
0934 ```
0935 <!--
0936 
0937 A "solution" version of the script for the exercise is included below -
0938 
0939 ```python
0940 #! /usr/bin/python
0941 
0942 #Import relevant packages
0943 import ROOT, math, array
0944 from ROOT import TCanvas, TColor, TGaxis, TH1F, TH2F, TPad, TStyle, gStyle, gPad, TGaxis, TLine, TMath, TPaveText, TTree, TVector3, TVector2
0945 import uproot as up
0946 
0947 #Define and open files
0948 infile="PATH_TO_FILE"
0949 ofile=ROOT.TFile.Open("ResolutionAnalysis_Exercise_OutPy.root", "RECREATE")
0950 
0951 # Open input file and define branches we want to look at with uproot
0952 events_tree = up.open(infile)["events"]
0953 
0954 # Get particle information
0955 partGenStat = events_tree["MCParticles.generatorStatus"].array()
0956 partMomX = events_tree["MCParticles.momentum.x"].array()
0957 partMomY = events_tree["MCParticles.momentum.y"].array()
0958 partMomZ = events_tree["MCParticles.momentum.z"].array()
0959 partPdg = events_tree["MCParticles.PDG"].array()
0960 
0961 # Get reconstructed track information
0962 trackMomX = events_tree["ReconstructedChargedParticles.momentum.x"].array()
0963 trackMomY = events_tree["ReconstructedChargedParticles.momentum.y"].array()
0964 trackMomZ = events_tree["ReconstructedChargedParticles.momentum.z"].array()
0965 
0966 # Get assocations between MCParticles and ReconstructedChargedParticles
0967 recoAssoc = events_tree["ReconstructedChargedParticleAssociations.recID"].array()
0968 simuAssoc = events_tree.["ReconstructedChargedParticleAssociations.simID"].array()
0969 
0970 # Define histograms below
0971 trackMomentumRes = ROOT.TH1D("trackMomentumRes","Track Momentum Resolution", 400, -2, 2)
0972 trackMomResP = ROOT.TH2D("trackMomResP", "Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 150);
0973 trackMomResEta = ROOT.TH2D("trackMomResEta", "Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0974 
0975 trackMomentumRes_e = ROOT.TH1D("trackMomentumRes_e","e^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0976 trackMomResP_e = ROOT.TH2D("trackMomResP_e", "e^{#pm} Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 25);
0977 trackMomResEta_e = ROOT.TH2D("trackMomResEta_e", "e^{#pm} Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0978 
0979 trackMomentumRes_mu = ROOT.TH1D("trackMomentumRes_mu","#mu^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0980 trackMomResP_mu = ROOT.TH2D("trackMomResP_mu", "#mu^{#pm} Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 25);
0981 trackMomResEta_mu = ROOT.TH2D("trackMomResEta_mu", "#mu^{#pm} Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0982 
0983 trackMomentumRes_pi = ROOT.TH1D("trackMomentumRes_pi","#pi^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0984 trackMomResP_pi = ROOT.TH2D("trackMomResP_pi", "#pi^{#pm} Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 150);
0985 trackMomResEta_pi = ROOT.TH2D("trackMomResEta_pi", "#pi^{#pm} Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0986 
0987 trackMomentumRes_K = ROOT.TH1D("trackMomentumRes_K","K^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0988 trackMomResP_K = ROOT.TH2D("trackMomResP_K", "K^{#pm} Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 150);
0989 trackMomResEta_K = ROOT.TH2D("trackMomResEta_K", "K^{#pm} Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0990 
0991 trackMomentumRes_p = ROOT.TH1D("trackMomentumRes_p","p Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0992 trackMomResP_p = ROOT.TH2D("trackMomResP_p", "p Track Momentum Resolution vs P; (P_{rec} - P_{MC})/P_{MC}; P_{MC}(GeV/c)", 400, -2, 2, 150, 0, 150);
0993 trackMomResEta_p = ROOT.TH2D("trackMomResEta_p", "p Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0994 
0995 matchedPartTrackDeltaEta = ROOT.TH1D("matchedPartTrackDeltaEta","#Delta#eta Between Matching Thrown and Reconstructe Charged Particle; #Delta#eta", 100, -0.25, 0.25)
0996 matchedPartTrackDeltaPhi = ROOT.TH1D("matchedPartTrackDeltaPhi","#Detla #phi Between Matching Thrown and Reconstructed Charged Particle; #Delta#phi", 200, -0.2, 0.2)
0997 matchedPartTrackDeltaR = ROOT.TH1D("matchedPartTrackDeltaR","#Delta R Between Matching Thrown and Reconstructed Charged Particle; #Delta R", 300, 0, 0.3)
0998 matchedPartTrackDeltaMom = ROOT.TH1D("matchedPartTrackDeltaMom","#Delta P Between Matching Thrown and Reconstructed Charged Particle; #Delta P", 200, -10, 10)
0999 
1000 # Add main analysis loop(s) below
1001 for i in range(0, len(events_tree)): # Loop over all events
1002     for j in range(0, len(partGenStat[i])): # Loop over all thrown particles
1003         if partGenStat[i][j] == 1: # Select stable particles
1004             pdg = abs(partPdg[i][j]) # Get PDG for each stable particle
1005             if(pdg == 11 or pdg == 13 or pdg == 211 or pdg == 321 or pdg == 2212):
1006                 trueMom = ROOT.TVector3(partMomX[i][j], partMomY[i][j], partMomZ[i][j])
1007                 trueEta = trueMom.PseudoRapidity()
1008                 truePhi = trueMom.Phi()
1009                 
1010                 for k in range(0,len(simuAssoc[i])): # Loop over associations to find matching ReconstructedChargedParticle
1011                     if (simuAssoc[i][k] == j):
1012                         recMom = ROOT.TVector3(trackMomX[i][recoAssoc[i][k]], trackMomY[i][recoAssoc[i][k]], trackMomZ[i][recoAssoc[i][k]])
1013                         deltaEta = trueEta - recMom.PseudoRapidity()
1014                         deltaPhi = TVector2. Phi_mpi_pi(truePhi - recMom.Phi())
1015                         deltaR = math.sqrt((deltaEta*deltaEta) + (deltaPhi*deltaPhi))
1016                         deltaMom = ((trueMom.Mag()) - (recMom.Mag()))
1017 
1018                         momRes = (recMom.Mag() - trueMom.Mag())/trueMom.Mag()
1019 
1020                         trackMomentumRes.Fill(momRes)
1021                         trackMomResP.Fill(momRes, trueMom.Mag())
1022                         trackMomResEta.Fill(momRes, trueEta)
1023                         
1024                         if( pdg == 11):
1025                             trackMomentumRes_e.Fill(momRes)
1026                             trackMomResP_e.Fill(momRes, trueMom.Mag())
1027                             trackMomResEta_e.Fill(momRes, trueEta)
1028                         elif( pdg == 13):
1029                             trackMomentumRes_mu.Fill(momRes)
1030                             trackMomResP_mu.Fill(momRes, trueMom.Mag())
1031                             trackMomResEta_mu.Fill(momRes, trueEta)
1032                         elif( pdg == 211):
1033                             trackMomentumRes_pi.Fill(momRes)
1034                             trackMomResP_pi.Fill(momRes, trueMom.Mag())
1035                             trackMomResEta_pi.Fill(momRes, trueEta)
1036                         elif( pdg == 321):
1037                             trackMomentumRes_K.Fill(momRes)
1038                             trackMomResP_K.Fill(momRes, trueMom.Mag())
1039                             trackMomResEta_K.Fill(momRes, trueEta)
1040                         elif( pdg == 2212):
1041                             trackMomentumRes_p.Fill(momRes)
1042                             trackMomResP_p.Fill(momRes, trueMom.Mag())
1043                             trackMomResEta_p.Fill(momRes, trueEta)
1044                             
1045                         matchedPartTrackDeltaEta.Fill(deltaEta)
1046                         matchedPartTrackDeltaPhi.Fill(deltaPhi)
1047                         matchedPartTrackDeltaR.Fill(deltaR)
1048                         matchedPartTrackDeltaMom.Fill(deltaMom)
1049                         
1050 # Write output histograms to file below
1051 trackMomentumRes.Write()
1052 trackMomResP.Write()
1053 trackMomResEta.Write()
1054 trackMomentumRes_e.Write()
1055 trackMomResP_e.Write()
1056 trackMomResEta_e.Write()
1057 trackMomentumRes_mu.Write()
1058 trackMomResP_mu.Write()
1059 trackMomResEta_mu.Write()
1060 trackMomentumRes_pi.Write()
1061 trackMomResP_pi.Write()
1062 trackMomResEta_pi.Write()
1063 trackMomentumRes_K.Write()
1064 trackMomResP_K.Write()
1065 trackMomResEta_K.Write()
1066 trackMomentumRes_p.Write()
1067 trackMomResP_p.Write()
1068 trackMomResEta_p.Write()
1069 
1070 matchedPartTrackDeltaEta.Write()
1071 matchedPartTrackDeltaPhi.Write()
1072 matchedPartTrackDeltaR.Write()
1073 matchedPartTrackDeltaMom.Write()
1074 
1075 # Close files
1076 ofile.Close()
1077 ```
1078 Insert your input file path and execute as the example code above.
1079 -->
1080 ## RDataFrames Example
1081 
1082 Note that only the initial stage of the efficiency example is presented here in RDF format. This example was kindly created by [Simon](https://github.com/simonge/EIC_Analysis/blob/main/Analysis-Tutorial/EfficiencyAnalysisRDF.C).
1083 
1084 ### EfficiencyAnalysisRDF.C
1085 
1086 Create a file called `EfficiencyAnalysisRDF.C` and paste the code below in. Remember to change the file path. 
1087 
1088 Execute this script via - `root -l -q EfficiencyAnalysisRDF.C++`. Do this within eic-shell or somewhere else with the correct EDM4hep/EDM4eic libraries installed.
1089 
1090 ```c++
1091 #include <edm4hep/utils/vector_utils.h>
1092 #include <edm4hep/MCParticle.h>
1093 #include <edm4eic/ReconstructedParticle.h>
1094 #include <ROOT/RDataFrame.hxx>
1095 #include <ROOT/RVec.hxx>
1096 #include <TFile.h>
1097 
1098 // Define aliases for the data types 
1099 using MCP = edm4hep::MCParticleData;
1100 using RecoP = edm4eic::ReconstructedParticleData;
1101 
1102 // Define function to vectorize the edm4hep::utils methods
1103 template <typename T>
1104 auto getEta = [](ROOT::VecOps::RVec<T> momenta) {
1105   return ROOT::VecOps::Map(momenta, [](const T& p) { return edm4hep::utils::eta(p.momentum); });
1106 };
1107 
1108 template <typename T>
1109 auto getPhi = [](ROOT::VecOps::RVec<T> momenta) {
1110   return ROOT::VecOps::Map(momenta, [](const T& p) { return edm4hep::utils::angleAzimuthal(p.momentum); });
1111 };
1112 
1113 // Define the function to perform the efficiency analysis
1114 void EfficiencyAnalysisRDF(TString infile="PATH_TO_FILE"){
1115    
1116   // Set up input file 
1117   ROOT::RDataFrame df("events", infile);
1118 
1119   // Define new dataframe node with additional columns
1120   auto df1 =  df.Define("statusFilter",  "MCParticles.generatorStatus == 1"    )
1121                 .Define("absPDG",        "abs(MCParticles.PDG)"                )
1122                 .Define("pdgFilter",     "absPDG == 11 || absPDG == 13 || absPDG == 211 || absPDG == 321 || absPDG == 2212")
1123                 .Define("particleFilter","statusFilter && pdgFilter"           )
1124                 .Define("filtMCParts",   "MCParticles[particleFilter]"         )
1125                 .Define("assoFilter",    "Take(particleFilter,ReconstructedChargedParticleAssociations.simID)") // Incase any of the associated particles happen to not be charged
1126                 .Define("assoMCParts",   "Take(MCParticles,ReconstructedChargedParticleAssociations.simID)[assoFilter]")
1127                 .Define("assoRecParts",  "Take(ReconstructedChargedParticles,ReconstructedChargedParticleAssociations.recID)[assoFilter]")
1128                 .Define("filtMCEta",     getEta<MCP>   , {"filtMCParts"} )
1129                 .Define("filtMCPhi",     getPhi<MCP>   , {"filtMCParts"} )
1130                 .Define("accoMCEta",     getEta<MCP>   , {"assoMCParts"} )
1131                 .Define("accoMCPhi",     getPhi<MCP>   , {"assoMCParts"} )
1132                 .Define("assoRecEta",    getEta<RecoP> , {"assoRecParts"})
1133                 .Define("assoRecPhi",    getPhi<RecoP> , {"assoRecParts"})
1134                 .Define("deltaR",        "ROOT::VecOps::DeltaR(assoRecEta, accoMCEta, assoRecPhi, accoMCPhi)");
1135 
1136   // Define histograms
1137   auto partEta                = df1.Histo1D({"partEta","Eta of Thrown Charged Particles;Eta",100,-5.,5.},"filtMCEta");
1138   auto matchedPartEta         = df1.Histo1D({"matchedPartEta","Eta of Thrown Charged Particles That Have Matching Track",100,-5.,5.},"accoMCEta");
1139   auto matchedPartTrackDeltaR = df1.Histo1D({"matchedPartTrackDeltaR","Delta R Between Matching Thrown and Reconstructed Charged Particle",5000,0.,5.},"deltaR");
1140 
1141   // Write histograms to file
1142   TFile *ofile = TFile::Open("EfficiencyAnalysis_Out_RDF.root","RECREATE");
1143 
1144   // Booked Define and Histo1D lazy actions are only performed here
1145   partEta->Write();
1146   matchedPartEta->Write();
1147   matchedPartTrackDeltaR->Write();
1148       
1149   ofile->Close(); // Close output file
1150 }
1151 ```
1152 <!--
1153 
1154 A "solution" using RDataFrames is included below, please see the notes following this script for some of my thoughts on RDataFrames -
1155 
1156 ```c++
1157 #include <edm4hep/utils/vector_utils.h>
1158 #include <edm4hep/MCParticle.h>
1159 #include <edm4eic/ReconstructedParticle.h>
1160 #include <ROOT/RDataFrame.hxx>
1161 #include <ROOT/RVec.hxx>
1162 #include <TFile.h>
1163 
1164 // Define aliases for the data types 
1165 using MCP = edm4hep::MCParticleData;
1166 using RecoP = edm4eic::ReconstructedParticleData;
1167 
1168 // Define function to vectorize the edm4hep::utils methods
1169 template <typename T>
1170 auto getEta = [](ROOT::VecOps::RVec<T> momenta) {
1171   return ROOT::VecOps::Map(momenta, [](const T& p) { return edm4hep::utils::eta(p.momentum); });
1172 };
1173 
1174 template <typename T>
1175 auto getPhi = [](ROOT::VecOps::RVec<T> momenta) {
1176   return ROOT::VecOps::Map(momenta, [](const T& p) { return edm4hep::utils::angleAzimuthal(p.momentum); });
1177 };
1178 
1179 template <typename T>
1180 auto getP = [](ROOT::VecOps::RVec<T> momenta) {
1181   //return ROOT::VecOps::Map(momenta, [](const T& p) { return (p.momentum); }); // This is a vector3f
1182   return ROOT::VecOps::Map(momenta, [](const T& p) { return edm4hep::utils::magnitude(p.momentum); }); // This is a the magnitude of that vector3f
1183 };
1184 
1185 // Define the function to perform the efficiency analysis
1186 void EfficiencyAnalysisRDF_Exercise(TString infile="PATH_TO_INPUT_FILE"){
1187    
1188   // Set up input file 
1189   ROOT::RDataFrame df("events", infile);
1190 
1191   // Define new dataframe node with additional columns
1192   auto df1 =  df.Define("statusFilter",  "MCParticles.generatorStatus == 1"    )
1193     .Define("absPDG",        "abs(MCParticles.PDG)"                )
1194     .Define("pdgFilter",     "absPDG == 11 || absPDG == 13 || absPDG == 211 || absPDG == 321 || absPDG == 2212")
1195     .Define("particleFilter","statusFilter && pdgFilter"           )
1196     .Define("filtMCParts",   "MCParticles[particleFilter]"         )
1197     .Define("assoFilter",    "Take(particleFilter,ReconstructedChargedParticleAssociations.simID)") // Incase any of the associated particles happen to not be charged
1198     .Define("assoMCParts",   "Take(MCParticles,ReconstructedChargedParticleAssociations.simID)[assoFilter]")
1199     .Define("assoRecParts",  "Take(ReconstructedChargedParticles,ReconstructedChargedParticleAssociations.recID)[assoFilter]")
1200     .Define("filtMCEta",     getEta<MCP>   , {"filtMCParts"} )
1201     .Define("filtMCPhi",     getPhi<MCP>   , {"filtMCParts"} )
1202     .Define("filtMCp",       getP<MCP>     , {"filtMCParts"} )
1203     .Define("assoMCEta",     getEta<MCP>   , {"assoMCParts"} )
1204     .Define("assoMCPhi",     getPhi<MCP>   , {"assoMCParts"} )
1205     .Define("assoMCp",       getP<MCP>     , {"assoMCParts"} )
1206     .Define("assoRecEta",    getEta<RecoP> , {"assoRecParts"})
1207     .Define("assoRecPhi",    getPhi<RecoP> , {"assoRecParts"})
1208     .Define("assoRecp",      getP<RecoP>   , {"assoRecParts"})
1209     .Define("deltaEta",      "assoMCEta - assoRecEta"        )
1210     .Define("deltaPhi",      "ROOT::VecOps::DeltaPhi(assoRecPhi, assoMCPhi)")
1211     .Define("deltaR",        "ROOT::VecOps::DeltaR(assoRecEta, assoMCEta, assoRecPhi, assoMCPhi)")
1212     .Define("deltaMom",      "assoMCp - assoRecp")
1213     .Define("recoEta",       getEta<RecoP>,  {"ReconstructedChargedParticles"})
1214     .Define("recoPhi",       getPhi<RecoP>,  {"ReconstructedChargedParticles"})
1215     .Define("recoP",         getP<RecoP>,    {"ReconstructedChargedParticles"});
1216 
1217   // Define histograms. We create a histogram with the usual naming/titles/bins/range etc, then specify how to fill the histogram based upon things we have defined for our dataframe
1218   auto partEta                = df1.Histo1D({"partEta","#eta of Thrown Charged Particles;#eta",120,-6.,6.},"filtMCEta");
1219   auto matchedPartEta         = df1.Histo1D({"matchedPartEta","#eta of Thrown Charged Particles That Have Matching Track;#eta",120,-6.,6.},"assoMCEta");
1220   auto partMom = df1.Histo1D({"partMom", "Momentum of Thrown Charged Particles (truth); P(GeV/c)", 150, 0, 150}, "filtMCp");
1221   auto matchedPartMom = df1.Histo1D({"matchedPartMom", "Momentum of Thrown Charged Particles (truth), with matching track; P(GeV/c)", 150, 0, 150}, "assoMCp");
1222   auto partPhi = df1.Histo1D({"partPhi", "#phi of Thrown Charged Particles (truth); #phi(rad)", 320, -3.2, 3.2},"filtMCPhi");
1223   auto matchedPartPhi = df1.Histo1D({"matchedPartPhi", "#phi of Thrown Charged Particles (truth), with matching track; #phi(rad)", 320, -3.2, 3.2}, "assoMCPhi");
1224   
1225   auto partPEta = df1.Histo2D({"partPEta", "P vs #eta of Thrown Charged Particles; P(GeV/c); #eta", 150, 0, 150, 120, -6, 6}, "filtMCp", "filtMCEta");
1226   auto matchedPartPEta = df1.Histo2D({"matchedPartPEta", "P vs #eta of Thrown Charged Particles, with matching track; P(GeV/c); #eta", 150, 0, 150, 120, -6, 6}, "assoMCp", "assoMCEta");
1227   auto partPhiEta = df1.Histo2D({"partPhiEta", "#phi vs #eta of Thrown Charged Particles; #phi(rad); #eta", 160, -3.2, 3.2, 120, -6, 6}, "filtMCPhi", "filtMCEta");
1228   auto matchedPartPhiEta = df1.Histo2D({"matchedPartPhiEta", "#phi vs #eta of Thrown Charged Particles; #phi(rad); #eta", 160, -3.2, 3.2, 120, -6, 6}, "assoMCPhi", "assoMCEta");
1229   
1230   auto matchedPartTrackDeltaEta = df1.Histo1D({"matchedPartTrackDeltaEta","#Delta#eta Between Matching Thrown and Reconstructed Charged Particle; #Delta#eta", 100, -0.25, 0.25}, "deltaEta");
1231   auto matchedPartTrackDeltaPhi = df1.Histo1D({"matchedPartTrackDeltaPhi","#Detla #phi Between Matching Thrown and Reconstructed Charged Particle; #Delta#phi", 200, -0.2, 0.2}, "deltaPhi");
1232   auto matchedPartTrackDeltaR = df1.Histo1D({"matchedPartTrackDeltaR","#Delta R Between Matching Thrown and Reconstructed Charged Particle;#Delta R",300,0.,0.3}, "deltaR");
1233   auto matchedPartTrackDeltaMom = df1.Histo1D({"matchedPartTrackDeltaMom","#Delta P Between Matching Thrown and Reconstructed Charged Particle; #Delta P", 200, -10, 10}, "deltaMom");
1234     
1235   // Define some histograms for our efficiencies - Done "old school" root style - Maybe the division can be done direct from a DF?
1236   TH1D *TrackEff_Eta = new TH1D("TrackEff_Eta", "Tracking efficiency as fn of #eta; #eta; Eff(%)", 120, -6, 6); 
1237   TH1D *TrackEff_Mom = new TH1D("TrackEff_Mom", "Tracking efficiency as fn of P; P(GeV/c); Eff(%)", 150, 0, 150); 
1238   TH1D *TrackEff_Phi = new TH1D("TrackEff_Phi", "Tracking efficiency as fn of #phi; #phi(rad); Eff(%)", 320, -3.2, 3.2);
1239   // 2D Efficiencies
1240   TH2D* TrackEff_PEta = new TH2D("TrackEff_PEta", "Tracking efficiency as fn of P and #eta; P(GeV/c); #eta", 150, 0, 150, 120, -6, 6);
1241   TH2D* TrackEff_PhiEta = new TH2D("TrackEff_PhiEta", "Tracking efficiency as fn of #phi and #eta; #phi(rad); #eta", 160, -3.2, 3.2, 120, -6, 6);
1242 
1243   auto ChargedEta = df1.Histo1D({"ChargedEta", "#eta of all charged particles; #eta", 120, -6, 6}, "recoEta");
1244   auto ChargedPhi = df1.Histo1D({"ChargedPhi", "#phi of all charged particles; #phi (rad)", 120, -3.2, 3.2}, "recoPhi");
1245   auto ChargedP = df1.Histo1D({"ChargedP", "P of all charged particles; P(GeV/c)", 150, 0, 150}, "recoP");
1246 
1247   // Write histograms to file
1248   TFile *ofile = TFile::Open("EfficiencyAnalysis_Exercise_Out_RDF.root","RECREATE");
1249 
1250   // Booked Define and Histo1D lazy actions are only performed here
1251   partEta->Write();
1252   matchedPartEta->Write();
1253   partPhi->Write();
1254   matchedPartPhi->Write();
1255   partMom->Write();
1256   matchedPartMom->Write();
1257   partPEta->Write();
1258   matchedPartPEta->Write();
1259   partPhiEta->Write();
1260   matchedPartPhiEta->Write();
1261   matchedPartTrackDeltaEta->Write();
1262   matchedPartTrackDeltaPhi->Write();
1263   matchedPartTrackDeltaR->Write();
1264   matchedPartTrackDeltaMom->Write();
1265 
1266   // Create efficiency histograms by dividing appropriately. Note we must actually get the pointer explicitly.
1267   TrackEff_Eta->Divide(matchedPartEta.GetPtr(), partEta.GetPtr(), 1, 1, "b");
1268   TrackEff_Mom->Divide(matchedPartMom.GetPtr(), partMom.GetPtr(), 1, 1, "b");  
1269   TrackEff_Phi->Divide(matchedPartPhi.GetPtr(), partPhi.GetPtr(), 1, 1, "b");
1270   TrackEff_PEta->Divide(matchedPartPEta.GetPtr(), partPEta.GetPtr(), 1, 1, "b");
1271   TrackEff_PhiEta->Divide(matchedPartPhiEta.GetPtr(), partPhiEta.GetPtr(), 1, 1, "b");
1272   
1273   TrackEff_Eta->Write();
1274   TrackEff_Mom->Write();
1275   TrackEff_Phi->Write();
1276   TrackEff_PEta->Write();
1277   TrackEff_PhiEta->Write();
1278 
1279   ChargedEta->Write();
1280   ChargedPhi->Write();
1281   ChargedP->Write();
1282                                 
1283   ofile->Close(); // Close output file
1284 }
1285 ```
1286 Insert your input file path and execute as the example code above.
1287 
1288 > Note:
1289 > - Before writing this example and solution, I have never used RDataFrames before. Some thoughts below.
1290 >   - I find them very difficult to work with. The processes that are "actually" going on are very obscured with RDataFrames.
1291 >   - Finding resources and examples online is much more difficult due to how new RDataFrames are.
1292 >     - This further complicates writing and working with them. 
1293 >   - Getting this example working and producing the same result as the python and ROOT examples took me as much time as preparing the rest of the tutorial.
1294 >   - The script itself seems to be very slow when running.
1295 >   - I don't think they're a good starting point for someone new to ROOT/Nuclear Physics data analysis. Too much is going on behind the scenes.
1296 >  - In summary, I personally would *not* recommend using RDataFrames for your analysis (at this point in time).
1297 >  - Note that this just my (SKay) thoughts and opinions though.
1298 {: .callout}
1299 
1300 Note that due to how much "fun" I had making the efficiency analysis exercise with RDataFrames, I won't be creating a solution for the resolution analysis exercises.
1301 -->
1302 
1303 {% include links.md %}