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));
191 std::vector<std::array<double, 2>>
Extent;
195 const size_t N = KinVars.size();
197 #pragma omp simd reduction(&:inside)
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]);
209 const size_t N = KinVars.size();
211 #pragma omp simd reduction(&:inside)
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]);
255 void InitUniform(
const std::vector<std::vector<double>>& InputEdges) {
261 for(
size_t iDim = 0; iDim <
BinEdges.size(); 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,
", "));
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");
291 if (TestedBins.empty())
return;
293 const size_t ExtentDim = TestedBins[0].Extent.size();
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;
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];
306 if (!(a_lo < b_hi && b_lo < a_hi)) {
307 OverlapsInAllDims =
false;
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) {
317 i, TestedBins[i].Extent[iDim][0], TestedBins[i].Extent[iDim][1],
318 j, TestedBins[j].Extent[iDim][0], TestedBins[j].Extent[iDim][1]);
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");
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;
353 std::vector<size_t> indices(Dim, 0);
355 std::function<void(
size_t)> scan = [&](
size_t d) {
356 if (gap_found)
return;
360 std::vector<double> point(Dim);
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);
370 for (
const auto& bin : TestedBins) {
371 if (bin.IsEventInside(point)) {
377 MACH3LOG_WARN(
"Gap detected in non-uniform binning at point [{:.2f}]", fmt::join(point,
", "));
382 for (
size_t i = 0; i + 1 < TestGridEdges[d].size(); ++i) {
397 for (
size_t d = 0; d < Dim; ++d) {
403 std::vector<int> MultiIndex(Dim, 0);
405 std::function<void(
size_t,
int)> scan;
406 scan = [&](
size_t d,
int LinearIndex) {
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];
416 for (
size_t iBin = 0; iBin <
Bins.size(); ++iBin) {
417 auto& bin =
Bins[iBin];
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)) {
433 std::vector<std::string> bin_extent_str(Dim);
434 std::vector<std::string> mega_edges_str(Dim);
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]);
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,
", "));
452 int NewLinearIndex = LinearIndex;
455 for (
size_t s = 0; s < d; ++s) stride *=
AxisNBins[s];
456 NewLinearIndex += i * stride;
460 scan(d + 1, NewLinearIndex);
468 void InitNonUniform(
const std::vector<std::vector<std::vector<double>>>& InputBins) {
471 Bins.resize(InputBins.size());
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...");
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);
486 for (
const auto& extent : NewExtent) {
487 if (extent.size() != 2) {
491 NewBin.
Extent.push_back({extent[0], extent[1]});
492 MACH3LOG_DEBUG(
"Adding extent for Bin {} Dim {}: [{:.2f}, {:.2f}]",
495 Bins[iBin] = std::move(NewBin);
501 constexpr
int BinsPerDimension = 10;
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();
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]);
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;
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]);
544 return GetBin(BinIndices);
554 int GetBin(
const std::vector<int>& BinIndices)
const {
556 for(
size_t i = 0; i < BinIndices.size(); ++i) {
557 BinNumber += BinIndices[i]*
Strides[i];
563 int FindBin(
const int Dimension,
const double Var,
const int NomBin)
const {
578 const std::vector<double>& Bin_Edges,
579 const std::vector<BinShiftLookup>& Bin_Lookup)
const {
582 if (KinVar < Bin_Edges[0] || KinVar >= Bin_Edges[N_Bins]) {
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;
595 if (KinVar < upper && KinVar >= lower) {
600 if (KinVar < lower && KinVar >= lower_lower) {
604 if (KinVar < upper_upper && KinVar >= upper) {
610 return static_cast<int>(std::distance(Bin_Edges.begin(), std::upper_bound(Bin_Edges.begin(), Bin_Edges.end(), KinVar)) - 1);
618 Bin_Lookup.resize(TotBins);
620 for(
int bin_i = 0; bin_i < TotBins; bin_i++){
622 double low_edge = Bin_Edges[bin_i];
623 double upper_edge = Bin_Edges[bin_i+1];
627 low_lower_edge = Bin_Edges[0];
629 low_lower_edge = Bin_Edges[bin_i-1];
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];
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;
657 for (
int i = 0; i < Dimension; ++i) {
668 for (
int i = 0; i < Dimension; ++i) {
681 for (
size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
684 return static_cast<int>(iSample);
688 MACH3LOG_ERROR(
"Couldn't find sample corresponding to bin {}", GlobalBin);
700 const int GlobalBin) {
701 for (
size_t iSample = 0; iSample < BinningInfo.size(); ++iSample) {
711 MACH3LOG_ERROR(
"Couldn't find local bin corresponding to bin {}", GlobalBin);
731 case 11:
return 0.00051099895;
732 case 13:
return 0.1056583755;
733 case 15:
return 1.77693;
742 case 211:
return 0.13957039;
743 case 111:
return 0.1349768;
744 case 221:
return 0.547862;
749 case 321:
return 0.493677;
751 case 2112:
return 0.939565;
752 case 2212:
return 0.938272;
753 case 3122:
return 1.115683;
754 case 3222:
return 1.118937;
755 case 3112:
return 1.197449;
756 case 3212:
return 1.192642;
758 case 1000050110:
return 10.255103;
759 case 1000060120:
return 11.177929;
760 case 1000070140:
return 13.043781;
761 case 1000080160:
return 14.899169;
762 case 1000090190:
return 17.696901;
763 case 1000110230:
return 21.414835;
764 case 1000130270:
return 25.133144;
765 case 1000140280:
return 26.060342;
766 case 1000190390:
return 36.294463;
767 case 1000180400:
return 37.224724;
768 case 1000220480:
return 44.663224;
769 case 1000300640:
return 59.549619;
785 switch(std::abs(NuPdg)){
787 NuOscillatorFlavour = NuOscillator::kElectron;
790 NuOscillatorFlavour = NuOscillator::kMuon;
793 NuOscillatorFlavour = NuOscillator::kTau;
796 MACH3LOG_ERROR(
"Unknown Neutrino PDG {}, cannot convert to NuOscillator type", NuPdg);
803 if(NuPdg < 0){NuOscillatorFlavour *= -1;}
805 return NuOscillatorFlavour;
811 inline std::string
FormatDouble(
const double value,
const int precision) {
813 std::ostringstream oss;
814 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 double *, 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.
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 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_
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.