MaCh3  2.8.0
Reference Guide
HistogramUtils.cpp
Go to the documentation of this file.
2 
4 // ROOT include
5 #include "TList.h"
6 #include "TObjArray.h"
7 #include "TObjString.h"
9 
10 // **************************************************
11 //KS: ROOT changes something with binning when moving from ROOT 5 to ROOT 6. If you open ROOT5 produced file with ROOT6 you will be missing 9 last bins
12 // However if you use ROOT6 and have ROOT6 file exactly the same code will work. Something have changed with how TH2Poly bins are stored in TFile
13 void CheckTH2PolyFileVersion(TFile *file) {
14 // **************************************************
15  int FileROOTVersion = file->GetVersion();
16  int MainFileROOTVersion = FileROOTVersion;
17 
18  // Remove last digit from number
19  // till only one digit is left
20  while (MainFileROOTVersion >= 10)
21  MainFileROOTVersion /= 10;
22 
23  std::string SystemROOTVersion = std::string(ROOT_RELEASE);
24  int MainSystemROOTVersion = SystemROOTVersion.at(0) - '0';
25 
26  if(MainFileROOTVersion != MainSystemROOTVersion)
27  {
28  MACH3LOG_ERROR("File was produced with: {} ROOT version", FileROOTVersion);
29  MACH3LOG_ERROR("Found: {} ROOT version in the system", SystemROOTVersion);
30  MACH3LOG_ERROR("For some documentation please visit me");
31  throw MaCh3Exception(__FILE__, __LINE__);
32  }
33 }
34 
35 // **************************************************
36 //WP: Helper function for calculating unbinned Integral of TH2Poly i.e including overflow
37 double OverflowIntegral(TH2Poly* poly) {
38 // **************************************************
39  double overflow = 0.;
40  //TH2Polys have 9 overflow bins
41  for(int iOverflow = -1; iOverflow > -10; iOverflow--)
42  {
43  overflow += poly->GetBinContent(iOverflow);
44  }
45  double IntegralUn = NoOverflowIntegral(poly) + overflow;
46 
47  return IntegralUn;
48 } // end function
49 
50 // **************************************************
51 //WP: Helper function for calculating binned Integral of TH2Poly i.e not including overflow
52 double NoOverflowIntegral(TH2Poly* poly) {
53 // **************************************************
54  double integral = 0.;
55  for(int i = 1; i < poly->GetNumberOfBins()+1; i++)
56  {
57  integral += poly->GetBinContent(i);
58  }
59 
60  return integral;
61 } // end function
62 
63 // **************************************************
64 //WP: Helper function for projecting TH2Poly onto the X axis
65 TH1D* PolyProjectionX(TObject* poly, const std::string& TempName, const std::vector<double>& xbins, const bool computeErrors) {
66 // **************************************************
67  TH1D* hProjX = new TH1D((TempName+"_x").c_str(),(TempName+"_x").c_str(), int(xbins.size()-1), &xbins[0]);
68 
69  //KS: Temp Histogram to store error, use double as this is thread safe
70  std::vector<double> hProjX_Error(hProjX->GetXaxis()->GetNbins() + 1, 0.0);
71  double xlow, xup, frac = 0.;
72 
73  //loop over bins in the poly
74  for (int i = 0; i < static_cast<TH2Poly*>(poly)->GetNumberOfBins(); i++)
75  {
76  //get bin and its edges
77  TH2PolyBin* bin = static_cast<TH2PolyBin*>(static_cast<TH2Poly*>(poly)->GetBins()->At(i));
78  xlow = bin->GetXMin();
79  xup = bin->GetXMax();
80 
81  //Loop over projected bins, find fraction of poly bin in each
82  for(int dx = 0; dx < int(xbins.size()); dx++)
83  {
84  if(xbins[dx+1] <= xlow || xbins[dx] >= xup)
85  {
86  frac = 0;
87  }
88  else if(xbins[dx] <= xlow && xbins[dx+1] >= xup)
89  {
90  frac = 1;
91  }
92  else if(xbins[dx] <= xlow && xbins[dx+1] <= xup)
93  {
94  frac = (xbins[dx+1]-xlow)/(xup-xlow);
95  }
96  else if(xbins[dx] >= xlow && xbins[dx+1] >= xup)
97  {
98  frac = (xup-xbins[dx])/(xup-xlow);
99  }
100  else if(xbins[dx] >= xlow && xbins[dx+1] <= xup)
101  {
102  frac = (xbins[dx+1]-xbins[dx])/(xup-xlow);
103  }
104  else
105  {
106  frac = 0;
107  }
108  hProjX->SetBinContent(dx+1,hProjX->GetBinContent(dx+1)+frac*bin->GetContent());
109  //KS: Follow ROOT implementation and sum up the variance
110  if(computeErrors)
111  {
112  //KS: TH2PolyBin doesn't have GetError so we have to use TH2Poly,
113  //but numbering of GetBinError is different than GetBins...
114  const double Temp_Err = frac*static_cast<TH2Poly*>(poly)->GetBinError(i+1)*frac*static_cast<TH2Poly*>(poly)->GetBinError(i+1);
115  hProjX_Error[dx+1] += Temp_Err;
116  }
117  }
118  }
119  //KS: The error is sqrt(summed variance)) https://root.cern.ch/doc/master/TH2_8cxx_source.html#l02266
120  if(computeErrors)
121  {
122  for (int i = 1; i <= hProjX->GetXaxis()->GetNbins(); ++i)
123  {
124  const double Error = std::sqrt(hProjX_Error[i]);
125  hProjX->SetBinError(i, Error);
126  }
127  }
128  return hProjX;
129 } // end project poly X function
130 
131 // **************************************************
132 //WP: Helper function for projecting TH2Poly onto the Y axis
133 TH1D* PolyProjectionY(TObject* poly, const std::string& TempName, const std::vector<double>& ybins, const bool computeErrors) {
134 // **************************************************
135  TH1D* hProjY = new TH1D((TempName+"_y").c_str(),(TempName+"_y").c_str(),int(ybins.size()-1),&ybins[0]);
136  //KS: Temp Histogram to store error, use double as this is thread safe
137  std::vector<double> hProjY_Error(hProjY->GetXaxis()->GetNbins() + 1, 0.0);
138  double ylow, yup, frac = 0.;
139 
140  //loop over bins in the poly
141  for (int i = 0; i < static_cast<TH2Poly*>(poly)->GetNumberOfBins(); i++)
142  {
143  //get bin and its edges
144  TH2PolyBin* bin = static_cast<TH2PolyBin*>(static_cast<TH2Poly*>(poly)->GetBins()->At(i));
145  ylow = bin->GetYMin();
146  yup = bin->GetYMax();
147 
148  //Loop over projected bins, find fraction of poly bin in each
149  for(int dy = 0; dy < int(ybins.size()); dy++)
150  {
151  if(ybins[dy+1]<=ylow || ybins[dy] >= yup)
152  {
153  frac = 0;
154  }
155  else if(ybins[dy] <= ylow && ybins[dy+1] >= yup)
156  {
157  frac = 1;
158  }
159  else if(ybins[dy] <= ylow && ybins[dy+1] <= yup)
160  {
161  frac = (ybins[dy+1]-ylow)/(yup-ylow);
162  }
163  else if(ybins[dy] >= ylow && ybins[dy+1] >= yup)
164  {
165  frac = (yup-ybins[dy])/(yup-ylow);
166  }
167  else if(ybins[dy] >= ylow && ybins[dy+1] <= yup)
168  {
169  frac = (ybins[dy+1]-ybins[dy])/(yup-ylow);
170  }
171  else
172  {
173  frac = 0;
174  }
175  hProjY->SetBinContent(dy+1,hProjY->GetBinContent(dy+1)+frac*bin->GetContent());
176  //KS: Follow ROOT implementation and sum up the variance
177  if(computeErrors)
178  {
179  //KS: TH2PolyBin doesn't have GetError so we have to use TH2Poly,
180  //but numbering of GetBinError is different than GetBins...
181  const double Temp_Err = frac*static_cast<TH2Poly*>(poly)->GetBinError(i+1)*frac*static_cast<TH2Poly*>(poly)->GetBinError(i+1);
182  hProjY_Error[dy+1] += Temp_Err;
183  }
184  }
185  }
186  //KS: The error is sqrt(summed variance)) https://root.cern.ch/doc/master/TH2_8cxx_source.html#l02266
187  if(computeErrors)
188  {
189  for (int i = 1; i <= hProjY->GetXaxis()->GetNbins(); ++i)
190  {
191  const double Error = std::sqrt(hProjY_Error[i]);
192  hProjY->SetBinError(i, Error);
193  }
194  }
195  return hProjY;
196 } // end project poly Y function
197 
198 // ****************
199 //WP: Normalise a th2poly
200 TH2Poly* NormalisePoly(TH2Poly *Histogram) {
201 // ****************
202  TH2Poly* HistCopy = static_cast<TH2Poly*>(Histogram->Clone());
203  double IntegralWidth = PolyIntegralWidth(HistCopy);
204  HistCopy = PolyScaleWidth(HistCopy, IntegralWidth);
205  std::string title = std::string(HistCopy->GetName())+"_norm";
206  HistCopy->SetNameTitle(title.c_str(), title.c_str());
207 
208  return HistCopy;
209 }
210 
211 // ****************
212 // Normalise a TH2Poly
213 void NormaliseTH2Poly(TH2Poly* Histogram){
214 // ****************
215  const double Integral = NoOverflowIntegral(Histogram);
216  for(int j = 1; j < Histogram->GetNumberOfBins()+1; j++)
217  {
218  Histogram->SetBinContent(j, Histogram->GetBinContent(j)/Integral);
219  }
220 }
221 
222 // ****************
223 // Make a ratio histogram
224 template<class HistType>
225 HistType* RatioHists(HistType *NumHist, HistType *DenomHist) {
226 // ****************
227  HistType *NumCopy = static_cast<HistType*>(NumHist->Clone());
228  std::string title = std::string(DenomHist->GetName()) + "_ratio";
229  NumCopy->SetNameTitle(title.c_str(), title.c_str());
230  NumCopy->Divide(DenomHist);
231 
232  return NumCopy;
233 }
234 
235 // ****************
236 // Make a ratio th2poly
237 TH2Poly* RatioPolys(TH2Poly *NumHist, TH2Poly *DenomHist) {
238 // ****************
239  TH2Poly *NumCopy = static_cast<TH2Poly*>(NumHist->Clone());
240  std::string title = std::string(DenomHist->GetName()) + "_ratio";
241  NumCopy->SetNameTitle(title.c_str(), title.c_str());
242 
243  for(int i = 1; i < NumCopy->GetNumberOfBins()+1; ++i) {
244  NumCopy->SetBinContent(i,NumHist->GetBinContent(i)/DenomHist->GetBinContent(i));
245  }
246  return NumCopy;
247 }
248 
249 // ****************
250 TH2D* ConvertTH2PolyToTH2D(TH2Poly *poly, TH2D *h2dhist) {
251 // ****************
252  double xlow, xup, ylow, yup;
253  std::string HistTempName = poly->GetName();
254 
255  HistTempName += "_";
256  //make the th2d
257  TH2D *hist = static_cast<TH2D*>(h2dhist->Clone());
258  hist->SetNameTitle(HistTempName.c_str(), HistTempName.c_str());
259 
260  for(int ix = 0; ix < hist->GetNbinsX() + 2; ix++) {
261  for(int iy = 0; iy < hist->GetNbinsY() + 2; iy++) {
262  hist->SetBinContent(ix, iy, 0);
263  }
264  }
265  //Loop over poly bins, find the corresponding th2d and setbincontent!
266  for(int i = 0; i< poly->GetNumberOfBins(); i++){
267  TH2PolyBin & polybin = static_cast<TH2PolyBin &>(*poly->GetBins()->At(i));
268  xlow = polybin.GetXMin();
269  xup = polybin.GetXMax();
270  ylow = polybin.GetYMin();
271  yup = polybin.GetYMax();
272  int xbin, ybin;
273 
274  xbin = hist->GetXaxis()->FindBin(xlow+(xup-xlow)/2);
275  ybin = hist->GetYaxis()->FindBin(ylow+(yup-ylow)/2);
276 
277  MACH3LOG_TRACE("Poly bin {}, xlow: {}, xup: {}, ylow: {}, yup: {}. Finding bin for ({}, {}). Found Bin ({}, {}) with content {}. But Poly content: {}",
278  i, xlow, xup, ylow, yup, (xlow + (xup - xlow) / 2), (ylow + (yup - ylow) / 2), xbin, ybin, polybin.GetContent(), poly->GetBinContent(i));
279  hist->SetBinContent(xbin, ybin, polybin.GetContent());
280  }
281  return hist;
282 }
283 // ****************
284 TH2Poly* ConvertTH2DtoTH2Poly(TH2D* hist) {
285 // ****************
286  // Make the x axis from the momentum of lepton
287  TAxis* xaxis = hist->GetXaxis();
288  // Make the y axis from the cos theta of lepton
289  TAxis* yaxis = hist->GetYaxis();
290 
291  TString histname = hist->GetName();
292  // Convert TH2D binning to TH2Poly
293  TH2Poly* poly = new TH2Poly();
294  poly->SetName(histname);
295  poly->SetTitle(histname);
296 
297  // Copy axis titles
298  poly->GetXaxis()->SetTitle(xaxis->GetTitle());
299  poly->GetYaxis()->SetTitle(yaxis->GetTitle());
300 
301  double xmax, xmin, ymax, ymin;
302  for (int iy = 1; iy <= yaxis->GetNbins(); iy++) {
303  ymax = yaxis->GetBinUpEdge(iy);
304  ymin = yaxis->GetBinLowEdge(iy);
305  for (int ix = 1; ix <= xaxis->GetNbins(); ix++) {
306  xmax = xaxis->GetBinUpEdge(ix);
307  xmin = xaxis->GetBinLowEdge(ix);
308  double binofx[] = {xmin, xmax, xmax, xmin};
309  double binofy[] = {ymin, ymin, ymax, ymax};
310  poly->AddBin(4, binofx, binofy);
311  }
312  }
313 
314  for (int iy = 1; iy <= yaxis->GetNbins(); iy++) {
315  ymax = yaxis->GetBinUpEdge(iy);
316  ymin = yaxis->GetBinLowEdge(iy);
317  for (int ix = 1; ix <= xaxis->GetNbins(); ix++) {
318  xmax = xaxis->GetBinUpEdge(ix);
319  xmin = xaxis->GetBinLowEdge(ix);
320 
321  // Get the content of the corresponding bin in TH2D and set it in TH2Poly
322  int bin = hist->GetBin(ix, iy);
323  double content = hist->GetBinContent(bin);
324  poly->SetBinContent(poly->FindBin((xmin + xmax) / 2, (ymin + ymax) / 2), content);
325  }
326  }
327 
328  return poly;
329 }
330 
331 // ****************
332 //WP: Scale a TH2Poly and divide by bin width
333 TH2Poly* PolyScaleWidth(TH2Poly *Histogram, double scale) {
334 // ****************
335  TH2Poly* HistCopy = static_cast<TH2Poly*>(Histogram->Clone());
336  double xlow, xup, ylow, yup, area;
337 
338  for(int i = 1; i < HistCopy->GetNumberOfBins()+1; i++)
339  {
340  TH2PolyBin* bin = static_cast<TH2PolyBin*>(Histogram->GetBins()->At(i - 1));
341  xlow = bin->GetXMin();
342  xup = bin->GetXMax();
343  ylow = bin->GetYMin();
344  yup = bin->GetYMax();
345  area = (xup-xlow)*(yup-ylow);
346  HistCopy->SetBinContent(i, Histogram->GetBinContent(i)/(area*scale));
347  }
348  return HistCopy;
349 }
350 
351 // ****************
352 //WP: Integral of TH2Poly multiplied by bin width
353 double PolyIntegralWidth(TH2Poly *Histogram) {
354 // ****************
355  double integral = 0;
356  double xlow, xup, ylow, yup, area;
357 
358  for(int i = 1; i < Histogram->GetNumberOfBins()+1; i++)
359  {
360  TH2PolyBin* bin = static_cast<TH2PolyBin*>(Histogram->GetBins()->At(i - 1));
361  xlow = bin->GetXMin();
362  xup = bin->GetXMax();
363  ylow = bin->GetYMin();
364  yup = bin->GetYMax();
365  area = (xup-xlow)*(yup-ylow);
366  integral += Histogram->GetBinContent(i)*area;
367  }
368  return integral;
369 }
370 
371 // *********************
372 TH2Poly* MakePolyHist(const std::string& name, const std::vector<double>& BinArray_x, const std::vector<double>& BinArray_y) {
373 // *********************
374  TH2Poly* poly = new TH2Poly();
375  poly->SetName(name.c_str());
376  poly->SetTitle(name.c_str());
377  double xmax, xmin, ymax, ymin;
378  for(unsigned int iy = 0; iy < BinArray_y.size()-1; iy++)
379  {
380  ymax = BinArray_y[iy+1];
381  ymin = BinArray_y[iy];
382  for(unsigned int ix = 0; ix < BinArray_x.size()-1; ix++)
383  {
384  xmax = BinArray_x[ix+1];
385  xmin = BinArray_x[ix];
386  double binofx[] = {xmin, xmax, xmax, xmin};
387  double binofy[] = {ymin, ymin, ymax, ymax};
388  poly->AddBin(4,binofx,binofy);
389  }
390  }
391  return poly;
392 }
393 
394 // *********************
395 //KS: Remove fitted TF1 from hist to make comparison easier
396 void RemoveFitter(TH1D* hist, const std::string& name) {
397 // *********************
398  TList *listOfFunctions = hist->GetListOfFunctions();
399  TF1 *fitter = dynamic_cast<TF1*>(listOfFunctions->FindObject(name.c_str()));
400 
401  listOfFunctions->Remove(fitter);
402  delete fitter;
403 }
404 
405 // ****************
406 // Make Poisson Fluctuation of TH1D hist
407 void MakeFluctuatedHistogramStandard(TH1D *FluctHist, TH1D* PolyHist){
408 // ****************
409  // Make the Poisson fluctuated hist
410  FluctHist->Reset("");
411  FluctHist->Fill(0.0, 0.0);
412 
413  for (int i = 1; i <= PolyHist->GetXaxis()->GetNbins(); ++i)
414  {
415  // Get the posterior predictive bin content
416  const double MeanContent = PolyHist->GetBinContent(i);
417  // Get a Poisson fluctuation of the content
418  const double Random = M3::rand::PoissonD(MeanContent);
419  // Set the fluctuated histogram content to the Poisson variation of the posterior predictive histogram
420  FluctHist->SetBinContent(i,Random);
421  }
422 }
423 
424 // ****************
425 // Make Poisson Fluctuation of TH2Poly hist
426 void MakeFluctuatedHistogramStandard(TH2Poly *FluctHist, TH2Poly* PolyHist) {
427 // ****************
428  // Make the Poisson fluctuated hist
429  FluctHist->Reset("");
430  FluctHist->Fill(0.0, 0.0, 0.0);
431 
432  for (int i = 1; i < FluctHist->GetNumberOfBins()+1; ++i)
433  {
434  // Get the posterior predictive bin content
435  const double MeanContent = PolyHist->GetBinContent(i);
436  // Get a Poisson fluctuation of the content
437  const double Random = M3::rand::PoissonD(MeanContent);
438  // Set the fluctuated histogram content to the Poisson variation of the posterior predictive histogram
439  FluctHist->SetBinContent(i,Random);
440  }
441 }
442 
443 // ****************
444 // Make Poisson Fluctuation of TH2D hist
445 void MakeFluctuatedHistogramStandard(TH2D* FluctHist, TH2D* Hist) {
446 // ****************
447  // Reset the histogram
448  FluctHist->Reset("");
449  FluctHist->Fill(0.0, 0.0, 0.0);
450 
451  // Loop over all bins
452  const int nBinsX = Hist->GetXaxis()->GetNbins();
453  const int nBinsY = Hist->GetYaxis()->GetNbins();
454  for (int ix = 1; ix <= nBinsX; ++ix) {
455  for (int iy = 1; iy <= nBinsY; ++iy) {
456  // Get the original bin content
457  const double MeanContent = Hist->GetBinContent(ix, iy);
458  // Generate Poisson fluctuation
459  const double Random = M3::rand::PoissonD(MeanContent);
460  // Set the Random content
461  FluctHist->SetBinContent(ix, iy, Random);
462  }
463  }
464 }
465 
466 // ****************
467 // Make Poisson Fluctuation of TH1D hist
468 void MakeFluctuatedHistogramAlternative(TH1D* FluctHist, TH1D* PolyHist){
469 // ****************
470  // Make the Poisson fluctuated hist
471  FluctHist->Reset("");
472  FluctHist->Fill(0.0, 0.0);
473 
474  const double evrate = PolyHist->Integral();
475  const int num = M3::rand::Poisson(evrate);
476  int count = 0;
477  while(count < num)
478  {
479  const double candidate = PolyHist->GetRandom();
480  FluctHist->Fill(candidate);
481  count++;
482  }
483 }
484 
485 // ****************
486 // Make Poisson Fluctuation of TH1D hist
487 void MakeFluctuatedHistogramAlternative(TH2D* FluctHist, TH2D* PolyHist) {
488 // ****************
489  FluctHist->Reset();
490  FluctHist->Fill(0.0, 0.0, 0.0);
491 
492  const double evrate = PolyHist->Integral();
493  const int num = M3::rand::Poisson(evrate);
494 
495  double x, y;
496  for (int count = 0; count < num; ++count) {
497  PolyHist->GetRandom2(x, y);
498  FluctHist->Fill(x, y);
499  }
500 }
501 // ****************
502 // KS: ROOT developers were too lazy do develop getRanom2 for TH2Poly, this implementation is based on:
503 // https://root.cern.ch/doc/master/classTH2.html#a883f419e1f6899f9c4255b458d2afe2e
504 int GetRandomPoly2(const TH2Poly* PolyHist) {
505 // ****************
506  const int nbins = PolyHist->GetNumberOfBins();
507  const double r1 = M3::rand::Uniform();;
508 
509  std::vector<double> fIntegral(nbins+2);
510  fIntegral[0] = 0.0;
511 
512  //KS: This is custom version of ComputeIntegral, once again ROOT was lazy :(
513  for (int i = 1; i < nbins+1; ++i)
514  {
515  fIntegral[i] = 0.0;
516  const double content = PolyHist->GetBinContent(i);
517  fIntegral[i] += fIntegral[i - 1] + content;
518  }
519  for (Int_t bin = 1; bin < nbins+1; ++bin) fIntegral[bin] /= fIntegral[nbins];
520  fIntegral[nbins+1] = PolyHist->GetEntries();
521 
522  //KS: We just return one rather then X and Y, this way we can use SetBinContent rather than Fill, which is faster
523  int iBin = int(TMath::BinarySearch(nbins, fIntegral.data(), r1));
524  //KS: Have to increment because TH2Poly has stupid offset arghh
525  iBin += 1;
526 
527  return iBin;
528 }
529 
530 // ****************
531 // Make Poisson fluctuation of TH2Poly hist
532 void MakeFluctuatedHistogramAlternative(TH2Poly *FluctHist, TH2Poly* PolyHist){
533 // ****************
534  // Make the Poisson fluctuated hist
535  FluctHist->Reset("");
536  FluctHist->Fill(0.0, 0.0, 0.0);
537 
538  const double evrate = NoOverflowIntegral(PolyHist);
539  const int num = M3::rand::Poisson(evrate);
540  int count = 0;
541  while(count < num)
542  {
543  const int iBin = GetRandomPoly2(PolyHist);
544  FluctHist->SetBinContent(iBin, FluctHist->GetBinContent(iBin) + 1);
545  count++;
546  }
547 }
548 
549 // ****************
550 //Fast and thread safe fill of violin histogram, it assumes both histograms have the same binning
551 void FastViolinFill(TH2D* violin, TH1D* hist_1d){
552 // ****************
553  for (int x = 0; x < violin->GetXaxis()->GetNbins(); ++x)
554  {
555  const int y = violin->GetYaxis()->FindBin(hist_1d->GetBinContent(x+1));
556  violin->SetBinContent(x+1, y, violin->GetBinContent(x+1, y)+1);
557  }
558 }
559 
560 
561 // ****************
562 //DB Get the Cherenkov momentum threshold in MeV
563 double returnCherenkovThresholdMomentum(const int PDG) {
564 // ****************
565  constexpr double refractiveIndex = 1.334; //DB From https://github.com/fiTQun/fiTQun/blob/646cf9c8ba3d4f7400bcbbde029d5ca15513a3bf/fiTQun_shared.cc#L757
566  double mass = M3::Utils::GetMassFromPDG(PDG)*1e3;
567  double momentumThreshold = mass/sqrt(refractiveIndex*refractiveIndex-1.);
568  return momentumThreshold;
569 }
570 
571 // **************************************************************************
572 // Recalculate Q^2 after Eb shift. Takes in shifted lepton momentum, lepton angle, and true neutrino energy
573 double CalculateQ2(double PLep, double PUpd, double EnuTrue, double InitialQ2){
574 // ***************************************************************************
575  constexpr double MLep = 0.10565837;
576  constexpr double MLep2 = MLep * MLep;
577 
578  // Calculate muon energy
579  double ELep = sqrt((MLep2)+(PLep*PLep));
580 
581  double CosTh = (2*EnuTrue*ELep - MLep2 - InitialQ2)/(2*EnuTrue*PLep);
582 
583  ELep = sqrt((MLep2)+(PUpd*PUpd));
584 
585  // Calculate the new Q2
586  double Q2Upd = -(MLep2) + 2.0*EnuTrue*(ELep - PUpd*CosTh);
587 
588  return Q2Upd - InitialQ2;
589 }
590 
591 // **************************************************************************
592 // Recalculate Enu after Eb shift. Takes in shifted lepton momentum, lepton angle, and binding energy change, and if nu/anu
593 double CalculateEnu(double PLep, double costh, double Eb, bool neutrino){
594 // ***************************************************************************
595  double mNeff = 0.93956536 - Eb / 1000.;
596  double mNoth = 0.93827203;
597 
598  if (!neutrino) {
599  mNeff = 0.93827203 - Eb / 1000.;
600  mNoth = 0.93956536;
601  }
603  constexpr double mLep = 0.10565837;
604  constexpr double mLep2 = mLep * mLep;
605 
606  const double eLep = sqrt(PLep * PLep + mLep2);
607  double Enu = (2 * mNeff * eLep - mLep2 + mNoth * mNoth - mNeff * mNeff) /(2 * (mNeff - eLep + PLep * costh));
608 
609  return Enu;
610 }
611 
612 // ***********************************************
613 std::unique_ptr<TH1D> MakeSummaryFromSpectra(const TH2D* Spectra,
614  const std::string& name) {
615 // ***********************************************
616  const int nBinsX = Spectra->GetNbinsX();
617  // Reuse the exact x binning
618  std::vector<double> edges(nBinsX + 1);
619  for (int i = 0; i < nBinsX; ++i) {
620  edges[i] = Spectra->GetXaxis()->GetBinLowEdge(i+1);
621  }
622  edges[nBinsX] = Spectra->GetXaxis()->GetBinUpEdge(nBinsX);
623 
624  auto h1 = std::make_unique<TH1D>(name.c_str(), name.c_str(), nBinsX, edges.data());
625  h1->SetDirectory(nullptr);
626  h1->Sumw2(true);
627 
628  // ---- Loop X bins and extract Y distribution
629  for (int ix = 1; ix <= nBinsX; ++ix) {
630  // Project THIS x-bin onto Y
631  std::unique_ptr<TH1D> slice(Spectra->ProjectionY(Form("_py_%d", ix), ix, ix));
632  slice->SetDirectory(nullptr);
633  if (slice->GetEntries() == 0) {
634  h1->SetBinContent(ix, 0.0);
635  h1->SetBinError(ix, 0.0);
636  continue;
637  }
638 
639  const double mean = slice->GetMean();
640  const double rms = slice->GetRMS();
641 
642  h1->SetBinContent(ix, mean);
643  h1->SetBinError(ix, rms);
644  }
645 
646  h1->GetXaxis()->SetTitle(Spectra->GetXaxis()->GetTitle());
647  h1->GetYaxis()->SetTitle("Events");
648 
649  return h1;
650 }
651 
652 // ************************************************
653 std::vector<double> BinRangeToBinEdges(YAML::Node const &bin_range) {
654 // ************************************************
655  bool is_lin = true;
656  YAML::Node bin_range_specifier;
657  if (bin_range["linspace"]) {
658  bin_range_specifier = bin_range["linspace"];
659  } else if (bin_range["logspace"]) {
660  is_lin = false;
661  bin_range_specifier = bin_range["logspace"];
662  } else {
663  std::stringstream ss;
664  ss << bin_range;
665  MACH3LOG_ERROR("When parsing binning, expected bin range specifier with "
666  "key linspace or logspace, but found,\n{}",
667  ss.str());
668  throw MaCh3Exception(__FILE__, __LINE__);
669  }
670 
671  auto nb = Get<int>(bin_range_specifier["nb"], __FILE__, __LINE__);
672  auto low = Get<double>(bin_range_specifier["low"], __FILE__, __LINE__);
673  auto up = Get<double>(bin_range_specifier["up"], __FILE__, __LINE__);
674 
675  std::vector<double> edges(nb + 1, low);
676  // force the last bin to be exactly as parsed to avoid numerical instabilities
677  // not quite lining back up with the end of the range, which could
678  // cause spurious errors or infinitesimally small bins for specifications
679  // like: [ { logspace: { nb: 10, 1E-1, 10}, 10, 11} ]
680  edges.back() = up;
681 
682  if (is_lin) {
683  double bw = (up - low) / nb;
684  for (int i = 0; i < (nb - 1); ++i) {
685  edges[i + 1] = edges[i] + bw;
686  }
687  } else {
688  double llow = std::log10(low);
689  double lup = std::log10(up);
690  double lbw = (lup - llow) / nb;
691  for (int i = 0; i < (nb - 1); ++i) {
692  edges[i + 1] = std::pow(10, llow + (i + 1) * lbw);
693  }
694  }
695 
696  return edges;
697 }
698 
699 // ************************************************
707 std::vector<double> BuildBinEdgesFromNode(YAML::Node const &bin_edges_node,
708  bool &found_range_specifier) {
709 // ************************************************
710  if (bin_edges_node.IsMap()) {
711  found_range_specifier = true;
712  return BinRangeToBinEdges(bin_edges_node);
713  }
714  std::vector<double> edges_builder;
715  if (!bin_edges_node.IsSequence()) {
716  std::stringstream ss;
717  ss << bin_edges_node;
719  "When parsing binning, expected to find a YAML map or sequence, "
720  "but found:\n{}",
721  ss.str());
722  throw MaCh3Exception(__FILE__, __LINE__);
723  }
724  for (auto const &it : bin_edges_node) {
725  if (it.IsScalar()) {
726  edges_builder.push_back(it.as<double>());
727  } else if (it.IsMap()) {
728  found_range_specifier = true;
729  auto range_edges = BinRangeToBinEdges(it);
730  std::copy(range_edges.begin(), range_edges.end(),
731  std::back_inserter(edges_builder));
732  } else {
733  std::stringstream ss;
734  ss << bin_edges_node;
736  "When parsing binning, expected elements in outer sequence to all be "
737  "either scalars or maps, but found:\n{}",
738  ss.str());
739  throw MaCh3Exception(__FILE__, __LINE__);
740  }
741  }
742 
743  // Check for duplicates or out-of-order bins
744  std::vector<double> edges;
745  for (size_t eb_it = 0; eb_it < edges_builder.size(); ++eb_it) {
746  if (edges.size()) {
747  if (edges_builder[eb_it] == edges.back()) { // remove duplicate edges
748  continue;
749  } else if (edges_builder[eb_it] < edges.back()) {
750  std::stringstream ss;
751  ss << "[ ";
752  for(auto const & e : edges_builder){
753  ss << fmt::format("{:.3g} ", e);
754  }
755  ss << "]";
757  "When parsing binning, found edges that were not monotonically "
758  "increasing, problem bin at index: {}:\n{}",
759  eb_it, ss.str());
760  throw MaCh3Exception(__FILE__, __LINE__);
761  }
762  }
763  edges.push_back(edges_builder[eb_it]);
764  }
765 
766  return edges;
767 }
768 
769 namespace M3 {
770 // **************************************************************************
771 TFile* Open(const std::string& Name, const std::string& Type, const std::string& File, const int Line) {
772 // **************************************************************************
773  TFile* OutFile = new TFile(Name.c_str(), Type.c_str());
774 
775  // Check if the file is successfully opened and usable
776  if (OutFile->IsZombie()) {
777  MACH3LOG_ERROR("Failed to open file: {}", Name);
778  std::string lowerType = Type;
779  std::transform(lowerType.begin(), lowerType.end(), lowerType.begin(), ::tolower);
780  if (lowerType == "recreate") {
781  MACH3LOG_ERROR("Check if directory exist");
782  }
783  delete OutFile;
784  throw MaCh3Exception(File, Line);
785  }
786  return OutFile;
787 }
788 
789 // ***************************************************************************
790 void ScaleHistogram(TH1* Sample_Hist, const double scale) {
791 // ***************************************************************************
792  for (int j = 0; j <= Sample_Hist->GetNbinsX(); ++j)
793  {
794  double num = Sample_Hist->GetBinContent(j);
795  double numErr = Sample_Hist->GetBinError(j);
796  double den = Sample_Hist->GetBinWidth(j);
797  double value = 0.;
798  double valueErr = 0.;
799  if (den != 0)
800  {
801  value = num/(den/scale);
802  valueErr = numErr/(den/scale);
803  Sample_Hist->SetBinContent(j,value);
804  Sample_Hist->SetBinError(j,valueErr);
805  }
806  }
807 }
808 // ***************************************************************************
809 //KS: Helper function check if data and MC binning matches
810 void CheckBinningMatch(const TH1D* Hist1, const TH1D* Hist2, const std::string& File, const int Line) {
811 // ***************************************************************************
812  if (Hist1->GetNbinsX() != Hist2->GetNbinsX()) {
813  MACH3LOG_ERROR("Number of bins does not match for TH1D: {} vs {}", Hist1->GetNbinsX(), Hist2->GetNbinsX());
814  throw MaCh3Exception(File, Line);
815  }
816  for (int i = 1; i <= Hist1->GetNbinsX(); ++i) {
817  if (std::fabs(Hist1->GetXaxis()->GetBinLowEdge(i) - Hist2->GetXaxis()->GetBinLowEdge(i)) > 0.001 ||
818  std::fabs(Hist1->GetXaxis()->GetBinUpEdge(i) - Hist2->GetXaxis()->GetBinUpEdge(i)) > 0.001) {
819  MACH3LOG_ERROR("Bin edges do not match for TH1D at bin {}", i);
820  throw MaCh3Exception(File, Line);
821  }
822  }
823 }
824 // ***************************************************************************
825 //KS: Helper function check if data and MC binning matches
826 void CheckBinningMatch(const TH2D* Hist1, const TH2D* Hist2, const std::string& File, const int Line) {
827 // ***************************************************************************
828  if (Hist1->GetNbinsX() != Hist2->GetNbinsX() || Hist1->GetNbinsY() != Hist2->GetNbinsY()) {
829  MACH3LOG_ERROR("Number of bins does not match for TH2D");
830  throw MaCh3Exception(File, Line);
831  }
832 
833  for (int i = 1; i <= Hist1->GetNbinsX(); ++i) {
834  if (std::fabs(Hist1->GetXaxis()->GetBinLowEdge(i) - Hist2->GetXaxis()->GetBinLowEdge(i)) > 0.001 ||
835  std::fabs(Hist1->GetXaxis()->GetBinUpEdge(i) - Hist2->GetXaxis()->GetBinUpEdge(i)) > 0.001) {
836  MACH3LOG_ERROR("X bin edges do not match for TH2D at bin {}", i);
837  throw MaCh3Exception(File, Line);
838  }
839  }
840  for (int j = 1; j <= Hist1->GetNbinsY(); ++j) {
841  if (std::fabs(Hist1->GetYaxis()->GetBinLowEdge(j) - Hist2->GetYaxis()->GetBinLowEdge(j)) > 0.001 ||
842  std::fabs(Hist1->GetYaxis()->GetBinUpEdge(j) - Hist2->GetYaxis()->GetBinUpEdge(j)) > 0.001) {
843  MACH3LOG_ERROR("Y bin edges do not match for TH2D at bin {}", j);
844  throw MaCh3Exception(File, Line);
845  }
846  }
847 }
848 
849 // ***************************************************************************
850 //KS: Helper function check if data and MC binning matches
851 void CheckBinningMatch(TH2Poly* Hist1, TH2Poly* Hist2, const std::string& File, const int Line) {
852 // ***************************************************************************
853  int NBins1 = Hist1->GetNumberOfBins();
854  int NBins2 = Hist2->GetNumberOfBins();
855  if (NBins1 != NBins2) {
856  MACH3LOG_ERROR("Number of bins does not match for TH2Poly: {} vs {}", NBins1, NBins2);
857  throw MaCh3Exception(File, Line);
858  }
859  for (int j = 1; j <= NBins1; j++)
860  {
861  //KS: There is weird offset between bin content and GetBins so this is correct, in spite of looking funny
862  TH2PolyBin* polybin1 = static_cast<TH2PolyBin*>(Hist1->GetBins()->At(j - 1));
863  TH2PolyBin* polybin2 = static_cast<TH2PolyBin*>(Hist2->GetBins()->At(j - 1));
864 
865  if( std::fabs(polybin2->GetXMin() - polybin1->GetXMin()) > 0.001 ||
866  std::fabs(polybin2->GetXMax() - polybin1->GetXMax()) > 0.001 ||
867  std::fabs(polybin2->GetYMin() - polybin1->GetYMin()) > 0.001 ||
868  std::fabs(polybin2->GetYMax() - polybin1->GetYMax()) > 0.001 )
869  {
870  MACH3LOG_ERROR("Binning doesn't match", Hist1->GetTitle());
871  MACH3LOG_ERROR("data x min {} x max {} y min {} y max {}", polybin2->GetXMin(), polybin2->GetXMax(), polybin2->GetYMin(), polybin2->GetYMax());
872  MACH3LOG_ERROR("mc x min {} x max {} y min {} y max {}", polybin1->GetXMin(), polybin1->GetXMax(), polybin1->GetYMin(), polybin1->GetYMax());
873  throw MaCh3Exception(File , Line );
874  }
875  }
876 }
877 
878 // ***************************************************************************
879 //KS: Convert TH2Poly into yaml config accepted by MaCh3
880 YAML::Node PolyToYaml(TH2Poly* Hist, const std::string& YamlName, const std::string& File, const int Line) {
881 // ***************************************************************************
882  if (!Hist) {
883  MACH3LOG_ERROR("Null TH2Poly pointer");
884  throw MaCh3Exception(File, Line);
885  }
886 
887  YAML::Node bins(YAML::NodeType::Sequence);
888  bins.SetStyle(YAML::EmitterStyle::Flow);
889  const int NBins = Hist->GetNumberOfBins();
890 
891  for (int j = 1; j <= NBins; j++)
892  {
893  TH2PolyBin* polybin = static_cast<TH2PolyBin*>(Hist->GetBins()->At(j - 1));
894 
895  double xmin = polybin->GetXMin();
896  double xmax = polybin->GetXMax();
897  double ymin = polybin->GetYMin();
898  double ymax = polybin->GetYMax();
899 
900  YAML::Node xNode(YAML::NodeType::Sequence);
901  xNode.SetStyle(YAML::EmitterStyle::Flow);
902  xNode.push_back(xmin);
903  xNode.push_back(xmax);
904 
905  YAML::Node yNode(YAML::NodeType::Sequence);
906  yNode.SetStyle(YAML::EmitterStyle::Flow);
907  yNode.push_back(ymin);
908  yNode.push_back(ymax);
909 
910  YAML::Node bin(YAML::NodeType::Sequence);
911  bin.SetStyle(YAML::EmitterStyle::Flow);
912  bin.push_back(xNode);
913  bin.push_back(yNode);
914 
915  bins.push_back(bin);
916  }
917 
918  YAML::Node result;
919  result[YamlName] = bins;
920 
921  return result;
922 }
923 
924 } //end M3 namespace
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
Definition: Core.h:126
#define _MaCh3_Safe_Include_End_
int GetRandomPoly2(const TH2Poly *PolyHist)
KS: ROOT developers were too lazy do develop getRanom2 for TH2Poly, this implementation is based on l...
void MakeFluctuatedHistogramStandard(TH1D *FluctHist, TH1D *PolyHist)
Make Poisson fluctuation of TH1D hist using default fast method.
TH2D * ConvertTH2PolyToTH2D(TH2Poly *poly, TH2D *h2dhist)
KS: Convert TH2D to TH2Poly.
void NormaliseTH2Poly(TH2Poly *Histogram)
Helper to Normalise histograms.
double returnCherenkovThresholdMomentum(const int PDG)
DB Get the Cherenkov momentum threshold in MeV.
double OverflowIntegral(TH2Poly *poly)
WP: Helper function for calculating unbinned Integral of TH2Poly i.e including overflow.
TH1D * PolyProjectionX(TObject *poly, const std::string &TempName, const std::vector< double > &xbins, const bool computeErrors)
WP: Poly Projectors.
std::vector< double > BuildBinEdgesFromNode(YAML::Node const &bin_edges_node, bool &found_range_specifier)
Builds a single dimension's bin edges from YAML::Node.
void FastViolinFill(TH2D *violin, TH1D *hist_1d)
KS: Fill Violin histogram with entry from a toy.
TH2Poly * NormalisePoly(TH2Poly *Histogram)
WP: Helper to Normalise histograms.
HistType * RatioHists(HistType *NumHist, HistType *DenomHist)
Helper to make ratio histograms.
std::vector< double > BinRangeToBinEdges(YAML::Node const &bin_range)
Converts a range (linspace/logspace) to a std::vector<double>
TH2Poly * RatioPolys(TH2Poly *NumHist, TH2Poly *DenomHist)
Helper to make ratio of TH2Polys.
TH2Poly * PolyScaleWidth(TH2Poly *Histogram, double scale)
WP: Helper to scale th2poly analogous to th2d scale with option "width".
void RemoveFitter(TH1D *hist, const std::string &name)
KS: Remove fitted TF1 from hist to make comparison easier.
double CalculateEnu(double PLep, double costh, double Eb, bool neutrino)
Recalculate Enu after Eb shift. Takes in shifted lepton momentum, lepton angle, and binding energy ch...
double PolyIntegralWidth(TH2Poly *Histogram)
WP: Helper to calc integral of th2poly analogous to th2d integra; with option "width".
_MaCh3_Safe_Include_Start_ _MaCh3_Safe_Include_End_ void CheckTH2PolyFileVersion(TFile *file)
KS: ROOT changes something with binning when moving from ROOT 5 to ROOT 6. If you open ROOT5 produced...
double NoOverflowIntegral(TH2Poly *poly)
WP: Helper function for calculating binned Integral of TH2Poly i.e not including overflow.
TH2Poly * ConvertTH2DtoTH2Poly(TH2D *hist)
KS: Convert TH2Poly to TH2D.
TH2Poly * MakePolyHist(const std::string &name, const std::vector< double > &BinArray_x, const std::vector< double > &BinArray_y)
WP: Helper function to create TH2Poly histogram with uniform binning.
TH1D * PolyProjectionY(TObject *poly, const std::string &TempName, const std::vector< double > &ybins, const bool computeErrors)
WP: Poly Projectors.
std::unique_ptr< TH1D > MakeSummaryFromSpectra(const TH2D *Spectra, const std::string &name)
Build a 1D posterior-predictive summary from a violin spectrum.
double CalculateQ2(double PLep, double PUpd, double EnuTrue, double InitialQ2)
Recalculate Q^2 after Eb shift. Takes in shifted lepton momentum, lepton angle, and true neutrino ene...
void MakeFluctuatedHistogramAlternative(TH1D *FluctHist, TH1D *PolyHist)
Make Poisson fluctuation of TH1D hist using slow method which is only for cross-check.
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_TRACE
Definition: MaCh3Logger.h:33
Custom exception class used throughout MaCh3.
constexpr double GetMassFromPDG(const int PDG)
Return mass for given PDG.
double PoissonD(const double mean)
Generate a Poisson-distributed random number with the given mean.
Definition: Random.h:127
double Uniform(const double min=0.0, const double max=1.0)
Generate a uniform random number in [min, max).
Definition: Random.h:116
int Poisson(const double mean)
Generate a Poisson-distributed integer with the given mean.
Definition: Random.h:122
Main namespace for MaCh3 software.
void CheckBinningMatch(const TH1D *Hist1, const TH1D *Hist2, const std::string &File, const int Line)
KS: Helper function check if data and MC binning matches.
void ScaleHistogram(TH1 *Sample_Hist, const double scale)
Scale histogram to get divided by bin width.
TFile * Open(const std::string &Name, const std::string &Type, const std::string &File, const int Line)
Opens a ROOT file with the given name and mode.
YAML::Node PolyToYaml(TH2Poly *Hist, const std::string &YamlName, const std::string &File, const int Line)
KS: Convert TH2Poly into yaml config accepted by MaCh3.