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 ## Install library
0556 # Could also use include(GNUInstallDirs)
0557 # and then destinations of the form ${CMAKE_INSTALL_INCLUDEDIR}
0558 install(TARGETS helloroot
0559   EXPORT helloroot-export
0560   LIBRARY DESTINATION lib
0561   ARCHIVE DESTINATION lib
0562   )
0563 
0564 ## Install headers
0565 install (DIRECTORY ${CMAKE_SOURCE_DIR}/include/helloroot
0566   DESTINATION  include/helloroot
0567   )
0568 
0569 ## Generate configuration file - this allows you to use cmake in another project 
0570 ## to find and link the installed helloroot library
0571 install(EXPORT helloroot-export
0572   FILE
0573   hellorootConfig.cmake
0574   NAMESPACE
0575     helloroot::
0576   DESTINATION
0577   cmake
0578   )
0579 
0580 ## Final message
0581 message( " Done!")
0582 ```
0583 
0584 The contents of helloroot.hh are -
0585 
0586 ```c++
0587 #ifndef HELLO_ROOT_H
0588 #define HELLO_ROOT_H
0589 
0590 void HelloRoot();
0591 
0592 #endif // HELLO_ROOT_H
0593 ```
0594 
0595 The contents of helloexec.cxx are -
0596 
0597 ```c++
0598 #include<helloroot/helloroot.hh>
0599 
0600 #include<iostream>
0601 #include<string>
0602 
0603 int main()
0604 {
0605     std::cout << "Hello from main " << std::endl;
0606     HelloRoot(); 
0607     
0608     return 0;
0609 }
0610 ```
0611 
0612 And finally, the contents of helloroot.cxx are -
0613 
0614 ```c++
0615 #include<helloroot/helloroot.hh>
0616 
0617 #include<iostream>
0618 #include<string>
0619 
0620 #include<TH1D.h>
0621 #include<TPad.h>
0622 
0623 void HelloRoot()
0624 {
0625   std::cout << "Hello from HelloRoot" << std::endl;
0626 
0627   // do something with root
0628   TH1D h("h", "h", 100, -5, 5);
0629   h.FillRandom("gaus", 1000);
0630   h.Draw();
0631   gPad->SaveAs("hello.png");
0632 
0633   return;
0634 }
0635 ```
0636 Please consult the README and script comments for further instructions.
0637 
0638 ## Python Uproot Scripts
0639 
0640 ### EfficiencyAnalysis.py
0641 
0642 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`.
0643 
0644 ```python
0645 #! /usr/bin/python
0646 
0647 #Import relevant packages
0648 import ROOT, math, array
0649 from ROOT import TH1F, TH2F, TMath, TTree, TVector3, TVector2
0650 import uproot as up
0651 
0652 #Define and open files
0653 infile="PATH_TO_INPUT_FILE"
0654 ofile=ROOT.TFile.Open("EfficiencyAnalysis_OutPy.root", "RECREATE")
0655 
0656 # Open input file and define branches we want to look at with uproot
0657 events_tree = up.open(infile)["events"]
0658 
0659 # Get particle information
0660 partGenStat = events_tree["MCParticles.generatorStatus"].array()
0661 partMomX = events_tree["MCParticles.momentum.x"].array()
0662 partMomY = events_tree["MCParticles.momentum.y"].array()
0663 partMomZ = events_tree["MCParticles.momentum.z"].array()
0664 partPdg = events_tree["MCParticles.PDG"].array()
0665 
0666 # Get reconstructed track information
0667 trackMomX = events_tree["ReconstructedChargedParticles.momentum.x"].array()
0668 trackMomY = events_tree["ReconstructedChargedParticles.momentum.y"].array()
0669 trackMomZ = events_tree["ReconstructedChargedParticles.momentum.z"].array()
0670 
0671 # Get assocations between MCParticles and ReconstructedChargedParticles
0672 recoAssoc = events_tree["ReconstructedChargedParticleAssociations.recID"].array()
0673 simuAssoc = events_tree["ReconstructedChargedParticleAssociations.simID"].array()
0674 
0675 # Define histograms below
0676 partEta = ROOT.TH1D("partEta","Eta of Thrown Charged Particles;Eta",100, -5 ,5 )
0677 matchedPartEta = ROOT.TH1D("matchedPartEta","Eta of Thrown Charged Particles That Have Matching Track", 100, -5 ,5);
0678 matchedPartTrackDeltaR = ROOT.TH1D("matchedPartTrackDeltaR","Delta R Between Matching Thrown and Reconstructed Charge Particle", 5000, 0, 5);
0679 
0680 # Add main analysis loop(s) below
0681 for i in range(0, len(partGenStat)): # Loop over all events
0682     for j in range(0, len(partGenStat[i])): # Loop over all thrown particles
0683         if partGenStat[i][j] == 1: # Select stable particles
0684             pdg = abs(partPdg[i][j]) # Get PDG for each stable particle
0685             if(pdg == 11 or pdg == 13 or pdg == 211 or pdg == 321 or pdg == 2212):
0686                 trueMom = ROOT.TVector3(partMomX[i][j], partMomY[i][j], partMomZ[i][j])
0687                 trueEta = trueMom.PseudoRapidity()
0688                 truePhi = trueMom.Phi()
0689                 partEta.Fill(trueEta)
0690                 for k in range(0,len(simuAssoc[i])): # Loop over associations to find matching ReconstructedChargedParticle
0691                     if (simuAssoc[i][k] == j):
0692                         recMom = ROOT.TVector3(trackMomX[i][recoAssoc[i][k]], trackMomY[i][recoAssoc[i][k]], trackMomZ[i][recoAssoc[i][k]])
0693                         deltaEta = trueEta - recMom.PseudoRapidity()
0694                         deltaPhi = TVector2. Phi_mpi_pi(truePhi - recMom.Phi())
0695                         deltaR = math.sqrt((deltaEta*deltaEta) + (deltaPhi*deltaPhi))
0696                         matchedPartEta.Fill(trueEta)
0697                         matchedPartTrackDeltaR.Fill(deltaR)
0698                         
0699 # Write output histograms to file below
0700 partEta.Write()
0701 matchedPartEta.Write()
0702 matchedPartTrackDeltaR.Write()
0703 
0704 # Close files
0705 ofile.Close()
0706 ```
0707 <!--
0708 
0709 A "solution" version of the script for the exercise is included below -
0710 
0711 ```python
0712 #! /usr/bin/python
0713 
0714 #Import relevant packages
0715 import ROOT, math, array
0716 from ROOT import TCanvas, TColor, TGaxis, TH1F, TH2F, TPad, TStyle, gStyle, gPad, TGaxis, TLine, TMath, TPaveText, TTree, TVector3, TVector2
0717 import uproot as up
0718 
0719 #Define and open files
0720 infile="PATH_TO_FILE"
0721 ofile=ROOT.TFile.Open("EfficiencyAnalysis_Exercise_OutPy.root", "RECREATE")
0722 
0723 # Open input file and define branches we want to look at with uproot
0724 events_tree = up.open(infile)["events"]
0725 
0726 # Get particle information
0727 partGenStat = events_tree["MCParticles.generatorStatus"].array()
0728 partMomX = events_tree["MCParticles.momentum.x"].array()
0729 partMomY = events_tree["MCParticles.momentum.y"].array()
0730 partMomZ = events_tree["MCParticles.momentum.z"].array()
0731 partPdg = events_tree["MCParticles.PDG"].array()
0732 
0733 # Get reconstructed track information
0734 trackMomX = events_tree["ReconstructedChargedParticles.momentum.x"].array()
0735 trackMomY = events_tree["ReconstructedChargedParticles.momentum.y"].array()
0736 trackMomZ = events_tree["ReconstructedChargedParticles.momentum.z"].array()
0737 
0738 # Get assocations between MCParticles and ReconstructedChargedParticles
0739 recoAssoc = events_tree["ReconstructedChargedParticleAssociations.recID"].array()
0740 simuAssoc = events_tree["ReconstructedChargedParticleAssociations.simID"].array()
0741 
0742 # Define histograms below
0743 partEta = ROOT.TH1D("partEta","#eta of Thrown Charged Particles; #eta", 120, -6, 6)
0744 matchedPartEta = ROOT.TH1D("matchedPartEta","#eta of Thrown Charged Particles That Have Matching Track; #eta", 120, -6, 6)
0745 partMom = ROOT.TH1D("partMom", "Momentum of Thrown Charged Particles (truth); P(GeV/c)", 150, 0, 150)
0746 matchedPartMom = ROOT.TH1D("matchedPartMom", "Momentum of Thrown Charged Particles (truth), with matching track; P(GeV/c)", 150, 0, 150)
0747 partPhi = ROOT.TH1D("partPhi", "#phi of Thrown Charged Particles (truth); #phi(rad)", 320, -3.2, 3.2)
0748 matchedPartPhi = ROOT.TH1D("matchedPartPhi", "#phi of Thrown Charged Particles (truth), with matching track; #phi(rad)", 320, -3.2, 3.2)
0749 
0750 partPEta = ROOT.TH2D("partPEta", "P vs #eta of Thrown Charged Particles; P(GeV/c); #eta", 150, 0, 150, 120, -6, 6)
0751 matchedPartPEta = ROOT.TH2D("matchedPartPEta", "P vs #eta of Thrown Charged Particles, with matching track; P(GeV/C); #eta", 150, 0, 150, 120, -6, 6)
0752 partPhiEta = ROOT.TH2D("partPhiEta", "#phi vs #eta of Thrown Charged Particles; #phi(rad); #eta", 160, -3.2, 3.2, 120, -6, 6)
0753 matchedPartPhiEta = ROOT.TH2D("matchedPartPhiEta", "#phi vs #eta of Thrown Charged Particles; #phi(rad); #eta", 160, -3.2, 3.2, 120, -6, 6)
0754 
0755 matchedPartTrackDeltaEta = ROOT.TH1D("matchedPartTrackDeltaEta","#Delta#eta Between Matching Thrown and Reconstructe Charged Particle; #Delta#eta", 100, -0.25, 0.25)
0756 matchedPartTrackDeltaPhi = ROOT.TH1D("matchedPartTrackDeltaPhi","#Detla #phi Between Matching Thrown and Reconstructed Charged Particle; #Delta#phi", 200, -0.2, 0.2)
0757 matchedPartTrackDeltaR = ROOT.TH1D("matchedPartTrackDeltaR","#Delta R Between Matching Thrown and Reconstructed Charged Particle; #Delta R", 300, 0, 0.3)
0758 matchedPartTrackDeltaMom = ROOT.TH1D("matchedPartTrackDeltaMom","#Delta P Between Matching Thrown and Reconstructed Charged Particle; #Delta P", 200, -10, 10)
0759 
0760 # Define some histograms for our efficiencies
0761 TrackEff_Eta = ROOT.TH1D("TrackEff_Eta", "Tracking efficiency as fn of #eta; #eta; Eff(%)", 120, -6, 6)
0762 TrackEffMom = ROOT.TH1D("TrackEff_Mom", "Tracking efficiency as fn of P; P(GeV/c); Eff(%)", 150, 0, 150)
0763 TrackEffPhi = ROOT.TH1D("TrackEff_Phi", "Tracking efficiency as fn of #phi; #phi(rad); Eff(%)", 320, -3.2, 3.2)
0764 
0765 # 2D Efficiencies
0766 TrackEff_PEta = ROOT.TH2D("TrackEff_PEta", "Tracking efficiency as fn of P and #eta; P(GeV/c); #eta", 150, 0, 150, 120, -6, 6)
0767 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)
0768 
0769 # All charged particle histos
0770 ChargedEta = ROOT.TH1D("ChargedEta", "#eta of all charged particles; #eta", 120, -6, 6)
0771 ChargedPhi = ROOT.TH1D("ChargedPhi", "#phi of all charged particles; #phi (rad)", 120, -3.2, 3.2)
0772 ChargedP = ROOT.TH1D("ChargedP", "P of all charged particles; P(GeV/c)", 150, 0, 150)
0773 
0774 # Add main analysis loop(s) below
0775 for i in range(0, len(partGenStat)): # Loop over all events
0776     for j in range(0, len(partGenStat[i])): # Loop over all thrown particles
0777         if partGenStat[i][j] == 1: # Select stable particles
0778             pdg = abs(partPdg[i][j]) # Get PDG for each stable particle
0779             if(pdg == 11 or pdg == 13 or pdg == 211 or pdg == 321 or pdg == 2212):
0780                 trueMom = ROOT.TVector3(partMomX[i][j], partMomY[i][j], partMomZ[i][j])
0781                 trueEta = trueMom.PseudoRapidity()
0782                 truePhi = trueMom.Phi()
0783                 partEta.Fill(trueEta)
0784                 partPhi.Fill(truePhi)
0785                 partMom.Fill(trueMom.Mag())
0786                 partPEta.Fill(trueMom.Mag(), trueEta)
0787                 partPhiEta.Fill(truePhi, trueEta)
0788                 for k in range(0,len(simuAssoc[i])): # Loop over associations to find matching ReconstructedChargedParticle
0789                     if (simuAssoc[i][k] == j):
0790                         recMom = ROOT.TVector3(trackMomX[i][recoAssoc[i][k]], trackMomY[i][recoAssoc[i][k]], trackMomZ[i][recoAssoc[i][k]])
0791                         deltaEta = trueEta - recMom.PseudoRapidity()
0792                         deltaPhi = TVector2. Phi_mpi_pi(truePhi - recMom.Phi())
0793                         deltaR = math.sqrt((deltaEta*deltaEta) + (deltaPhi*deltaPhi))
0794                         deltaMom = ((trueMom.Mag()) - (recMom.Mag()))
0795                         matchedPartTrackDeltaEta.Fill(deltaEta)
0796                         matchedPartTrackDeltaPhi.Fill(deltaPhi)
0797                         matchedPartTrackDeltaR.Fill(deltaR)
0798                         matchedPartTrackDeltaMom.Fill(deltaMom)
0799                         matchedPartEta.Fill(trueEta)
0800                         matchedPartPhi.Fill(truePhi)
0801                         matchedPartMom.Fill(trueMom.Mag())
0802                         matchedPartPEta.Fill(trueMom.Mag(), trueEta)
0803                         matchedPartPhiEta.Fill(truePhi, trueEta)
0804     for x in range (0, len(trackMomX[i])): # Loop over all charged particles, thrown or not
0805         CPartMom = ROOT.TVector3(trackMomX[i][x], trackMomY[i][x], trackMomZ[i][x])
0806         CPartEta = CPartMom.PseudoRapidity()
0807         CPartPhi = CPartMom.Phi()
0808         ChargedEta.Fill(CPartEta)
0809         ChargedPhi.Fill(CPartPhi)
0810         ChargedP.Fill(CPartMom.Mag())
0811         
0812 # Write output histograms to file below
0813 partEta.Write()
0814 matchedPartEta.Write()
0815 partMom.Write()
0816 matchedPartMom.Write()
0817 partPhi.Write()
0818 matchedPartPhi.Write()
0819 partPEta.Write()
0820 matchedPartPEta.Write()
0821 partPhiEta.Write()
0822 matchedPartPhiEta.Write()
0823 matchedPartTrackDeltaEta.Write()
0824 matchedPartTrackDeltaPhi.Write()
0825 matchedPartTrackDeltaR.Write()
0826 matchedPartTrackDeltaMom.Write()
0827 ChargedEta.Write()
0828 ChargedPhi.Write()
0829 ChargedP.Write()
0830 TrackEff_Eta.Divide(matchedPartEta, partEta, 1, 1, "b")
0831 TrackEffMom.Divide(matchedPartMom, partMom, 1, 1, "b")
0832 TrackEffPhi.Divide(matchedPartPhi, partPhi, 1, 1, "b")
0833 TrackEff_PEta.Divide(matchedPartPEta, partPEta, 1, 1, "b")
0834 TrackEff_PhiEta.Divide(matchedPartPhiEta, partPhiEta, 1, 1, "b")
0835 TrackEff_Eta.Write()
0836 TrackEffMom.Write()
0837 TrackEffPhi.Write()
0838 TrackEff_PEta.Write()
0839 TrackEff_PhiEta.Write()
0840 
0841 # Close files
0842 ofile.Close()
0843 ```
0844 Insert your input file path and execute as the example code above.
0845 -->
0846 ### ResolutionAnalysis.py
0847 
0848 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`.
0849 
0850 ```python
0851 #! /usr/bin/python
0852 
0853 #Import relevant packages
0854 import ROOT, math, array
0855 from ROOT import TH1F, TH2F, TMath, TTree, TVector3, TVector2
0856 import uproot as up
0857 
0858 #Define and open files
0859 infile="PATH_TO_INPUT_FILE"
0860 ofile=ROOT.TFile.Open("ResolutionAnalysis_OutPy.root", "RECREATE")
0861 
0862 # Open input file and define branches we want to look at with uproot
0863 events_tree = up.open(infile)["events"]
0864 
0865 # Get particle information
0866 partGenStat = events_tree["MCParticles.generatorStatus"].array()
0867 partMomX = events_tree["MCParticles.momentum.x"].array()
0868 partMomY = events_tree["MCParticles.momentum.y"].array()
0869 partMomZ = events_tree["MCParticles.momentum.z"].array()
0870 partPdg = events_tree["MCParticles.PDG"].array()
0871 
0872 # Get reconstructed track information
0873 trackMomX = events_tree["ReconstructedChargedParticles.momentum.x"].array()
0874 trackMomY = events_tree["ReconstructedChargedParticles.momentum.y"].array()
0875 trackMomZ = events_tree["ReconstructedChargedParticles.momentum.z"].array()
0876 
0877 # Get assocations between MCParticles and ReconstructedChargedParticles
0878 recoAssoc = events_tree["ReconstructedChargedParticleAssociations.recID"].array()
0879 simuAssoc = events_tree["ReconstructedChargedParticleAssociations.simID"].array()
0880 
0881 # Define histograms below
0882 trackMomentumRes = ROOT.TH1D("trackMomentumRes","Track Momentum Resolution", 400, -2, 2)
0883 
0884 matchedPartTrackDeltaEta = ROOT.TH1D("matchedPartTrackDeltaEta","#Delta#eta Between Matching Thrown and Reconstructe Charged Particle; #Delta#eta", 100, -0.25, 0.25)
0885 matchedPartTrackDeltaPhi = ROOT.TH1D("matchedPartTrackDeltaPhi","#Detla #phi Between Matching Thrown and Reconstructed Charged Particle; #Delta#phi", 200, -0.2, 0.2)
0886 matchedPartTrackDeltaR = ROOT.TH1D("matchedPartTrackDeltaR","#Delta R Between Matching Thrown and Reconstructed Charged Particle; #Delta R", 300, 0, 0.3)
0887 matchedPartTrackDeltaMom = ROOT.TH1D("matchedPartTrackDeltaMom","#Delta P Between Matching Thrown and Reconstructed Charged Particle; #Delta P", 200, -10, 10)
0888 
0889 # Add main analysis loop(s) below
0890 for i in range(0, len(partGenStat)): # Loop over all events
0891     for j in range(0, len(partGenStat[i])): # Loop over all thrown particles
0892         if partGenStat[i][j] == 1: # Select stable particles
0893             pdg = abs(partPdg[i][j]) # Get PDG for each stable particle
0894             if(pdg == 11 or pdg == 13 or pdg == 211 or pdg == 321 or pdg == 2212):
0895                 trueMom = ROOT.TVector3(partMomX[i][j], partMomY[i][j], partMomZ[i][j])
0896                 trueEta = trueMom.PseudoRapidity()
0897                 truePhi = trueMom.Phi()
0898                 for k in range(0,len(simuAssoc[i])): # Loop over associations to find matching ReconstructedChargedParticle
0899                     if (simuAssoc[i][k] == j):
0900                         recMom = ROOT.TVector3(trackMomX[i][recoAssoc[i][k]], trackMomY[i][recoAssoc[i][k]], trackMomZ[i][recoAssoc[i][k]])
0901                         deltaEta = trueEta - recMom.PseudoRapidity()
0902                         deltaPhi = TVector2. Phi_mpi_pi(truePhi - recMom.Phi())
0903                         deltaR = math.sqrt((deltaEta*deltaEta) + (deltaPhi*deltaPhi))
0904                         deltaMom = ((trueMom.Mag()) - (recMom.Mag()))
0905                         momRes = (recMom.Mag() - trueMom.Mag())/trueMom.Mag()
0906                         matchedPartTrackDeltaEta.Fill(deltaEta)
0907                         matchedPartTrackDeltaPhi.Fill(deltaPhi)
0908                         matchedPartTrackDeltaR.Fill(deltaR)
0909                         matchedPartTrackDeltaMom.Fill(deltaMom)
0910                         trackMomentumRes.Fill(momRes)
0911                         
0912 # Write output histograms to file below
0913 trackMomentumRes.Write()
0914 matchedPartTrackDeltaEta.Write()
0915 matchedPartTrackDeltaPhi.Write()
0916 matchedPartTrackDeltaR.Write()
0917 matchedPartTrackDeltaMom.Write()
0918 
0919 # Close files
0920 ofile.Close()
0921 ```
0922 <!--
0923 
0924 A "solution" version of the script for the exercise is included below -
0925 
0926 ```python
0927 #! /usr/bin/python
0928 
0929 #Import relevant packages
0930 import ROOT, math, array
0931 from ROOT import TCanvas, TColor, TGaxis, TH1F, TH2F, TPad, TStyle, gStyle, gPad, TGaxis, TLine, TMath, TPaveText, TTree, TVector3, TVector2
0932 import uproot as up
0933 
0934 #Define and open files
0935 infile="PATH_TO_FILE"
0936 ofile=ROOT.TFile.Open("ResolutionAnalysis_Exercise_OutPy.root", "RECREATE")
0937 
0938 # Open input file and define branches we want to look at with uproot
0939 events_tree = up.open(infile)["events"]
0940 
0941 # Get particle information
0942 partGenStat = events_tree["MCParticles.generatorStatus"].array()
0943 partMomX = events_tree["MCParticles.momentum.x"].array()
0944 partMomY = events_tree["MCParticles.momentum.y"].array()
0945 partMomZ = events_tree["MCParticles.momentum.z"].array()
0946 partPdg = events_tree["MCParticles.PDG"].array()
0947 
0948 # Get reconstructed track information
0949 trackMomX = events_tree["ReconstructedChargedParticles.momentum.x"].array()
0950 trackMomY = events_tree["ReconstructedChargedParticles.momentum.y"].array()
0951 trackMomZ = events_tree["ReconstructedChargedParticles.momentum.z"].array()
0952 
0953 # Get assocations between MCParticles and ReconstructedChargedParticles
0954 recoAssoc = events_tree["ReconstructedChargedParticleAssociations.recID"].array()
0955 simuAssoc = events_tree.["ReconstructedChargedParticleAssociations.simID"].array()
0956 
0957 # Define histograms below
0958 trackMomentumRes = ROOT.TH1D("trackMomentumRes","Track Momentum Resolution", 400, -2, 2)
0959 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);
0960 trackMomResEta = ROOT.TH2D("trackMomResEta", "Track Momentum Resolution vs #eta; (P_{rec} - P_{MC})/P_{MC}; #eta_{MC}", 400, -2, 2, 120, -6, 6);
0961 
0962 trackMomentumRes_e = ROOT.TH1D("trackMomentumRes_e","e^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0963 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);
0964 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);
0965 
0966 trackMomentumRes_mu = ROOT.TH1D("trackMomentumRes_mu","#mu^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0967 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);
0968 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);
0969 
0970 trackMomentumRes_pi = ROOT.TH1D("trackMomentumRes_pi","#pi^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0971 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);
0972 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);
0973 
0974 trackMomentumRes_K = ROOT.TH1D("trackMomentumRes_K","K^{#pm} Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0975 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);
0976 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);
0977 
0978 trackMomentumRes_p = ROOT.TH1D("trackMomentumRes_p","p Track Momentum Resolution; (P_{rec} - P_{MC})/P_{MC}", 400, -2, 2);
0979 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);
0980 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);
0981 
0982 matchedPartTrackDeltaEta = ROOT.TH1D("matchedPartTrackDeltaEta","#Delta#eta Between Matching Thrown and Reconstructe Charged Particle; #Delta#eta", 100, -0.25, 0.25)
0983 matchedPartTrackDeltaPhi = ROOT.TH1D("matchedPartTrackDeltaPhi","#Detla #phi Between Matching Thrown and Reconstructed Charged Particle; #Delta#phi", 200, -0.2, 0.2)
0984 matchedPartTrackDeltaR = ROOT.TH1D("matchedPartTrackDeltaR","#Delta R Between Matching Thrown and Reconstructed Charged Particle; #Delta R", 300, 0, 0.3)
0985 matchedPartTrackDeltaMom = ROOT.TH1D("matchedPartTrackDeltaMom","#Delta P Between Matching Thrown and Reconstructed Charged Particle; #Delta P", 200, -10, 10)
0986 
0987 # Add main analysis loop(s) below
0988 for i in range(0, len(partGenStat)): # Loop over all events
0989     for j in range(0, len(partGenStat[i])): # Loop over all thrown particles
0990         if partGenStat[i][j] == 1: # Select stable particles
0991             pdg = abs(partPdg[i][j]) # Get PDG for each stable particle
0992             if(pdg == 11 or pdg == 13 or pdg == 211 or pdg == 321 or pdg == 2212):
0993                 trueMom = ROOT.TVector3(partMomX[i][j], partMomY[i][j], partMomZ[i][j])
0994                 trueEta = trueMom.PseudoRapidity()
0995                 truePhi = trueMom.Phi()
0996                 for k in range(0,len(simuAssoc[i])): # Loop over associations to find matching ReconstructedChargedParticle
0997                     if (simuAssoc[i][k] == j):
0998                         recMom = ROOT.TVector3(trackMomX[i][recoAssoc[i][k]], trackMomY[i][recoAssoc[i][k]], trackMomZ[i][recoAssoc[i][k]])
0999                         deltaEta = trueEta - recMom.PseudoRapidity()
1000                         deltaPhi = TVector2. Phi_mpi_pi(truePhi - recMom.Phi())
1001                         deltaR = math.sqrt((deltaEta*deltaEta) + (deltaPhi*deltaPhi))
1002                         deltaMom = ((trueMom.Mag()) - (recMom.Mag()))
1003                         momRes = (recMom.Mag() - trueMom.Mag())/trueMom.Mag()
1004                         trackMomentumRes.Fill(momRes)
1005                         trackMomResP.Fill(momRes, trueMom.Mag())
1006                         trackMomResEta.Fill(momRes, trueEta)
1007                         if( pdg == 11):
1008                             trackMomentumRes_e.Fill(momRes)
1009                             trackMomResP_e.Fill(momRes, trueMom.Mag())
1010                             trackMomResEta_e.Fill(momRes, trueEta)
1011                         elif( pdg == 13):
1012                             trackMomentumRes_mu.Fill(momRes)
1013                             trackMomResP_mu.Fill(momRes, trueMom.Mag())
1014                             trackMomResEta_mu.Fill(momRes, trueEta)
1015                         elif( pdg == 211):
1016                             trackMomentumRes_pi.Fill(momRes)
1017                             trackMomResP_pi.Fill(momRes, trueMom.Mag())
1018                             trackMomResEta_pi.Fill(momRes, trueEta)
1019                         elif( pdg == 321):
1020                             trackMomentumRes_K.Fill(momRes)
1021                             trackMomResP_K.Fill(momRes, trueMom.Mag())
1022                             trackMomResEta_K.Fill(momRes, trueEta)
1023                         elif( pdg == 2212):
1024                             trackMomentumRes_p.Fill(momRes)
1025                             trackMomResP_p.Fill(momRes, trueMom.Mag())
1026                             trackMomResEta_p.Fill(momRes, trueEta)                            
1027                         matchedPartTrackDeltaEta.Fill(deltaEta)
1028                         matchedPartTrackDeltaPhi.Fill(deltaPhi)
1029                         matchedPartTrackDeltaR.Fill(deltaR)
1030                         matchedPartTrackDeltaMom.Fill(deltaMom)
1031                         
1032 # Write output histograms to file below
1033 trackMomentumRes.Write()
1034 trackMomResP.Write()
1035 trackMomResEta.Write()
1036 trackMomentumRes_e.Write()
1037 trackMomResP_e.Write()
1038 trackMomResEta_e.Write()
1039 trackMomentumRes_mu.Write()
1040 trackMomResP_mu.Write()
1041 trackMomResEta_mu.Write()
1042 trackMomentumRes_pi.Write()
1043 trackMomResP_pi.Write()
1044 trackMomResEta_pi.Write()
1045 trackMomentumRes_K.Write()
1046 trackMomResP_K.Write()
1047 trackMomResEta_K.Write()
1048 trackMomentumRes_p.Write()
1049 trackMomResP_p.Write()
1050 trackMomResEta_p.Write()
1051 matchedPartTrackDeltaEta.Write()
1052 matchedPartTrackDeltaPhi.Write()
1053 matchedPartTrackDeltaR.Write()
1054 matchedPartTrackDeltaMom.Write()
1055 
1056 # Close files
1057 ofile.Close()
1058 ```
1059 Insert your input file path and execute as the example code above.
1060 -->
1061 ## RDataFrames Example
1062 
1063 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).
1064 
1065 ### EfficiencyAnalysisRDF.C
1066 
1067 Create a file called `EfficiencyAnalysisRDF.C` and paste the code below in. Remember to change the file path. 
1068 
1069 Execute this script via - `root -l -q EfficiencyAnalysisRDF.C++`. Do this within eic-shell or somewhere else with the correct EDM4hep/EDM4eic libraries installed.
1070 
1071 ```c++
1072 #include <edm4hep/utils/vector_utils.h>
1073 #include <edm4hep/MCParticle.h>
1074 #include <edm4eic/ReconstructedParticle.h>
1075 #include <ROOT/RDataFrame.hxx>
1076 #include <ROOT/RVec.hxx>
1077 #include <TFile.h>
1078 
1079 // Define aliases for the data types 
1080 using MCP = edm4hep::MCParticleData;
1081 using RecoP = edm4eic::ReconstructedParticleData;
1082 
1083 // Define function to vectorize the edm4hep::utils methods
1084 template <typename T>
1085 auto getEta = [](ROOT::VecOps::RVec<T> momenta) {
1086   return ROOT::VecOps::Map(momenta, [](const T& p) { return edm4hep::utils::eta(p.momentum); });
1087 };
1088 
1089 template <typename T>
1090 auto getPhi = [](ROOT::VecOps::RVec<T> momenta) {
1091   return ROOT::VecOps::Map(momenta, [](const T& p) { return edm4hep::utils::angleAzimuthal(p.momentum); });
1092 };
1093 
1094 // Define the function to perform the efficiency analysis
1095 void EfficiencyAnalysisRDF(TString infile="PATH_TO_FILE"){
1096    
1097   // Set up input file 
1098   ROOT::RDataFrame df("events", infile);
1099 
1100   // Define new dataframe node with additional columns
1101   auto df1 =  df.Define("statusFilter",  "MCParticles.generatorStatus == 1"    )
1102                 .Define("absPDG",        "abs(MCParticles.PDG)"                )
1103                 .Define("pdgFilter",     "absPDG == 11 || absPDG == 13 || absPDG == 211 || absPDG == 321 || absPDG == 2212")
1104                 .Define("particleFilter","statusFilter && pdgFilter"           )
1105                 .Define("filtMCParts",   "MCParticles[particleFilter]"         )
1106                 .Define("assoFilter",    "Take(particleFilter,ReconstructedChargedParticleAssociations.simID)") // Incase any of the associated particles happen to not be charged
1107                 .Define("assoMCParts",   "Take(MCParticles,ReconstructedChargedParticleAssociations.simID)[assoFilter]")
1108                 .Define("assoRecParts",  "Take(ReconstructedChargedParticles,ReconstructedChargedParticleAssociations.recID)[assoFilter]")
1109                 .Define("filtMCEta",     getEta<MCP>   , {"filtMCParts"} )
1110                 .Define("filtMCPhi",     getPhi<MCP>   , {"filtMCParts"} )
1111                 .Define("accoMCEta",     getEta<MCP>   , {"assoMCParts"} )
1112                 .Define("accoMCPhi",     getPhi<MCP>   , {"assoMCParts"} )
1113                 .Define("assoRecEta",    getEta<RecoP> , {"assoRecParts"})
1114                 .Define("assoRecPhi",    getPhi<RecoP> , {"assoRecParts"})
1115                 .Define("deltaR",        "ROOT::VecOps::DeltaR(assoRecEta, accoMCEta, assoRecPhi, accoMCPhi)");
1116 
1117   // Define histograms
1118   auto partEta                = df1.Histo1D({"partEta","Eta of Thrown Charged Particles;Eta",100,-5.,5.},"filtMCEta");
1119   auto matchedPartEta         = df1.Histo1D({"matchedPartEta","Eta of Thrown Charged Particles That Have Matching Track",100,-5.,5.},"accoMCEta");
1120   auto matchedPartTrackDeltaR = df1.Histo1D({"matchedPartTrackDeltaR","Delta R Between Matching Thrown and Reconstructed Charged Particle",5000,0.,5.},"deltaR");
1121 
1122   // Write histograms to file
1123   TFile *ofile = TFile::Open("EfficiencyAnalysis_Out_RDF.root","RECREATE");
1124 
1125   // Booked Define and Histo1D lazy actions are only performed here
1126   partEta->Write();
1127   matchedPartEta->Write();
1128   matchedPartTrackDeltaR->Write();
1129       
1130   ofile->Close(); // Close output file
1131 }
1132 ```
1133 <!--
1134 
1135 A "solution" using RDataFrames is included below, please see the notes following this script for some of my thoughts on RDataFrames -
1136 
1137 ```c++
1138 #include <edm4hep/utils/vector_utils.h>
1139 #include <edm4hep/MCParticle.h>
1140 #include <edm4eic/ReconstructedParticle.h>
1141 #include <ROOT/RDataFrame.hxx>
1142 #include <ROOT/RVec.hxx>
1143 #include <TFile.h>
1144 
1145 // Define aliases for the data types 
1146 using MCP = edm4hep::MCParticleData;
1147 using RecoP = edm4eic::ReconstructedParticleData;
1148 
1149 // Define function to vectorize the edm4hep::utils methods
1150 template <typename T>
1151 auto getEta = [](ROOT::VecOps::RVec<T> momenta) {
1152   return ROOT::VecOps::Map(momenta, [](const T& p) { return edm4hep::utils::eta(p.momentum); });
1153 };
1154 
1155 template <typename T>
1156 auto getPhi = [](ROOT::VecOps::RVec<T> momenta) {
1157   return ROOT::VecOps::Map(momenta, [](const T& p) { return edm4hep::utils::angleAzimuthal(p.momentum); });
1158 };
1159 
1160 template <typename T>
1161 auto getP = [](ROOT::VecOps::RVec<T> momenta) {
1162   //return ROOT::VecOps::Map(momenta, [](const T& p) { return (p.momentum); }); // This is a vector3f
1163   return ROOT::VecOps::Map(momenta, [](const T& p) { return edm4hep::utils::magnitude(p.momentum); }); // This is a the magnitude of that vector3f
1164 };
1165 
1166 // Define the function to perform the efficiency analysis
1167 void EfficiencyAnalysisRDF_Exercise(TString infile="PATH_TO_INPUT_FILE"){
1168    
1169   // Set up input file 
1170   ROOT::RDataFrame df("events", infile);
1171 
1172   // Define new dataframe node with additional columns
1173   auto df1 =  df.Define("statusFilter",  "MCParticles.generatorStatus == 1"    )
1174     .Define("absPDG",        "abs(MCParticles.PDG)"                )
1175     .Define("pdgFilter",     "absPDG == 11 || absPDG == 13 || absPDG == 211 || absPDG == 321 || absPDG == 2212")
1176     .Define("particleFilter","statusFilter && pdgFilter"           )
1177     .Define("filtMCParts",   "MCParticles[particleFilter]"         )
1178     .Define("assoFilter",    "Take(particleFilter,ReconstructedChargedParticleAssociations.simID)") // Incase any of the associated particles happen to not be charged
1179     .Define("assoMCParts",   "Take(MCParticles,ReconstructedChargedParticleAssociations.simID)[assoFilter]")
1180     .Define("assoRecParts",  "Take(ReconstructedChargedParticles,ReconstructedChargedParticleAssociations.recID)[assoFilter]")
1181     .Define("filtMCEta",     getEta<MCP>   , {"filtMCParts"} )
1182     .Define("filtMCPhi",     getPhi<MCP>   , {"filtMCParts"} )
1183     .Define("filtMCp",       getP<MCP>     , {"filtMCParts"} )
1184     .Define("assoMCEta",     getEta<MCP>   , {"assoMCParts"} )
1185     .Define("assoMCPhi",     getPhi<MCP>   , {"assoMCParts"} )
1186     .Define("assoMCp",       getP<MCP>     , {"assoMCParts"} )
1187     .Define("assoRecEta",    getEta<RecoP> , {"assoRecParts"})
1188     .Define("assoRecPhi",    getPhi<RecoP> , {"assoRecParts"})
1189     .Define("assoRecp",      getP<RecoP>   , {"assoRecParts"})
1190     .Define("deltaEta",      "assoMCEta - assoRecEta"        )
1191     .Define("deltaPhi",      "ROOT::VecOps::DeltaPhi(assoRecPhi, assoMCPhi)")
1192     .Define("deltaR",        "ROOT::VecOps::DeltaR(assoRecEta, assoMCEta, assoRecPhi, assoMCPhi)")
1193     .Define("deltaMom",      "assoMCp - assoRecp")
1194     .Define("recoEta",       getEta<RecoP>,  {"ReconstructedChargedParticles"})
1195     .Define("recoPhi",       getPhi<RecoP>,  {"ReconstructedChargedParticles"})
1196     .Define("recoP",         getP<RecoP>,    {"ReconstructedChargedParticles"});
1197 
1198   // 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
1199   auto partEta                = df1.Histo1D({"partEta","#eta of Thrown Charged Particles;#eta",120,-6.,6.},"filtMCEta");
1200   auto matchedPartEta         = df1.Histo1D({"matchedPartEta","#eta of Thrown Charged Particles That Have Matching Track;#eta",120,-6.,6.},"assoMCEta");
1201   auto partMom = df1.Histo1D({"partMom", "Momentum of Thrown Charged Particles (truth); P(GeV/c)", 150, 0, 150}, "filtMCp");
1202   auto matchedPartMom = df1.Histo1D({"matchedPartMom", "Momentum of Thrown Charged Particles (truth), with matching track; P(GeV/c)", 150, 0, 150}, "assoMCp");
1203   auto partPhi = df1.Histo1D({"partPhi", "#phi of Thrown Charged Particles (truth); #phi(rad)", 320, -3.2, 3.2},"filtMCPhi");
1204   auto matchedPartPhi = df1.Histo1D({"matchedPartPhi", "#phi of Thrown Charged Particles (truth), with matching track; #phi(rad)", 320, -3.2, 3.2}, "assoMCPhi");
1205   
1206   auto partPEta = df1.Histo2D({"partPEta", "P vs #eta of Thrown Charged Particles; P(GeV/c); #eta", 150, 0, 150, 120, -6, 6}, "filtMCp", "filtMCEta");
1207   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");
1208   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");
1209   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");
1210   
1211   auto matchedPartTrackDeltaEta = df1.Histo1D({"matchedPartTrackDeltaEta","#Delta#eta Between Matching Thrown and Reconstructed Charged Particle; #Delta#eta", 100, -0.25, 0.25}, "deltaEta");
1212   auto matchedPartTrackDeltaPhi = df1.Histo1D({"matchedPartTrackDeltaPhi","#Detla #phi Between Matching Thrown and Reconstructed Charged Particle; #Delta#phi", 200, -0.2, 0.2}, "deltaPhi");
1213   auto matchedPartTrackDeltaR = df1.Histo1D({"matchedPartTrackDeltaR","#Delta R Between Matching Thrown and Reconstructed Charged Particle;#Delta R",300,0.,0.3}, "deltaR");
1214   auto matchedPartTrackDeltaMom = df1.Histo1D({"matchedPartTrackDeltaMom","#Delta P Between Matching Thrown and Reconstructed Charged Particle; #Delta P", 200, -10, 10}, "deltaMom");
1215     
1216   // Define some histograms for our efficiencies - Done "old school" root style - Maybe the division can be done direct from a DF?
1217   TH1D *TrackEff_Eta = new TH1D("TrackEff_Eta", "Tracking efficiency as fn of #eta; #eta; Eff(%)", 120, -6, 6); 
1218   TH1D *TrackEff_Mom = new TH1D("TrackEff_Mom", "Tracking efficiency as fn of P; P(GeV/c); Eff(%)", 150, 0, 150); 
1219   TH1D *TrackEff_Phi = new TH1D("TrackEff_Phi", "Tracking efficiency as fn of #phi; #phi(rad); Eff(%)", 320, -3.2, 3.2);
1220   // 2D Efficiencies
1221   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);
1222   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);
1223 
1224   auto ChargedEta = df1.Histo1D({"ChargedEta", "#eta of all charged particles; #eta", 120, -6, 6}, "recoEta");
1225   auto ChargedPhi = df1.Histo1D({"ChargedPhi", "#phi of all charged particles; #phi (rad)", 120, -3.2, 3.2}, "recoPhi");
1226   auto ChargedP = df1.Histo1D({"ChargedP", "P of all charged particles; P(GeV/c)", 150, 0, 150}, "recoP");
1227 
1228   // Write histograms to file
1229   TFile *ofile = TFile::Open("EfficiencyAnalysis_Exercise_Out_RDF.root","RECREATE");
1230 
1231   // Booked Define and Histo1D lazy actions are only performed here
1232   partEta->Write();
1233   matchedPartEta->Write();
1234   partPhi->Write();
1235   matchedPartPhi->Write();
1236   partMom->Write();
1237   matchedPartMom->Write();
1238   partPEta->Write();
1239   matchedPartPEta->Write();
1240   partPhiEta->Write();
1241   matchedPartPhiEta->Write();
1242   matchedPartTrackDeltaEta->Write();
1243   matchedPartTrackDeltaPhi->Write();
1244   matchedPartTrackDeltaR->Write();
1245   matchedPartTrackDeltaMom->Write();
1246 
1247   // Create efficiency histograms by dividing appropriately. Note we must actually get the pointer explicitly.
1248   TrackEff_Eta->Divide(matchedPartEta.GetPtr(), partEta.GetPtr(), 1, 1, "b");
1249   TrackEff_Mom->Divide(matchedPartMom.GetPtr(), partMom.GetPtr(), 1, 1, "b");  
1250   TrackEff_Phi->Divide(matchedPartPhi.GetPtr(), partPhi.GetPtr(), 1, 1, "b");
1251   TrackEff_PEta->Divide(matchedPartPEta.GetPtr(), partPEta.GetPtr(), 1, 1, "b");
1252   TrackEff_PhiEta->Divide(matchedPartPhiEta.GetPtr(), partPhiEta.GetPtr(), 1, 1, "b");
1253   
1254   TrackEff_Eta->Write();
1255   TrackEff_Mom->Write();
1256   TrackEff_Phi->Write();
1257   TrackEff_PEta->Write();
1258   TrackEff_PhiEta->Write();
1259 
1260   ChargedEta->Write();
1261   ChargedPhi->Write();
1262   ChargedP->Write();
1263                                 
1264   ofile->Close(); // Close output file
1265 }
1266 ```
1267 Insert your input file path and execute as the example code above.
1268 
1269 > Note:
1270 > - Before writing this example and solution, I have never used RDataFrames before. Some thoughts below.
1271 >   - I find them very difficult to work with. The processes that are "actually" going on are very obscured with RDataFrames.
1272 >   - Finding resources and examples online is much more difficult due to how new RDataFrames are.
1273 >     - This further complicates writing and working with them. 
1274 >   - 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.
1275 >   - The script itself seems to be very slow when running.
1276 >   - 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.
1277 >  - In summary, I personally would *not* recommend using RDataFrames for your analysis (at this point in time).
1278 >  - Note that this just my (SKay) thoughts and opinions though.
1279 {: .callout}
1280 
1281 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.
1282 -->
1283 
1284 {% include links.md %}