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));
200 std::vector<std::array<double, 2>>
Extent;
204 const size_t N = KinVars.size();
206 #pragma omp simd reduction(&:inside)
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]);
218 const size_t N = KinVars.size();
220 #pragma omp simd reduction(&:inside)
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]);
266 void InitUniform(
const std::vector<std::vector<double>>& InputEdges) {
272 for(
size_t iDim = 0; iDim <
BinEdges.size(); 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: [{}]",
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");
302 if (TestedBins.empty())
return;
304 const size_t ExtentDim = TestedBins[0].Extent.size();
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;
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];
317 if (!(a_lo < b_hi && b_lo < a_hi)) {
318 OverlapsInAllDims =
false;
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) {
328 i, TestedBins[i].Extent[iDim][0], TestedBins[i].Extent[iDim][1],
329 j, TestedBins[j].Extent[iDim][0], TestedBins[j].Extent[iDim][1]);
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");
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;
364 std::vector<size_t> indices(Dim, 0);
366 std::function<void(
size_t)> scan = [&](
size_t d) {
367 if (gap_found)
return;
371 std::vector<double> point(Dim);
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);
381 for (
const auto& bin : TestedBins) {
382 if (bin.IsEventInside(point)) {
393 for (
size_t i = 0; i + 1 < TestGridEdges[d].size(); ++i) {
408 for (
size_t d = 0; d < Dim; ++d) {
414 std::vector<int> MultiIndex(Dim, 0);
416 std::function<void(
size_t,
int)> scan;
417 scan = [&](
size_t d,
int LinearIndex) {
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];
427 for (
size_t iBin = 0; iBin <
Bins.size(); ++iBin) {
428 auto& bin =
Bins[iBin];
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)) {
444 std::vector<std::string> bin_extent_str(Dim);
445 std::vector<std::string> mega_edges_str(Dim);
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]);
452 MACH3LOG_DEBUG(
"MegaBin {} (multi-index [{}], edges {}) assigned Bin {} with extents {}",
463 int NewLinearIndex = LinearIndex;
466 for (
size_t s = 0; s < d; ++s) stride *=
AxisNBins[s];
467 NewLinearIndex += i * stride;
471 scan(d + 1, NewLinearIndex);
479 void InitNonUniform(
const std::vector<std::vector<std::vector<double>>>& InputBins) {
482 Bins.resize(InputBins.size());
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...");
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);
497 for (
const auto& extent : NewExtent) {
498 if (extent.size() != 2) {
502 NewBin.
Extent.push_back({extent[0], extent[1]});
503 MACH3LOG_DEBUG(
"Adding extent for Bin {} Dim {}: [{:.2f}, {:.2f}]",
506 Bins[iBin] = std::move(NewBin);
512 constexpr
int BinsPerDimension = 10;
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();
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]);
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;
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]);
555 return GetBin(BinIndices);
565 int GetBin(
const std::vector<int>& BinIndices)
const {
567 for(
size_t i = 0; i < BinIndices.size(); ++i) {
568 BinNumber += BinIndices[i]*
Strides[i];
589 const std::vector<double>& Bin_Edges,
590 const std::vector<BinShiftLookup>& Bin_Lookup)
const _noexcept_ {
593 if (KinVar < Bin_Edges[0] || KinVar >= Bin_Edges[N_Bins]) {
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;
606 if (KinVar < upper && KinVar >= lower) {
611 if (KinVar < lower && KinVar >= lower_lower) {
615 if (KinVar < upper_upper && KinVar >= upper) {
621 return static_cast<int>(std::distance(Bin_Edges.begin(), std::upper_bound(Bin_Edges.begin(), Bin_Edges.end(), KinVar)) - 1);
629 Bin_Lookup.resize(TotBins);
631 for(
int bin_i = 0; bin_i < TotBins; bin_i++){
633 double low_edge = Bin_Edges[bin_i];
634 double upper_edge = Bin_Edges[bin_i+1];
638 low_lower_edge = Bin_Edges[0];
640 low_lower_edge = Bin_Edges[bin_i-1];
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];
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;
668 for (
int i = 0; i < Dimension; ++i) {
679 for (
int i = 0; i < Dimension; ++i) {
692 for (
size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
695 return static_cast<int>(iSample);
699 MACH3LOG_ERROR(
"Couldn't find sample corresponding to bin {}", GlobalBin);
711 const int GlobalBin) {
712 for (
size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
722 MACH3LOG_ERROR(
"Couldn't find local bin corresponding to bin {}", GlobalBin);
742 case 11:
return 0.00051099895;
743 case 13:
return 0.1056583755;
744 case 15:
return 1.77693;
753 case 211:
return 0.13957039;
754 case 111:
return 0.1349768;
755 case 221:
return 0.547862;
756 case 331:
return 0.95778;
761 case 321:
return 0.493677;
762 case 113:
return 0.77526;
763 case 213:
return 0.77511;
764 case 223:
return 0.78266;
765 case 411:
return 1.86966;
766 case 421:
return 1.86484;
767 case 431:
return 1.96835;
769 case 2112:
return 0.939565;
770 case 2212:
return 0.938272;
771 case 3122:
return 1.115683;
772 case 3222:
return 1.118937;
773 case 3112:
return 1.197449;
774 case 3212:
return 1.192642;
775 case 3312:
return 1.32171;
776 case 3322:
return 1.31486;
777 case 3334:
return 1.67245;
778 case 4122:
return 2.28646;
779 case 4212:
return 2.45265;
780 case 4222:
return 2.45397;
782 case 1000050110:
return 10.255103;
783 case 1000060120:
return 11.177929;
784 case 1000070140:
return 13.043781;
785 case 1000080160:
return 14.899169;
786 case 1000090190:
return 17.696901;
787 case 1000110230:
return 21.414835;
788 case 1000130270:
return 25.133144;
789 case 1000140280:
return 26.060342;
790 case 1000190390:
return 36.294463;
791 case 1000180400:
return 37.224724;
792 case 1000220480:
return 44.663224;
793 case 1000300640:
return 59.549619;
809 switch(std::abs(NuPdg)){
811 NuOscillatorFlavour = NuOscillator::kElectron;
814 NuOscillatorFlavour = NuOscillator::kMuon;
817 NuOscillatorFlavour = NuOscillator::kTau;
820 MACH3LOG_ERROR(
"Unknown Neutrino PDG {}, cannot convert to NuOscillator type", NuPdg);
827 if(NuPdg < 0){NuOscillatorFlavour *= -1;}
829 return NuOscillatorFlavour;
835 inline std::string
FormatDouble(
const double value,
const int precision) {
837 std::ostringstream oss;
838 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...
std::string join(const Container &values, std::string_view separator=" ")
Join all values in a container into a single string.
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 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.