EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pgunqa.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file pgunqa.cxx
1 // Create similar QA plots as qaplots.cxx but using a particle gun
2 // This implementation also shows how to smear an individual particle
3 
4 // reusing the same header since it mostly sets up
5 // default parameters
6 #include "qaplots.hh"
7 
8 #include <eicsmear/functions.h>
10 
12 #include "eicsmear/smear/Smear.h"
15 
16 #include "eicsmeardetectors.hh"
17 
18 // Note: The remaining includes are not necessary for eic-smear usage
19 #include <TSystem.h>
20 #include <TRandom.h>
21 #include <TDatabasePDG.h>
22 #include <TFile.h>
23 #include <TChain.h>
24 #include <TCanvas.h>
25 #include <TProfile.h>
26 #include <TLegend.h>
27 #include <TText.h>
28 #include <TStyle.h>
29 
30 #include <string>
31 #include <iomanip>
32 #include <cctype>
33 #include <exception>
34 #include <vector>
35 #include <map>
36 
37 // Convenience only
38 using std::cout;
39 using std::cerr;
40 using std::endl;
41 using std::string;
42 using std::vector;
43 using std::map;
44 
45 // some helpers
46 static void initializepidqabook(const qaparameters& qapars, map<int,pidqacollection>& qabook );
47 static void FillParticleQA( map<int,pidqacollection>& qabook, const Particle* const inParticle, const Smear::ParticleMCS* const inParticleS );
48 static void PlotQA ( const qaparameters& qapars, map<int,pidqacollection>& qabook );
49 
50 static double EtaFromTheta( const double theta );
51 static double ThetaFromEta( const double eta );
52 
53 int main(int argc, char* argv[]){
54 
55  // Parse arguments
56  // ---------------
57  // Defaults are set in qaplots.h
58  qaparameters qapars = ParseArguments ( argc, argv );
59 
60  // First try to instantiate the detector
61  // to avoid pointlessly transforming if that doesn't work
62  // ------------------------------------------------------
63  Smear::Detector detector;
64  if ( qapars.beam_mom_nn < 0 ) detector = BuildByName(qapars.detstring);
65  else detector = BuildByName(qapars.detstring, qapars.beam_mom_nn);
66 
67  if ( detector.GetNDevices() == 0 ) {
68  cerr << "Detector sepcified as " << qapars.detstring
69  << " not recognized or empty." << endl;
70  return -1;
71  }
72 
73  // Open histo root file and book histograms
74  // ----------------------------------------
75  TFile * outfile = new TFile ( qapars.outfilebase + ".pgun." + qapars.detstring + ".root", "RECREATE");
76 
77  // A collection of histos and maybe other info for every pid
78  // pidqacollection is defined in the header
79  map<int,pidqacollection> qabook;
80  // By default, use the standard particles
81  if ( qapars.pids.size() == 0 ) qapars.pids = { 11, 211, 321, 2212, 2112 }; // e, pi, K, p, n
82  initializepidqabook ( qapars, qabook );
83 
84  // the initialization routine determines ranges,
85  // use those for range limits
86  TH2D* h = qabook[ qapars.pids[0] ].dPhi_p;
87  // reject super low momenta
88  double pmin = std::max ( h->GetXaxis()->GetXmin(), 0.1);
89  double pmax = h->GetXaxis()->GetXmax();
90 
91  // push theta away a litle bit from 0, pi
92  double eps = 1e-5;
93  h = qabook[ qapars.pids[0] ].DelP_th;
94  double thmin = std::max ( h->GetXaxis()->GetXmin(), eps );
95  double thmax = std::min ( h->GetXaxis()->GetXmax(), TMath::Pi() - eps);
96  // want to generate in eta though
97  double etamin = EtaFromTheta(thmin);
98  double etamax = EtaFromTheta(thmax);
99 
100  double phimin = -TMath::Pi();
101  double phimax = TMath::Pi();
102 
103  // helper for mass, charge
104  TDatabasePDG* db = TDatabasePDG::Instance();
105 
106  // -------------------
107  // Loop over Particles
108  // -------------------
109  Particle* inParticle=nullptr;
110  Smear::ParticleMCS* inParticleS=nullptr;
111  for(int np=0; np<qapars.nparticles; np++){
112  if ( !(np%20000) ) cout << "Generated " << np*qapars.pids.size() << " particles" << endl;
113 
114  for ( auto pid : qapars.pids ){
115  // helper
116  TParticlePDG* pdg_particle = db->GetParticle(pid);
117 
118  // random charge
119  if ( std::abs(pdg_particle->Charge()) <1e-1){
120  if ( gRandom->Integer(2) == 1) pid = -pid;
121  }
122  // Create not smeared particle
123  // flat in phi, eta, pt (!)
124 
125  // double eta = gRandom->Uniform (etamin,etamax);
126  double theta = gRandom->Uniform (thmin,thmax);
127  double eta = EtaFromTheta(theta);
128 
129  double phi = gRandom->Uniform (phimin,phimax);
130  // double mom = gRandom->Uniform (pmin,pmax);
131  // double pt = mom * sin (theta);
132  double pt=-1;
133  double mom=-1;
134  do{
135  pt = gRandom->Uniform (0,pmax);
136  mom = pt / sin ( ThetaFromEta(eta) );
137  // cout << pt << " " << mom << endl;
138  } while ( mom < pmin || mom > pmax);
139 
140  TLorentzVector input_vect;
141  input_vect.SetPtEtaPhiM ( pt, eta , phi, pdg_particle->Mass());
142 
143  if ( inParticle ) delete inParticle;
144  inParticle = new Particle();
145  inParticle->SetId(pid); // PDG particle code
146  inParticle->SetIndex(0); // Particle index in event
147  inParticle->SetStatus(1); // Particle status code: like in PYTHIA, Beagle, etc
148  inParticle->Set4Vector(input_vect);
149 
150  // Smear the particle
151  if ( inParticleS ) delete inParticleS;
152  inParticleS = detector.Smear( *inParticle);
153 
154  // Particle was not smeared
155  if( !inParticleS ) continue;
156 
157  // ----------------
158  // Particle-wise QA
159  // ----------------
160  // Following function just contains many statements of the form
161  // coll.dEta_p->Fill(inParticle->GetP(), inParticle->GetEta() - inParticleS->GetEta());
162  FillParticleQA( qabook, inParticle, inParticleS );
163  }
164  }
165  if ( inParticle ) delete inParticle;
166  if ( inParticleS ) delete inParticleS;
167 
168  // NOTE: The remainder of this long file is tedious and explicit creation and filling of histograms
169  // The Fill*QA functions demonstrate how to access values in detail
170  PlotQA( qapars, qabook );
171  outfile->Write();
172 
173  return 0;
174 }
175 
176 // -----------------------------------------------------------------------------
177 // -----------------------------------------------------------------------------
178 
179 void FillParticleQA( map<int,pidqacollection>& qabook, const Particle* const inParticle, const Smear::ParticleMCS* const inParticleS ){
180 
181  // If any component is smeared, all others are either smeared or 0 (meaning "not detected").
182 
183  // Fill histograms
184  for ( auto& pidcoll : qabook ){
185  auto& pid = pidcoll.first;
186  auto& coll = pidcoll.second;
187  if ( pid==0 || inParticle->GetPdgCode() == pid ){
188 
189  if ( inParticleS->IsPSmeared() ){
190  auto delP = (inParticle->GetP() - inParticleS->GetP()) / inParticle->GetP();
191  coll.DelP_th->Fill(inParticle->GetTheta(), delP);
192  coll.DelP_eta->Fill(inParticle->GetEta(), delP);
193  }
194 
195  if ( inParticleS->IsESmeared() ){
196  auto delE = (inParticle->GetE() - inParticleS->GetE()) / inParticle->GetE();
197  coll.DelE_E->Fill(inParticle->GetE(), delE);
198  coll.DelE_th->Fill(inParticle->GetTheta(), delE);
199  coll.DelE_eta->Fill(inParticle->GetEta(), delE);
200  }
201 
202  if ( inParticleS->IsThetaSmeared() ){
203  coll.dTh_p->Fill(inParticle->GetP(), inParticle->GetTheta() - inParticleS->GetTheta());
204  coll.dEta_p->Fill(inParticle->GetP(), inParticle->GetEta() - inParticleS->GetEta());
205  }
206 
207  if ( inParticleS->IsPhiSmeared() ){
208  coll.dPhi_p->Fill(inParticle->GetP(), inParticle->GetPhi() - inParticleS->GetPhi());
209  }
210  }
211  }
212 }
213 
214 // -----------------------------------------------------------------------------
215 
216 qaparameters ParseArguments ( int argc, char* argv[] ){
217  vector<string> arguments(argv + 1, argv + argc);
218  bool argsokay=true;
219  qaparameters qapars;
220  try{
221  for ( auto parg = arguments.begin() ; parg!=arguments.end() ; ++parg){
222  string arg=*parg;
223  if ( arg == "-h" ){
224  argsokay=false;
225  break;
226  } else if ( arg == "-o" ){
227  if (++parg == arguments.end() ){ argsokay=false; break; }
228  qapars.outfilebase=*parg;
229  } else if ( arg == "-N" ){
230  if (++parg==arguments.end() ){ argsokay=false; break; }
231  qapars.nparticles=std::stoi(parg->data());
232  } else if ( arg == "-addpid" ){
233  if ( ++parg == arguments.end() ){ argsokay=false; break; }
234  qapars.pids.push_back(std::stoi(parg->data()));
235  } else if ( arg == "-det" ){
236  if (++parg == arguments.end() ){ argsokay=false; break; }
237  qapars.detstring=*parg;
238  for (auto & c: qapars.detstring) c = toupper(c);
239  if ( TString(qapars.detstring).Contains("MATRIX") && TString(qapars.detstring).Contains("FF")){
240  if (++parg == arguments.end() ){ argsokay=false; break; }
241  qapars.beam_mom_nn = std::stoi(parg->data());
242  }
243  if ( TString(qapars.detstring).Contains("CORE") && !TString(qapars.detstring).Contains("B")){
244  if (++parg == arguments.end() ){ argsokay=false; break; }
245  qapars.beam_mom_nn = std::stod(parg->data());
246  }
247  } else {
248  argsokay=false;
249  break;
250  }
251  }
252  } catch ( const std::exception& e){
253  cerr << "Caught exception during argument parsing: "
254  << e.what() << endl;
255  argsokay=false;
256  }
257 
258  if ( !argsokay ) {
259  cerr << "usage: " << argv[0] << endl
260  << " [-o OutFileBase] (extension will be added)" << endl
261  << " [-N #particles per pid]" << endl
262  << " [-addpid pid] (can be called multiple times)" << endl
263  << " [-det detstring] matrix, matrixff [beam_mom_nn], handbook, perfect, beast, ephenix, zeus, jleic (capitalization does not matter.)" << endl
264  << endl;
265  throw std::runtime_error("Not a valid list of options");
266  }
267  for (auto & c: qapars.detstring) c = toupper(c);
268 
269  return qapars;
270 }
271 
272 // ---------------------------------------------------------------
273 void initializepidqabook(const qaparameters& qapars, map<int,pidqacollection>& qabook ){
274  gStyle->SetHistLineColor(kRed); // for Profiles
275 
276  TString s;
277  float pmin = 0;
278  float pmax = 20;
279  int pbins = 80;
280 
281  float dpmin = -0.15;
282  float dpmax = 0.15;
283  int dpbins = 100;
284 
285  float emin = 0;
286  float emax = 20;
287  int ebins = 80;
288 
289  float demin = -1.5;
290  float demax = 1.5;
291  int debins = 100;
292 
293  float thmin = 0;
294  float thmax = TMath::Pi();
295  int thbins = 64;
296 
297  float dthmin = -0.1;
298  float dthmax = 0.1;
299  int dthbins = 100;
300 
301  float etamin = -5;
302  float etamax = 5;
303  int etabins = 100;
304 
305  float detamin = -0.1;
306  float detamax = 0.1;
307  int detabins = 100;
308 
309  double phimin = -TMath::Pi();
310  double phimax = TMath::Pi();
311  int phibins = 64;
312 
313  float dphimin = -0.1;
314  float dphimax = 0.1;
315  int dphibins = 100;
316 
317  for ( auto pid : qapars.pids ){
318  pid = abs ( pid ); // ignoring charge
319 
320  s = qapars.detstring + "_DelE_E_"; s += pid;
321  qabook[pid].DelE_E = new TH2D( s,s+";E;#Delta E/E", ebins, emin, emax, debins, demin, demax);
322 
323  s = qapars.detstring + "_dPhi_p_"; s += pid;
324  qabook[pid].dPhi_p = new TH2D( s,s+";p;#Delta#phi", pbins, pmin, pmax, dphibins, dphimin, dphimax );
325 
326  s = qapars.detstring + "_DelP_th_"; s += pid;
327  qabook[pid].DelP_th = new TH2D( s,s+";#theta;#Delta p/p", thbins, thmin, thmax, dpbins, dpmin, dpmax);
328 
329  s = qapars.detstring + "_DelE_th_"; s += pid;
330  qabook[pid].DelE_th = new TH2D( s,s+";#theta;#Delta E/E", thbins, thmin, thmax, debins, demin, demax);
331 
332  s = qapars.detstring + "_dTh_p_"; s += pid;
333  qabook[pid].dTh_p = new TH2D( s,s+";p;#Delta#theta", pbins, pmin, pmax, dthbins, dthmin, dthmax );
334 
335  s = qapars.detstring + "_DelP_eta_"; s += pid;
336  qabook[pid].DelP_eta = new TH2D( s,s+";#eta;#Delta p/p", etabins, etamin, etamax, dpbins, dpmin, dpmax);
337 
338  s = qapars.detstring + "_DelE_eta_"; s += pid;
339  qabook[pid].DelE_eta = new TH2D( s,s+";#eta;#Delta E/E", etabins, etamin, etamax, debins, demin, demax);
340 
341  s = qapars.detstring + "_dEta_p_"; s += pid;
342  qabook[pid].dEta_p = new TH2D( s,s+";p;#Delta#eta", pbins, pmin, pmax, detabins, detamin, detamax );
343 
344  }
345 
346 }
347 
348 // ---------------------------------------------------------------
349 void PlotQA ( const qaparameters& qapars, map<int,pidqacollection>& qabook ){
350 
351  // Stat position and size
352  // -----------------------
353  gStyle->SetStatX(0.55);
354  gStyle->SetStatW(0.15);
355  gStyle->SetStatY(0.9);
356  gStyle->SetStatH(0.15);
357 
358 
359  // Suppress obnoxious flood of
360  // "Current canvas added to pdf file"
361  gErrorIgnoreLevel = kWarning;
362 
363  // prep a pdf collection
364  new TCanvas;
365  auto pdfname = qapars.outfilebase + ".pgun." + qapars.detstring + ".pdf";
366  gPad->SaveAs( pdfname + "[" );
367 
368  // particle QA
369  // -----------
370  for ( auto& pidcoll : qabook ){
371  auto& pid = pidcoll.first;
372  auto& coll = pidcoll.second;
373 
374  // option "s" in Profile shows rms
375 
376  coll.DelP_th->Draw("colz");
377  gPad->SaveAs( pdfname );
378 
379  coll.DelP_eta->Draw("colz");
380  coll.DelP_eta->ProfileX("_px",1,-1,"s")->Draw("same");
381  gPad->SaveAs( pdfname );
382 
383  gStyle->SetStatX(0.25); // reposition stat box
384  coll.DelE_th->Draw("colz");
385  gPad->SaveAs( pdfname );
386 
387  coll.DelE_eta->Draw("colz");
388  coll.DelE_eta->ProfileX("_px",1,-1,"s")->Draw("same");
389  gPad->SaveAs( pdfname );
390  gStyle->SetStatX(0.55); // reposition stat box
391 
392  coll.DelE_E->Draw("colz");
393  coll.DelE_E->ProfileX("_px",1,-1,"s")->Draw("same");
394  gPad->SaveAs( pdfname );
395 
396  coll.dTh_p->Draw("colz");
397  gPad->SaveAs( pdfname );
398 
399  coll.dEta_p->Draw("colz");
400  coll.dEta_p->ProfileX("_px",1,-1,"s")->Draw("same");
401  gPad->SaveAs( pdfname );
402 
403  coll.dPhi_p->Draw("colz");
404  coll.dPhi_p->ProfileX("_px",1,-1,"s")->Draw("same");
405  gPad->SaveAs( pdfname );
406  }
407 
408  // return to standard warning level
409  gErrorIgnoreLevel = kInfo;
410 
411  // close the pdf collection
412  gPad->SaveAs( pdfname + "]" );
413 }
414 // ---------------------------------------------------------------
415 double EtaFromTheta( const double theta ) {
416  return log ( tan ( 0.5*theta ));
417 };
418 // -------------------------------------------------------------------
419 double ThetaFromEta( const double eta ) {
420  if ( !std::isnan(eta) && !std::isinf(eta) ) {
421  return 2.0 * atan( exp( -eta ));
422  }
423  throw std::runtime_error("ThetaFromEta called with NaN or Inf");
424  return -1;
425 }