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));
210 std::vector<std::array<double, 2>>
Extent;
214 const size_t N = KinVars.size();
216 #pragma omp simd reduction(&:inside)
218 for(
size_t i = 0; i < N; ++i) {
219 const double Var = KinVars[i];
220 const bool in_bin = (Var >
Extent[i][0]) & (Var <=
Extent[i][1]);
228 const size_t N = KinVars.size();
230 #pragma omp simd reduction(&:inside)
232 for (
size_t i = 0; i < N; ++i) {
233 const double Var = *KinVars[i];
234 const bool in_bin = (Var >
Extent[i][0]) & (Var <=
Extent[i][1]);
276 void InitUniform(
const std::vector<std::vector<double>>& InputEdges) {
282 for(
size_t iDim = 0; iDim <
BinEdges.size(); iDim++)
285 if (!std::is_sorted(Edges.begin(), Edges.end())) {
286 MACH3LOG_ERROR(
"Bin edges for Dim {} must be in increasing order in sample config. Bin edges passed: [{}]",
287 iDim, fmt::join(Edges,
", "));
293 MACH3LOG_ERROR(
"No binning specified for Dim {} of sample binning, please add some binning to the sample config", iDim);
294 MACH3LOG_ERROR(
"Please ensure BinEdges are correctly configured for all dimensions");
312 if (TestedBins.empty())
return;
314 const size_t ExtentDim = TestedBins[0].Extent.size();
316 for (
size_t i = 0; i < TestedBins.size(); ++i) {
317 for (
size_t j = i + 1; j < TestedBins.size(); ++j) {
318 bool OverlapsInAllDims =
true;
320 for (
size_t iDim = 0; iDim < ExtentDim; ++iDim) {
321 const double a_lo = TestedBins[i].Extent[iDim][0];
322 const double a_hi = TestedBins[i].Extent[iDim][1];
323 const double b_lo = TestedBins[j].Extent[iDim][0];
324 const double b_hi = TestedBins[j].Extent[iDim][1];
327 if (!(a_lo < b_hi && b_lo < a_hi)) {
328 OverlapsInAllDims =
false;
333 if (OverlapsInAllDims) {
334 MACH3LOG_ERROR(
"Overlapping non-uniform bins detected: Bin {} and Bin {}", i, j);
335 for (
size_t iDim = 0; iDim < ExtentDim; ++iDim) {
338 i, TestedBins[i].Extent[iDim][0], TestedBins[i].Extent[iDim][1],
339 j, TestedBins[j].Extent[iDim][0], TestedBins[j].Extent[iDim][1]);
354 const std::vector<double>& MinVal,
355 const std::vector<double>& MaxVal,
356 size_t ValidationBinsPerDim = 100)
const {
357 bool gap_found =
false;
358 if (TestedBins.empty())
return;
359 const size_t Dim = TestedBins[0].Extent.size();
360 if (MinVal.size() != Dim || MaxVal.size() != Dim) {
361 MACH3LOG_ERROR(
"MinVal/MaxVal size does not match dimension of bins");
366 std::vector<std::vector<double>> TestGridEdges(Dim);
367 for (
size_t d = 0; d < Dim; ++d) {
368 TestGridEdges[d].resize(ValidationBinsPerDim + 1);
369 const double width = (MaxVal[d] - MinVal[d]) /
static_cast<double>(ValidationBinsPerDim);
370 for (
size_t i = 0; i <= ValidationBinsPerDim; ++i)
371 TestGridEdges[d][i] = MinVal[d] +
static_cast<double>(i) * width;
374 std::vector<size_t> indices(Dim, 0);
376 std::function<void(
size_t)> scan = [&](
size_t d) {
377 if (gap_found)
return;
381 std::vector<double> point(Dim);
384 for (
size_t i = 0; i < Dim; ++i) {
385 const double lo = TestGridEdges[i][indices[i]];
386 const double hi = TestGridEdges[i][indices[i] + 1];
387 point[i] = 0.5 * (lo + hi);
391 for (
const auto& bin : TestedBins) {
392 if (bin.IsEventInside(point)) {
398 MACH3LOG_WARN(
"Gap detected in non-uniform binning at point [{:.2f}]", fmt::join(point,
", "));
403 for (
size_t i = 0; i + 1 < TestGridEdges[d].size(); ++i) {
418 for (
size_t d = 0; d < Dim; ++d) {
424 std::vector<int> MultiIndex(Dim, 0);
426 std::function<void(
size_t,
int)> scan;
427 scan = [&](
size_t d,
int LinearIndex) {
430 std::vector<std::array<double, 2>> CellEdges(Dim);
431 for (
size_t i = 0; i < Dim; ++i) {
432 CellEdges[i][0] =
BinEdges[i][MultiIndex[i]];
433 CellEdges[i][1] =
BinEdges[i][MultiIndex[i] + 1];
437 for (
size_t iBin = 0; iBin <
Bins.size(); ++iBin) {
438 auto& bin =
Bins[iBin];
440 for (
size_t i = 0; i < Dim; ++i) {
441 const double a_lo = bin.Extent[i][0];
442 const double a_hi = bin.Extent[i][1];
443 const double b_lo = CellEdges[i][0];
444 const double b_hi = CellEdges[i][1];
445 if (!(a_hi > b_lo && a_lo < b_hi)) {
454 std::vector<std::string> bin_extent_str(Dim);
455 std::vector<std::string> mega_edges_str(Dim);
457 for (
size_t i = 0; i < Dim; ++i) {
458 bin_extent_str[i] = fmt::format(
"[{:.3f}, {:.3f}]", bin.Extent[i][0], bin.Extent[i][1]);
459 mega_edges_str[i] = fmt::format(
"[{:.3f}, {:.3f}]", CellEdges[i][0], CellEdges[i][1]);
462 MACH3LOG_DEBUG(
"MegaBin {} (multi-index [{}], edges {}) assigned Bin {} with extents {}",
463 LinearIndex, fmt::join(MultiIndex,
","), fmt::join(mega_edges_str,
", "),
464 iBin, fmt::join(bin_extent_str,
", "));
473 int NewLinearIndex = LinearIndex;
476 for (
size_t s = 0; s < d; ++s) stride *=
AxisNBins[s];
477 NewLinearIndex += i * stride;
481 scan(d + 1, NewLinearIndex);
489 void InitNonUniform(
const std::vector<std::vector<std::vector<double>>>& InputBins) {
492 Bins.resize(InputBins.size());
494 size_t ExtentDim = InputBins[0].size();
495 if (ExtentDim == 1) {
496 MACH3LOG_ERROR(
"Trying to initialise Non-Uniform binning for single dimension, this is silly...");
499 for(
size_t iBin = 0; iBin < InputBins.size(); iBin++) {
500 const auto& NewExtent = InputBins[iBin];
501 if (NewExtent.size() != ExtentDim) {
502 MACH3LOG_ERROR(
"Dimension of Bin {} is {}, while others have {}", iBin, NewExtent.size(), ExtentDim);
507 for (
const auto& extent : NewExtent) {
508 if (extent.size() != 2) {
512 NewBin.
Extent.push_back({extent[0], extent[1]});
513 MACH3LOG_DEBUG(
"Adding extent for Bin {} Dim {}: [{:.2f}, {:.2f}]",
516 Bins[iBin] = std::move(NewBin);
522 constexpr
int BinsPerDimension = 10;
526 std::vector<double> MinVal(ExtentDim), MaxVal(ExtentDim);
527 for (
size_t iDim = 0; iDim < ExtentDim; iDim++) {
528 MinVal[iDim] = std::numeric_limits<double>::max();
529 MaxVal[iDim] = std::numeric_limits<double>::lowest();
532 for (
const auto& bin :
Bins) {
533 MinVal[iDim] = std::min(MinVal[iDim], bin.Extent[iDim][0]);
534 MaxVal[iDim] = std::max(MaxVal[iDim], bin.Extent[iDim][1]);
537 MACH3LOG_DEBUG(
"Mapping binning: Dim {} Min = {:.2f}, Max = {:.2f}", iDim, MinVal[iDim], MaxVal[iDim]);
538 BinEdges[iDim].resize(BinsPerDimension + 1);
539 double BinWidth = (MaxVal[iDim] - MinVal[iDim]) /
static_cast<double>(BinsPerDimension);
540 for (
size_t iEdge = 0; iEdge <= BinsPerDimension; iEdge++) {
541 BinEdges[iDim][iEdge] = MinVal[iDim] +
static_cast<double>(iEdge) * BinWidth;
558 for(
int iDim = 0; iDim < static_cast<int>(BinIndices.size()); iDim++){
559 if (BinIndices[iDim] < 0 || BinIndices[iDim] >=
AxisNBins[iDim]) {
560 MACH3LOG_ERROR(
"{}: Bin indices out of range: Dim = {}, Bin={}, max Ndim Bin={}",
561 __func__, iDim, BinIndices[iDim],
AxisNBins[iDim]);
565 return GetBin(BinIndices);
575 int GetBin(
const std::vector<int>& BinIndices)
const {
577 for(
size_t i = 0; i < BinIndices.size(); ++i) {
578 BinNumber += BinIndices[i]*
Strides[i];
584 int FindBin(
const int Dimension,
const double Var,
const int NomBin)
const {
599 const std::vector<double>& Bin_Edges,
600 const std::vector<BinShiftLookup>& Bin_Lookup)
const {
603 if (KinVar < Bin_Edges[0] || KinVar >= Bin_Edges[N_Bins]) {
610 const double lower = Bin.lower_binedge;
611 const double upper = Bin.upper_binedge;
612 const double lower_lower = Bin.lower_lower_binedge;
613 const double upper_upper = Bin.upper_upper_binedge;
616 if (KinVar < upper && KinVar >= lower) {
621 if (KinVar < lower && KinVar >= lower_lower) {
625 if (KinVar < upper_upper && KinVar >= upper) {
631 return static_cast<int>(std::distance(Bin_Edges.begin(), std::upper_bound(Bin_Edges.begin(), Bin_Edges.end(), KinVar)) - 1);
639 Bin_Lookup.resize(TotBins);
641 for(
int bin_i = 0; bin_i < TotBins; bin_i++){
643 double low_edge = Bin_Edges[bin_i];
644 double upper_edge = Bin_Edges[bin_i+1];
648 low_lower_edge = Bin_Edges[0];
650 low_lower_edge = Bin_Edges[bin_i-1];
653 if (bin_i + 2 < TotBins) {
654 upper_upper_edge = Bin_Edges[bin_i + 2];
655 }
else if (bin_i + 1 < TotBins) {
656 upper_upper_edge = Bin_Edges[bin_i + 1];
659 Bin_Lookup[bin_i].lower_binedge = low_edge;
660 Bin_Lookup[bin_i].upper_binedge = upper_edge;
661 Bin_Lookup[bin_i].lower_lower_binedge = low_lower_edge;
662 Bin_Lookup[bin_i].upper_upper_binedge = upper_upper_edge;
678 for (
int i = 0; i < Dimension; ++i) {
689 for (
int i = 0; i < Dimension; ++i) {
702 for (
size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
705 return static_cast<int>(iSample);
709 MACH3LOG_ERROR(
"Couldn't find sample corresponding to bin {}", GlobalBin);
721 const int GlobalBin) {
722 for (
size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
732 MACH3LOG_ERROR(
"Couldn't find local bin corresponding to bin {}", GlobalBin);
753 case 11:
return 0.00051099895;
754 case 13:
return 0.1056583755;
755 case 15:
return 1.77693;
764 case 211:
return 0.13957039;
765 case 111:
return 0.1349768;
766 case 221:
return 0.547862;
771 case 321:
return 0.493677;
773 case 2112:
return 0.939565;
774 case 2212:
return 0.938272;
775 case 3122:
return 1.115683;
776 case 3222:
return 1.118937;
777 case 3112:
return 1.197449;
778 case 3212:
return 1.192642;
780 case 1000050110:
return 10.255103;
781 case 1000060120:
return 11.177929;
782 case 1000070140:
return 13.043781;
783 case 1000080160:
return 14.899169;
784 case 1000090190:
return 17.696901;
785 case 1000110230:
return 21.414835;
786 case 1000130270:
return 25.133144;
787 case 1000140280:
return 26.060342;
788 case 1000190390:
return 36.294463;
789 case 1000180400:
return 37.224724;
790 case 1000220480:
return 44.663224;
791 case 1000300640:
return 59.549619;
807 switch(std::abs(NuPdg)){
809 NuOscillatorFlavour = NuOscillator::kElectron;
812 NuOscillatorFlavour = NuOscillator::kMuon;
815 NuOscillatorFlavour = NuOscillator::kTau;
818 MACH3LOG_ERROR(
"Unknown Neutrino PDG {}, cannot convert to NuOscillator type", NuPdg);
825 if(NuPdg < 0){NuOscillatorFlavour *= -1;}
827 return NuOscillatorFlavour;
833 inline std::string
FormatDouble(
const double value,
const int precision) {
835 std::ostringstream oss;
836 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_
KS: Restore warning checking after including external headers.
#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.
std::function< void(const M3::float_t *, std::size_t)> FuncParFuncType
HH - a shorthand type for funcpar functions.
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.
int PDGToNuOscillatorFlavour(const int NuPdg)
Convert from PDG flavour to NuOscillator type beware that in the case of anti-neutrinos the NuOscilla...
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...
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
Small struct used for applying shifts due to functional params.
FuncParFuncType * funcPtr
Pointer to shifting function.
const M3::float_t * valuePtr
Pointer to parameter value.
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.