MaCh3  2.4.2
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 struct KinematicCut {
150 // ***************************
157 };
158 
159 // ***************************
163 // ***************************
165  const double* valuePtr = nullptr;
168 };
169 
170 // ***************************
173 // ***************************
182 };
183 
184 // ***************************
186 struct BinInfo {
187 // ***************************
191  std::vector<std::array<double, 2>> Extent;
193  bool IsEventInside(const std::vector<double>& KinVars) const _noexcept_ {
194  bool inside = true;
195  const size_t N = KinVars.size();
196  #ifdef MULTITHREAD
197  #pragma omp simd reduction(&:inside)
198  #endif
199  for(size_t i = 0; i < N; ++i) {
200  const double Var = KinVars[i];
201  const bool in_bin = (Var > Extent[i][0]) & (Var <= Extent[i][1]);
202  inside &= in_bin;
203  }
204  return inside;
205  }
207  bool IsEventInside(const std::vector<const double*>& KinVars) const _noexcept_ {
208  bool inside = true;
209  const size_t N = KinVars.size();
210  #ifdef MULTITHREAD
211  #pragma omp simd reduction(&:inside)
212  #endif
213  for (size_t i = 0; i < N; ++i) {
214  const double Var = *KinVars[i];
215  const bool in_bin = (Var > Extent[i][0]) & (Var <= Extent[i][1]);
216  inside &= in_bin;
217  }
218  return inside;
219  }
220 };
221 
222 // ***************************
233 // ***************************
235  std::vector<std::vector<double>> BinEdges;
237  std::vector<int> AxisNBins;
238 
244  std::vector <std::vector<BinShiftLookup> > BinLookup;
246  std::vector<int> Strides;
248  bool Uniform = true;
250  std::vector<BinInfo> Bins;
252  std::vector<std::vector<int>> BinGridMapping;
253 
255  void InitUniform(const std::vector<std::vector<double>>& InputEdges) {
256  BinEdges = InputEdges;
257  Uniform = true;
258  AxisNBins.resize(BinEdges.size());
259 
260  nBins = 1;
261  for(size_t iDim = 0; iDim < BinEdges.size(); iDim++)
262  {
263  const auto& Edges = BinEdges[iDim];
264  if (!std::is_sorted(Edges.begin(), Edges.end())) {
265  MACH3LOG_ERROR("VarBins for Dim {} must be in increasing order in sample config, VarBins: [{}]",
266  iDim, fmt::join(Edges, ", "));
267  throw MaCh3Exception(__FILE__, __LINE__);
268  }
269 
270  //Sanity check that some binning has been specified
271  if(BinEdges[iDim].size() == 0){
272  MACH3LOG_ERROR("No binning specified for Dim {} of sample binning, please add some binning to the sample config", iDim);
273  MACH3LOG_ERROR("Please ensure BinEdges are correctly configured for all dimensions");
274  throw MaCh3Exception(__FILE__, __LINE__);
275  }
276 
277  // Set the number of bins for this dimension
278  AxisNBins[iDim] = static_cast<int>(BinEdges[iDim].size()) - 1;
279  // Update total number of bins
280  nBins *= AxisNBins[iDim];
281 
282  MACH3LOG_INFO("{}-Dim Binning: [{:.2f}]", iDim, fmt::join(BinEdges[iDim], ", "));
283  }
284  GlobalOffset = 0;
286  InitialiseBinMigrationLookUp(static_cast<int>(BinEdges.size()));
287  }
288 
290  void CheckBinsDoNotOverlap(const std::vector<BinInfo>& TestedBins) const {
291  if (TestedBins.empty()) return;
292 
293  const size_t ExtentDim = TestedBins[0].Extent.size();
294 
295  for (size_t i = 0; i < TestedBins.size(); ++i) {
296  for (size_t j = i + 1; j < TestedBins.size(); ++j) {
297  bool OverlapsInAllDims = true;
298 
299  for (size_t iDim = 0; iDim < ExtentDim; ++iDim) {
300  const double a_lo = TestedBins[i].Extent[iDim][0];
301  const double a_hi = TestedBins[i].Extent[iDim][1];
302  const double b_lo = TestedBins[j].Extent[iDim][0];
303  const double b_hi = TestedBins[j].Extent[iDim][1];
304 
305  // [low, high) overlap check
306  if (!(a_lo < b_hi && b_lo < a_hi)) {
307  OverlapsInAllDims = false;
308  break;
309  }
310  }
311 
312  if (OverlapsInAllDims) {
313  MACH3LOG_ERROR("Overlapping non-uniform bins detected: Bin {} and Bin {}", i, j);
314  for (size_t iDim = 0; iDim < ExtentDim; ++iDim) {
315  MACH3LOG_ERROR(" Dim {}: Bin {} [{}, {}), Bin {} [{}, {})",
316  iDim,
317  i, TestedBins[i].Extent[iDim][0], TestedBins[i].Extent[iDim][1],
318  j, TestedBins[j].Extent[iDim][0], TestedBins[j].Extent[iDim][1]);
319  }
320  throw MaCh3Exception(__FILE__, __LINE__);
321  }
322  }
323  }
324  }
325 
332  void CheckBinsHaveNoGaps(const std::vector<BinInfo>& TestedBins,
333  const std::vector<double>& MinVal,
334  const std::vector<double>& MaxVal,
335  size_t ValidationBinsPerDim = 100) const {
336  bool gap_found = false;
337  if (TestedBins.empty()) return;
338  const size_t Dim = TestedBins[0].Extent.size();
339  if (MinVal.size() != Dim || MaxVal.size() != Dim) {
340  MACH3LOG_ERROR("MinVal/MaxVal size does not match dimension of bins");
341  throw MaCh3Exception(__FILE__, __LINE__);
342  }
343 
344  // Build a fine validation grid from the provided min/max
345  std::vector<std::vector<double>> TestGridEdges(Dim);
346  for (size_t d = 0; d < Dim; ++d) {
347  TestGridEdges[d].resize(ValidationBinsPerDim + 1);
348  const double width = (MaxVal[d] - MinVal[d]) / static_cast<double>(ValidationBinsPerDim);
349  for (size_t i = 0; i <= ValidationBinsPerDim; ++i)
350  TestGridEdges[d][i] = MinVal[d] + static_cast<double>(i) * width;
351  }
352  // Precompute midpoints of each test cell
353  std::vector<size_t> indices(Dim, 0);
354 
355  std::function<void(size_t)> scan = [&](size_t d) {
356  if (gap_found) return; // stop recursion
357 
358  // Base case: we have selected a cell in every dimension
359  if (d == Dim) {
360  std::vector<double> point(Dim);
361 
362  // Compute the midpoint of the current N-D grid cell
363  for (size_t i = 0; i < Dim; ++i) {
364  const double lo = TestGridEdges[i][indices[i]];
365  const double hi = TestGridEdges[i][indices[i] + 1];
366  point[i] = 0.5 * (lo + hi);
367  }
368 
369  // Check coverage
370  for (const auto& bin : TestedBins) {
371  if (bin.IsEventInside(point)) {
372  return; // covered
373  }
374  }
375 
376  // Not covered by any bin → gap
377  MACH3LOG_WARN("Gap detected in non-uniform binning at point [{:.2f}]", fmt::join(point, ", "));
378  gap_found = true;
379  return;
380  }
381 
382  for (size_t i = 0; i + 1 < TestGridEdges[d].size(); ++i) {
383  indices[d] = i;
384  scan(d + 1);
385  }
386  };
387  // Start recursion at dimension 0
388  scan(0);
389  }
390 
393  const size_t Dim = BinEdges.size();
394 
395  // Compute total number of "mega bins"
396  int NGridBins = 1;
397  for (size_t d = 0; d < Dim; ++d) {
398  NGridBins *= AxisNBins[d];
399  }
400  BinGridMapping.resize(NGridBins);
401 
402  // Helper: convert linear index to multi-dimensional index
403  std::vector<int> MultiIndex(Dim, 0);
404 
405  std::function<void(size_t, int)> scan;
406  scan = [&](size_t d, int LinearIndex) {
407  if (d == Dim) {
408  // Compute the edges of the current mega-bin
409  std::vector<std::array<double, 2>> CellEdges(Dim);
410  for (size_t i = 0; i < Dim; ++i) {
411  CellEdges[i][0] = BinEdges[i][MultiIndex[i]];
412  CellEdges[i][1] = BinEdges[i][MultiIndex[i] + 1];
413  }
414 
415  // Check overlap with all non-uniform bins
416  for (size_t iBin = 0; iBin < Bins.size(); ++iBin) {
417  auto& bin = Bins[iBin];
418  bool overlap = true;
419  for (size_t i = 0; i < Dim; ++i) {
420  const double a_lo = bin.Extent[i][0];
421  const double a_hi = bin.Extent[i][1];
422  const double b_lo = CellEdges[i][0];
423  const double b_hi = CellEdges[i][1];
424  if (!(a_hi > b_lo && a_lo < b_hi)) { // overlap condition
425  overlap = false;
426  break;
427  }
428  }
429  if (overlap) {
430  BinGridMapping[LinearIndex].push_back(static_cast<int>(iBin));
431 
432  // Debug statement: show both small bin extent AND mega-bin edges
433  std::vector<std::string> bin_extent_str(Dim);
434  std::vector<std::string> mega_edges_str(Dim);
435 
436  for (size_t i = 0; i < Dim; ++i) {
437  bin_extent_str[i] = fmt::format("[{:.3f}, {:.3f}]", bin.Extent[i][0], bin.Extent[i][1]);
438  mega_edges_str[i] = fmt::format("[{:.3f}, {:.3f}]", CellEdges[i][0], CellEdges[i][1]);
439  }
440 
441  MACH3LOG_DEBUG("MegaBin {} (multi-index [{}], edges {}) assigned Bin {} with extents {}",
442  LinearIndex, fmt::join(MultiIndex, ","), fmt::join(mega_edges_str, ", "),
443  iBin, fmt::join(bin_extent_str, ", "));
444  }
445  }
446  return;
447  }
448 
449  // Loop over all bins along this dimension
450  for (int i = 0; i < AxisNBins[d]; ++i) {
451  MultiIndex[d] = i;
452  int NewLinearIndex = LinearIndex;
453  if (d > 0) {
454  int stride = 1;
455  for (size_t s = 0; s < d; ++s) stride *= AxisNBins[s];
456  NewLinearIndex += i * stride;
457  } else {
458  NewLinearIndex = i;
459  }
460  scan(d + 1, NewLinearIndex);
461  }
462  };
463  // Start the recursive scan over all dimensions, beginning at dimension 0 with linear index 0
464  scan(0, 0);
465  }
466 
468  void InitNonUniform(const std::vector<std::vector<std::vector<double>>>& InputBins) {
469  Uniform = false;
470  nBins = 0;
471  Bins.resize(InputBins.size());
472 
473  size_t ExtentDim = InputBins[0].size();
474  if (ExtentDim == 1) {
475  MACH3LOG_ERROR("Trying to initialise Non-Uniform binning for single dimension, this is silly...");
476  throw MaCh3Exception(__FILE__, __LINE__);
477  }
478  for(size_t iBin = 0; iBin < InputBins.size(); iBin++) {
479  const auto& NewExtent = InputBins[iBin];
480  if (NewExtent.size() != ExtentDim) {
481  MACH3LOG_ERROR("Dimension of Bin {} is {}, while others have {}", iBin, NewExtent.size(), ExtentDim);
482  throw MaCh3Exception(__FILE__, __LINE__);
483  }
484 
485  BinInfo NewBin;
486  for (const auto& extent : NewExtent) {
487  if (extent.size() != 2) {
488  MACH3LOG_ERROR("Extent size is not 2 for Bin {}", iBin);
489  throw MaCh3Exception(__FILE__, __LINE__);
490  }
491  NewBin.Extent.push_back({extent[0], extent[1]});
492  MACH3LOG_DEBUG("Adding extent for Bin {} Dim {}: [{:.2f}, {:.2f}]",
493  iBin, NewBin.Extent.size()-1, NewBin.Extent.back()[0], NewBin.Extent.back()[1]);
494  }
495  Bins[iBin] = std::move(NewBin);
496  nBins++;
497  }
498  // Ensure we do not have weird overlaps
501  constexpr int BinsPerDimension = 10;
502  // Now we create huge map, which will allow to easily find non uniform bins
503  BinEdges.resize(ExtentDim);
504  AxisNBins.resize(ExtentDim);
505  std::vector<double> MinVal(ExtentDim), MaxVal(ExtentDim);
506  for (size_t iDim = 0; iDim < ExtentDim; iDim++) {
507  MinVal[iDim] = std::numeric_limits<double>::max();
508  MaxVal[iDim] = std::numeric_limits<double>::lowest();
509 
510  // Find min and max for this dimension
511  for (const auto& bin : Bins) {
512  MinVal[iDim] = std::min(MinVal[iDim], bin.Extent[iDim][0]);
513  MaxVal[iDim] = std::max(MaxVal[iDim], bin.Extent[iDim][1]);
514  }
515 
516  MACH3LOG_DEBUG("Mapping binning: Dim {} Min = {:.2f}, Max = {:.2f}", iDim, MinVal[iDim], MaxVal[iDim]);
517  BinEdges[iDim].resize(BinsPerDimension + 1);
518  double BinWidth = (MaxVal[iDim] - MinVal[iDim]) / static_cast<double>(BinsPerDimension);
519  for (size_t iEdge = 0; iEdge <= BinsPerDimension; iEdge++) {
520  BinEdges[iDim][iEdge] = MinVal[iDim] + static_cast<double>(iEdge) * BinWidth;
521  }
522  AxisNBins[iDim] = BinsPerDimension;
523  MACH3LOG_DEBUG("Mapping binning: Dim {} BinEdges = [{:.2f}]", iDim, fmt::join(BinEdges[iDim], ", "));
524  }
525  CheckBinsHaveNoGaps(Bins, MinVal, MaxVal, 200);
526  GlobalOffset = 0;
528  InitialiseBinMigrationLookUp(static_cast<int>(BinEdges.size()));
531  }
532 
536  int GetBinSafe(const std::vector<int>& BinIndices) const {
537  for(int iDim = 0; iDim < static_cast<int>(BinIndices.size()); iDim++){
538  if (BinIndices[iDim] < 0 || BinIndices[iDim] >= AxisNBins[iDim]) {
539  MACH3LOG_ERROR("{}: Bin indices out of range: Dim = {}, Bin={}, max Ndim Bin={}",
540  __func__, iDim, BinIndices[iDim], AxisNBins[iDim]);
541  throw MaCh3Exception(__FILE__, __LINE__);
542  }
543  }
544  return GetBin(BinIndices);
545  }
546 
554  int GetBin(const std::vector<int>& BinIndices) const {
555  int BinNumber = 0;
556  for(size_t i = 0; i < BinIndices.size(); ++i) {
557  BinNumber += BinIndices[i]*Strides[i];
558  }
559  return BinNumber;
560  }
561 
563  int FindBin(const int Dimension, const double Var, const int NomBin) const {
564  return FindBin(Var, NomBin, AxisNBins[Dimension], BinEdges[Dimension], BinLookup[Dimension]);
565  }
566 
575  int FindBin(const double KinVar,
576  const int NomBin,
577  const int N_Bins,
578  const std::vector<double>& Bin_Edges,
579  const std::vector<BinShiftLookup>& Bin_Lookup) const {
580  //DB Check to see if momentum shift has moved bins
581  //DB - First , check to see if the event is outside of the binning range and skip event if it is
582  if (KinVar < Bin_Edges[0] || KinVar >= Bin_Edges[N_Bins]) {
583  return M3::UnderOverFlowBin;
584  }
585  // KS: If NomBin is UnderOverFlowBin we must do binary search :(
586  if(NomBin > M3::UnderOverFlowBin) {
587  // KS: Get reference to avoid repeated indexing and help with performance
588  const BinShiftLookup& _restrict_ Bin = Bin_Lookup[NomBin];
589  const double lower = Bin.lower_binedge;
590  const double upper = Bin.upper_binedge;
591  const double lower_lower = Bin.lower_lower_binedge;
592  const double upper_upper = Bin.upper_upper_binedge;
593 
594  //DB - Second, check to see if the event is still in the nominal bin
595  if (KinVar < upper && KinVar >= lower) {
596  return NomBin;
597  }
598  //DB - Thirdly, check the adjacent bins first as Eb+CC+EScale shifts aren't likely to move an Erec more than 1bin width
599  //Shifted down one bin from the event bin at nominal
600  if (KinVar < lower && KinVar >= lower_lower) {
601  return NomBin-1;
602  }
603  //Shifted up one bin from the event bin at nominal
604  if (KinVar < upper_upper && KinVar >= upper) {
605  return NomBin+1;
606  }
607  }
608  //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
609  // KS: Perform binary search to find correct bin. We already checked if isn't outside of bounds
610  return static_cast<int>(std::distance(Bin_Edges.begin(), std::upper_bound(Bin_Edges.begin(), Bin_Edges.end(), KinVar)) - 1);
611  }
612 
617  void InitialiseLookUpSingleDimension(std::vector<BinShiftLookup>& Bin_Lookup, const std::vector<double>& Bin_Edges, const int TotBins) {
618  Bin_Lookup.resize(TotBins);
619  //Set rw_pdf_bin and upper_binedge and lower_binedge for each skmc_base
620  for(int bin_i = 0; bin_i < TotBins; bin_i++){
621  double low_lower_edge = M3::_DEFAULT_RETURN_VAL_;
622  double low_edge = Bin_Edges[bin_i];
623  double upper_edge = Bin_Edges[bin_i+1];
624  double upper_upper_edge = M3::_DEFAULT_RETURN_VAL_;
625 
626  if (bin_i == 0) {
627  low_lower_edge = Bin_Edges[0];
628  } else {
629  low_lower_edge = Bin_Edges[bin_i-1];
630  }
631 
632  if (bin_i + 2 < TotBins) {
633  upper_upper_edge = Bin_Edges[bin_i + 2];
634  } else if (bin_i + 1 < TotBins) {
635  upper_upper_edge = Bin_Edges[bin_i + 1];
636  }
637 
638  Bin_Lookup[bin_i].lower_binedge = low_edge;
639  Bin_Lookup[bin_i].upper_binedge = upper_edge;
640  Bin_Lookup[bin_i].lower_lower_binedge = low_lower_edge;
641  Bin_Lookup[bin_i].upper_upper_binedge = upper_upper_edge;
642  }
643  }
644 
654  void InitialiseStrides(const int Dimension) {
655  Strides.resize(Dimension);
656  int stride = 1;
657  for (int i = 0; i < Dimension; ++i) {
658  Strides[i] = stride;
659  // Multiply stride by the number of bins in this axis
660  stride *= AxisNBins[i];
661  }
662  }
663 
666  void InitialiseBinMigrationLookUp(const int Dimension) {
667  BinLookup.resize(Dimension);
668  for (int i = 0; i < Dimension; ++i) {
670  }
671 
672  InitialiseStrides(Dimension);
673  }
674 };
675 
680 inline int GetSampleFromGlobalBin(const std::vector<SampleBinningInfo>& BinningInfo, const int GlobalBin) {
681  for (size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
682  const SampleBinningInfo& info = BinningInfo[iSample];
683  if (GlobalBin >= info.GlobalOffset && GlobalBin < info.GlobalOffset + info.nBins) {
684  return static_cast<int>(iSample);
685  }
686  }
687 
688  MACH3LOG_ERROR("Couldn't find sample corresponding to bin {}", GlobalBin);
689  throw MaCh3Exception(__FILE__, __LINE__);
690 
691  // GlobalBin is out of range for all samples
692  return M3::_BAD_INT_;
693 }
694 
699 inline int GetLocalBinFromGlobalBin(const std::vector<SampleBinningInfo>& BinningInfo,
700  const int GlobalBin) {
701  for (size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
702  const SampleBinningInfo& info = BinningInfo[iSample];
703 
704  if (GlobalBin >= info.GlobalOffset &&
705  GlobalBin < info.GlobalOffset + info.nBins)
706  {
707  return GlobalBin - info.GlobalOffset;
708  }
709  }
710 
711  MACH3LOG_ERROR("Couldn't find local bin corresponding to bin {}", GlobalBin);
712  throw MaCh3Exception(__FILE__, __LINE__);
713 
714  return M3::_BAD_INT_;
715 }
716 
717 // ***************************
718 // A handy namespace for variables extraction
719 namespace MaCh3Utils {
720 // ***************************
721  // *****************************
727  inline double GetMassFromPDG(const int PDG) {
728  // *****************************
729  switch (abs(PDG)) {
730  // Leptons
731  case 11: return 0.00051099895; // e
732  case 13: return 0.1056583755; // mu
733  case 15: return 1.77693; // tau
734  // Neutrinos
735  case 12:
736  case 14:
737  case 16:
738  return 0.;
739  // Photon
740  case 22: return 0.;
741  // Mesons
742  case 211: return 0.13957039; // pi_+/-
743  case 111: return 0.1349768; // pi_0
744  case 221: return 0.547862; // eta
745  case 311: // K_0
746  case 130: // K_0_L
747  case 310: // K_0_S
748  return 0.497611;
749  case 321: return 0.493677; // K_+/-
750  // Baryons
751  case 2112: return 0.939565; // n
752  case 2212: return 0.938272; // p
753  case 3122: return 1.115683; // lambda
754  case 3222: return 1.118937; // sig_+
755  case 3112: return 1.197449; // sig_-
756  case 3212: return 1.192642; // sig_0
757  // Nuclei
758  case 1000050110: return 10.255103; // Boron-11
759  case 1000060120: return 11.177929; // Carbon-12
760  case 1000070140: return 13.043781; // Nitrogen-14
761  case 1000080160: return 14.899169; // Oxygen-16
762  case 1000090190: return 17.696901; // Fluorine-19
763  case 1000110230: return 21.414835; // Sodium-23
764  case 1000130270: return 25.133144; // Aluminum-27
765  case 1000140280: return 26.060342; // Silicon-28
766  case 1000190390: return 36.294463; // Potassium-39
767  case 1000180400: return 37.224724; // Argon-40
768  case 1000220480: return 44.663224; // Titanium-48
769  case 1000300640: return 59.549619; // Zinc-64
770  default:
771  MACH3LOG_ERROR("Haven't got a saved mass for PDG: {}", PDG);
772  MACH3LOG_ERROR("Please implement me!");
773  throw MaCh3Exception(__FILE__, __LINE__);
774  } // End switch
775  return 0;
776  }
777  // ***************************
778 
779  // ***************************
783  inline int PDGToNuOscillatorFlavour(const int NuPdg) {
784  int NuOscillatorFlavour = M3::_BAD_INT_;
785  switch(std::abs(NuPdg)){
786  case NuPDG::kNue:
787  NuOscillatorFlavour = NuOscillator::kElectron;
788  break;
789  case NuPDG::kNumu:
790  NuOscillatorFlavour = NuOscillator::kMuon;
791  break;
792  case NuPDG::kNutau:
793  NuOscillatorFlavour = NuOscillator::kTau;
794  break;
795  default:
796  MACH3LOG_ERROR("Unknown Neutrino PDG {}, cannot convert to NuOscillator type", NuPdg);
797  break;
798  }
799 
800  //This is very cheeky but if the PDG is negative then multiply the PDG by -1
801  // This is consistent with the treatment that NuOscillator expects as enums only
802  // exist for the generic matter flavour and not the anti-matter version
803  if(NuPdg < 0){NuOscillatorFlavour *= -1;}
804 
805  return NuOscillatorFlavour;
806  }
807  // ***************************
808 
809  // ***************************
811  inline std::string FormatDouble(const double value, const int precision) {
812  // ***************************
813  std::ostringstream oss;
814  oss << std::fixed << std::setprecision(precision) << value;
815  return oss.str();
816  }
817 
818 } // end MaCh3Utils 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:140
#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 double *, 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
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 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
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...
int PDGToNuOscillatorFlavour(const int NuPdg)
Convert from PDG flavour to NuOscillator type beware that in the case of anti-neutrinos the NuOscilla...
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.
const double * valuePtr
Pointer to parameter value.
FuncParFuncType * funcPtr
Pointer to shifting function.
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.