MaCh3  2.6.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 // ***************************
181 // ***************************
190 };
191 
192 // ***************************
194 struct BinInfo {
195 // ***************************
199  std::vector<std::array<double, 2>> Extent;
201  bool IsEventInside(const std::vector<double>& KinVars) const _noexcept_ {
202  bool inside = true;
203  const size_t N = KinVars.size();
204  #ifdef MULTITHREAD
205  #pragma omp simd reduction(&:inside)
206  #endif
207  for(size_t i = 0; i < N; ++i) {
208  const double Var = KinVars[i];
209  const bool in_bin = (Var > Extent[i][0]) & (Var <= Extent[i][1]);
210  inside &= in_bin;
211  }
212  return inside;
213  }
215  bool IsEventInside(const std::vector<const double*>& KinVars) const _noexcept_ {
216  bool inside = true;
217  const size_t N = KinVars.size();
218  #ifdef MULTITHREAD
219  #pragma omp simd reduction(&:inside)
220  #endif
221  for (size_t i = 0; i < N; ++i) {
222  const double Var = *KinVars[i];
223  const bool in_bin = (Var > Extent[i][0]) & (Var <= Extent[i][1]);
224  inside &= in_bin;
225  }
226  return inside;
227  }
228 };
229 
230 // ***************************
241 // ***************************
243  std::vector<std::vector<double>> BinEdges;
245  std::vector<int> AxisNBins;
246 
252  std::vector <std::vector<BinShiftLookup> > BinLookup;
254  std::vector<int> Strides;
256  bool Uniform = true;
258  std::vector<BinInfo> Bins;
260  std::vector<std::vector<int>> BinGridMapping;
261 
264  // all bin edges along that dimension. Get 1 less bin than number of edges.
265  void InitUniform(const std::vector<std::vector<double>>& InputEdges) {
266  BinEdges = InputEdges;
267  Uniform = true;
268  AxisNBins.resize(BinEdges.size());
269 
270  nBins = 1;
271  for(size_t iDim = 0; iDim < BinEdges.size(); iDim++)
272  {
273  const auto& Edges = BinEdges[iDim];
274  if (!std::is_sorted(Edges.begin(), Edges.end())) {
275  MACH3LOG_ERROR("Bin edges for Dim {} must be in increasing order in sample config. Bin edges passed: [{}]",
276  iDim, fmt::join(Edges, ", "));
277  throw MaCh3Exception(__FILE__, __LINE__);
278  }
279 
280  //Sanity check that some binning has been specified
281  if(BinEdges[iDim].size() == 0){
282  MACH3LOG_ERROR("No binning specified for Dim {} of sample binning, please add some binning to the sample config", iDim);
283  MACH3LOG_ERROR("Please ensure BinEdges are correctly configured for all dimensions");
284  throw MaCh3Exception(__FILE__, __LINE__);
285  }
286 
287  // Set the number of bins for this dimension
288  AxisNBins[iDim] = static_cast<int>(BinEdges[iDim].size()) - 1;
289  // Update total number of bins
290  nBins *= AxisNBins[iDim];
291 
292  MACH3LOG_INFO("{}-Dim Binning: [{:.2f}]", iDim, fmt::join(BinEdges[iDim], ", "));
293  }
294  GlobalOffset = 0;
296  InitialiseBinMigrationLookUp(static_cast<int>(BinEdges.size()));
297  }
298 
300  void CheckBinsDoNotOverlap(const std::vector<BinInfo>& TestedBins) const {
301  if (TestedBins.empty()) return;
302 
303  const size_t ExtentDim = TestedBins[0].Extent.size();
304 
305  for (size_t i = 0; i < TestedBins.size(); ++i) {
306  for (size_t j = i + 1; j < TestedBins.size(); ++j) {
307  bool OverlapsInAllDims = true;
308 
309  for (size_t iDim = 0; iDim < ExtentDim; ++iDim) {
310  const double a_lo = TestedBins[i].Extent[iDim][0];
311  const double a_hi = TestedBins[i].Extent[iDim][1];
312  const double b_lo = TestedBins[j].Extent[iDim][0];
313  const double b_hi = TestedBins[j].Extent[iDim][1];
314 
315  // [low, high) overlap check
316  if (!(a_lo < b_hi && b_lo < a_hi)) {
317  OverlapsInAllDims = false;
318  break;
319  }
320  }
321 
322  if (OverlapsInAllDims) {
323  MACH3LOG_ERROR("Overlapping non-uniform bins detected: Bin {} and Bin {}", i, j);
324  for (size_t iDim = 0; iDim < ExtentDim; ++iDim) {
325  MACH3LOG_ERROR(" Dim {}: Bin {} [{}, {}), Bin {} [{}, {})",
326  iDim,
327  i, TestedBins[i].Extent[iDim][0], TestedBins[i].Extent[iDim][1],
328  j, TestedBins[j].Extent[iDim][0], TestedBins[j].Extent[iDim][1]);
329  }
330  throw MaCh3Exception(__FILE__, __LINE__);
331  }
332  }
333  }
334  }
335 
342  void CheckBinsHaveNoGaps(const std::vector<BinInfo>& TestedBins,
343  const std::vector<double>& MinVal,
344  const std::vector<double>& MaxVal,
345  size_t ValidationBinsPerDim = 100) const {
346  bool gap_found = false;
347  if (TestedBins.empty()) return;
348  const size_t Dim = TestedBins[0].Extent.size();
349  if (MinVal.size() != Dim || MaxVal.size() != Dim) {
350  MACH3LOG_ERROR("MinVal/MaxVal size does not match dimension of bins");
351  throw MaCh3Exception(__FILE__, __LINE__);
352  }
353 
354  // Build a fine validation grid from the provided min/max
355  std::vector<std::vector<double>> TestGridEdges(Dim);
356  for (size_t d = 0; d < Dim; ++d) {
357  TestGridEdges[d].resize(ValidationBinsPerDim + 1);
358  const double width = (MaxVal[d] - MinVal[d]) / static_cast<double>(ValidationBinsPerDim);
359  for (size_t i = 0; i <= ValidationBinsPerDim; ++i)
360  TestGridEdges[d][i] = MinVal[d] + static_cast<double>(i) * width;
361  }
362  // Precompute midpoints of each test cell
363  std::vector<size_t> indices(Dim, 0);
364 
365  std::function<void(size_t)> scan = [&](size_t d) {
366  if (gap_found) return; // stop recursion
367 
368  // Base case: we have selected a cell in every dimension
369  if (d == Dim) {
370  std::vector<double> point(Dim);
371 
372  // Compute the midpoint of the current N-D grid cell
373  for (size_t i = 0; i < Dim; ++i) {
374  const double lo = TestGridEdges[i][indices[i]];
375  const double hi = TestGridEdges[i][indices[i] + 1];
376  point[i] = 0.5 * (lo + hi);
377  }
378 
379  // Check coverage
380  for (const auto& bin : TestedBins) {
381  if (bin.IsEventInside(point)) {
382  return; // covered
383  }
384  }
385 
386  // Not covered by any bin → gap
387  MACH3LOG_WARN("Gap detected in non-uniform binning at point [{:.2f}]", fmt::join(point, ", "));
388  gap_found = true;
389  return;
390  }
391 
392  for (size_t i = 0; i + 1 < TestGridEdges[d].size(); ++i) {
393  indices[d] = i;
394  scan(d + 1);
395  }
396  };
397  // Start recursion at dimension 0
398  scan(0);
399  }
400 
403  const size_t Dim = BinEdges.size();
404 
405  // Compute total number of "mega bins"
406  int NGridBins = 1;
407  for (size_t d = 0; d < Dim; ++d) {
408  NGridBins *= AxisNBins[d];
409  }
410  BinGridMapping.resize(NGridBins);
411 
412  // Helper: convert linear index to multi-dimensional index
413  std::vector<int> MultiIndex(Dim, 0);
414 
415  std::function<void(size_t, int)> scan;
416  scan = [&](size_t d, int LinearIndex) {
417  if (d == Dim) {
418  // Compute the edges of the current mega-bin
419  std::vector<std::array<double, 2>> CellEdges(Dim);
420  for (size_t i = 0; i < Dim; ++i) {
421  CellEdges[i][0] = BinEdges[i][MultiIndex[i]];
422  CellEdges[i][1] = BinEdges[i][MultiIndex[i] + 1];
423  }
424 
425  // Check overlap with all non-uniform bins
426  for (size_t iBin = 0; iBin < Bins.size(); ++iBin) {
427  auto& bin = Bins[iBin];
428  bool overlap = true;
429  for (size_t i = 0; i < Dim; ++i) {
430  const double a_lo = bin.Extent[i][0];
431  const double a_hi = bin.Extent[i][1];
432  const double b_lo = CellEdges[i][0];
433  const double b_hi = CellEdges[i][1];
434  if (!(a_hi > b_lo && a_lo < b_hi)) { // overlap condition
435  overlap = false;
436  break;
437  }
438  }
439  if (overlap) {
440  BinGridMapping[LinearIndex].push_back(static_cast<int>(iBin));
441 
442  // Debug statement: show both small bin extent AND mega-bin edges
443  std::vector<std::string> bin_extent_str(Dim);
444  std::vector<std::string> mega_edges_str(Dim);
445 
446  for (size_t i = 0; i < Dim; ++i) {
447  bin_extent_str[i] = fmt::format("[{:.3f}, {:.3f}]", bin.Extent[i][0], bin.Extent[i][1]);
448  mega_edges_str[i] = fmt::format("[{:.3f}, {:.3f}]", CellEdges[i][0], CellEdges[i][1]);
449  }
450 
451  MACH3LOG_DEBUG("MegaBin {} (multi-index [{}], edges {}) assigned Bin {} with extents {}",
452  LinearIndex, fmt::join(MultiIndex, ","), fmt::join(mega_edges_str, ", "),
453  iBin, fmt::join(bin_extent_str, ", "));
454  }
455  }
456  return;
457  }
458 
459  // Loop over all bins along this dimension
460  for (int i = 0; i < AxisNBins[d]; ++i) {
461  MultiIndex[d] = i;
462  int NewLinearIndex = LinearIndex;
463  if (d > 0) {
464  int stride = 1;
465  for (size_t s = 0; s < d; ++s) stride *= AxisNBins[s];
466  NewLinearIndex += i * stride;
467  } else {
468  NewLinearIndex = i;
469  }
470  scan(d + 1, NewLinearIndex);
471  }
472  };
473  // Start the recursive scan over all dimensions, beginning at dimension 0 with linear index 0
474  scan(0, 0);
475  }
476 
478  void InitNonUniform(const std::vector<std::vector<std::vector<double>>>& InputBins) {
479  Uniform = false;
480  nBins = 0;
481  Bins.resize(InputBins.size());
482 
483  size_t ExtentDim = InputBins[0].size();
484  if (ExtentDim == 1) {
485  MACH3LOG_ERROR("Trying to initialise Non-Uniform binning for single dimension, this is silly...");
486  throw MaCh3Exception(__FILE__, __LINE__);
487  }
488  for(size_t iBin = 0; iBin < InputBins.size(); iBin++) {
489  const auto& NewExtent = InputBins[iBin];
490  if (NewExtent.size() != ExtentDim) {
491  MACH3LOG_ERROR("Dimension of Bin {} is {}, while others have {}", iBin, NewExtent.size(), ExtentDim);
492  throw MaCh3Exception(__FILE__, __LINE__);
493  }
494 
495  BinInfo NewBin;
496  for (const auto& extent : NewExtent) {
497  if (extent.size() != 2) {
498  MACH3LOG_ERROR("Extent size is not 2 for Bin {}", iBin);
499  throw MaCh3Exception(__FILE__, __LINE__);
500  }
501  NewBin.Extent.push_back({extent[0], extent[1]});
502  MACH3LOG_DEBUG("Adding extent for Bin {} Dim {}: [{:.2f}, {:.2f}]",
503  iBin, NewBin.Extent.size()-1, NewBin.Extent.back()[0], NewBin.Extent.back()[1]);
504  }
505  Bins[iBin] = std::move(NewBin);
506  nBins++;
507  }
508  // Ensure we do not have weird overlaps
511  constexpr int BinsPerDimension = 10;
512  // Now we create huge map, which will allow to easily find non uniform bins
513  BinEdges.resize(ExtentDim);
514  AxisNBins.resize(ExtentDim);
515  std::vector<double> MinVal(ExtentDim), MaxVal(ExtentDim);
516  for (size_t iDim = 0; iDim < ExtentDim; iDim++) {
517  MinVal[iDim] = std::numeric_limits<double>::max();
518  MaxVal[iDim] = std::numeric_limits<double>::lowest();
519 
520  // Find min and max for this dimension
521  for (const auto& bin : Bins) {
522  MinVal[iDim] = std::min(MinVal[iDim], bin.Extent[iDim][0]);
523  MaxVal[iDim] = std::max(MaxVal[iDim], bin.Extent[iDim][1]);
524  }
525 
526  MACH3LOG_DEBUG("Mapping binning: Dim {} Min = {:.2f}, Max = {:.2f}", iDim, MinVal[iDim], MaxVal[iDim]);
527  BinEdges[iDim].resize(BinsPerDimension + 1);
528  double BinWidth = (MaxVal[iDim] - MinVal[iDim]) / static_cast<double>(BinsPerDimension);
529  for (size_t iEdge = 0; iEdge <= BinsPerDimension; iEdge++) {
530  BinEdges[iDim][iEdge] = MinVal[iDim] + static_cast<double>(iEdge) * BinWidth;
531  }
532  AxisNBins[iDim] = BinsPerDimension;
533  MACH3LOG_DEBUG("Mapping binning: Dim {} BinEdges = [{:.2f}]", iDim, fmt::join(BinEdges[iDim], ", "));
534  }
535  CheckBinsHaveNoGaps(Bins, MinVal, MaxVal, 200);
536  GlobalOffset = 0;
538  InitialiseBinMigrationLookUp(static_cast<int>(BinEdges.size()));
541  }
542 
546  int GetBinSafe(const std::vector<int>& BinIndices) const {
547  for(int iDim = 0; iDim < static_cast<int>(BinIndices.size()); iDim++){
548  if (BinIndices[iDim] < 0 || BinIndices[iDim] >= AxisNBins[iDim]) {
549  MACH3LOG_ERROR("{}: Bin indices out of range: Dim = {}, Bin={}, max Ndim Bin={}",
550  __func__, iDim, BinIndices[iDim], AxisNBins[iDim]);
551  throw MaCh3Exception(__FILE__, __LINE__);
552  }
553  }
554  return GetBin(BinIndices);
555  }
556 
564  int GetBin(const std::vector<int>& BinIndices) const {
565  int BinNumber = 0;
566  for(size_t i = 0; i < BinIndices.size(); ++i) {
567  BinNumber += BinIndices[i]*Strides[i];
568  }
569  return BinNumber;
570  }
571 
573  int FindBin(const int Dimension, const double Var, const int NomBin) const {
574  return FindBin(Var, NomBin, AxisNBins[Dimension], BinEdges[Dimension], BinLookup[Dimension]);
575  }
576 
585  int FindBin(const double KinVar,
586  const int NomBin,
587  const int N_Bins,
588  const std::vector<double>& Bin_Edges,
589  const std::vector<BinShiftLookup>& Bin_Lookup) const {
590  //DB Check to see if momentum shift has moved bins
591  //DB - First , check to see if the event is outside of the binning range and skip event if it is
592  if (KinVar < Bin_Edges[0] || KinVar >= Bin_Edges[N_Bins]) {
593  return M3::UnderOverFlowBin;
594  }
595  // KS: If NomBin is UnderOverFlowBin we must do binary search :(
596  if(NomBin > M3::UnderOverFlowBin) {
597  // KS: Get reference to avoid repeated indexing and help with performance
598  const BinShiftLookup& _restrict_ Bin = Bin_Lookup[NomBin];
599  const double lower = Bin.lower_binedge;
600  const double upper = Bin.upper_binedge;
601  const double lower_lower = Bin.lower_lower_binedge;
602  const double upper_upper = Bin.upper_upper_binedge;
603 
604  //DB - Second, check to see if the event is still in the nominal bin
605  if (KinVar < upper && KinVar >= lower) {
606  return NomBin;
607  }
608  //DB - Thirdly, check the adjacent bins first as Eb+CC+EScale shifts aren't likely to move an Erec more than 1bin width
609  //Shifted down one bin from the event bin at nominal
610  if (KinVar < lower && KinVar >= lower_lower) {
611  return NomBin-1;
612  }
613  //Shifted up one bin from the event bin at nominal
614  if (KinVar < upper_upper && KinVar >= upper) {
615  return NomBin+1;
616  }
617  }
618  //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
619  // KS: Perform binary search to find correct bin. We already checked if isn't outside of bounds
620  return static_cast<int>(std::distance(Bin_Edges.begin(), std::upper_bound(Bin_Edges.begin(), Bin_Edges.end(), KinVar)) - 1);
621  }
622 
627  void InitialiseLookUpSingleDimension(std::vector<BinShiftLookup>& Bin_Lookup, const std::vector<double>& Bin_Edges, const int TotBins) {
628  Bin_Lookup.resize(TotBins);
629  //Set rw_pdf_bin and upper_binedge and lower_binedge for each skmc_base
630  for(int bin_i = 0; bin_i < TotBins; bin_i++){
631  double low_lower_edge = M3::_DEFAULT_RETURN_VAL_;
632  double low_edge = Bin_Edges[bin_i];
633  double upper_edge = Bin_Edges[bin_i+1];
634  double upper_upper_edge = M3::_DEFAULT_RETURN_VAL_;
635 
636  if (bin_i == 0) {
637  low_lower_edge = Bin_Edges[0];
638  } else {
639  low_lower_edge = Bin_Edges[bin_i-1];
640  }
641 
642  if (bin_i + 2 < TotBins) {
643  upper_upper_edge = Bin_Edges[bin_i + 2];
644  } else if (bin_i + 1 < TotBins) {
645  upper_upper_edge = Bin_Edges[bin_i + 1];
646  }
647 
648  Bin_Lookup[bin_i].lower_binedge = low_edge;
649  Bin_Lookup[bin_i].upper_binedge = upper_edge;
650  Bin_Lookup[bin_i].lower_lower_binedge = low_lower_edge;
651  Bin_Lookup[bin_i].upper_upper_binedge = upper_upper_edge;
652  }
653  }
654 
664  void InitialiseStrides(const int Dimension) {
665  Strides.resize(Dimension);
666  int stride = 1;
667  for (int i = 0; i < Dimension; ++i) {
668  Strides[i] = stride;
669  // Multiply stride by the number of bins in this axis
670  stride *= AxisNBins[i];
671  }
672  }
673 
676  void InitialiseBinMigrationLookUp(const int Dimension) {
677  BinLookup.resize(Dimension);
678  for (int i = 0; i < Dimension; ++i) {
680  }
681 
682  InitialiseStrides(Dimension);
683  }
684 };
685 
690 inline int GetSampleFromGlobalBin(const std::vector<SampleBinningInfo>& BinningInfo, const int GlobalBin) {
691  for (size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
692  const SampleBinningInfo& info = BinningInfo[iSample];
693  if (GlobalBin >= info.GlobalOffset && GlobalBin < info.GlobalOffset + info.nBins) {
694  return static_cast<int>(iSample);
695  }
696  }
697 
698  MACH3LOG_ERROR("Couldn't find sample corresponding to bin {}", GlobalBin);
699  throw MaCh3Exception(__FILE__, __LINE__);
700 
701  // GlobalBin is out of range for all samples
702  return M3::_BAD_INT_;
703 }
704 
709 inline int GetLocalBinFromGlobalBin(const std::vector<SampleBinningInfo>& BinningInfo,
710  const int GlobalBin) {
711  for (size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
712  const SampleBinningInfo& info = BinningInfo[iSample];
713 
714  if (GlobalBin >= info.GlobalOffset &&
715  GlobalBin < info.GlobalOffset + info.nBins)
716  {
717  return GlobalBin - info.GlobalOffset;
718  }
719  }
720 
721  MACH3LOG_ERROR("Couldn't find local bin corresponding to bin {}", GlobalBin);
722  throw MaCh3Exception(__FILE__, __LINE__);
723 
724  return M3::_BAD_INT_;
725 }
726 
727 // ***************************
728 // A handy namespace for variables extraction
729 namespace M3 {
730 namespace Utils {
731 // ***************************
732  // *****************************
737  inline constexpr double GetMassFromPDG(const int PDG) {
738  // *****************************
739  switch (abs(PDG)) {
740  // Leptons
741  case 11: return 0.00051099895; // e
742  case 13: return 0.1056583755; // mu
743  case 15: return 1.77693; // tau
744  // Neutrinos
745  case 12:
746  case 14:
747  case 16:
748  return 0.;
749  // Photon
750  case 22: return 0.;
751  // Mesons
752  case 211: return 0.13957039; // pi_+/-
753  case 111: return 0.1349768; // pi_0
754  case 221: return 0.547862; // eta
755  case 331: return 0.95778; // eta'
756  case 311: // K_0
757  case 130: // K_0_L
758  case 310: // K_0_S
759  return 0.497611;
760  case 321: return 0.493677; // K_+/-
761  case 113: return 0.77526; // rho_0
762  case 213: return 0.77511; // rho_+/-
763  case 223: return 0.78266; // omega
764  case 411: return 1.86966; // D_+/-
765  case 421: return 1.86484; // D_0
766  case 431: return 1.96835; // D_s+/-
767  // Baryons
768  case 2112: return 0.939565; // n
769  case 2212: return 0.938272; // p
770  case 3122: return 1.115683; // Lambda
771  case 3222: return 1.118937; // Sig_+
772  case 3112: return 1.197449; // Sig_-
773  case 3212: return 1.192642; // Sig_0
774  case 3312: return 1.32171; // Xi_+/-
775  case 3322: return 1.31486; // Xi_0
776  case 3334: return 1.67245; // Omega_+/-
777  case 4122: return 2.28646; // Lambda_c+
778  case 4212: return 2.45265; // Sigma_c+
779  case 4222: return 2.45397; // Sigma_c++
780  // Nuclei
781  case 1000050110: return 10.255103; // Boron-11
782  case 1000060120: return 11.177929; // Carbon-12
783  case 1000070140: return 13.043781; // Nitrogen-14
784  case 1000080160: return 14.899169; // Oxygen-16
785  case 1000090190: return 17.696901; // Fluorine-19
786  case 1000110230: return 21.414835; // Sodium-23
787  case 1000130270: return 25.133144; // Aluminum-27
788  case 1000140280: return 26.060342; // Silicon-28
789  case 1000190390: return 36.294463; // Potassium-39
790  case 1000180400: return 37.224724; // Argon-40
791  case 1000220480: return 44.663224; // Titanium-48
792  case 1000300640: return 59.549619; // Zinc-64
793  default:
794  MACH3LOG_ERROR("Haven't got a saved mass for PDG: {}", PDG);
795  MACH3LOG_ERROR("Please implement me!");
796  throw MaCh3Exception(__FILE__, __LINE__);
797  } // End switch
798  return 0;
799  }
800  // ***************************
801 
802  // ***************************
806  inline int PDGToNuOscillatorFlavour(const int NuPdg) {
807  int NuOscillatorFlavour = M3::_BAD_INT_;
808  switch(std::abs(NuPdg)){
809  case NuPDG::kNue:
810  NuOscillatorFlavour = NuOscillator::kElectron;
811  break;
812  case NuPDG::kNumu:
813  NuOscillatorFlavour = NuOscillator::kMuon;
814  break;
815  case NuPDG::kNutau:
816  NuOscillatorFlavour = NuOscillator::kTau;
817  break;
818  default:
819  MACH3LOG_ERROR("Unknown Neutrino PDG {}, cannot convert to NuOscillator type", NuPdg);
820  break;
821  }
822 
823  //This is very cheeky but if the PDG is negative then multiply the PDG by -1
824  // This is consistent with the treatment that NuOscillator expects as enums only
825  // exist for the generic matter flavour and not the anti-matter version
826  if(NuPdg < 0){NuOscillatorFlavour *= -1;}
827 
828  return NuOscillatorFlavour;
829  }
830  // ***************************
831 
832  // ***************************
834  inline std::string FormatDouble(const double value, const int precision) {
835  // ***************************
836  std::ostringstream oss;
837  oss << std::fixed << std::setprecision(precision) << value;
838  return oss.str();
839  }
840 } // end Utils namespace
841 } // 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_
#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.
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.
constexpr double GetMassFromPDG(const int PDG)
Return mass for given PDG.
int PDGToNuOscillatorFlavour(const int NuPdg)
Convert from PDG flavour to NuOscillator type beware that in the case of anti-neutrinos the NuOscilla...
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
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
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.