MaCh3  2.5.0
Reference Guide
SampleStructs.h
Go to the documentation of this file.
1 #pragma once
2 
3 // C++ includes
4 #include <set>
5 #include <list>
6 #include <unordered_map>
7 
8 // MaCh3 includes
10 #include "Manager/MaCh3Logger.h"
11 #include "Manager/Core.h"
13 
15 // ROOT include
16 #include "TSpline.h"
17 #include "TObjString.h"
18 #include "TFile.h"
19 #include "TF1.h"
20 #include "TH2Poly.h"
21 #include "TH1.h"
22 // NuOscillator includes
23 #include "Constants/OscillatorConstants.h"
25 
33 
34 
35 // *****************
37 enum TargetMat {
38 // *****************
39  kTarget_H = 1,
40  kTarget_C = 12,
41  kTarget_N = 14,
42  kTarget_O = 16,
43  kTarget_Al = 27,
44  kTarget_Ar = 40,
45  kTarget_Ti = 48,
46  kTarget_Fe = 56,
47  kTarget_Pb = 207
48 };
49 
50 // *****************
52 inline std::string TargetMat_ToString(const TargetMat i) {
53 // *****************
54  std::string name;
55 
56  switch(i) {
57  case kTarget_H:
58  name = "Hydrogen";
59  break;
60  case kTarget_C:
61  name = "Carbon";
62  break;
63  case kTarget_N:
64  name = "Nitrogen";
65  break;
66  case kTarget_O:
67  name = "Oxygen";
68  break;
69  case kTarget_Al:
70  name = "Aluminium";
71  break;
72  case kTarget_Ar:
73  name = "Argon";
74  break;
75  case kTarget_Ti:
76  name = "Titanium";
77  break;
78  case kTarget_Fe:
79  name = "Iron";
80  break;
81  case kTarget_Pb:
82  name = "Lead";
83  break;
84  default:
85  name = "TargetMat_Undefined";
86  break;
87  }
88 
89  return name;
90 }
91 
92 // *****************
94 enum NuPDG {
95 // *****************
96  kNue = 12,
97  kNumu = 14,
98  kNutau = 16,
99  kNueBar = -12,
100  kNumuBar = -14,
101  kNutauBar = -16
102 };
103 
112 };
113 
114 // **************************************************
116 inline std::string TestStatistic_ToString(const TestStatistic TestStat) {
117 // **************************************************
118  std::string name = "";
119 
120  switch(TestStat) {
122  name = "Poisson";
123  break;
125  name = "Barlow-Beeston";
126  break;
128  name = "IceCube";
129  break;
131  name = "Pearson";
132  break;
134  name = "Dembinski-Abdelmotteleb";
135  break;
137  MACH3LOG_ERROR("kNTestStatistics is not a valid TestStatistic!");
138  throw MaCh3Exception(__FILE__, __LINE__);
139  default:
140  MACH3LOG_ERROR("UNKNOWN LIKELIHOOD SPECIFIED!");
141  MACH3LOG_ERROR("You gave test-statistic {}", static_cast<int>(TestStat));
142  throw MaCh3Exception(__FILE__ , __LINE__ );
143  }
144  return name;
145 }
146 
147 // **************************************************
149 inline TestStatistic TestStatFromString(const std::string& likelihood) {
150 // **************************************************
151  for(int i = 0; i < kNTestStatistics; i++) {
152  if(likelihood == TestStatistic_ToString(TestStatistic(i))) {
153  return TestStatistic(i);
154  }
155  }
156 
157  MACH3LOG_ERROR("Wrong form of test-statistic specified!");
158  MACH3LOG_ERROR("You gave {} and I only support:", likelihood);
159  for(int i = 0; i < kNTestStatistics; i++)
160  {
162  }
163  throw MaCh3Exception(__FILE__ , __LINE__ );
164 }
165 
166 // ***************************
168 struct KinematicCut {
169 // ***************************
176 };
177 
178 // ***************************
182 // ***************************
184  const M3::float_t* valuePtr = nullptr;
187 };
188 
189 // ***************************
192 // ***************************
201 };
202 
203 // ***************************
205 struct BinInfo {
206 // ***************************
210  std::vector<std::array<double, 2>> Extent;
212  bool IsEventInside(const std::vector<double>& KinVars) const _noexcept_ {
213  bool inside = true;
214  const size_t N = KinVars.size();
215  #ifdef MULTITHREAD
216  #pragma omp simd reduction(&:inside)
217  #endif
218  for(size_t i = 0; i < N; ++i) {
219  const double Var = KinVars[i];
220  const bool in_bin = (Var > Extent[i][0]) & (Var <= Extent[i][1]);
221  inside &= in_bin;
222  }
223  return inside;
224  }
226  bool IsEventInside(const std::vector<const double*>& KinVars) const _noexcept_ {
227  bool inside = true;
228  const size_t N = KinVars.size();
229  #ifdef MULTITHREAD
230  #pragma omp simd reduction(&:inside)
231  #endif
232  for (size_t i = 0; i < N; ++i) {
233  const double Var = *KinVars[i];
234  const bool in_bin = (Var > Extent[i][0]) & (Var <= Extent[i][1]);
235  inside &= in_bin;
236  }
237  return inside;
238  }
239 };
240 
241 // ***************************
252 // ***************************
254  std::vector<std::vector<double>> BinEdges;
256  std::vector<int> AxisNBins;
257 
263  std::vector <std::vector<BinShiftLookup> > BinLookup;
265  std::vector<int> Strides;
267  bool Uniform = true;
269  std::vector<BinInfo> Bins;
271  std::vector<std::vector<int>> BinGridMapping;
272 
275  // all bin edges along that dimension. Get 1 less bin than number of edges.
276  void InitUniform(const std::vector<std::vector<double>>& InputEdges) {
277  BinEdges = InputEdges;
278  Uniform = true;
279  AxisNBins.resize(BinEdges.size());
280 
281  nBins = 1;
282  for(size_t iDim = 0; iDim < BinEdges.size(); iDim++)
283  {
284  const auto& Edges = BinEdges[iDim];
285  if (!std::is_sorted(Edges.begin(), Edges.end())) {
286  MACH3LOG_ERROR("Bin edges for Dim {} must be in increasing order in sample config. Bin edges passed: [{}]",
287  iDim, fmt::join(Edges, ", "));
288  throw MaCh3Exception(__FILE__, __LINE__);
289  }
290 
291  //Sanity check that some binning has been specified
292  if(BinEdges[iDim].size() == 0){
293  MACH3LOG_ERROR("No binning specified for Dim {} of sample binning, please add some binning to the sample config", iDim);
294  MACH3LOG_ERROR("Please ensure BinEdges are correctly configured for all dimensions");
295  throw MaCh3Exception(__FILE__, __LINE__);
296  }
297 
298  // Set the number of bins for this dimension
299  AxisNBins[iDim] = static_cast<int>(BinEdges[iDim].size()) - 1;
300  // Update total number of bins
301  nBins *= AxisNBins[iDim];
302 
303  MACH3LOG_INFO("{}-Dim Binning: [{:.2f}]", iDim, fmt::join(BinEdges[iDim], ", "));
304  }
305  GlobalOffset = 0;
307  InitialiseBinMigrationLookUp(static_cast<int>(BinEdges.size()));
308  }
309 
311  void CheckBinsDoNotOverlap(const std::vector<BinInfo>& TestedBins) const {
312  if (TestedBins.empty()) return;
313 
314  const size_t ExtentDim = TestedBins[0].Extent.size();
315 
316  for (size_t i = 0; i < TestedBins.size(); ++i) {
317  for (size_t j = i + 1; j < TestedBins.size(); ++j) {
318  bool OverlapsInAllDims = true;
319 
320  for (size_t iDim = 0; iDim < ExtentDim; ++iDim) {
321  const double a_lo = TestedBins[i].Extent[iDim][0];
322  const double a_hi = TestedBins[i].Extent[iDim][1];
323  const double b_lo = TestedBins[j].Extent[iDim][0];
324  const double b_hi = TestedBins[j].Extent[iDim][1];
325 
326  // [low, high) overlap check
327  if (!(a_lo < b_hi && b_lo < a_hi)) {
328  OverlapsInAllDims = false;
329  break;
330  }
331  }
332 
333  if (OverlapsInAllDims) {
334  MACH3LOG_ERROR("Overlapping non-uniform bins detected: Bin {} and Bin {}", i, j);
335  for (size_t iDim = 0; iDim < ExtentDim; ++iDim) {
336  MACH3LOG_ERROR(" Dim {}: Bin {} [{}, {}), Bin {} [{}, {})",
337  iDim,
338  i, TestedBins[i].Extent[iDim][0], TestedBins[i].Extent[iDim][1],
339  j, TestedBins[j].Extent[iDim][0], TestedBins[j].Extent[iDim][1]);
340  }
341  throw MaCh3Exception(__FILE__, __LINE__);
342  }
343  }
344  }
345  }
346 
353  void CheckBinsHaveNoGaps(const std::vector<BinInfo>& TestedBins,
354  const std::vector<double>& MinVal,
355  const std::vector<double>& MaxVal,
356  size_t ValidationBinsPerDim = 100) const {
357  bool gap_found = false;
358  if (TestedBins.empty()) return;
359  const size_t Dim = TestedBins[0].Extent.size();
360  if (MinVal.size() != Dim || MaxVal.size() != Dim) {
361  MACH3LOG_ERROR("MinVal/MaxVal size does not match dimension of bins");
362  throw MaCh3Exception(__FILE__, __LINE__);
363  }
364 
365  // Build a fine validation grid from the provided min/max
366  std::vector<std::vector<double>> TestGridEdges(Dim);
367  for (size_t d = 0; d < Dim; ++d) {
368  TestGridEdges[d].resize(ValidationBinsPerDim + 1);
369  const double width = (MaxVal[d] - MinVal[d]) / static_cast<double>(ValidationBinsPerDim);
370  for (size_t i = 0; i <= ValidationBinsPerDim; ++i)
371  TestGridEdges[d][i] = MinVal[d] + static_cast<double>(i) * width;
372  }
373  // Precompute midpoints of each test cell
374  std::vector<size_t> indices(Dim, 0);
375 
376  std::function<void(size_t)> scan = [&](size_t d) {
377  if (gap_found) return; // stop recursion
378 
379  // Base case: we have selected a cell in every dimension
380  if (d == Dim) {
381  std::vector<double> point(Dim);
382 
383  // Compute the midpoint of the current N-D grid cell
384  for (size_t i = 0; i < Dim; ++i) {
385  const double lo = TestGridEdges[i][indices[i]];
386  const double hi = TestGridEdges[i][indices[i] + 1];
387  point[i] = 0.5 * (lo + hi);
388  }
389 
390  // Check coverage
391  for (const auto& bin : TestedBins) {
392  if (bin.IsEventInside(point)) {
393  return; // covered
394  }
395  }
396 
397  // Not covered by any bin → gap
398  MACH3LOG_WARN("Gap detected in non-uniform binning at point [{:.2f}]", fmt::join(point, ", "));
399  gap_found = true;
400  return;
401  }
402 
403  for (size_t i = 0; i + 1 < TestGridEdges[d].size(); ++i) {
404  indices[d] = i;
405  scan(d + 1);
406  }
407  };
408  // Start recursion at dimension 0
409  scan(0);
410  }
411 
414  const size_t Dim = BinEdges.size();
415 
416  // Compute total number of "mega bins"
417  int NGridBins = 1;
418  for (size_t d = 0; d < Dim; ++d) {
419  NGridBins *= AxisNBins[d];
420  }
421  BinGridMapping.resize(NGridBins);
422 
423  // Helper: convert linear index to multi-dimensional index
424  std::vector<int> MultiIndex(Dim, 0);
425 
426  std::function<void(size_t, int)> scan;
427  scan = [&](size_t d, int LinearIndex) {
428  if (d == Dim) {
429  // Compute the edges of the current mega-bin
430  std::vector<std::array<double, 2>> CellEdges(Dim);
431  for (size_t i = 0; i < Dim; ++i) {
432  CellEdges[i][0] = BinEdges[i][MultiIndex[i]];
433  CellEdges[i][1] = BinEdges[i][MultiIndex[i] + 1];
434  }
435 
436  // Check overlap with all non-uniform bins
437  for (size_t iBin = 0; iBin < Bins.size(); ++iBin) {
438  auto& bin = Bins[iBin];
439  bool overlap = true;
440  for (size_t i = 0; i < Dim; ++i) {
441  const double a_lo = bin.Extent[i][0];
442  const double a_hi = bin.Extent[i][1];
443  const double b_lo = CellEdges[i][0];
444  const double b_hi = CellEdges[i][1];
445  if (!(a_hi > b_lo && a_lo < b_hi)) { // overlap condition
446  overlap = false;
447  break;
448  }
449  }
450  if (overlap) {
451  BinGridMapping[LinearIndex].push_back(static_cast<int>(iBin));
452 
453  // Debug statement: show both small bin extent AND mega-bin edges
454  std::vector<std::string> bin_extent_str(Dim);
455  std::vector<std::string> mega_edges_str(Dim);
456 
457  for (size_t i = 0; i < Dim; ++i) {
458  bin_extent_str[i] = fmt::format("[{:.3f}, {:.3f}]", bin.Extent[i][0], bin.Extent[i][1]);
459  mega_edges_str[i] = fmt::format("[{:.3f}, {:.3f}]", CellEdges[i][0], CellEdges[i][1]);
460  }
461 
462  MACH3LOG_DEBUG("MegaBin {} (multi-index [{}], edges {}) assigned Bin {} with extents {}",
463  LinearIndex, fmt::join(MultiIndex, ","), fmt::join(mega_edges_str, ", "),
464  iBin, fmt::join(bin_extent_str, ", "));
465  }
466  }
467  return;
468  }
469 
470  // Loop over all bins along this dimension
471  for (int i = 0; i < AxisNBins[d]; ++i) {
472  MultiIndex[d] = i;
473  int NewLinearIndex = LinearIndex;
474  if (d > 0) {
475  int stride = 1;
476  for (size_t s = 0; s < d; ++s) stride *= AxisNBins[s];
477  NewLinearIndex += i * stride;
478  } else {
479  NewLinearIndex = i;
480  }
481  scan(d + 1, NewLinearIndex);
482  }
483  };
484  // Start the recursive scan over all dimensions, beginning at dimension 0 with linear index 0
485  scan(0, 0);
486  }
487 
489  void InitNonUniform(const std::vector<std::vector<std::vector<double>>>& InputBins) {
490  Uniform = false;
491  nBins = 0;
492  Bins.resize(InputBins.size());
493 
494  size_t ExtentDim = InputBins[0].size();
495  if (ExtentDim == 1) {
496  MACH3LOG_ERROR("Trying to initialise Non-Uniform binning for single dimension, this is silly...");
497  throw MaCh3Exception(__FILE__, __LINE__);
498  }
499  for(size_t iBin = 0; iBin < InputBins.size(); iBin++) {
500  const auto& NewExtent = InputBins[iBin];
501  if (NewExtent.size() != ExtentDim) {
502  MACH3LOG_ERROR("Dimension of Bin {} is {}, while others have {}", iBin, NewExtent.size(), ExtentDim);
503  throw MaCh3Exception(__FILE__, __LINE__);
504  }
505 
506  BinInfo NewBin;
507  for (const auto& extent : NewExtent) {
508  if (extent.size() != 2) {
509  MACH3LOG_ERROR("Extent size is not 2 for Bin {}", iBin);
510  throw MaCh3Exception(__FILE__, __LINE__);
511  }
512  NewBin.Extent.push_back({extent[0], extent[1]});
513  MACH3LOG_DEBUG("Adding extent for Bin {} Dim {}: [{:.2f}, {:.2f}]",
514  iBin, NewBin.Extent.size()-1, NewBin.Extent.back()[0], NewBin.Extent.back()[1]);
515  }
516  Bins[iBin] = std::move(NewBin);
517  nBins++;
518  }
519  // Ensure we do not have weird overlaps
522  constexpr int BinsPerDimension = 10;
523  // Now we create huge map, which will allow to easily find non uniform bins
524  BinEdges.resize(ExtentDim);
525  AxisNBins.resize(ExtentDim);
526  std::vector<double> MinVal(ExtentDim), MaxVal(ExtentDim);
527  for (size_t iDim = 0; iDim < ExtentDim; iDim++) {
528  MinVal[iDim] = std::numeric_limits<double>::max();
529  MaxVal[iDim] = std::numeric_limits<double>::lowest();
530 
531  // Find min and max for this dimension
532  for (const auto& bin : Bins) {
533  MinVal[iDim] = std::min(MinVal[iDim], bin.Extent[iDim][0]);
534  MaxVal[iDim] = std::max(MaxVal[iDim], bin.Extent[iDim][1]);
535  }
536 
537  MACH3LOG_DEBUG("Mapping binning: Dim {} Min = {:.2f}, Max = {:.2f}", iDim, MinVal[iDim], MaxVal[iDim]);
538  BinEdges[iDim].resize(BinsPerDimension + 1);
539  double BinWidth = (MaxVal[iDim] - MinVal[iDim]) / static_cast<double>(BinsPerDimension);
540  for (size_t iEdge = 0; iEdge <= BinsPerDimension; iEdge++) {
541  BinEdges[iDim][iEdge] = MinVal[iDim] + static_cast<double>(iEdge) * BinWidth;
542  }
543  AxisNBins[iDim] = BinsPerDimension;
544  MACH3LOG_DEBUG("Mapping binning: Dim {} BinEdges = [{:.2f}]", iDim, fmt::join(BinEdges[iDim], ", "));
545  }
546  CheckBinsHaveNoGaps(Bins, MinVal, MaxVal, 200);
547  GlobalOffset = 0;
549  InitialiseBinMigrationLookUp(static_cast<int>(BinEdges.size()));
552  }
553 
557  int GetBinSafe(const std::vector<int>& BinIndices) const {
558  for(int iDim = 0; iDim < static_cast<int>(BinIndices.size()); iDim++){
559  if (BinIndices[iDim] < 0 || BinIndices[iDim] >= AxisNBins[iDim]) {
560  MACH3LOG_ERROR("{}: Bin indices out of range: Dim = {}, Bin={}, max Ndim Bin={}",
561  __func__, iDim, BinIndices[iDim], AxisNBins[iDim]);
562  throw MaCh3Exception(__FILE__, __LINE__);
563  }
564  }
565  return GetBin(BinIndices);
566  }
567 
575  int GetBin(const std::vector<int>& BinIndices) const {
576  int BinNumber = 0;
577  for(size_t i = 0; i < BinIndices.size(); ++i) {
578  BinNumber += BinIndices[i]*Strides[i];
579  }
580  return BinNumber;
581  }
582 
584  int FindBin(const int Dimension, const double Var, const int NomBin) const {
585  return FindBin(Var, NomBin, AxisNBins[Dimension], BinEdges[Dimension], BinLookup[Dimension]);
586  }
587 
596  int FindBin(const double KinVar,
597  const int NomBin,
598  const int N_Bins,
599  const std::vector<double>& Bin_Edges,
600  const std::vector<BinShiftLookup>& Bin_Lookup) const {
601  //DB Check to see if momentum shift has moved bins
602  //DB - First , check to see if the event is outside of the binning range and skip event if it is
603  if (KinVar < Bin_Edges[0] || KinVar >= Bin_Edges[N_Bins]) {
604  return M3::UnderOverFlowBin;
605  }
606  // KS: If NomBin is UnderOverFlowBin we must do binary search :(
607  if(NomBin > M3::UnderOverFlowBin) {
608  // KS: Get reference to avoid repeated indexing and help with performance
609  const BinShiftLookup& _restrict_ Bin = Bin_Lookup[NomBin];
610  const double lower = Bin.lower_binedge;
611  const double upper = Bin.upper_binedge;
612  const double lower_lower = Bin.lower_lower_binedge;
613  const double upper_upper = Bin.upper_upper_binedge;
614 
615  //DB - Second, check to see if the event is still in the nominal bin
616  if (KinVar < upper && KinVar >= lower) {
617  return NomBin;
618  }
619  //DB - Thirdly, check the adjacent bins first as Eb+CC+EScale shifts aren't likely to move an Erec more than 1bin width
620  //Shifted down one bin from the event bin at nominal
621  if (KinVar < lower && KinVar >= lower_lower) {
622  return NomBin-1;
623  }
624  //Shifted up one bin from the event bin at nominal
625  if (KinVar < upper_upper && KinVar >= upper) {
626  return NomBin+1;
627  }
628  }
629  //DB - If we end up in this loop, the event has been shifted outside of its nominal bin, but is still within the allowed binning range
630  // KS: Perform binary search to find correct bin. We already checked if isn't outside of bounds
631  return static_cast<int>(std::distance(Bin_Edges.begin(), std::upper_bound(Bin_Edges.begin(), Bin_Edges.end(), KinVar)) - 1);
632  }
633 
638  void InitialiseLookUpSingleDimension(std::vector<BinShiftLookup>& Bin_Lookup, const std::vector<double>& Bin_Edges, const int TotBins) {
639  Bin_Lookup.resize(TotBins);
640  //Set rw_pdf_bin and upper_binedge and lower_binedge for each skmc_base
641  for(int bin_i = 0; bin_i < TotBins; bin_i++){
642  double low_lower_edge = M3::_DEFAULT_RETURN_VAL_;
643  double low_edge = Bin_Edges[bin_i];
644  double upper_edge = Bin_Edges[bin_i+1];
645  double upper_upper_edge = M3::_DEFAULT_RETURN_VAL_;
646 
647  if (bin_i == 0) {
648  low_lower_edge = Bin_Edges[0];
649  } else {
650  low_lower_edge = Bin_Edges[bin_i-1];
651  }
652 
653  if (bin_i + 2 < TotBins) {
654  upper_upper_edge = Bin_Edges[bin_i + 2];
655  } else if (bin_i + 1 < TotBins) {
656  upper_upper_edge = Bin_Edges[bin_i + 1];
657  }
658 
659  Bin_Lookup[bin_i].lower_binedge = low_edge;
660  Bin_Lookup[bin_i].upper_binedge = upper_edge;
661  Bin_Lookup[bin_i].lower_lower_binedge = low_lower_edge;
662  Bin_Lookup[bin_i].upper_upper_binedge = upper_upper_edge;
663  }
664  }
665 
675  void InitialiseStrides(const int Dimension) {
676  Strides.resize(Dimension);
677  int stride = 1;
678  for (int i = 0; i < Dimension; ++i) {
679  Strides[i] = stride;
680  // Multiply stride by the number of bins in this axis
681  stride *= AxisNBins[i];
682  }
683  }
684 
687  void InitialiseBinMigrationLookUp(const int Dimension) {
688  BinLookup.resize(Dimension);
689  for (int i = 0; i < Dimension; ++i) {
691  }
692 
693  InitialiseStrides(Dimension);
694  }
695 };
696 
701 inline int GetSampleFromGlobalBin(const std::vector<SampleBinningInfo>& BinningInfo, const int GlobalBin) {
702  for (size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
703  const SampleBinningInfo& info = BinningInfo[iSample];
704  if (GlobalBin >= info.GlobalOffset && GlobalBin < info.GlobalOffset + info.nBins) {
705  return static_cast<int>(iSample);
706  }
707  }
708 
709  MACH3LOG_ERROR("Couldn't find sample corresponding to bin {}", GlobalBin);
710  throw MaCh3Exception(__FILE__, __LINE__);
711 
712  // GlobalBin is out of range for all samples
713  return M3::_BAD_INT_;
714 }
715 
720 inline int GetLocalBinFromGlobalBin(const std::vector<SampleBinningInfo>& BinningInfo,
721  const int GlobalBin) {
722  for (size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
723  const SampleBinningInfo& info = BinningInfo[iSample];
724 
725  if (GlobalBin >= info.GlobalOffset &&
726  GlobalBin < info.GlobalOffset + info.nBins)
727  {
728  return GlobalBin - info.GlobalOffset;
729  }
730  }
731 
732  MACH3LOG_ERROR("Couldn't find local bin corresponding to bin {}", GlobalBin);
733  throw MaCh3Exception(__FILE__, __LINE__);
734 
735  return M3::_BAD_INT_;
736 }
737 
738 // ***************************
739 // A handy namespace for variables extraction
740 namespace M3 {
741 namespace Utils {
742 // ***************************
743  // *****************************
749  inline double GetMassFromPDG(const int PDG) {
750  // *****************************
751  switch (abs(PDG)) {
752  // Leptons
753  case 11: return 0.00051099895; // e
754  case 13: return 0.1056583755; // mu
755  case 15: return 1.77693; // tau
756  // Neutrinos
757  case 12:
758  case 14:
759  case 16:
760  return 0.;
761  // Photon
762  case 22: return 0.;
763  // Mesons
764  case 211: return 0.13957039; // pi_+/-
765  case 111: return 0.1349768; // pi_0
766  case 221: return 0.547862; // eta
767  case 311: // K_0
768  case 130: // K_0_L
769  case 310: // K_0_S
770  return 0.497611;
771  case 321: return 0.493677; // K_+/-
772  // Baryons
773  case 2112: return 0.939565; // n
774  case 2212: return 0.938272; // p
775  case 3122: return 1.115683; // lambda
776  case 3222: return 1.118937; // sig_+
777  case 3112: return 1.197449; // sig_-
778  case 3212: return 1.192642; // sig_0
779  // Nuclei
780  case 1000050110: return 10.255103; // Boron-11
781  case 1000060120: return 11.177929; // Carbon-12
782  case 1000070140: return 13.043781; // Nitrogen-14
783  case 1000080160: return 14.899169; // Oxygen-16
784  case 1000090190: return 17.696901; // Fluorine-19
785  case 1000110230: return 21.414835; // Sodium-23
786  case 1000130270: return 25.133144; // Aluminum-27
787  case 1000140280: return 26.060342; // Silicon-28
788  case 1000190390: return 36.294463; // Potassium-39
789  case 1000180400: return 37.224724; // Argon-40
790  case 1000220480: return 44.663224; // Titanium-48
791  case 1000300640: return 59.549619; // Zinc-64
792  default:
793  MACH3LOG_ERROR("Haven't got a saved mass for PDG: {}", PDG);
794  MACH3LOG_ERROR("Please implement me!");
795  throw MaCh3Exception(__FILE__, __LINE__);
796  } // End switch
797  return 0;
798  }
799  // ***************************
800 
801  // ***************************
805  inline int PDGToNuOscillatorFlavour(const int NuPdg) {
806  int NuOscillatorFlavour = M3::_BAD_INT_;
807  switch(std::abs(NuPdg)){
808  case NuPDG::kNue:
809  NuOscillatorFlavour = NuOscillator::kElectron;
810  break;
811  case NuPDG::kNumu:
812  NuOscillatorFlavour = NuOscillator::kMuon;
813  break;
814  case NuPDG::kNutau:
815  NuOscillatorFlavour = NuOscillator::kTau;
816  break;
817  default:
818  MACH3LOG_ERROR("Unknown Neutrino PDG {}, cannot convert to NuOscillator type", NuPdg);
819  break;
820  }
821 
822  //This is very cheeky but if the PDG is negative then multiply the PDG by -1
823  // This is consistent with the treatment that NuOscillator expects as enums only
824  // exist for the generic matter flavour and not the anti-matter version
825  if(NuPdg < 0){NuOscillatorFlavour *= -1;}
826 
827  return NuOscillatorFlavour;
828  }
829  // ***************************
830 
831  // ***************************
833  inline std::string FormatDouble(const double value, const int precision) {
834  // ***************************
835  std::ostringstream oss;
836  oss << std::fixed << std::setprecision(precision) << value;
837  return oss.str();
838  }
839 } // end Utils namespace
840 } // end M3 namespace
KS: Core MaCh3 definitions and compile-time configuration utilities.
#define _noexcept_
KS: noexcept can help with performance but is terrible for debugging, this is meant to help easy way ...
Definition: Core.h:96
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
Definition: Core.h:126
#define _MaCh3_Safe_Include_End_
KS: Restore warning checking after including external headers.
Definition: Core.h:141
#define _restrict_
KS: Using restrict limits the effects of pointer aliasing, aiding optimizations. While reading I foun...
Definition: Core.h:108
Defines the custom exception class used throughout MaCh3.
MaCh3 Logging utilities built on top of SPDLOG.
#define MACH3LOG_DEBUG
Definition: MaCh3Logger.h:34
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
Definitions of generic parameter structs and utility templates for MaCh3.
std::function< void(const M3::float_t *, std::size_t)> FuncParFuncType
HH - a shorthand type for funcpar functions.
NuPDG
Enum to track the incoming neutrino species.
Definition: SampleStructs.h:94
@ kNutauBar
Tau antineutrino.
@ kNutau
Tau neutrino.
Definition: SampleStructs.h:98
@ kNumuBar
Muon antineutrino.
@ kNueBar
Electron antineutrino.
Definition: SampleStructs.h:99
@ kNue
Electron neutrino.
Definition: SampleStructs.h:96
@ kNumu
Muon neutrino.
Definition: SampleStructs.h:97
TestStatistic TestStatFromString(const std::string &likelihood)
Convert a string to a TestStatistic enum.
std::string TestStatistic_ToString(const TestStatistic TestStat)
Convert a LLH type to a string.
std::string TargetMat_ToString(const TargetMat i)
Converted the Target Mat to a string.
Definition: SampleStructs.h:52
int GetSampleFromGlobalBin(const std::vector< SampleBinningInfo > &BinningInfo, const int GlobalBin)
Get the sample index corresponding to a global bin number.
TargetMat
Enum to track the target material.
Definition: SampleStructs.h:37
@ kTarget_Fe
Iron (Atomic number 26)
Definition: SampleStructs.h:46
@ kTarget_C
Carbon 12 (Atomic number 6)
Definition: SampleStructs.h:40
@ kTarget_Al
Aluminum (Atomic number 13)
Definition: SampleStructs.h:43
@ kTarget_H
Hydrogen (Atomic number 1)
Definition: SampleStructs.h:39
@ kTarget_Ti
Titanium (Atomic number 22)
Definition: SampleStructs.h:45
@ kTarget_Ar
Argon (Atomic number 18)
Definition: SampleStructs.h:44
@ kTarget_N
Nitrogen (Atomic number 7)
Definition: SampleStructs.h:41
@ kTarget_Pb
Lead (Atomic number 82)
Definition: SampleStructs.h:47
@ kTarget_O
Oxygen 16 (Atomic number 8)
Definition: SampleStructs.h:42
TestStatistic
Make an enum of the test statistic that we're using.
@ kNTestStatistics
Number of test statistics.
@ kPearson
Standard Pearson likelihood .
@ kBarlowBeeston
Barlow-Beeston () following Conway approximation ()
@ kIceCube
Based on .
@ kDembinskiAbdelmotteleb
Based on .
@ kPoisson
Standard Poisson likelihood .
int GetLocalBinFromGlobalBin(const std::vector< SampleBinningInfo > &BinningInfo, const int GlobalBin)
Get the local (sample) bin index from a global bin number.
Custom exception class used throughout MaCh3.
int PDGToNuOscillatorFlavour(const int NuPdg)
Convert from PDG flavour to NuOscillator type beware that in the case of anti-neutrinos the NuOscilla...
double GetMassFromPDG(const int PDG)
Return mass for given PDG.
std::string FormatDouble(const double value, const int precision)
Convert double into string for precision, useful for playing with yaml if you don't want to have in c...
Main namespace for MaCh3 software.
constexpr static const double _BAD_DOUBLE_
Default value used for double initialisation.
Definition: Core.h:53
double float_t
Definition: Core.h:37
constexpr static const int UnderOverFlowBin
Mark bin which is overflow or underflow in MaCh3 binning.
Definition: Core.h:91
constexpr static const int _BAD_INT_
Default value used for int initialisation.
Definition: Core.h:55
constexpr static const double _DEFAULT_RETURN_VAL_
Definition: Core.h:56
KS: This hold bin extents in N-Dimensions allowing to check if Bin falls into.
bool IsEventInside(const std::vector< const double * > &KinVars) const _noexcept_
Checks if a given event (point) falls inside the bin using pointer array.
bool IsEventInside(const std::vector< double > &KinVars) const _noexcept_
Checks if a given event (point) falls inside the bin.
std::vector< std::array< double, 2 > > Extent
KS: Store bin lookups allowing to quickly find bin after migration.
double lower_lower_binedge
lower to check if shift has moved the event to different bin
double upper_upper_binedge
upper to check if shift has moved the event to different bin
double lower_binedge
lower to check if shift has moved the event to different bin
double upper_binedge
upper to check if shift has moved the event to different bin
Small struct used for applying shifts due to functional params.
FuncParFuncType * funcPtr
Pointer to shifting function.
const M3::float_t * valuePtr
Pointer to parameter value.
KS: Small struct used for applying kinematic cuts.
double UpperBound
Upper bound on which we apply cut.
double LowerBound
Lower bound on which we apply cut.
int ParamToCutOnIt
Index or enum value identifying the kinematic variable to cut on.
KS: Struct storing all information required for sample binning.
void InitialiseLookUpSingleDimension(std::vector< BinShiftLookup > &Bin_Lookup, const std::vector< double > &Bin_Edges, const int TotBins)
Initializes lookup arrays for efficient bin migration in a single dimension.
void InitUniform(const std::vector< std::vector< double >> &InputEdges)
Initialise Uniform Binning.
std::vector< BinInfo > Bins
Bins used only for non-uniform.
int GlobalOffset
If you have binning for multiple samples and trying to define 1D vector let's.
std::vector< std::vector< int > > BinGridMapping
This grid tells what bins are associated with with what BinEdges of Grid Binnins.
int GetBinSafe(const std::vector< int > &BinIndices) const
Get linear bin index from ND bin indices with additional checks.
void InitNonUniform(const std::vector< std::vector< std::vector< double >>> &InputBins)
Initialise Non-Uniform Binning.
void InitialiseBinMigrationLookUp(const int Dimension)
Initialise special lookup arrays allowing to more efficiently perform bin-migration These arrays stor...
void InitialiseGridMapping()
Initialise Non-Uniform Binning.
int FindBin(const double KinVar, const int NomBin, const int N_Bins, const std::vector< double > &Bin_Edges, const std::vector< BinShiftLookup > &Bin_Lookup) const
DB Find the relevant bin in the PDF for each event.
void InitialiseStrides(const int Dimension)
Initialise stride factors for linear bin index calculation.
int GetBin(const std::vector< int > &BinIndices) const
Convert N-dimensional bin indices to a linear bin index.
std::vector< std::vector< double > > BinEdges
Vector to hold N-axis bin-edges.
std::vector< int > Strides
Stride factors for converting N-dimensional bin indices to a linear index.
void CheckBinsDoNotOverlap(const std::vector< BinInfo > &TestedBins) const
Check that non-uniform bin extents do not overlap.
int FindBin(const int Dimension, const double Var, const int NomBin) const
DB Find the relevant bin in the PDF for each event.
std::vector< int > AxisNBins
Number of N-axis bins in the histogram used for likelihood calculation.
bool Uniform
Tells whether to use inform binning grid or non-uniform.
int nBins
Number of total bins.
void CheckBinsHaveNoGaps(const std::vector< BinInfo > &TestedBins, const std::vector< double > &MinVal, const std::vector< double > &MaxVal, size_t ValidationBinsPerDim=100) const
Check that non-uniform bins fully cover the bounding box (no gaps)
std::vector< std::vector< BinShiftLookup > > BinLookup
Bin lookups for all dimensions.