MaCh3  2.8.0
Reference Guide
BinnedSplineHandler.cpp
Go to the documentation of this file.
1 #include "BinnedSplineHandler.h"
2 #include <memory>
3 
4 #ifdef MaCh3_CUDA
6 #endif
7 
9 #include "TROOT.h"
10 #include "TKey.h"
11 #include "TH3F.h"
13 
14 #pragma GCC diagnostic ignored "-Wuseless-cast"
15 
16 //****************************************
18 //****************************************
19  if (!ParHandler_) {
20  MACH3LOG_ERROR("Trying to create BinnedSplineHandler with uninitialized covariance object");
21  throw MaCh3Exception(__FILE__, __LINE__);
22  }
23  ParHandler = ParHandler_;
24 
25  if (!Modes_) {
26  MACH3LOG_ERROR("Trying to create BinnedSplineHandler with uninitialized MaCh3Modes object");
27  throw MaCh3Exception(__FILE__, __LINE__);
28  }
29  Modes = Modes_;
30 
31  // Keep these in class scope, important for using 1 monolith/sample!
32  MonolithIndex = 0; //Keeps track of the monolith index we're on when filling arrays (declared here so we can have multiple FillSampleArray calls)
33  CoeffIndex = 0; //Keeps track of our indexing the coefficient arrays [x, ybcd]
34 }
35 
36 //****************************************
38 //****************************************
39  if(manycoeff_arr != nullptr) delete[] manycoeff_arr;
40  if(xcoeff_arr != nullptr) delete[] xcoeff_arr;
41  #ifdef MaCh3_CUDA
42  if (useGPU) {
43  //KS: Since we declared them using CUDA alloc we have to free memory using also cuda functions
45  delete gpu_monolith;
46  } else
47  #endif
48  {
49  if(SplineSegments != nullptr) delete[] SplineSegments;
50  if(ParamValues != nullptr) delete[] ParamValues;
51  }
52 }
53 
54 //****************************************
56 //****************************************
57  //Call once everything's been allocated in SampleHandlerBase, cleans up junk from memory!
58  //Not a huge saving but it's better than leaving everything up to the compiler
59  MACH3LOG_INFO("Cleaning up spline memory");
61  IndexVectMap.clear();
77 }
78 
79 //****************************************
80 //Adds samples to the large array
81 void BinnedSplineHandler::AddSample(const std::string& SampleName,
82  const std::string& SampleTitle,
83  const std::vector<std::string>& OscChanFileNames,
84  const std::vector<std::string>& SplineVarNames) {
85 //****************************************
86  SampleNames.push_back(SampleName);
87  SampleTitles.push_back(SampleTitle);
88  Dimensions.push_back(static_cast<int>(SplineVarNames.size()));
89  DimensionLabels.push_back(SplineVarNames);
90 
91  int nSplineParam = ParHandler->GetNumParamsFromSampleName(SampleName, SystType::kSpline);
92  nSplineParams.push_back(nSplineParam);
93 
94  //This holds the global index of the spline i.e. 0 -> _fNumPar
95  std::vector<int> GlobalSystIndex_Sample = ParHandler->GetGlobalSystIndexFromSampleName(SampleName, SystType::kSpline);
96  //Keep track of this for all the samples
97  GlobalSystIndex.push_back(GlobalSystIndex_Sample);
98 
99  std::vector<SplineInterpolation> SplineInterpolation_Sample = ParHandler->GetSplineInterpolationFromSampleName(SampleName);
100  // Keep track of this for all samples
101  SplineInterpolationTypes.push_back(SplineInterpolation_Sample);
102 
103  std::vector<std::string> SplineFileParPrefixNames_Sample = ParHandler->GetSplineParsNamesFromSampleName(SampleName);
104  SplineFileParPrefixNames.push_back(SplineFileParPrefixNames_Sample);
105 
106  MACH3LOG_INFO("Create SplineModeVecs_Sample");
107  std::vector<std::vector<int>> SplineModeVecs_Sample = StripDuplicatedModes(ParHandler->GetSplineModeVecFromSampleName(SampleName));
108  MACH3LOG_INFO("SplineModeVecs_Sample is of size {}", SplineModeVecs_Sample.size());
109  SplineModeVecs.push_back(SplineModeVecs_Sample);
110 
111  MACH3LOG_INFO("SplineModeVecs is of size {}", SplineModeVecs.size());
112 
113  int nOscChan = int(OscChanFileNames.size());
114  nOscChans.push_back(nOscChan);
115 
116  PrintSampleDetails(SampleTitle);
117 
118  std::vector<std::vector<TAxis *>> SampleBinning(nOscChan);
119  for (int iOscChan = 0; iOscChan < nOscChan; iOscChan++)
120  {
121  SampleBinning[iOscChan] = FindSplineBinning(OscChanFileNames[iOscChan], SampleTitle);
122  }
123  MACH3LOG_INFO("#----------------------------------------------------------------------------------------------------------------------------------#");
124  SplineBinning.push_back(SampleBinning);
125 
126  BuildSampleIndexingArray(SampleTitle);
127  PrintArrayDetails(SampleTitle);
128  MACH3LOG_INFO("#----------------------------------------------------------------------------------------------------------------------------------#");
129 
130  FillSampleArray(SampleTitle, OscChanFileNames);
131  MACH3LOG_INFO("#----------------------------------------------------------------------------------------------------------------------------------#");
132 }
133 
134 //****************************************
136 //****************************************
137  // Map: iSample → iSyst → modeSuffix → {totalSplines, zeroCount}
138  std::map<unsigned int, std::map<unsigned int, std::map<std::string, std::pair<unsigned int, unsigned int>>>> systZeroCounts;
139  for (const auto& entry : IndexVect) {
140  unsigned int iSample = entry.iSample;
141  unsigned int iSyst = entry.iSyst;
142 
143  std::string SampleName = SampleNames[iSample];
144  // Get the mode suffix string
145  std::string modeSuffix = Modes->GetSplineSuffixFromMaCh3Mode(SplineModeVecs[iSample][entry.iSyst][entry.iMode]);
146  auto& counts = systZeroCounts[iSample][iSyst][modeSuffix];
147 
148  counts.first++; // totalSplines
149  if (entry.value == -1)
150  {
151  counts.second++; // zeroCount
152  if (counts.second > 1)
153  {
155  "Sample '{}' | OscChan {} | Syst '{}' | Mode '{}' | Var {} => Value: {}",
156  SampleTitles[iSample],
157  entry.iOscChan,
158  SplineFileParPrefixNames[iSample][iSyst],
159  modeSuffix,
160  fmt::join(entry.iVar, " "),
161  entry.value
162  );
163  }
164  }
165  }
166 
167  // KS: Let's print this atrocious mess...
168  for (const auto& samplePair : systZeroCounts) {
169  unsigned int iSample = samplePair.first;
170  std::vector<std::string> SplineFileParPrefixNames_Sample = ParHandler->GetParsNamesFromSampleName(SampleNames[iSample], kSpline);
171  for (const auto& systPair : samplePair.second) {
172  unsigned int iSyst = systPair.first;
173  const auto& systName = SplineFileParPrefixNames_Sample[iSyst];
174  for (const auto& modePair : systPair.second) {
175  const auto& modeSuffix = modePair.first;
176  const auto& counts = modePair.second;
178  "Sample '{}': Systematic '{}' has missing splines in mode '{}'. Expected Splines: {}, Missing Splines: {}",
179  SampleTitles[iSample],
180  systName,
181  modeSuffix,
182  counts.first,
183  counts.second
184  );
185  }
186  }
187  }
188 }
189 
190 //****************************************
192 //****************************************
193  PrepForReweight();
194  auto NSplines_All = CountNumberOfLoadedSplines(false, 1);
196  if (static_cast<unsigned int>(NSplines_All) != MonolithIndex) {
198  MACH3LOG_ERROR("Something's gone wrong when we tried to get the size of your monolith");
199  MACH3LOG_ERROR("NSplines_valid is {}", NSplines_valid);
200  MACH3LOG_ERROR("MonolithIndex is {}", MonolithIndex);
201  throw MaCh3Exception(__FILE__ , __LINE__ );
202  }
203 
204  MACH3LOG_INFO("Now transferring splines to a monolith if size {}", NSplines_valid);
205  // Maps single spline object with single parameter
206  paramNo_arr.resize(NSplines_valid);
208  nKnots_arr.resize(NSplines_valid);
209  monolith_index.resize(NSplines_All);
210 
213 
214  int ValidSplineCounter = 0;
215  int ValidKnotCounter = 0;
216  for (const auto& entry : IndexVect) {
217  int splineindex = entry.value;
218  if(splinevec_Monolith[splineindex]) {
219  cpu_spline_weights[ValidSplineCounter] = 1.0;
220 
221  bool foundUniqueSpline = false;
222  // We are trying to match Spline Object with single parameter (like MAQE)
223  for (int iUniqueSyst = 0; iUniqueSyst < nParams; iUniqueSyst++)
224  {
225  if (SplineFileParPrefixNames[entry.iSample][entry.iSyst] == UniqueSystNames[iUniqueSyst])
226  {
227  paramNo_arr[ValidSplineCounter] = static_cast<short int>(iUniqueSyst);
228  foundUniqueSpline = true;
229  break;
230  }
231  } //unique syst loop end
232 
233  // If current spline object hasn't been matched with actual parameter this means misconfiguration
234  if (!foundUniqueSpline)
235  {
236  MACH3LOG_ERROR("Unique spline index not found");
237  MACH3LOG_ERROR("For Spline {}", SplineFileParPrefixNames[entry.iSample][entry.iSyst]);
238  MACH3LOG_ERROR("Couldn't match {} with any of the following {} systs:", SplineFileParPrefixNames[entry.iSample][entry.iSyst], nParams);
239  for (int iUniqueSyst = 0; iUniqueSyst < nParams; iUniqueSyst++)
240  {
241  MACH3LOG_ERROR("{},", UniqueSystNames.at(iUniqueSyst));
242  }//unique syst loop end
243  throw MaCh3Exception(__FILE__ , __LINE__ );
244  }
245 
246  monolith_index[splineindex] = ValidSplineCounter;
247  int splineKnots = splinevec_Monolith[splineindex]->GetNp();
248  //Now to fill up our coefficient arrays
249  M3::float_t* tmpXCoeffArr = new M3::float_t[splineKnots];
250  M3::float_t* tmpManyCoeffArr = new M3::float_t[splineKnots*_nCoeff_];
251 
252  GetSplineCoeff_SepMany(splineindex, tmpXCoeffArr, tmpManyCoeffArr);
253 
254  nKnots_arr[ValidSplineCounter] = ValidKnotCounter;
255  for(int i = 0; i < splineKnots; i++){
256  xcoeff_arr[paramNo_arr[ValidSplineCounter]*_max_knots + i] = tmpXCoeffArr[i];
257 
258  for(int j = 0; j < _nCoeff_; j++){
259  manycoeff_arr[(ValidKnotCounter+i)*_nCoeff_+j]=tmpManyCoeffArr[i*_nCoeff_+j];
260  }
261  }
262  ValidKnotCounter += splineKnots;
263  delete[] tmpXCoeffArr;
264  delete[] tmpManyCoeffArr;
265  ValidSplineCounter++;
266  } else {
267  monolith_index[splineindex] = M3::_BAD_INT_;
268  }
269  }
270 
271  MoveToGPU();
272 
273  // initialise spline segment structure
274  SetupSegments();
275 }
276 
277 // *****************************************
279 // *****************************************
280  // There's a parameter mapping that goes from spline parameter to a global parameter index
281  // Find the spline segments
283 
284  #ifdef MaCh3_CUDA
285  if (useGPU) { // GPU calculations
286  // The main call to the GPU
288  cpu_spline_weights.data(),
289  ParamValues,
291  } else
292  #endif
293  { // CPU-only calculations
294  // KS: Huge MP loop over all valid splines
296  }
297 }
298 
299 //****************************************
301 //****************************************
302  #ifdef MULTITHREAD
303  #pragma omp parallel for simd
304  #endif
305  for (unsigned int splineNum = 0; splineNum < NSplines_valid; ++splineNum)
306  {
307  //CW: Which Parameter we are accessing
308  const short int Param = paramNo_arr[splineNum];
309  //CW: Avoids doing costly binary search on GPU
310  const short int segment = short(SplineSegments[Param]);
311 
312  //KS: Segment for coeff_x is simply parameter*max knots + segment as each parameters has the same spacing
313  const short int segment_X = short(Param*_max_knots+segment);
314 
315  //KS: Find knot position in out monolithical structure
316  const unsigned int CurrentKnotPos = (nKnots_arr[splineNum]+segment) * _nCoeff_;
317 
318  // These are what we can extract from the TSpline3
319  const M3::float_t y = manycoeff_arr[CurrentKnotPos+kCoeffY];
320  const M3::float_t b = manycoeff_arr[CurrentKnotPos+kCoeffB];
321  const M3::float_t c = manycoeff_arr[CurrentKnotPos+kCoeffC];
322  const M3::float_t d = manycoeff_arr[CurrentKnotPos+kCoeffD];
323 
324  // Get the variation for this reconfigure for the i-th parameter
326  const M3::float_t xvar = (*SplineInfoArray[Param].splineParsPointer);
327  // The Delta(x) = xvar - x
328  const M3::float_t dx = xvar - xcoeff_arr[segment_X];
329 
330  //CW: Wooow, let's use some fancy intrinsic and pull down the processing time by <1% from normal multiplication! HURRAY
331  cpu_spline_weights[splineNum] = M3::fmaf_t(dx, M3::fmaf_t(dx, M3::fmaf_t(dx, d, c), b), y);
332  // Or for the more "easy to read" version:
333  //cpu_spline_weights[splineNum] = (fY+dx*(fB+dx*(fC+dx*fD)));
334  }
335 }
336 
337 //****************************************
338 //Creates an array to be filled with monolith indexes for each sample (allows for indexing between 7D binning and 1D Vector)
339 //Only need 1 indexing array everything else interfaces with this to get binning properties
340 void BinnedSplineHandler::BuildSampleIndexingArray(const std::string& SampleTitle) {
341 //****************************************
342  int iSample = GetSampleIndex(SampleTitle);
343  for (int iOscChan = 0; iOscChan < nOscChans[iSample]; ++iOscChan)
344  {
345  for (int iSyst = 0; iSyst < nSplineParams[iSample]; ++iSyst)
346  {
347  int nModesInSyst = static_cast<int>(SplineModeVecs[iSample][iSyst].size());
348  for (int iMode = 0; iMode < nModesInSyst; ++iMode)
349  {
350  const int nDims = static_cast<int>(SplineBinning[iSample][iOscChan].size());
351  std::vector<int> nBins(nDims);
352  for (int iDim = 0; iDim < nDims; ++iDim) {
353  nBins[iDim] = SplineBinning[iSample][iOscChan][iDim]->GetNbins();
354  }
355  // Current bin index for each dimension, e.g. {0,0,0} for a 3D spline.
356  std::vector<int> iVar(nDims, 0);
357  // Loop over every possible combination of kinematic bins.
358  while (true)
359  {
360  SplineIndex entry;
361  entry.value = -1;
362  entry.iSample = iSample;
363  entry.iOscChan = iOscChan;
364  entry.iSyst = iSyst;
365  entry.iMode = iMode;
366  entry.iVar = iVar;
367 
368  IndexVect.push_back(entry);
369  IndexVectMap[std::make_tuple(iSample, iOscChan, iSyst, iMode, iVar)] = static_cast<int>(IndexVect.size() - 1);
370 
371  int iDim = nDims - 1;
372  while (iDim >= 0 && ++iVar[iDim] == nBins[iDim]) {
373  iVar[iDim] = 0;
374  --iDim;
375  }
376 
377  if (iDim < 0)
378  break;
379  } // Over kinematic variables
380  } // Over modes
381  } // Over systs
382  } // Over channels
383 }
384 
385 //****************************************
386 std::vector<TAxis *> BinnedSplineHandler::FindSplineBinning(const std::string& FileName,
387  const std::string& SampleTitle) {
388 //****************************************
389  int iSample = GetSampleIndex(SampleTitle);
390  TH1* Axis_Hist = nullptr;
391 
392  auto File = std::unique_ptr<TFile>(TFile::Open(FileName.c_str(), "READ"));
393  if (!File || File->IsZombie())
394  {
395  MACH3LOG_ERROR("File {} not found", FileName);
396  MACH3LOG_ERROR("This is caused by something here! {} : {}", __FILE__, __LINE__);
397  throw MaCh3Exception(__FILE__ , __LINE__ );
398  }
399 
400  MACH3LOG_INFO("Finding binning for:");
401  MACH3LOG_INFO("{}", FileName);
402 
403  std::string TemplateName = "dev_tmp_0_0";
404  TObject *Obj = File->Get(TemplateName.c_str());
405  //If you can't find dev_tmp_0_0 then this will cause a problem
406  if (!Obj)
407  {
408  TemplateName = "dev_tmp.0.0";
409  Obj = File->Get(TemplateName.c_str());
410  if (!Obj)
411  {
412  MACH3LOG_ERROR("Could not find dev_tmp_0_0 in spline file. Spline binning cannot be set!");
413  MACH3LOG_ERROR("FileName: {}", FileName);
414  throw MaCh3Exception(__FILE__ , __LINE__ );
415  }
416  }
417 
418  //Now check if dev_tmp_0_0 is a TH2 i.e. specifying the dimensions of the splines is 2D
419  bool isHist2D = Obj->IsA() == TH2F::Class();
420  //For T2K annoyingly all objects are TH3Fs
421  bool isHist3D = Obj->IsA() == TH3F::Class();
422  if (!isHist2D && !isHist3D)
423  {
424  MACH3LOG_ERROR("Object doesn't inherit from either TH2D and TH3D - Odd A");
425  throw MaCh3Exception(__FILE__ , __LINE__ );
426  }
427 
428  if (isHist2D)
429  {
430  if (Dimensions[iSample] != 2)
431  {
432  MACH3LOG_ERROR("Trying to load a 2D spline template when nDim={}", Dimensions[iSample]);
433  throw MaCh3Exception(__FILE__, __LINE__);
434  }
435  Axis_Hist = File->Get<TH2F>(TemplateName.c_str());
436  }
437 
438  if (isHist3D)
439  {
440  Axis_Hist = File->Get<TH3F>((TemplateName.c_str()));
441  if (Dimensions[iSample] != 3 && Axis_Hist->GetZaxis()->GetNbins() != 1)
442  {
443  MACH3LOG_ERROR("Trying to load a 3D spline template when nDim={}", Dimensions[iSample]);
444  throw MaCh3Exception(__FILE__ , __LINE__ );
445  }
446  }
448  std::vector<TAxis*> ReturnVec(3);
449  ReturnVec[0] = static_cast<TAxis*>(Axis_Hist->GetXaxis()->Clone());
450  ReturnVec[1] = static_cast<TAxis*>(Axis_Hist->GetYaxis()->Clone());
451 
452  if (Dimensions[iSample] == 3) {
453  ReturnVec[2] = static_cast<TAxis*>(Axis_Hist->GetZaxis()->Clone());
454  } else {
455  // creating dummy binning
456  constexpr double DummyEdges[2] = {-1e15, 1e15};
457  ReturnVec[2] = new TAxis(1, DummyEdges);
458  }
459 
460  for (unsigned int iAxis = 0; iAxis < ReturnVec.size(); ++iAxis) {
461  PrintBinning(ReturnVec[iAxis]);
462  }
463 
464  MACH3LOG_INFO("Left PrintBinning now tidying up");
465  return ReturnVec;
466 }
467 
468 //****************************************
470 //****************************************
471  // index in global spline space
472  int Index = IndexVectMap.at(std::make_tuple(Variables.iSample, Variables.iOscChan, Variables.iSyst,
473  Variables.iMode, Variables.iVar));
474  // global in space after removing flat splines
475  auto index_flattened = monolith_index[IndexVect[Index].value];
476  // Very unlikely to get response from flat spline. Could perform some optimisation and remove these but leave for now.
477  if(index_flattened == M3::_BAD_INT_) {
478  return &M3::Unity;
479  } else {
480  return &cpu_spline_weights[index_flattened];
481  }
482 }
483 
484 //****************************************
485 int BinnedSplineHandler::CountNumberOfLoadedSplines(bool NonFlat, int Verbosity) const {
486 //****************************************
487  std::vector<int> SampleAll(SampleTitles.size(), 0);
488  std::vector<int> SampleNonFlat(SampleTitles.size(), 0);
489 
490  int FullCounter_All = 0;
491  int FullCounter_NonFlat = 0;
492 
493  for (unsigned int index = 0; index < IndexVect.size(); index++)
494  {
495  const auto& entry = IndexVect[index];
496 
497  int iSample = entry.iSample;
498 
499  std::string SampleTitle = SampleTitles[iSample];
500 
501  if (!isValidSplineIndex(SampleTitle, entry.iOscChan, entry.iSyst,
502  entry.iMode, entry.iVar)) {
503  continue;
504  }
505  SampleAll[iSample]++;
506 
507  if (splinevec_Monolith[entry.value]) {
508  SampleNonFlat[iSample]++;
509  }
510  }
511 
512  // Print per-sample summary
513  for (size_t iSample = 0; iSample < SampleTitles.size(); iSample++)
514  {
515  MACH3LOG_DEBUG("{:<10} has {:<10} splines, of which {:<10} are not flat",
516  SampleTitles[iSample],
517  SampleAll[iSample],
518  SampleNonFlat[iSample]);
519 
520  FullCounter_All += SampleAll[iSample];
521  FullCounter_NonFlat += SampleNonFlat[iSample];
522  }
523 
524  if (Verbosity > 0) {
525  MACH3LOG_INFO("Total number of splines loaded: {}", FullCounter_All);
526  MACH3LOG_INFO("Total number of non-flat splines loaded: {}", FullCounter_NonFlat);
527  }
528 
529  if (NonFlat) {
530  return FullCounter_NonFlat;
531  } else {
532  return FullCounter_All;
533  }
534 }
535 
536 //****************************************
538 //****************************************
539  std::vector<TSpline3_red*> UniqueSystSplines;
540  bool FoundNonFlatSpline = false;
541  int SampleCounter = M3::_BAD_INT_;
542  int SystCounter = M3::_BAD_INT_;
543  // DB Find all the Unique systs across each sample and oscillation channel
544  // This assumes that each occurrence of the same systematic spline has the same knot spacing
545  // Which is a reasonable assumption for the current implementation of spline evaluations
546  for (const auto& entry : IndexVect)
547  {
548  int splineindex = entry.value;
549  // KS: reset if we moved to another sample or syst
550  if(SampleCounter != entry.iSample || SystCounter != entry.iSyst) {
551  SampleCounter = entry.iSample;
552  SystCounter = entry.iSyst;
553  FoundNonFlatSpline = false;
554  }
555  std::string SystName = SplineFileParPrefixNames[entry.iSample][entry.iSyst];
556  bool FoundSyst = false;
557  for (unsigned int iFoundSyst = 0; iFoundSyst < UniqueSystNames.size(); iFoundSyst++) {
558  if (SystName == UniqueSystNames[iFoundSyst]) {
559  FoundSyst = true;
560  }
561  }
562  if (FoundSyst) continue;
563 
564  if (splinevec_Monolith[splineindex])
565  {
566  UniqueSystSplines.push_back(splinevec_Monolith[splineindex]);
567  UniqueSystIndices.push_back(GlobalSystIndex[entry.iSample][entry.iSyst]);
568 
569  FoundNonFlatSpline = true;
570  }
571  if (FoundNonFlatSpline) {
572  UniqueSystNames.push_back(SystName);
573  } else {
574  MACH3LOG_DEBUG("{} syst has no response in sample {}", SystName, entry.iSample);
575  MACH3LOG_DEBUG("Whilst this isn't necessarily a problem, it seems odd");
576  }
577  } // end loop over indices
578  nParams = static_cast<short int>(UniqueSystSplines.size());
579 
580  // DB Find the number of splines knots which assumes each instance of the syst has the same number of knots
581  SplineInfoArray.resize(nParams);
582  for (int iSpline = 0; iSpline < nParams; iSpline++)
583  {
584  SplineInfoArray[iSpline].nPts = static_cast<M3::int_t>(UniqueSystSplines[iSpline]->GetNp());
585  SplineInfoArray[iSpline].xPts.resize(SplineInfoArray[iSpline].nPts);
586  SplineInfoArray[iSpline].splineParsPointer = ParHandler->RetPointer(UniqueSystIndices[iSpline]);
587  for (int iKnot = 0; iKnot < SplineInfoArray[iSpline].nPts; iKnot++)
588  {
589  M3::float_t xPoint;
590  M3::float_t yPoint;
591  UniqueSystSplines[iSpline]->GetKnot(iKnot, xPoint, yPoint);
592  SplineInfoArray[iSpline].xPts[iKnot] = xPoint;
593  }
594  }
595  M3::Utils::TablePrinter table({{"Spline Index", 10, 15, M3::Utils::Alignment::Left},
596  {"Syst Name", 10, 20, M3::Utils::Alignment::Left},
597  {"nKnots", 10, 6, M3::Utils::Alignment::Right}});
598 
599  MACH3LOG_INFO("nUniqueSysts: {}", nParams);
600  for (int iUniqueSyst = 0; iUniqueSyst < nParams; iUniqueSyst++) {
601  table.AddRow(iUniqueSyst, UniqueSystNames[iUniqueSyst], SplineInfoArray[iUniqueSyst].nPts);
602  }
603 
604  for (const auto& line : table.Render()) {
605  MACH3LOG_INFO("{}", line);
606  }
607 
608  int nCombinations_FlatSplines = 0;
609  int nCombinations_All = 0;
610  // DB Now actually loop over splines to determine which are all null i.e. flat
611  for (unsigned int index = 0; index < IndexVect.size(); index++)
612  {
613  int splineindex = IndexVect[index].value;;
614  nCombinations_All++;
615  if (!splinevec_Monolith[splineindex]) {
616  nCombinations_FlatSplines++;
617  }
618  }
619 
620  // We need to grab the maximum number of knots
621  MACH3LOG_INFO("Number of combinations of Sample, OscChan, Syst and Mode which have entirely flat response: {} / {}", nCombinations_FlatSplines, nCombinations_All);
622 }
623 
624 //****************************************
625 // Rather work with spline coefficients in the splines, let's copy ND and use coefficient arrays
626 void BinnedSplineHandler::GetSplineCoeff_SepMany(int splineindex, M3::float_t* &xArray, M3::float_t* &manyArray) {
627 //****************************************
628  //No point evaluating a flat spline
629  int nPoints = splinevec_Monolith[splineindex]->GetNp();
630 
631  for (int i = 0; i < nPoints; i++) {
632  xArray[i] = 1.0;
633  for (int j = 0; j < _nCoeff_; j++) {
634  manyArray[i*_nCoeff_+j] = 1.0;
635  }
636  }
637 
638  for(int i=0; i<nPoints; i++) {
639  M3::float_t x = M3::float_t(-999.99);
640  M3::float_t y = M3::float_t(-999.99);
641  M3::float_t b = M3::float_t(-999.99);
642  M3::float_t c = M3::float_t(-999.99);
643  M3::float_t d = M3::float_t(-999.99);
644  splinevec_Monolith[splineindex]->GetCoeff(i, x, y, b, c, d);
645 
646  // Store the coefficients for each knot contiguously in memory
647  // 4 because manyArray stores y,b,c,d
648  xArray[i] = x;
649  manyArray[i * _nCoeff_ + kCoeffY] = y;
650  manyArray[i * _nCoeff_ + kCoeffB] = b;
651  manyArray[i * _nCoeff_ + kCoeffC] = c;
652  manyArray[i * _nCoeff_ + kCoeffD] = d;
653  }
654 
655  //We now clean up the splines!
656  delete splinevec_Monolith[splineindex];
657  splinevec_Monolith[splineindex] = nullptr;
658 }
659 
660 //****************************************
661 //Returns sample index in
662 int BinnedSplineHandler::GetSampleIndex(const std::string& SampleTitle) const {
663 //****************************************
664  for (size_t iSample = 0; iSample < SampleTitles.size(); ++iSample) {
665  if (SampleTitle == SampleTitles[iSample]) {
666  return static_cast<int>(iSample);
667  }
668  }
669  MACH3LOG_ERROR("Sample name not found: {}", SampleTitle);
670  throw MaCh3Exception(__FILE__, __LINE__);
671 }
672 
673 //****************************************
674 void BinnedSplineHandler::PrintSampleDetails(const std::string& SampleTitle) const {
675 //****************************************
676  const int iSample = GetSampleIndex(SampleTitle);
677 
678  MACH3LOG_INFO("Details about sample: {:<20}", SampleTitles[iSample]);
679  MACH3LOG_INFO("\t Dimension: {:<35}", Dimensions[iSample]);
680  MACH3LOG_INFO("\t nSplineParam: {:<35}", nSplineParams[iSample]);
681  MACH3LOG_INFO("\t nOscChan: {:<35}", nOscChans[iSample]);
682 }
683 
684 //****************************************
685 void BinnedSplineHandler::PrintArrayDetails(const std::string& SampleTitle) const {
686 //****************************************
687  int iSample = GetSampleIndex(SampleTitle);
688  // count oscillation channels
689  std::map<int, std::set<int>> OscToSysts;
690 
691  for (const auto& entry : IndexVect) {
692  if (entry.iSample != iSample) continue;
693  OscToSysts[entry.iOscChan].insert(entry.iSyst);
694  }
695  MACH3LOG_INFO("Sample {} has {} oscillation channels", SampleTitle, OscToSysts.size());
696 
697  for (const auto& OscPair : OscToSysts) {
698  int osc = OscPair.first;
699  const std::set<int>& SystSet = OscPair.second;
700  MACH3LOG_INFO("Oscillation channel {} has {} systematics", osc, SystSet.size());
701  }
702 }
703 
704 //****************************************
705 bool BinnedSplineHandler::isValidSplineIndex(const std::string& SampleTitle, int iOscChan,
706  int iSyst, int iMode, const std::vector<int>& iVar) const {
707 //****************************************
708  int iSample = GetSampleIndex(SampleTitle);
709 
710  bool found = IndexVectMap.find(std::make_tuple(iSample, iOscChan, iSyst, iMode, iVar)) != IndexVectMap.end();
711 
712  if (!found)
713  {
714  MACH3LOG_ERROR("Given iSample: {}", iSample);
715  MACH3LOG_ERROR("Given iOscChan: {}", iOscChan);
716  MACH3LOG_ERROR("Given iSyst: {}", iSyst);
717  MACH3LOG_ERROR("Given iMode: {}", iMode);
718  for (size_t i = 0; i < iVar.size(); ++i) {
719  MACH3LOG_ERROR("Given iVar{}: {}", i, iVar[i]);
720  }
721  MACH3LOG_ERROR("Come visit me at : {} : {}", __FILE__, __LINE__);
722  throw MaCh3Exception(__FILE__, __LINE__);
723  }
724 
725  return true;
726 }
727 
728 //****************************************
729 void BinnedSplineHandler::PrintBinning(TAxis *Axis) const {
730 //****************************************
731  const int NBins = Axis->GetNbins();
732  std::string text = "";
733  for (int iBin = 0; iBin <= NBins; iBin++) {
734  text += fmt::format("{} ", Axis->GetXbins()->GetAt(iBin));
735  }
736  MACH3LOG_INFO("{}", text);
737 }
738 
739 //****************************************
740 std::vector<SplineIndex> BinnedSplineHandler::GetEventSplines(const std::string& SampleTitle,
741  int iOscChan, int EventMode, const std::vector<double>& VarVals) {
742 //****************************************
743  std::vector<SplineIndex> ReturnVec;
744  int SampleIndex = GetSampleIndex(SampleTitle);
745 
746  int Mode = -1;
747  std::string SuffixForEventMode = Modes->GetSplineSuffixFromMaCh3Mode(EventMode);
748  for (int iMode = 0; iMode< Modes->GetNModes(); iMode++) {
749  if (SuffixForEventMode == Modes->GetSplineSuffixFromMaCh3Mode(iMode)) {
750  Mode = iMode;
751  break;
752  }
753  }
754  if (Mode == -1) {
755  return ReturnVec;
756  }
757 
758  std::vector<int> var_bins;
759  for (size_t i = 0; i < VarVals.size(); ++i) {
760  int bin = SplineBinning[SampleIndex][iOscChan][i]->FindBin(VarVals[i]) - 1;
761  if (bin < 0 || bin >= SplineBinning[SampleIndex][iOscChan][i]->GetNbins()) {
762  return ReturnVec;
763  }
764  var_bins.push_back(bin);
765  }
766 
767  for(int iSyst=0; iSyst < nSplineParams[SampleIndex]; iSyst++){
768  std::vector<int> spline_modes = SplineModeVecs[SampleIndex][iSyst];
769  int nSampleModes = static_cast<int>(spline_modes.size());
770 
771  //ETA - look here at the length of spline_modes and what you're actually comparing against
772  for(int iMode = 0; iMode<nSampleModes; iMode++) {
773  //Only consider if the event mode (Mode) matches ones of the spline modes
774  if (Mode == spline_modes[iMode]) {
775  int index = IndexVectMap.at(std::make_tuple(SampleIndex, iOscChan, iSyst, iMode,
776  var_bins));
777  int splineID = IndexVect[index].value;
778  //Also check that the spline isn't flat
779  if(monolith_index[splineID] != M3::_BAD_INT_) {
780  SplineIndex idx;
781  idx.iSample = SampleIndex;
782  idx.iOscChan = iOscChan;
783  idx.iSyst = iSyst;
784  idx.iMode = iMode;
785  idx.iVar = var_bins;
786  ReturnVec.push_back(idx);
787  }
788  }
789  }
790  }
791  return ReturnVec;
792 }
793 
794 //****************************************
795 // checks if there are multiple modes with the same SplineSuffix
796 // (for example if CCRES and CCCoherent are treated as one spline mode)
797 std::vector< std::vector<int> > BinnedSplineHandler::StripDuplicatedModes(const std::vector< std::vector<int> >& InputVector) const {
798 //****************************************
799  //ETA - this is of size nPars from the syst model
800  size_t InputVectorSize = InputVector.size();
801  std::vector< std::vector<int> > ReturnVec(InputVectorSize);
802 
803  //ETA - loop over all systematics
804  for (size_t iSyst=0;iSyst<InputVectorSize;iSyst++) {
805  std::vector<int> TmpVec;
806  std::vector<std::string> TestVec;
807 
808  //Loop over the modes that we've listed in ParHandler
809  for (unsigned int iMode = 0 ; iMode < InputVector[iSyst].size() ; iMode++) {
810  int Mode = InputVector[iSyst][iMode];
811  std::string ModeName = Modes->GetSplineSuffixFromMaCh3Mode(Mode);
812 
813  bool IncludeMode = true;
814  for (auto TestString : TestVec) {
815  if (ModeName == TestString) {
816  IncludeMode = false;
817  break;
818  }
819  }
820 
821  if (IncludeMode) {
822  TmpVec.push_back(Mode);
823  TestVec.push_back(ModeName);
824  }
825  }
826 
827  ReturnVec[iSyst] = TmpVec;
828  }
829  return ReturnVec;
830 }
831 
832 //****************************************
834 //****************************************
835  #ifdef MaCh3_CUDA
836  if(!useGPU) return;
838  unsigned int event_size_max = _max_knots * nParams;
839 
841  CoeffIndex, // How many entries in coefficient array (*4 for the "many" array)
842  NSplines_valid, // What's the number of splines we have (also number of entries in gpu_nPoints_arr)
843  0,
844  event_size_max //Knots times event number of unique splines
845  );
846 
849  xcoeff_arr,
850  paramNo_arr,
851  nKnots_arr,
852 
853  nParams,
855  _max_knots,
856  CoeffIndex);
857 
858  // Delete all the coefficient arrays from the CPU once they are on the GPU
859  delete[] manycoeff_arr; manycoeff_arr = nullptr;
860  delete[] xcoeff_arr; xcoeff_arr = nullptr;
863  MACH3LOG_INFO("Good GPU loading");
864  #endif
865 }
866 
867 void BinnedSplineHandler::FillSampleArray(const std::string& SampleTitle, const std::vector<std::string>& OscChanFileNames)
868 {
869  int iSample = GetSampleIndex(SampleTitle);
870  int nOscChannels = nOscChans[iSample];
871 
872  for (int iOscChan = 0; iOscChan < nOscChannels; iOscChan++) {
873  MACH3LOG_INFO("Processing: {}", OscChanFileNames[iOscChan]);
874  TSpline3* mySpline = nullptr;
875  TSpline3_red* Spline = nullptr;
876 
877  std::set<std::string> SplineFileNames;
878  auto File = std::unique_ptr<TFile>(TFile::Open(OscChanFileNames[iOscChan].c_str()));
879 
880  if (!File || File->IsZombie()) {
881  MACH3LOG_ERROR("File {} not found", OscChanFileNames[iOscChan]);
882  throw MaCh3Exception(__FILE__, __LINE__);
883  }
884 
885  //This is the MC specific part of the code
886  //i.e. we always assume that the splines are just store in single TDirectory and they're all in there as single objects
887  for (auto k : *File->GetListOfKeys()) {
888  auto Key = static_cast<TKey*>(k);
889  TClass *Class = gROOT->GetClass(Key->GetClassName(), false);
890  if(!Class->InheritsFrom("TSpline3")) {
891  continue;
892  }
893 
894  std::string FullSplineName = std::string(Key->GetName());
895 
896  if (SplineFileNames.count(FullSplineName) > 0) {
897  MACH3LOG_CRITICAL("Skipping spline - Found a spline whose name has already been encountered before: {}", FullSplineName);
898  continue;
899  }
900  SplineFileNames.insert(FullSplineName);
901 
902  std::vector<std::string> Tokens = GetTokensFromSplineName(FullSplineName);
903 
904  TString Syst = Tokens[kSystToken];
905  TString Mode = Tokens[kModeToken];
906  std::vector<int> VarBins;
907  VarBins.reserve(Tokens.size() - kVarBinToken);
908  for (std::size_t i = kVarBinToken; i < Tokens.size(); ++i) {
909  VarBins.push_back(std::stoi(Tokens.at(i)));
910  }
911 
912  int SystNum = -1;
913  for (unsigned iSyst = 0; iSyst < SplineFileParPrefixNames[iSample].size(); iSyst++) {
914  if (Syst == SplineFileParPrefixNames[iSample][iSyst]) {
915  SystNum = iSyst;
916  break;
917  }
918  }
919 
920  // If the syst doesn't match any of the spline names then skip it
921  if (SystNum == -1){
922  MACH3LOG_DEBUG("Couldn't Match any systematic name in ParameterHandler with spline name: {}" , FullSplineName);
923  continue;
924  }
925 
926  int ModeNum = -1;
927  for (unsigned int iMode = 0; iMode < SplineModeVecs[iSample][SystNum].size(); iMode++) {
928  if (Mode == Modes->GetSplineSuffixFromMaCh3Mode(SplineModeVecs[iSample][SystNum][iMode])) {
929  ModeNum = iMode;
930  break;
931  }
932  }
933 
934  if (ModeNum == -1) {
935  //DB - If you have splines in the root file that you don't want to use (e.g. removing a mode from a syst), this will cause a throw
936  // Therefore include as debug warning and continue instead
937  MACH3LOG_DEBUG("Couldn't find mode for {} in {}. Problem Spline is : {} ", Mode, Syst, FullSplineName);
938  continue;
939  }
940 
941  mySpline = Key->ReadObject<TSpline3>();
942  // loop over all the spline knots and check their value
943  if (isValidSplineIndex(SampleTitle, iOscChan, SystNum, ModeNum, VarBins)) {
944  MACH3LOG_TRACE("Pushed back monolith for spline {}", FullSplineName);
945  // if the value is 1 then set the flat bool to false
946  int nKnots = mySpline->GetNp();
947  bool isFlat = true;
948  for (int iKnot = 0; iKnot < nKnots; iKnot++) {
949  double x, y = M3::_BAD_DOUBLE_;
950  mySpline->GetKnot(iKnot, x, y);
951  if (y < 0.99999 || y > 1.00001)
952  {
953  isFlat = false;
954  break;
955  }
956  }
957 
958  //Rather than keeping a mega vector of splines then converting, this should just keep everything nice in memory!
959  int index = IndexVectMap.at(std::make_tuple(iSample, iOscChan, SystNum, ModeNum, VarBins));
960  IndexVect[index].value = MonolithIndex;
961  // Should save memory rather saving [x_i_0 ,... x_i_maxknots] for every spline!
962  if (isFlat) {
963  splinevec_Monolith.push_back(nullptr);
964  delete mySpline;
965  } else {
966  ApplyKnotWeightCapTSpline3(mySpline, SystNum, ParHandler);
967  Spline = new TSpline3_red(mySpline, SplineInterpolationTypes[iSample][SystNum]);
968  if(mySpline) delete mySpline;
969 
970  splinevec_Monolith.push_back(Spline);
971  CoeffIndex+=nKnots;
972  _max_knots = std::max(_max_knots, static_cast<short int>(nKnots));
973  }
974  //Incrementing MonolithIndex to keep track of number of valid spline indices
975  MonolithIndex+=1;
976  } else {
977  //Potentially you are not a valid spline index
978  delete mySpline;
979  }
980  }//End of loop over all TKeys in file
981 
982  //A bit of clean up
983  File->Delete("*");
984  File->Close();
985  } //End of oscillation channel loop
986 }
987 
988 // *****************************************
989 // Load SplineMonolith from ROOT file
990 void BinnedSplineHandler::LoadSplineFile(std::string FileName) {
991 // *****************************************
992  M3::AddPath(FileName);
993 
994  // Check for spaces in the filename
995  size_t pos = FileName.find(' ');
996  if (pos != std::string::npos) {
997  MACH3LOG_WARN("Filename ({}) contains spaces. Replacing spaces with underscores.", FileName);
998  while ((pos = FileName.find(' ')) != std::string::npos) {
999  FileName[pos] = '_';
1000  }
1001  }
1002  auto SplineFile = std::make_unique<TFile>(FileName.c_str(), "OPEN");
1003 
1004  TMacro *ConfigCov = SplineFile->Get<TMacro>("ParameterHandler");
1005  // Config which was in MCMC from which we are starting
1006  YAML::Node CovSettings = TMacroToYAML(*ConfigCov);
1007  // Config from currently used cov object
1008  YAML::Node ConfigCurrent = ParHandler->GetConfig();
1009 
1010  if (!compareYAMLNodes(CovSettings, ConfigCurrent))
1011  {
1012  MACH3LOG_ERROR("Loading precomputed spline file, however encountered different YAML config, please regenerate input");
1013  throw MaCh3Exception(__FILE__ , __LINE__ );
1014  }
1015 
1016  LoadSettingsDir(SplineFile);
1017  LoadMonolithDir(SplineFile);
1018  LoadIndexDir(SplineFile);
1019  LoadFastSplineInfoDir(SplineFile);
1020 
1021  for (int iSpline = 0; iSpline < nParams; iSpline++) {
1022  SplineInfoArray[iSpline].splineParsPointer = ParHandler->RetPointer(UniqueSystIndices[iSpline]);
1023  }
1024  SplineFile->Close();
1025 
1026  MoveToGPU();
1027 
1028  // initialise spline segment structure
1029  SetupSegments();
1030 }
1031 
1032 // *****************************************
1033 // KS: Prepare Fast Spline Info within SplineFile
1034 void BinnedSplineHandler::LoadSettingsDir(std::unique_ptr<TFile>& SplineFile) {
1035 // *****************************************
1036  TTree *Settings = SplineFile->Get<TTree>("Settings");
1037  int CoeffIndex_temp;
1038  unsigned int NSplines_valid_temp;
1039  short int nParams_temp, max_knots_temp;
1040  Settings->SetBranchAddress("CoeffIndex", &CoeffIndex_temp);
1041  Settings->SetBranchAddress("NSplines_valid", &NSplines_valid_temp);
1042  Settings->SetBranchAddress("nParams", &nParams_temp);
1043  Settings->SetBranchAddress("max_knots", &max_knots_temp);
1044 
1045  int SplineBinning_size1, SplineBinning_size2, SplineBinning_size3;
1046  Settings->SetBranchAddress("SplineBinning_size1", &SplineBinning_size1);
1047  Settings->SetBranchAddress("SplineBinning_size2", &SplineBinning_size2);
1048  Settings->SetBranchAddress("SplineBinning_size3", &SplineBinning_size3);
1049  int SplineModeVecs_size1, SplineModeVecs_size2, SplineModeVecs_size3;
1050  Settings->SetBranchAddress("SplineModeVecs_size1", &SplineModeVecs_size1);
1051  Settings->SetBranchAddress("SplineModeVecs_size2", &SplineModeVecs_size2);
1052  Settings->SetBranchAddress("SplineModeVecs_size3", &SplineModeVecs_size3);
1053  std::vector<std::string>* SampleNames_temp = nullptr;
1054  Settings->SetBranchAddress("SampleNames", &SampleNames_temp);
1055  std::vector<std::string>* SampleTitles_temp = nullptr;
1056  Settings->SetBranchAddress("SampleTitles", &SampleTitles_temp);
1057  std::vector<int>* nSplineParams_temp = nullptr;
1058  Settings->SetBranchAddress("nSplineParams", &nSplineParams_temp);
1059  Settings->GetEntry(0);
1060 
1061  CoeffIndex = CoeffIndex_temp;
1062  NSplines_valid = NSplines_valid_temp;
1063  SampleNames = *SampleNames_temp;
1064  SampleTitles = *SampleTitles_temp;
1065  nSplineParams = *nSplineParams_temp;
1066 
1067  nParams = nParams_temp;
1068  _max_knots = max_knots_temp;
1069 
1070  auto Resize3D = [](auto& vec, int d1, int d2, int d3) {
1071  vec.resize(d1);
1072  for (int i = 0; i < d1; ++i) {
1073  vec[i].resize(d2);
1074  for (int j = 0; j < d2; ++j) {
1075  vec[i][j].resize(d3);
1076  }
1077  }
1078  };
1079 
1080  Resize3D(SplineBinning, SplineBinning_size1, SplineBinning_size2, SplineBinning_size3);
1081  Resize3D(SplineModeVecs, SplineModeVecs_size1, SplineModeVecs_size2, SplineModeVecs_size3);
1082 }
1083 
1084 // *****************************************
1085 // KS: Prepare Fast Spline Info within SplineFile
1086 void BinnedSplineHandler::LoadMonolithDir(std::unique_ptr<TFile>& SplineFile) {
1087 // *****************************************
1088  TTree *MonolithTree = SplineFile->Get<TTree>("MonolithTree");
1089 
1091  MonolithTree->SetBranchAddress("manycoeff", manycoeff_arr);
1093  std::vector<int>* monolith_index_temp = nullptr;
1094  MonolithTree->SetBranchAddress("monolith_index", &monolith_index_temp);
1095 
1096  // Load vectors
1097  std::vector<unsigned int>* nKnots_arr_temp = nullptr;
1098  MonolithTree->SetBranchAddress("nKnots_arr", &nKnots_arr_temp);
1099  std::vector<short int>* paramNo_arr_temp = nullptr;
1100  MonolithTree->SetBranchAddress("paramNo_arr", &paramNo_arr_temp);
1101  std::vector<int>* UniqueSystIndices_temp = nullptr;
1102  MonolithTree->SetBranchAddress("UniqueSystIndices", &UniqueSystIndices_temp);
1103 
1104  // Allocate and load xcoeff_arr
1106  MonolithTree->SetBranchAddress("xcoeff", xcoeff_arr);
1107 
1108  MonolithTree->GetEntry(0);
1109 
1110  nKnots_arr = *nKnots_arr_temp;
1111  paramNo_arr = *paramNo_arr_temp;
1112  UniqueSystIndices = *UniqueSystIndices_temp;
1113  monolith_index = *monolith_index_temp;
1114 }
1115 
1116 // *****************************************
1117 // KS: Prepare Fast Spline Info within SplineFile
1118 void BinnedSplineHandler::LoadIndexDir(std::unique_ptr<TFile>& SplineFile) {
1119 // *****************************************
1120  TTree *IndexTree = SplineFile->Get<TTree>("IndexVec");
1121 
1122  SplineIndex* IndexTemp = nullptr;
1123  IndexTree->SetBranchAddress("SplineIndex", &IndexTemp);
1124  IndexVect.resize(IndexTree->GetEntries());
1125  // Fill indexvec with data from IndexTree
1126  for (Long64_t iEntry = 0; iEntry < IndexTree->GetEntries(); ++iEntry) {
1127  IndexTree->GetEntry(iEntry);
1128  IndexVect[iEntry] = *IndexTemp;
1129 
1130  auto key = std::make_tuple(IndexTemp->iSample, IndexTemp->iOscChan, IndexTemp->iSyst,
1131  IndexTemp->iMode, IndexTemp->iVar);
1132  IndexVectMap[key] = static_cast<int>(iEntry);
1133  }
1134 
1135  // Load SplineBinning data
1136  TTree *SplineBinningTree = SplineFile->Get<TTree>("SplineBinningTree");
1137  std::vector<int> indices(3);
1138  SplineBinningTree->SetBranchAddress("i", &indices[0]);
1139  SplineBinningTree->SetBranchAddress("j", &indices[1]);
1140  SplineBinningTree->SetBranchAddress("k", &indices[2]);
1141  TAxis* axis = nullptr;
1142  SplineBinningTree->SetBranchAddress("axis", &axis);
1143 
1144  // Reconstruct TAxis objects
1145  for (Long64_t entry = 0; entry < SplineBinningTree->GetEntries(); ++entry) {
1146  SplineBinningTree->GetEntry(entry);
1147  int i = indices[0];
1148  int j = indices[1];
1149  int k = indices[2];
1150  SplineBinning[i][j][k] = static_cast<TAxis*>(axis->Clone());
1151  }
1152 
1153  std::vector<int> indices_mode(3);
1154  int mode_value;
1155  TTree *SplineModeTree = SplineFile->Get<TTree>("SplineModeTree");
1156  SplineModeTree->SetBranchAddress("i", &indices_mode[0]);
1157  SplineModeTree->SetBranchAddress("j", &indices_mode[1]);
1158  SplineModeTree->SetBranchAddress("k", &indices_mode[2]);
1159  SplineModeTree->SetBranchAddress("value", &mode_value);
1160 
1161  // Fill SplineModeVecs with values from the tree
1162  for (Long64_t entry = 0; entry < SplineModeTree->GetEntries(); ++entry) {
1163  SplineModeTree->GetEntry(entry);
1164  int i = indices_mode[0];
1165  int j = indices_mode[1];
1166  int k = indices_mode[2];
1167  SplineModeVecs[i][j][k] = mode_value;
1168  }
1169 }
1170 
1171 // *****************************************
1172 // Save SplineMonolith into ROOT file
1173 void BinnedSplineHandler::PrepareSplineFile(std::string FileName) {
1174 // *****************************************
1175  M3::AddPath(FileName);
1176  // Check for spaces in the filename
1177  size_t pos = FileName.find(' ');
1178  if (pos != std::string::npos) {
1179  MACH3LOG_WARN("Filename ({}) contains spaces. Replacing spaces with underscores.", FileName);
1180  while ((pos = FileName.find(' ')) != std::string::npos) {
1181  FileName[pos] = '_';
1182  }
1183  }
1184  // Save ROOT File
1185  auto SplineFile = std::make_unique<TFile>(FileName.c_str(), "recreate");
1186  YAML::Node ConfigCurrent = ParHandler->GetConfig();
1187  TMacro ConfigSave = YAMLtoTMacro(ConfigCurrent, "ParameterHandler");
1188  ConfigSave.Write();
1189 
1190  PrepareSettingsDir(SplineFile);
1191  PrepareMonolithDir(SplineFile);
1192  PrepareIndexDir(SplineFile);
1193  PrepareOtherInfoDir(SplineFile);
1194  PrepareFastSplineInfoDir(SplineFile);
1195 
1196  SplineFile->Close();
1197 }
1198 
1199 // *****************************************
1200 void BinnedSplineHandler::PrepareSettingsDir(std::unique_ptr<TFile>& SplineFile) const {
1201 // *****************************************
1202  TTree *Settings = new TTree("Settings", "Settings");
1203  int CoeffIndex_temp = CoeffIndex;
1204  unsigned int NSplines_valid_temp = NSplines_valid;
1205  short int nParams_temp = nParams;
1206  short int max_knots_temp = _max_knots;
1207 
1208  Settings->Branch("CoeffIndex", &CoeffIndex_temp, "CoeffIndex/I");
1209  Settings->Branch("NSplines_valid", &NSplines_valid_temp, "NSplines_valid/i");
1210  Settings->Branch("nParams", &nParams_temp, "nParams/S");
1211  Settings->Branch("max_knots", &max_knots_temp, "max_knots/S");
1212 
1213  int SplineBinning_size1 = static_cast<int>(SplineBinning.size());
1214  int SplineBinning_size2 = (SplineBinning_size1 > 0) ? static_cast<int>(SplineBinning[0].size()) : 0;
1215  int SplineBinning_size3 = (SplineBinning_size2 > 0) ? static_cast<int>(SplineBinning[0][0].size()) : 0;
1216 
1217  Settings->Branch("SplineBinning_size1", &SplineBinning_size1, "SplineBinning_size1/I");
1218  Settings->Branch("SplineBinning_size2", &SplineBinning_size2, "SplineBinning_size2/I");
1219  Settings->Branch("SplineBinning_size3", &SplineBinning_size3, "SplineBinning_size3/I");
1220 
1221  int SplineModeVecs_size1 = static_cast<int>(SplineModeVecs.size());
1222  int SplineModeVecs_size2 = (SplineModeVecs_size1 > 0) ? static_cast<int>(SplineModeVecs[0].size()) : 0;
1223  int SplineModeVecs_size3 = (SplineModeVecs_size2 > 0) ? static_cast<int>(SplineModeVecs[0][0].size()) : 0;
1224 
1225  Settings->Branch("SplineModeVecs_size1", &SplineModeVecs_size1, "SplineModeVecs_size1/I");
1226  Settings->Branch("SplineModeVecs_size2", &SplineModeVecs_size2, "SplineModeVecs_size2/I");
1227  Settings->Branch("SplineModeVecs_size3", &SplineModeVecs_size3, "SplineModeVecs_size3/I");
1228 
1229  std::vector<std::string> SampleNames_temp = SampleNames;
1230  Settings->Branch("SampleNames", &SampleNames_temp);
1231  std::vector<std::string> SampleTitles_temp = SampleTitles;
1232  Settings->Branch("SampleTitles", &SampleTitles_temp);
1233  std::vector<int> nSplineParams_temp = nSplineParams;
1234  Settings->Branch("nSplineParams", &nSplineParams_temp);
1235 
1236  Settings->Fill();
1237  SplineFile->cd();
1238  Settings->Write();
1239  delete Settings;
1240 }
1241 
1242 // *****************************************
1243 void BinnedSplineHandler::PrepareMonolithDir(std::unique_ptr<TFile>& SplineFile) const {
1244 // *****************************************
1245  if(manycoeff_arr == nullptr){
1246  MACH3LOG_ERROR("{} Will not work as manycoeff_arr is nullptr :(", __func__);
1247  MACH3LOG_ERROR("Could be GPU dysfunctionality");
1248  throw MaCh3Exception(__FILE__, __LINE__);
1249  }
1250  TTree *MonolithTree = new TTree("MonolithTree", "MonolithTree");
1251  MonolithTree->Branch("manycoeff", manycoeff_arr, Form("manycoeff[%d]/%s", CoeffIndex * _nCoeff_, M3::float_t_str_repr));
1252 
1253  std::vector<int> monolith_index_temp = monolith_index;
1254  MonolithTree->Branch("monolith_index", &monolith_index_temp);
1255  std::vector<unsigned int> nKnots_arr_temp = nKnots_arr;
1256  MonolithTree->Branch("nKnots_arr", &nKnots_arr_temp);
1257  std::vector<short int> paramNo_arr_temp = paramNo_arr;
1258  MonolithTree->Branch("paramNo_arr", &paramNo_arr_temp);
1259  std::vector<int> UniqueSystIndices_temp = UniqueSystIndices;
1260  MonolithTree->Branch("UniqueSystIndices", &UniqueSystIndices_temp);
1261  MonolithTree->Branch("xcoeff", xcoeff_arr, Form("xcoeff[%d]/%s", _max_knots * nParams, M3::float_t_str_repr));
1262 
1263  MonolithTree->Fill();
1264  SplineFile->cd();
1265  MonolithTree->Write();
1266  delete MonolithTree;
1267 }
1268 
1269 // *****************************************
1270 void BinnedSplineHandler::PrepareIndexDir(std::unique_ptr<TFile>& SplineFile) const {
1271 // *****************************************
1272  // Create a TTree to store the data
1273  TTree *IndexTree = new TTree("IndexVec", "IndexVec");
1274  SplineIndex entry;
1275  IndexTree->Branch("SplineIndex", &entry);
1276  for (const auto& e : IndexVect) {
1277  entry = e;
1278  IndexTree->Fill();
1279  }
1280 
1281  SplineFile->cd();
1282  IndexTree->Write();
1283  delete IndexTree;
1284 }
1285 
1286 // *****************************************
1287 void BinnedSplineHandler::PrepareOtherInfoDir(std::unique_ptr<TFile>& SplineFile) const {
1288 // *****************************************
1289  // Create a new tree for SplineBinning data
1290  TTree *SplineBinningTree = new TTree("SplineBinningTree", "SplineBinningTree");
1291  std::vector<int> indices(3); // To store the 3D indices
1292  TAxis* axis = nullptr;
1293  SplineBinningTree->Branch("i", &indices[0], "i/I");
1294  SplineBinningTree->Branch("j", &indices[1], "j/I");
1295  SplineBinningTree->Branch("k", &indices[2], "k/I");
1296  SplineBinningTree->Branch("axis", "TAxis", &axis);
1297 
1298  // Fill the SplineBinningTree
1299  for (size_t i = 0; i < SplineBinning.size(); ++i) {
1300  for (size_t j = 0; j < SplineBinning[i].size(); ++j) {
1301  for (size_t k = 0; k < SplineBinning[i][j].size(); ++k) {
1302  axis = SplineBinning[i][j][k];
1303  indices[0] = static_cast<int>(i);
1304  indices[1] = static_cast<int>(j);
1305  indices[2] = static_cast<int>(k);
1306  SplineBinningTree->Fill();
1307  }
1308  }
1309  }
1310  SplineFile->cd();
1311  SplineBinningTree->Write();
1312  delete SplineBinningTree;
1313 
1314  std::vector<int> indices_mode(3); // to store 3D indices
1315  int mode_value;
1316 
1317  TTree *SplineModeTree = new TTree("SplineModeTree", "SplineModeTree");
1318  // Create branches for indices and value
1319  SplineModeTree->Branch("i", &indices_mode[0], "i/I");
1320  SplineModeTree->Branch("j", &indices_mode[1], "j/I");
1321  SplineModeTree->Branch("k", &indices_mode[2], "k/I");
1322  SplineModeTree->Branch("value", &mode_value, "value/I");
1323 
1324  // Fill the tree
1325  for (size_t i = 0; i < SplineModeVecs.size(); ++i) {
1326  for (size_t j = 0; j < SplineModeVecs[i].size(); ++j) {
1327  for (size_t k = 0; k < SplineModeVecs[i][j].size(); ++k) {
1328  indices_mode[0] = static_cast<int>(i);
1329  indices_mode[1] = static_cast<int>(j);
1330  indices_mode[2] = static_cast<int>(k);
1331  mode_value = SplineModeVecs[i][j][k];
1332  SplineModeTree->Fill();
1333  }
1334  }
1335  }
1336  // Write the tree to the file
1337  SplineFile->cd();
1338  SplineModeTree->Write();
1339  delete SplineModeTree;
1340 }
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
Definition: Core.h:126
#define _MaCh3_Safe_Include_End_
#define MACH3LOG_CRITICAL
Definition: MaCh3Logger.h:38
#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
#define MACH3LOG_TRACE
Definition: MaCh3Logger.h:33
void CleanContainer(T &)
Base case: do nothing for non-pointer types.
void CleanVector(T &)
Base case: do nothing for non-vector types.
@ kSpline
For splined parameters (1D)
constexpr int _nCoeff_
KS: We store coefficients {y,b,c,d} in one array one by one, this is only to define it once rather th...
Definition: SplineCommon.h:18
@ kCoeffB
Coefficient B.
Definition: SplineCommon.h:26
@ kCoeffD
Coefficient D.
Definition: SplineCommon.h:28
@ kCoeffY
Coefficient Y.
Definition: SplineCommon.h:25
@ kCoeffC
Coefficient C.
Definition: SplineCommon.h:27
void ApplyKnotWeightCapTSpline3(TSpline3 *&Spline, const int splineParsIndex, ParameterHandlerGeneric *ParHandler)
EM: Apply capping to knot weight for specified spline parameter. param graph needs to have been set i...
Definition: SplineStructs.h:99
bool isFlat(TSpline3_red *&spl)
CW: Helper function used in the constructor, tests to see if the spline is flat.
TMacro YAMLtoTMacro(const YAML::Node &yaml_node, const std::string &name)
Convert a YAML node to a ROOT TMacro object.
Definition: YamlHelper.h:166
YAML::Node TMacroToYAML(const TMacro &macro)
KS: Convert a ROOT TMacro object to a YAML node.
Definition: YamlHelper.h:151
bool compareYAMLNodes(const YAML::Node &node1, const YAML::Node &node2, bool Mute=false)
Compare if yaml nodes are identical.
Definition: YamlHelper.h:185
bool isValidSplineIndex(const std::string &SampleTitle, int iSyst, int iOscChan, int iMode, const std::vector< int > &iVar) const
Ensure we have spline for a given bin.
virtual void FillSampleArray(const std::string &SampleTitle, const std::vector< std::string > &OscChanFileNames)
Loads and processes splines from ROOT files for a given sample.
std::vector< short int > paramNo_arr
CW: CPU array with the number of points per spline (not per spline point!)
void LoadIndexDir(std::unique_ptr< TFile > &SplineFile)
KS: Load preprocessed Index.
ParameterHandlerGeneric * ParHandler
Pointer to covariance from which we get information about spline params.
void CalcSplineWeights() final
CPU based code which eval weight for each spline.
std::vector< std::vector< std::string > > SplineFileParPrefixNames
[Sample][Syst]
void LoadSettingsDir(std::unique_ptr< TFile > &SplineFile)
KS: Load preprocessed Settings.
void PrintBinning(TAxis *Axis) const
Print spline binning.
void MoveToGPU()
CW: The shared initialiser from constructors of TResponseFunction_red.
void PrepareSplineFile(std::string FileName) final
KS: Prepare spline file that can be used for fast loading.
void CleanUpMemory()
Remove setup variables not needed for spline evaluations.
std::vector< SplineIndex > GetEventSplines(const std::string &SampleTitle, int iOscChan, int EventMode, const std::vector< double > &VarVals)
Return the splines which affect a given event.
void PrepForReweight()
Initialise flat structure.
void InvestigateMissingSplines() const
This function will find missing splines in file.
std::vector< int > nSplineParams
std::vector< std::vector< std::vector< int > > > SplineModeVecs
std::vector< int > nOscChans
M3::float_t * xcoeff_arr
x coefficients for each spline
std::vector< std::string > SampleTitles
void PrintSampleDetails(const std::string &SampleTitle) const
Print info like Sample ID of spline params etc.
std::vector< std::string > UniqueSystNames
name of each spline parameter
std::vector< M3::float_t > cpu_spline_weights
Stores weight from spline evaluation for each single spline.
std::vector< std::vector< SplineInterpolation > > SplineInterpolationTypes
spline interpolation types for each sample. These vectors are from a call to GetSplineInterpolationFr...
std::vector< SplineIndex > IndexVect
Variables related to determined which modes have splines and which piggy-back of other modes.
void GetSplineCoeff_SepMany(int splineindex, M3::float_t *&xArray, M3::float_t *&manyArray)
Rather work with spline coefficients in the splines, let's copy ND and use coefficient arrays.
void Evaluate() final
CW: This Eval should be used when using two separate x,{y,a,b,c,d} arrays to store the weights; proba...
std::vector< int > Dimensions
void PrepareIndexDir(std::unique_ptr< TFile > &SplineFile) const
KS: Prepare Index Info within SplineFile.
void BuildSampleIndexingArray(const std::string &SampleTitle)
Only need 1 indexing array everything else interfaces with this to get binning properties.
int CountNumberOfLoadedSplines(bool NonFlat=false, int Verbosity=0) const
Count how many splines we have.
std::vector< int > monolith_index
keep track which splines are flat (-1) otherwise map with flattened splines
std::vector< unsigned int > nKnots_arr
KS: CPU Number of knots per spline.
void AddSample(const std::string &SampleName, const std::string &SampleTitle, const std::vector< std::string > &OscChanFileNames, const std::vector< std::string > &SplineVarNames)
add oscillation channel to spline monolith
std::vector< TAxis * > FindSplineBinning(const std::string &FileName, const std::string &SampleTitle)
Grab histograms with spline binning.
std::vector< std::vector< std::string > > DimensionLabels
std::vector< std::vector< int > > StripDuplicatedModes(const std::vector< std::vector< int > > &InputVector) const
Creates an array to be filled with monolith indexes for each sample (allows for indexing between 7D b...
void LoadSplineFile(std::string FileName) final
KS: Load preprocessed spline file.
void PrepareMonolithDir(std::unique_ptr< TFile > &SplineFile) const
KS: Prepare Monolith Info within SplineFile.
void LoadMonolithDir(std::unique_ptr< TFile > &SplineFile)
KS: Load preprocessed Monolith.
std::vector< std::vector< std::vector< TAxis * > > > SplineBinning
Holds TAxis for [sample][channel][dimension].
std::vector< TSpline3_red * > splinevec_Monolith
holds each spline object before stripping into coefficient monolith
void PrepareOtherInfoDir(std::unique_ptr< TFile > &SplineFile) const
KS: Prepare Other Info within SplineFile.
void PrepareSettingsDir(std::unique_ptr< TFile > &SplineFile) const
KS: Prepare Settings Info within SplineFile.
const M3::float_t * RetPointer(const SplineIndex &Variables) const
get pointer to spline weight based on bin variables
MaCh3Modes * Modes
pointer to MaCh3 Mode from which we get spline suffix
void PrintArrayDetails(const std::string &SampleTitle) const
Print info like Sample ID of spline params etc.
std::map< std::tuple< int, int, int, int, std::vector< int > >, int > IndexVectMap
Map between spline origin/properties (iSample, iOscChan, iSyst, iMode, iVar1, iVar2,...
std::vector< std::vector< int > > GlobalSystIndex
This holds the global spline index and is used to grab the current parameter value to evaluate spline...
BinnedSplineHandler(ParameterHandlerGeneric *ParamHandler, MaCh3Modes *Modes_, const bool Use_GPU=true)
Constructor.
virtual ~BinnedSplineHandler()
Destructor.
virtual std::vector< std::string > GetTokensFromSplineName(const std::string &FullSplineName)=0
Extract metadata tokens encoded in a spline name. allows experiment to have different formats of spli...
std::vector< std::string > SampleNames
void TransferToMonolith()
flatten multidimensional spline array into proper monolith
std::vector< int > UniqueSystIndices
Global index of each spline param, it allows us to match spline ordering with global.
int GetSampleIndex(const std::string &SampleTitle) const
Get index of sample based on name.
M3::float_t * manycoeff_arr
ybcd coefficients for each spline
Creates fancy formatted text tables for terminal output.
Definition: TablePrinter.h:50
Custom exception class used throughout MaCh3.
KS: Class describing MaCh3 modes used in the analysis, it is being initialised from config.
Definition: MaCh3Modes.h:142
int GetNModes() const
KS: Get number of modes, keep in mind actual number is +1 greater due to unknown category.
Definition: MaCh3Modes.h:155
std::string GetSplineSuffixFromMaCh3Mode(const int Index)
DB: Get binned spline mode suffix from MaCh3 Mode.
Definition: MaCh3Modes.cpp:243
const M3::float_t * RetPointer(const int iParam) const
DB Pointer return to param position.
YAML::Node GetConfig() const
Getter to return a copy of the YAML node.
Class responsible for handling of systematic error parameters with different types defined in the con...
int GetNumParamsFromSampleName(const std::string &SampleName, const SystType Type) const
DB Grab the number of parameters for the relevant SampleName.
const std::vector< std::string > GetParsNamesFromSampleName(const std::string &SampleName, const SystType Type) const
DB Grab the parameter names for the relevant SampleName.
const std::vector< std::string > GetSplineParsNamesFromSampleName(const std::string &SampleName) const
DB Get spline parameters depending on given SampleName.
const std::vector< int > GetGlobalSystIndexFromSampleName(const std::string &SampleName, const SystType Type) const
DB Get spline parameters depending on given SampleName.
const std::vector< SplineInterpolation > GetSplineInterpolationFromSampleName(const std::string &SampleName) const
Get the interpolation types for splines affecting a particular SampleName.
const std::vector< std::vector< int > > GetSplineModeVecFromSampleName(const std::string &SampleName) const
DB Grab the Spline Modes for the relevant SampleName.
Base class for calculating weight from spline.
Definition: SplineBase.h:30
SplineMonolithGPU * gpu_monolith
KS: Store info about Spline monolith, this allow to obtain better step time. As all necessary informa...
Definition: SplineBase.h:120
unsigned int NSplines_valid
Number of valid splines.
Definition: SplineBase.h:117
short int nParams
Number of parameters that have splines.
Definition: SplineBase.h:113
short int _max_knots
Max knots for production.
Definition: SplineBase.h:115
void FindSplineSegment()
CW:Code used in step by step reweighting, Find Spline Segment for each param.
Definition: SplineBase.cpp:58
short int * SplineSegments
Definition: SplineBase.h:109
std::vector< FastSplineInfo > SplineInfoArray
Definition: SplineBase.h:106
float * ParamValues
Store parameter values they are not in FastSplineInfo as in case of GPU we need to copy paste it to G...
Definition: SplineBase.h:111
bool useGPU
Whether to use GPU or not.
Definition: SplineBase.h:123
void LoadFastSplineInfoDir(std::unique_ptr< TFile > &SplineFile)
KS: Load preprocessed FastSplineInfo.
Definition: SplineBase.cpp:181
void PrepareFastSplineInfoDir(std::unique_ptr< TFile > &SplineFile) const
KS: Prepare Fast Spline Info within SplineFile.
Definition: SplineBase.cpp:153
void SetupSegments()
Since we find segment before actual reweight and sometimes we want to copy it to GPU we have differen...
Definition: SplineBase.cpp:208
Class responsible for calculating spline weight on GPU.
__host__ void InitGPU_SplineMonolith(unsigned int total_nknots, unsigned int n_splines, unsigned int n_tf1, int Eve_size)
Allocate memory on gpu for spline monolith.
__host__ void CleanupPinnedMemory(M3::float_t *cpu_total_weights, short int *segment, float *vals)
Clean up pinned variables at CPU.
__host__ void RunGPU_SplineMonolith_Binned(M3::float_t *cpu_spline_weights, float *vals, short int *segment)
__host__ void CopyToGPU_SplineMonolith_Binned(M3::float_t *manycoeff_arr, M3::float_t *xcoeff_arr, const std::vector< short int > &uniquesplinevec_Monolith, const std::vector< unsigned int > &coeffindexvec, const int n_params, const unsigned int n_splines, const short int spline_size, const unsigned int total_nknots)
Copies data from CPU to GPU for the spline monolith.
CW: Reduced TSpline3 class.
MaCh3 event-by-event cross-section spline code.
std::string join(const Container &values, std::string_view separator=" ")
Join all values in a container into a single string.
Definition: TablePrinter.h:36
@ Right
Align text to the right.
@ Left
Align text to the left.
constexpr static const double _BAD_DOUBLE_
Default value used for double initialisation.
Definition: Core.h:53
double float_t
Definition: Core.h:37
TFile * Open(const std::string &Name, const std::string &Type, const std::string &File, const int Line)
Opens a ROOT file with the given name and mode.
constexpr static const char * float_t_str_repr
Definition: Core.h:40
constexpr static const float_t Unity
Definition: Core.h:64
constexpr static const int _BAD_INT_
Default value used for int initialisation.
Definition: Core.h:55
constexpr T fmaf_t(T x, T y, T z)
Function template for fused multiply-add.
Definition: Core.h:45
void AddPath(std::string &FilePath)
Prepends the MACH3 environment path to FilePath if it is not already present.
Definition: Monitor.cpp:382
int int_t
Definition: Core.h:38
Flat representation of a spline index entry.
Definition: SplineCommon.h:33
std::vector< int > iVar
Kinematic bins index, assumed to be size of 3 for now.
Definition: SplineCommon.h:49
int iSample
Sample index.
Definition: SplineCommon.h:41
int iMode
Mode index within a systematic.
Definition: SplineCommon.h:47
int value
Index into the flattened spline weight vector.
Definition: SplineCommon.h:39
int iOscChan
Oscillation channel index.
Definition: SplineCommon.h:43
int iSyst
Systematic parameter index.
Definition: SplineCommon.h:45