6 #include <unordered_map>
17 #include "TObjString.h"
23 #include "Constants/OscillatorConstants.h"
85 name =
"TargetMat_Undefined";
118 std::string name =
"";
125 name =
"Barlow-Beeston";
134 name =
"Dembinski-Abdelmotteleb";
141 MACH3LOG_ERROR(
"You gave test-statistic {}",
static_cast<int>(TestStat));
199 std::vector<std::array<double, 2>>
Extent;
203 const size_t N = KinVars.size();
205 #pragma omp simd reduction(&:inside)
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]);
217 const size_t N = KinVars.size();
219 #pragma omp simd reduction(&:inside)
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]);
265 void InitUniform(
const std::vector<std::vector<double>>& InputEdges) {
271 for(
size_t iDim = 0; iDim <
BinEdges.size(); 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,
", "));
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");
301 if (TestedBins.empty())
return;
303 const size_t ExtentDim = TestedBins[0].Extent.size();
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;
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];
316 if (!(a_lo < b_hi && b_lo < a_hi)) {
317 OverlapsInAllDims =
false;
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) {
327 i, TestedBins[i].Extent[iDim][0], TestedBins[i].Extent[iDim][1],
328 j, TestedBins[j].Extent[iDim][0], TestedBins[j].Extent[iDim][1]);
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");
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;
363 std::vector<size_t> indices(Dim, 0);
365 std::function<void(
size_t)> scan = [&](
size_t d) {
366 if (gap_found)
return;
370 std::vector<double> point(Dim);
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);
380 for (
const auto& bin : TestedBins) {
381 if (bin.IsEventInside(point)) {
387 MACH3LOG_WARN(
"Gap detected in non-uniform binning at point [{:.2f}]", fmt::join(point,
", "));
392 for (
size_t i = 0; i + 1 < TestGridEdges[d].size(); ++i) {
407 for (
size_t d = 0; d < Dim; ++d) {
413 std::vector<int> MultiIndex(Dim, 0);
415 std::function<void(
size_t,
int)> scan;
416 scan = [&](
size_t d,
int LinearIndex) {
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];
426 for (
size_t iBin = 0; iBin <
Bins.size(); ++iBin) {
427 auto& bin =
Bins[iBin];
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)) {
443 std::vector<std::string> bin_extent_str(Dim);
444 std::vector<std::string> mega_edges_str(Dim);
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]);
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,
", "));
462 int NewLinearIndex = LinearIndex;
465 for (
size_t s = 0; s < d; ++s) stride *=
AxisNBins[s];
466 NewLinearIndex += i * stride;
470 scan(d + 1, NewLinearIndex);
478 void InitNonUniform(
const std::vector<std::vector<std::vector<double>>>& InputBins) {
481 Bins.resize(InputBins.size());
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...");
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);
496 for (
const auto& extent : NewExtent) {
497 if (extent.size() != 2) {
501 NewBin.
Extent.push_back({extent[0], extent[1]});
502 MACH3LOG_DEBUG(
"Adding extent for Bin {} Dim {}: [{:.2f}, {:.2f}]",
505 Bins[iBin] = std::move(NewBin);
511 constexpr
int BinsPerDimension = 10;
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();
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]);
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;
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]);
554 return GetBin(BinIndices);
564 int GetBin(
const std::vector<int>& BinIndices)
const {
566 for(
size_t i = 0; i < BinIndices.size(); ++i) {
567 BinNumber += BinIndices[i]*
Strides[i];
573 int FindBin(
const int Dimension,
const double Var,
const int NomBin)
const {
588 const std::vector<double>& Bin_Edges,
589 const std::vector<BinShiftLookup>& Bin_Lookup)
const {
592 if (KinVar < Bin_Edges[0] || KinVar >= Bin_Edges[N_Bins]) {
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;
605 if (KinVar < upper && KinVar >= lower) {
610 if (KinVar < lower && KinVar >= lower_lower) {
614 if (KinVar < upper_upper && KinVar >= upper) {
620 return static_cast<int>(std::distance(Bin_Edges.begin(), std::upper_bound(Bin_Edges.begin(), Bin_Edges.end(), KinVar)) - 1);
628 Bin_Lookup.resize(TotBins);
630 for(
int bin_i = 0; bin_i < TotBins; bin_i++){
632 double low_edge = Bin_Edges[bin_i];
633 double upper_edge = Bin_Edges[bin_i+1];
637 low_lower_edge = Bin_Edges[0];
639 low_lower_edge = Bin_Edges[bin_i-1];
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];
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;
667 for (
int i = 0; i < Dimension; ++i) {
678 for (
int i = 0; i < Dimension; ++i) {
691 for (
size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
694 return static_cast<int>(iSample);
698 MACH3LOG_ERROR(
"Couldn't find sample corresponding to bin {}", GlobalBin);
710 const int GlobalBin) {
711 for (
size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
721 MACH3LOG_ERROR(
"Couldn't find local bin corresponding to bin {}", GlobalBin);
741 case 11:
return 0.00051099895;
742 case 13:
return 0.1056583755;
743 case 15:
return 1.77693;
752 case 211:
return 0.13957039;
753 case 111:
return 0.1349768;
754 case 221:
return 0.547862;
755 case 331:
return 0.95778;
760 case 321:
return 0.493677;
761 case 113:
return 0.77526;
762 case 213:
return 0.77511;
763 case 223:
return 0.78266;
764 case 411:
return 1.86966;
765 case 421:
return 1.86484;
766 case 431:
return 1.96835;
768 case 2112:
return 0.939565;
769 case 2212:
return 0.938272;
770 case 3122:
return 1.115683;
771 case 3222:
return 1.118937;
772 case 3112:
return 1.197449;
773 case 3212:
return 1.192642;
774 case 3312:
return 1.32171;
775 case 3322:
return 1.31486;
776 case 3334:
return 1.67245;
777 case 4122:
return 2.28646;
778 case 4212:
return 2.45265;
779 case 4222:
return 2.45397;
781 case 1000050110:
return 10.255103;
782 case 1000060120:
return 11.177929;
783 case 1000070140:
return 13.043781;
784 case 1000080160:
return 14.899169;
785 case 1000090190:
return 17.696901;
786 case 1000110230:
return 21.414835;
787 case 1000130270:
return 25.133144;
788 case 1000140280:
return 26.060342;
789 case 1000190390:
return 36.294463;
790 case 1000180400:
return 37.224724;
791 case 1000220480:
return 44.663224;
792 case 1000300640:
return 59.549619;
808 switch(std::abs(NuPdg)){
810 NuOscillatorFlavour = NuOscillator::kElectron;
813 NuOscillatorFlavour = NuOscillator::kMuon;
816 NuOscillatorFlavour = NuOscillator::kTau;
819 MACH3LOG_ERROR(
"Unknown Neutrino PDG {}, cannot convert to NuOscillator type", NuPdg);
826 if(NuPdg < 0){NuOscillatorFlavour *= -1;}
828 return NuOscillatorFlavour;
834 inline std::string
FormatDouble(
const double value,
const int precision) {
836 std::ostringstream oss;
837 oss << std::fixed << std::setprecision(precision) << value;
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 ...
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
#define _MaCh3_Safe_Include_End_
#define _restrict_
KS: Using restrict limits the effects of pointer aliasing, aiding optimizations. While reading I foun...
Defines the custom exception class used throughout MaCh3.
MaCh3 Logging utilities built on top of SPDLOG.
Definitions of generic parameter structs and utility templates for MaCh3.
NuPDG
Enum to track the incoming neutrino species.
@ kNutauBar
Tau antineutrino.
@ kNumuBar
Muon antineutrino.
@ kNueBar
Electron antineutrino.
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.
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.
@ kTarget_Fe
Iron (Atomic number 26)
@ kTarget_C
Carbon 12 (Atomic number 6)
@ kTarget_Al
Aluminum (Atomic number 13)
@ kTarget_H
Hydrogen (Atomic number 1)
@ kTarget_Ti
Titanium (Atomic number 22)
@ kTarget_Ar
Argon (Atomic number 18)
@ kTarget_N
Nitrogen (Atomic number 7)
@ kTarget_Pb
Lead (Atomic number 82)
@ kTarget_O
Oxygen 16 (Atomic number 8)
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 ()
@ 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.
constexpr static const int UnderOverFlowBin
Mark bin which is overflow or underflow in MaCh3 binning.
constexpr static const int _BAD_INT_
Default value used for int initialisation.
constexpr static const double _DEFAULT_RETURN_VAL_
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.