EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ffqaplots.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file ffqaplots.cxx
1 // Based on qaplots.cxx with a more focused selection
2 // of far forward plots
3 // It's assumed you've already created the input root file
4 // for example with qaplots. Otherwise uncomment the
5 // BuildTree command below
6 
7 
8 
9 #include "ffqaplots.hh"
10 
11 #include <eicsmear/functions.h>
13 
18 #include "eicsmear/smear/EventS.h"
19 #include "eicsmear/smear/Smear.h"
22 
23 #include "eicsmeardetectors.hh"
24 
25 // Note: The remaining includes are not necessary for eic-smear usage
26 #include <TSystem.h>
27 #include <TDatabasePDG.h>
28 #include <TFile.h>
29 #include <TChain.h>
30 #include <TCanvas.h>
31 #include <TProfile.h>
32 #include <TLegend.h>
33 #include <TText.h>
34 #include <TStyle.h>
35 
36 #include <string>
37 #include <iomanip>
38 #include <cctype>
39 #include <exception>
40 #include <vector>
41 #include <map>
42 
43 // Convenience only
44 using std::cout;
45 using std::cerr;
46 using std::endl;
47 using std::string;
48 using std::vector;
49 using std::map;
50 
51 // some helpers
52 static const TString getrootname(const qaparameters& qapars );
53 static void initializepidqabook(const qaparameters& qapars, map<int,pidqacollection>& qabook );
54 static void initializeeventqa(const qaparameters& qapars, eventqacollection& eventqa );
55 static void FillEventQA( eventqacollection& eventqa , const erhic::EventMC* const inEvent, const Smear::Event* const inEventS );
56 static void FillParticleQA( map<int,pidqacollection>& qabook,
57  const Particle* const inParticle,
58  const Smear::ParticleMCS* const inParticleS,
59  const qaparameters& qapars);
60 static void PlotQA ( const qaparameters& qapars, eventqacollection& eventqa, map<int,pidqacollection>& qabook );
61 
62 int main(int argc, char* argv[]){
63 
64  // Parse arguments
65  // ---------------
66  // Defaults are set in qaplots.h
67  qaparameters qapars = ParseArguments ( argc, argv );
68 
69  // Set up output name
70  TString rootname = getrootname(qapars);
71 
72  // First try to instantiate the detector
73  // to avoid pointlessly transforming if that doesn't work
74  // ------------------------------------------------------
75  Smear::Detector detector;
76  if ( qapars.beam_mom_nn < 0 ) detector = BuildByName(qapars.detstring);
77  else detector = BuildByName(qapars.detstring, qapars.beam_mom_nn);
78  if ( detector.GetNDevices() == 0 ) {
79  cerr << "Detector sepcified as " << qapars.detstring
80  << " with beam_mom_nn=" << qapars.beam_mom_nn
81  << " not recognized or empty." << endl;
82  return -1;
83  }
84 
85  // Convert input file to tree
86  // --------------------------
87  if ( gSystem->AccessPathName( rootname ) ){ // quoting the ROOT documentation: "Attention, bizarre convention of return value!!"
88  cout << " ======================= " << endl;
89  cout << " Transforming input file " << endl
90  << qapars.txtfilename << endl
91  << " into root file " << endl
92  << rootname << endl;
93  auto buildresult = BuildTree(qapars.txtfilename.c_str(), qapars.outpath.c_str(), qapars.nevents);
94  if ( buildresult !=0 ){
95  cerr << "Failed to build a tree from " << qapars.txtfilename << endl;
96  return buildresult;
97  }
98  } else {
99  cout << " ======================= " << endl;
100  cout << " Reusing existing root file " << rootname << endl;
101  }
102 
103  // Smear the tree
104  // --------------
105  TString smearedname = rootname;
106  smearedname.ReplaceAll (".root",".smeared.root" );
107  // Can disable warnings here.
108  // Many warnings are harmless ( x or y can be smeared to values >1)
109  // But it is recommended to leave it on and follow up on "inf", "nan" etc. if you test a new detector
111  SmearTree( detector, rootname.Data(), smearedname.Data());
112 
113  // -------------
114  // Load the tree
115  // -------------
116  TChain* inTree = new TChain("EICTree");
117  inTree->Add(rootname);
118  inTree->AddFriend("Smeared",smearedname);
119 
120  // Setup Input Event Buffer
121  erhic::EventMC* inEvent(NULL);
122  Smear::Event* inEventS(NULL);
123  inTree->SetBranchAddress("event",&inEvent);
124  inTree->SetBranchAddress("eventS",&inEventS);
125 
126  // Open histo root file and book histograms
127  // ----------------------------------------
128  TFile * outfile = new TFile ( qapars.outfilebase + qapars.detstring + "_" + qapars.beam_mom_nn + ".root", "RECREATE");
129 
130  // A collection of event-wise qa plots, like kinematics
131  // eventqacollection is defined in the header
132  eventqacollection eventqa = {}; // this syntax initializes everything to 0
133  initializeeventqa ( qapars, eventqa );
134 
135  // We'll also have a collection of histos and maybe other info for every pid
136  // pidqacollection is defined in the header
137  map<int,pidqacollection> qabook;
138  // By default, use the standard particles
139  if ( qapars.pids.size() == 0 ) qapars.pids = { 2212, 2112 }; // p, n
140  // if ( qapars.pids.size() == 0 ) qapars.pids = { 2212, 2112, 22, 130, 3122 }; // p, n, gamma, K0L, Lambda0
141  initializepidqabook ( qapars, qabook );
142 
143  // -------------
144  // Analysis loop
145  // -------------
146  for(long iEvent=0; iEvent<inTree->GetEntries(); iEvent++){
147 
148  //Read next Event
149  if(inTree->GetEntry(iEvent) <=0) break;
150  if(iEvent%10000 == 0) cout << "Event " << iEvent << endl;
151 
152  // -------------
153  // event-wise QA
154  // -------------
155  // Following function just contains many statements of the form
156  // if ( inEventS->GetQ2()>0 ) eventqa.Q2_NM->Fill ( std::log10(inEvent->GetQ2()), std::log10(inEventS->GetQ2()));
157  FillEventQA( eventqa, inEvent, inEventS );
158 
159  // -------------------
160  // Loop over Particles
161  // -------------------
162  for(int j=0; j<inEventS->GetNTracks(); j++){
163  // Skip beam
164  if ( j<3 ) continue;
165 
166  const Smear::ParticleMCS* inParticleS = inEventS->GetTrack(j); // Smeared Particle
167  const Particle* inParticle = inEvent->GetTrack(j); // Unsmeared Particle
168 
169  // Skip non-final particles.
170  if ( inParticle->GetStatus() != 1 ) continue;
171 
172  // Particle was not smeared
173  if(inParticleS == NULL) continue;
174 
175  // ----------------
176  // Particle-wise QA
177  // ----------------
178  // Following function just contains many statements of the form
179  // coll.dEta_p->Fill(inParticle->GetP(), inParticle->GetEta() - inParticleS->GetEta());
180  FillParticleQA( qabook, inParticle, inParticleS, qapars );
181  }
182  }
183 
184  // NOTE: The remainder of this long file is tedious and explicit creation and filling of histograms
185  // The Fill*QA functions demonstrate how to access values in detail, but this is the end of the
186  // basic work flow to use eic-smear from start to finish!
187 
188  qapars.usedevents = inTree->GetEntries();
189  PlotQA( qapars, eventqa, qabook );
190  outfile->Write();
191 
192  return 0;
193 }
194 
195 // -----------------------------------------------------------------------------
196 // -----------------------------------------------------------------------------
197 
198 void FillEventQA( eventqacollection& eventqa , const erhic::EventMC* const inEvent, const Smear::Event* const inEventS ){
199 
200  // Q2
201  // if ( eventqa.Q2_JB && inEventS->GetQ2JacquetBlondel()>0 ) eventqa.Q2_JB->Fill ( std::log10(inEvent->GetQ2()), std::log10(inEventS->GetQ2JacquetBlondel()));
202  // else eventqa.missedQ2_JB++;
203  // if ( eventqa.Q2_DA && inEventS->GetQ2DoubleAngle()>0 ) eventqa.Q2_DA->Fill ( std::log10(inEvent->GetQ2()), std::log10(inEventS->GetQ2DoubleAngle()));
204  // else eventqa.missedQ2_DA++;
205 
206  // y
207  // if ( eventqa.y_JB && inEventS->GetYJacquetBlondel()>0 ) eventqa.y_JB->Fill ( inEvent->GetY(), inEventS->GetYJacquetBlondel());
208  // else eventqa.missedy_JB++;
209  // if ( eventqa.y_DA && inEventS->GetYDoubleAngle()>0 ) eventqa.y_DA->Fill ( inEvent->GetY(), inEventS->GetYDoubleAngle());
210  // else eventqa.missedy_DA++;
211 
212  // x
213  // if ( eventqa.x_JB && inEventS->GetXJacquetBlondel()>0 ) eventqa.x_JB->Fill ( std::log10(inEvent->GetX()), std::log10(inEventS->GetXJacquetBlondel()));
214  // else eventqa.missedx_JB++;
215  // if ( eventqa.x_DA && inEventS->GetXDoubleAngle()>0 ) eventqa.x_DA->Fill ( std::log10(inEvent->GetX()), std::log10(inEventS->GetXDoubleAngle()));
216  // else eventqa.missedx_DA++;
217 
218 }
219 // ---------------------------------------------------------------
220 void FillParticleQA( map<int,pidqacollection>& qabook,
221  const Particle* const inParticle,
222  const Smear::ParticleMCS* const inParticleS,
223  const qaparameters& qapars){
224  // If any component is smeared, all others are either smeared or 0 (meaning "not detected").
225  // Could detect the latter case (with a given accuracy):
226  // const double epsilon = 1e-9;
227 
228  // Fill histograms
229  for ( auto& pidcoll : qabook ){
230  auto& pid = pidcoll.first;
231  auto& coll = pidcoll.second;
232  if ( pid==0 || inParticle->GetPdgCode() == pid ){
233 
234  auto th = inParticle->GetTheta();
235 
236  auto P = inParticle->GetP();
237  coll.P_th->Fill(th, P);
238 
239  auto Pt = inParticle->GetPt();
240  coll.Pt_th->Fill(th, Pt);
241 
242  auto xL = inParticle->GetP() / qapars.beam_mom_nn;
243  coll.xL_th->Fill(th, xL);
244 
245  coll.Phi_theta->Fill(th, inParticle->GetPhi());
246  }
247  }
248 }
249 
250 // -----------------------------------------------------------------------------
251 
252 qaparameters ParseArguments ( int argc, char* argv[] ){
253  vector<string> arguments(argv + 1, argv + argc);
254  bool argsokay=true;
255  qaparameters qapars;
256  try{
257  for ( auto parg = arguments.begin() ; parg!=arguments.end() ; ++parg){
258  string arg=*parg;
259  if ( arg == "-h" ){
260  argsokay=false;
261  break;
262  } else if ( arg == "-o" ){
263  if (++parg == arguments.end() ){ argsokay=false; break; }
264  qapars.outfilebase=*parg;
265  } else if ( arg == "-i" ){
266  if (++parg ==arguments.end() ){ argsokay=false; break; }
267  qapars.txtfilename=*parg;
268  } else if ( arg == "-N" ){
269  if (++parg==arguments.end() ){ argsokay=false; break; }
270  qapars.nevents=std::stoi(parg->data());
271  } else if ( arg == "-addpid" ){
272  if ( ++parg == arguments.end() ){ argsokay=false; break; }
273  qapars.pids.push_back(std::stoi(parg->data()));
274  } else if ( arg == "-det" ){
275  if (++parg == arguments.end() ){ argsokay=false; break; }
276  qapars.detstring=*parg;
277  for (auto & c: qapars.detstring) c = toupper(c);
278  if ( TString(qapars.detstring).Contains("MATRIX") && TString(qapars.detstring).Contains("FF")){
279  if (++parg == arguments.end() ){ argsokay=false; break; }
280  qapars.beam_mom_nn = std::stoi(parg->data());
281  }
282  } else {
283  argsokay=false;
284  break;
285  }
286  }
287  } catch ( const std::exception& e){
288  cerr << "Caught exception during argument parsing: "
289  << e.what() << endl;
290  argsokay=false;
291  }
292 
293  if ( !argsokay ) {
294  cerr << "usage: " << argv[0] << endl
295  << " [-i txtfilename] (Lund-style file)" << endl
296  << " [-o OutFileBase] (extension will be added)" << endl
297  << " [-N Nevents] (<0 for all)" << endl
298  << " [-addpid pid] (can be called multiple times)" << endl
299  << " [-det detstring] matrix, matrixff [beam_mom_nn], handbook, perfect, beast, ephenix, zeus, jleic (capitalization does not matter.)" << endl
300  << endl;
301  throw std::runtime_error("Not a valid list of options");
302  }
303  for (auto & c: qapars.detstring) c = toupper(c);
304 
305  return qapars;
306 }
307 
308 // ---------------------------------------------------------------
309 const TString getrootname(const qaparameters& qapars ){
310  // The root file name is created by replacing the extension by ".root"
311 
312  TString rootname = qapars.txtfilename;
313 
314  // Remove zip extension, if there is one.
315  if ( rootname.EndsWith(".gz", TString::kIgnoreCase) ||
316  rootname.EndsWith(".zip", TString::kIgnoreCase) )
317  rootname.Replace(rootname.Last('.'), rootname.Length(), "");
318 
319  // Remove the remaining extension, if there is one.
320  if (rootname.Last('.') > -1) {
321  rootname.Replace(rootname.Last('.'), rootname.Length(), "");
322  } // if
323 
324  // BuildTree includes event number in partial transformation
325  if ( qapars.nevents>=0 ) {
326  rootname += ".";
327  rootname += qapars.nevents;
328  rootname += "event";
329  }
330  rootname += ".root";
331  rootname = gSystem->BaseName( rootname );
332  rootname.Prepend( qapars.outpath );
333  return rootname;
334 }
335 // ---------------------------------------------------------------
336 void initializepidqabook(const qaparameters& qapars, map<int,pidqacollection>& qabook ){
337  gStyle->SetHistLineColor(kRed); // for Profiles
338 
339  TString s, t;
340  auto pdgdb=TDatabasePDG::Instance() ;
341 
342  // everything happens between 1e-7 and 20 mrad
343  // will try log bins
344  float thmin = 0;
345  float thmax = 0.025; // some room to the side, endcap starts at 35 mrad or 60 mrad - expand here to see this
346  int thbins = 100;
347  int logthbins = 100;
348 
349  float pmin = 0;
350  float pmax = qapars.beam_mom_nn*1.1; // going a bit above so you can see if your beam_mom_nn is wrong
351  int pbins = 100;
352 
353  float ptmin = 0;
354  float ptmax = 2; // unlikely to exceed 2.5
355  int ptbins = 50;
356 
357  float xLmin = 0;
358  float xLmax = 1.1; // going a bit above so you can see if your beam_mom_nn is wrong
359  int xLbins = 100;
360 
361  float phimin = 0;
362  float phimax = TMath::TwoPi();
363  int phibins = 64;
364 
365  for ( auto pid : qapars.pids ){
366  // pid = abs ( pid );
367  TString pdgname = pdgdb->GetParticle(pid)->GetName();
368  t = qapars.detstring + " " + qapars.beam_mom_nn + " " + pdgname;
369 
370  s = qapars.detstring + "_P_th_"; s += pid;
371  qabook[pid].P_th = new TH2D( s,t+" P vs. #theta;#theta;P", thbins, thmin, thmax, pbins, pmin, pmax);
372 
373  s = qapars.detstring + "_Pt_th_"; s += pid;
374  qabook[pid].Pt_th = new TH2D( s,t+" p_{T} vs. #theta;#theta;Pt", thbins, thmin, thmax, ptbins, ptmin, ptmax);
375 
376  s = qapars.detstring + "_xL_th_"; s += pid;
377  qabook[pid].xL_th = new TH2D( s,t+" x_{L} vs. #theta;#theta;xL", thbins, thmin, thmax, xLbins, xLmin, xLmax);
378 
379  s = qapars.detstring + "_phi_theta_"; s += pid;
380  qabook[pid].Phi_theta = new TH2D( s,t+" #phi vs. #theta;#theta;#phi", thbins, thmin, thmax, phibins, phimin, phimax );
381 
382  }
383 
384 }
385 
386 // ---------------------------------------------------------------
387 void initializeeventqa(const qaparameters& qapars, eventqacollection& eventqa ){
388  // recording log of x, y, Q2
389  // keep around if we want to reactivate them
390  return;
391 
392  // TString s;
393 
394  // float Q2min = 1e-3;
395  // float Q2max = 1e5;
396  // int logQ2bins = 240;
397 
398  // s = qapars.detstring + "_LogQ2_JB";
399  // eventqa.Q2_JB = new TH2D( s,s+";log Q^{2};log Q^{2}_{JB}", logQ2bins, std::log10(Q2min), std::log10(Q2max), logQ2bins, std::log10(Q2min), std::log10(Q2max));
400 
401  // s = qapars.detstring + "_LogQ2_DA";
402  // eventqa.Q2_DA = new TH2D( s,s+";log Q^{2};log Q^{2}_{DA}", logQ2bins, std::log10(Q2min), std::log10(Q2max), logQ2bins, std::log10(Q2min), std::log10(Q2max));
403 
404  // float ymin = 0;
405  // float ymax = 1.2; // see y>1 as well
406  // int ybins = 100;
407 
408  // s = qapars.detstring + "_y_JB";
409  // eventqa.y_JB = new TH2D( s,s+";y;y_{JB}", ybins, ymin, ymax, ybins, ymin, ymax);
410 
411  // s = qapars.detstring + "_y_DA";
412  // eventqa.y_DA = new TH2D( s,s+";y;y_{DA}", ybins, ymin, ymax, ybins, ymin, ymax);
413 
414  // float xmin = 1e-4;
415  // float xmax = 1e1; // see x>1 as well
416  // int logxbins = 200;
417 
418  // s = qapars.detstring + "_Logx_JB";
419  // eventqa.x_JB = new TH2D( s,s+";log x;log x_{JB}", logxbins, std::log10(xmin), std::log10(xmax), logxbins, std::log10(xmin), std::log10(xmax));
420 
421  // s = qapars.detstring + "_Logx_DA";
422  // eventqa.x_DA = new TH2D( s,s+";log x;log x_{DA}", logxbins, std::log10(xmin), std::log10(xmax), logxbins, std::log10(xmin), std::log10(xmax));
423 
424 }
425 
426 // ---------------------------------------------------------------
427 void PlotQA ( const qaparameters& qapars, eventqacollection& eventqa, map<int,pidqacollection>& qabook ){
428 
429  // Stat position and size
430  // -----------------------
431  gStyle->SetStatX(0.25);
432  gStyle->SetStatW(0.15);
433  gStyle->SetStatY(0.9);
434  gStyle->SetStatH(0.15);
435 
436  // Position of the "Missed: " box
437  float missx = 0.55;
438  float missy = 0.2;
439  float missy2 = 0.8;
440  TText t;
441  t.SetNDC();
442 
443  // Suppress obnoxious flood of
444  // "Current canvas added to pdf file"
445  gErrorIgnoreLevel = kWarning;
446 
447  // prep a pdf collection
448  new TCanvas;
449  auto pdfname = qapars.outfilebase + qapars.detstring + "_" + qapars.beam_mom_nn + ".pdf";
450  gPad->SaveAs( pdfname + "[" );
451 
452  // event-wise qa
453 
454  // // DA
455  // if ( eventqa.y_DA ) {
456  // eventqa.y_DA->Draw("colz");
457  // t.DrawText( missx,missy, Form("Missed: %ld / %ld",eventqa.missedy_DA, qapars.usedevents));
458  // gPad->SaveAs( pdfname );
459  // }
460  // if ( eventqa.x_DA ) {
461  // eventqa.x_DA->Draw("colz");
462  // t.DrawText( missx,missy, Form("Missed: %ld / %ld",eventqa.missedx_DA, qapars.usedevents));
463  // gPad->SaveAs( pdfname );
464  // }
465  // if ( eventqa.Q2_DA ) {
466  // eventqa.Q2_DA->Draw("colz");
467  // t.DrawText( missx,missy, Form("Missed: %ld / %ld",eventqa.missedQ2_DA, qapars.usedevents));
468  // gPad->SaveAs( pdfname );
469  // }
470  // // JB
471  // if ( eventqa.y_JB ) {
472  // eventqa.y_JB->Draw("colz");
473  // t.DrawText( missx,missy, Form("Missed: %ld / %ld",eventqa.missedy_JB, qapars.usedevents));
474  // gPad->SaveAs( pdfname );
475  // }
476  // if ( eventqa.x_JB ) {
477  // eventqa.x_JB->Draw("colz");
478  // t.DrawText( missx,missy, Form("Missed: %ld / %ld",eventqa.missedx_JB, qapars.usedevents));
479  // gPad->SaveAs( pdfname );
480  // }
481  // if ( eventqa.Q2_JB ) {
482  // eventqa.Q2_JB->Draw("colz");
483  // t.DrawText( missx,missy, Form("Missed: %ld / %ld",eventqa.missedQ2_JB, qapars.usedevents));
484  // gPad->SaveAs( pdfname );
485  // }
486 
487  // particle QA
488  // -----------
489  // reposition stat box
490  gStyle->SetStatX(0.89);
491  gStyle->SetStatW(0.15);
492  gStyle->SetStatY(0.89);
493  gStyle->SetStatH(0.15);
494 
495  // iterating over the qabook is numerically sorted by pid
496  // We'd rather use the order specified in the pids vector
497  // for ( auto& pidcoll : qabook ){
498  // auto& pid = pidcoll.first;
499  // auto& coll = pidcoll.second;
500 
501  for ( auto& pid : qapars.pids ){
502  auto& coll = qabook[pid];
503 
504  coll.Phi_theta->Draw("colz");
505  gPad->SaveAs( pdfname );
506 
507  coll.P_th->Draw("colz");
508  gPad->SaveAs( pdfname );
509 
510  coll.Pt_th->Draw("colz");
511  gPad->SaveAs( pdfname );
512 
513  coll.xL_th->Draw("colz");
514  gPad->SaveAs( pdfname );
515 
516  }
517 
518  // return to standard warning level
519  gErrorIgnoreLevel = kInfo;
520 
521  // close the pdf collection
522  gPad->SaveAs( pdfname +"]" );
523 }
524 // ---------------------------------------------------------------