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