MaCh3  2.6.1
Reference Guide
UnbinnedSplineHandler.cpp
Go to the documentation of this file.
2 
3 #ifdef MaCh3_CUDA
5 #endif
6 
7 #pragma GCC diagnostic ignored "-Wuseless-cast"
8 
9 // *****************************************
10 //Set everything to NULL or 0
12 // *****************************************
13 #ifdef MaCh3_CUDA
14  MACH3LOG_INFO("Using GPU version event by event monolith");
15  gpu_spline_handler = nullptr;
16 #endif
17 
19 
20  nKnots = 0;
21  nTF1coeff = 0;
22  NEvents = 0;
23  _max_knots = 0;
24 
25  NSplines_valid = 0;
26  NTF1_valid = 0;
27 
28  cpu_weights_spline_var = nullptr;
29  cpu_weights_tf1_var = nullptr;
30 
31  cpu_total_weights = nullptr;
32 }
33 
34 // *****************************************
35 UnbinnedSplineHandler::UnbinnedSplineHandler(std::vector<std::vector<TResponseFunction_red*> > &MasterSpline,
36  const std::vector<RespFuncType> &SplineType,
37  const bool SaveFlatTree,
38  const std::string& _FastSplineName) : SplineBase() {
39 // *****************************************
40  //KS: If true it will save spline monolith into huge ROOT file
41  SaveSplineFile = SaveFlatTree;
42  FastSplineName = _FastSplineName;
43  Initialise();
44  MACH3LOG_INFO("-- GPUING WITH arrays and master spline containing TResponseFunction_red");
45 
46  // Convert the TSpline3 pointers to the reduced form and call the reduced constructor
47  PrepareForGPU(MasterSpline, SplineType);
48 }
49 
50 // *****************************************
51 // The shared initialiser from constructors of TSpline3 and TSpline3_red
52 void UnbinnedSplineHandler::PrepareForGPU(std::vector<std::vector<TResponseFunction_red*> > &MasterSpline, const std::vector<RespFuncType> &SplineType) {
53 // *****************************************
54  // Scan for the max number of knots, the number of events (number of splines), and number of parameters
55  int maxnSplines = 0;
56  ScanMasterSpline(MasterSpline,
57  NEvents,
58  _max_knots,
59  nParams,
60  maxnSplines,
62  nKnots,
63  NTF1_valid,
64  nTF1coeff,
65  SplineType);
66 
67  MACH3LOG_INFO("Found {} events", NEvents);
68  MACH3LOG_INFO("Found {} knots at max", _max_knots);
69  MACH3LOG_INFO("Found {} parameters", nParams);
70  MACH3LOG_INFO("Found {} maximum number of splines in an event", maxnSplines);
71  MACH3LOG_INFO("Found total {} knots in all splines", nKnots);
72  MACH3LOG_INFO("Number of splines = {}", NSplines_valid);
73  MACH3LOG_INFO("Found total {} coeffs in all TF1", nTF1coeff);
74  MACH3LOG_INFO("Number of TF1 = {}", NTF1_valid);
75 
76  unsigned int event_size_max = _max_knots * nParams;
77  // Declare the {x}, {y,b,c,d} arrays for all possible splines which the event has
78  // We'll filter off the flat and "disabled" (e.g. CCQE event should not have MARES spline) ones in the next for loop, but need to declare these beasts here
79 
80  // Declare the {y,b,c,d} for each knot
81  // float because GPU precision (could change to double, but will incur significant speed reduction on GPU unless you're very rich!)
82  cpu_spline_handler->coeff_many.resize(nKnots*_nCoeff_); // *4 because we store y,b,c,d parameters in this array
83  //KS: For x coeff we assume that for given dial (MAQE) spacing is identical,
84  // here we are sloppy and assume each dial has the same number of knots, not a big problem
85  cpu_spline_handler->coeff_x.resize(event_size_max, -999);
86 
87  //CW: With TF1 we only save the coefficients and the order of the polynomial
88  // Makes most sense to have one large monolithic array, but then it becomes impossible to tell apart a coefficient from a "number of points". So have two arrays: one of coefficients and one of number of points
89  // Let's first assume all are of _max_knots size
90  // Now declare the arrays for each point in the valid splines which the event actually has (i.e. include the splines that the event undergoes)
91  // Also make array with the number of points per spline (not per spline point!)
92  // float because GPU precision (could change to double, but will incur significant speed reduction on GPU unless you're very rich!)
94  cpu_coeff_TF1_many.resize(nTF1coeff); // *5 because this array holds a,b,c,d,e parameters
95 
96  //KS: Map keeping track how many parameters applies to each event, we keep two numbers here {number of splines per event, index where splines start for a given event}
97  cpu_nParamPerEvent.resize(2 * NEvents, -1);
98  cpu_nParamPerEvent_tf1.resize(2 * NEvents, -1);
99 
100  // Make array with the number of points per spline (not per spline point!)
102  //KS: And array which tells where each spline stars in a big monolith array, sort of knot map
105 
106  // Temporary arrays to hold the coefficients for each spline
107  // We get one x, one y, one b,... for each point, so only need to be _max_knots big
108  //KS: Some params has less splines but this is all right main array will get proper number while this temp will be deleted
109  float *x_tmp = new float[_max_knots]();
110  float *many_tmp = new float[_max_knots*_nCoeff_]();
111  float *temp_coeffs = new float[_nTF1Coeff_]();
112 
113  // Count the number of events
114  unsigned int KnotCounter = 0;
115  unsigned int TF1PointsCounter = 0;
116  unsigned int NSplinesCounter = 0;
117  unsigned int TF1sCounter = 0;
118  int ParamCounter = 0;
119  int ParamCounterGlobal = 0;
120  int ParamCounter_TF1 = 0;
121  int ParamCounterGlobalTF1 = 0;
122  // Loop over events and extract the spline coefficients
123  for(unsigned int EventCounter = 0; EventCounter < MasterSpline.size(); ++EventCounter) {
124  // Structure of MasterSpline is std::vector<std::vector<TSpline3*>>
125  // A conventional iterator to count which parameter a given spline should be applied to
126  for(unsigned int ParamNumber = 0; ParamNumber < MasterSpline[EventCounter].size(); ++ParamNumber) {
127  // If nullptr we don't have this spline for the event, so move to next spline
128  if (MasterSpline[EventCounter][ParamNumber] == nullptr) continue;
129 
130  if(SplineType[ParamNumber] == kTSpline3_red)
131  {
132  //KS: how much knots each spline has
133  int nPoints_tmp = 0;
134  // Get a pointer to the current spline for this event
135  TResponseFunction_red* TespFunc = MasterSpline[EventCounter][ParamNumber];
136  TSpline3_red* CurrSpline = static_cast<TSpline3_red*>(TespFunc);
137 
138  // If the number of knots are greater than 2 the spline is not a dummy and we should extract coefficients to load onto the GPU
139  GetSplineCoeff_SepMany(CurrSpline, nPoints_tmp, x_tmp, many_tmp);
140 
141  //KS: One knot means flat spline so ignore
142  if (nPoints_tmp == 1) continue;
143  for (int j = 0; j < _max_knots; ++j) {
144  cpu_spline_handler->coeff_x[ParamNumber*_max_knots + j] = x_tmp[j];
145  }
146  //KS: Contrary to X coeff we keep for other coeff only filled knots, there is no much gain for doing so for x coeff
147  for (int j = 0; j < nPoints_tmp; ++j) {
148  for (int k = 0; k < _nCoeff_; k++) {
149  cpu_spline_handler->coeff_many[KnotCounter*_nCoeff_ + j*_nCoeff_ + k] = many_tmp[j*_nCoeff_+k];
150  }
151  }
152  // Set the parameter number for this spline
153  cpu_spline_handler->paramNo_arr[NSplinesCounter] = short(ParamNumber);
154  //KS: Fill map when each spline starts
155  cpu_spline_handler->nKnots_arr[NSplinesCounter] = KnotCounter;
156  KnotCounter += nPoints_tmp;
157 
158  ++ParamCounter;
159  // Increment the counter for the number of good splines we have
160  ++NSplinesCounter;
161  }
162  else if (SplineType[ParamNumber] == kTF1_red)
163  {
164  // Don't actually use this ever -- we give each spline the maximum number of points found in all splines
165  int nPoints_tmp = 0;
166  // Get a pointer to the current spline for this event
167  TF1_red* CurrSpline = dynamic_cast<TF1_red*>(MasterSpline[EventCounter][ParamNumber]);
168 
169  // If the number of knots are greater than 2 the spline is not a dummy and we should extract coefficients to load onto the GPU
170  GetTF1Coeff(CurrSpline, nPoints_tmp, temp_coeffs);
171  for (int j = 0; j < _nTF1Coeff_; ++j) {
172  cpu_coeff_TF1_many[TF1PointsCounter+j] = temp_coeffs[j];
173  }
174  // Save the number of points for this spline
175  cpu_nPoints_arr[TF1sCounter] = short(nPoints_tmp);
176 
177  TF1PointsCounter += nPoints_tmp;
178  // Set the parameter number for this spline
179  cpu_paramNo_TF1_arr[TF1sCounter] = short(ParamNumber);
180  ++ParamCounter_TF1;
181  // Increment the counter for the number of good splines we have
182  ++TF1sCounter;
183  }
184  //KS: Don't delete in debug
185  #ifndef MACH3_DEBUG
186  delete MasterSpline[EventCounter][ParamNumber];
187  MasterSpline[EventCounter][ParamNumber] = nullptr;
188  #endif
189  } // End the loop over the parameters in the MasterSpline
190  cpu_nParamPerEvent[2*EventCounter] = ParamCounter;
191  cpu_nParamPerEvent[2*EventCounter+1] = ParamCounterGlobal;
192  ParamCounterGlobal += ParamCounter;
193 
194  cpu_nParamPerEvent_tf1[2*EventCounter] = ParamCounter_TF1;
195  cpu_nParamPerEvent_tf1[2*EventCounter+1] = ParamCounterGlobalTF1;
196  ParamCounterGlobalTF1 += ParamCounter_TF1;
197 
198  ParamCounter = 0;
199  ParamCounter_TF1 = 0;
200  } // End the loop over the number of events
201  delete[] many_tmp;
202  delete[] x_tmp;
203  delete[] temp_coeffs;
204 
205  int BadXCounter = 0;
206  for (unsigned int j = 0; j < event_size_max; j++) {
207  if (cpu_spline_handler->coeff_x[j] == -999) BadXCounter++;
208  // Perform checks that all entries have been modified from initial values
209  if (cpu_spline_handler->coeff_x[j] == -999 && BadXCounter < 5) {
210  MACH3LOG_WARN("***** BAD X !! *****");
211  MACH3LOG_WARN("Indicates some parameter doesn't have a single spline");
212  MACH3LOG_WARN("j = {}", j);
213  //throw MaCh3Exception(__FILE__ , __LINE__ );
214  }
215  if(BadXCounter == 5) MACH3LOG_WARN("There is more unutilised knots although I will stop spamming");
216  }
217 
218  MACH3LOG_WARN("Found in total {} BAD X", BadXCounter);
219  //KS: This is tricky as this variable use both by CPU and GPU, however if use CUDA we use cudaMallocHost
220  #ifndef MaCh3_CUDA
222  cpu_weights_spline_var = new float[NSplines_valid]();
223  cpu_weights_tf1_var = new float[NTF1_valid]();
224  #endif
225 
226  // Print some info; could probably make this to a separate function
229 
230  MoveToGPU();
231 
232  // Can pass the spline segments to the GPU instead of the values
233  // Make these here and only refill them for each loop, avoiding unnecessary new/delete on each reconfigure
234  SetupSegments();
235 }
236 
237 // *****************************************
238 // The shared initialiser from constructors of TSpline3 and TSpline3_red
240 // *****************************************
241  #ifdef MaCh3_CUDA
242  unsigned int event_size_max = _max_knots * nParams;
243  MACH3LOG_INFO("Total size = {:.2f} MB memory on CPU to move to GPU",
244  (double(sizeof(float) * nKnots * _nCoeff_) + double(sizeof(float) * event_size_max) / 1.E6 +
245  double(sizeof(short int) * NSplines_valid)) / 1.E6);
246  MACH3LOG_INFO("Total TF1 size = {:.2f} MB memory on CPU to move to GPU",
247  double(sizeof(float) * NTF1_valid * _nTF1Coeff_) / 1.E6);
248  MACH3LOG_INFO("GPU weight array (GPU->CPU every step) = {:.2f} MB", static_cast<double>(sizeof(float)) * (NSplines_valid + NTF1_valid) / 1.0e6);
249  MACH3LOG_INFO("Since you are running Total event weight mode then GPU weight array (GPU->CPU every step) = {:.2f} MB",
250  double(sizeof(float) * NEvents) / 1.E6);
251  MACH3LOG_INFO("Parameter value array (CPU->GPU every step) = {:.4f} MB", double(sizeof(float) * nParams) / 1.E6);
252  //CW: With the new set-up we have: 1 coefficient array of size coeff_array_size, all same size
253  // 1 coefficient array of size coeff_array_size*4, holding y,b,c,d in order (y11,b11,c11,d11; y12,b12,c12,d12;...) where ynm is n = spline number, m = spline point. Should really make array so that order is (y11,b11,c11,d11; y21,b21,c21,d21;...) because it will optimise cache hits I think; try this if you have time
254  // return gpu_weights
255 
257 
258  // The gpu_XY arrays don't actually need initialising, since they are only placeholders for what we'll move onto the GPU. As long as we cudaMalloc the size of the arrays correctly there shouldn't be any problems
259  // Can probably make this a bit prettier but will do for now
260  // Could be a lot smaller of a function...
263  NEvents,
264  nKnots, // How many entries in coefficient array (*4 for the "many" array)
265  NSplines_valid, // What's the number of splines we have (also number of entries in gpu_nPoints_arr)
266  NTF1_valid,
267  event_size_max //Knots times event number of unique splines
268  );
269 
270  // Move number of splines and spline size to constant GPU memory; every thread does not need a copy...
271  // The implementation lives in splines/gpuSplineUtils.cu
272  // The GPU splines don't actually need declaring but is good for demonstration, kind of
273  // fixed by passing const reference
276 
277  // TFI related now
280  NEvents,
283  nParams,
285  _max_knots,
286  nKnots,
287  NTF1_valid);
288 
289  // Delete all the coefficient arrays from the CPU once they are on the GPU
294  delete cpu_spline_handler;
295  cpu_spline_handler = nullptr;
296  MACH3LOG_INFO("Good GPU loading");
297  #endif
298 }
299 
300 // Need to specify template functions in header
301 // *****************************************
302 // Scan the master spline to get the maximum number of knots in any of the TSpline3*
303 void UnbinnedSplineHandler::ScanMasterSpline(std::vector<std::vector<TResponseFunction_red*> > & MasterSpline,
304  unsigned int &nEvents,
305  short int &MaxPoints,
306  short int &numParams,
307  int &nSplines,
308  unsigned int &NSplinesValid,
309  unsigned int &numKnots,
310  unsigned int &nTF1Valid,
311  unsigned int &nTF1_coeff,
312  const std::vector<RespFuncType> &SplineType) {
313 // *****************************************
314  // Need to extract: the total number of events
315  // number of parameters
316  // maximum number of knots
317  MaxPoints = 0;
318  nEvents = 0;
319  numParams = 0;
320  nSplines = 0;
321  numKnots = 0;
322  NSplinesValid = 0;
323  nTF1Valid = 0;
324  nTF1_coeff = 0;
325 
326  // Check the number of events
327  nEvents = int(MasterSpline.size());
328 
329  // Maximum number of splines one event can have (scan through and find this number)
330  int nMaxSplines_PerEvent = 0;
331 
332  //KS: We later check that each event has the same number of splines so this is fine
333  numParams = short(MasterSpline[0].size());
334  // Initialise
335  SplineInfoArray.resize(numParams);
336 
337  // Loop over each parameter
338  for(unsigned int EventCounter = 0; EventCounter < MasterSpline.size(); ++EventCounter) {
339  // Check that each event has each spline saved
340  if (numParams > 0) {
341  int TempSize = int(MasterSpline[EventCounter].size());
342  if (TempSize != numParams) {
343  MACH3LOG_ERROR("Found {} parameters for event {}", TempSize, EventCounter);
344  MACH3LOG_ERROR("but was expecting {} since that's what I found for the previous event", numParams);
345  MACH3LOG_ERROR("Somehow this event has a different number of spline parameters... Please study further!");
346  throw MaCh3Exception(__FILE__ , __LINE__ );
347  }
348  }
349  numParams = short(MasterSpline[EventCounter].size());
350 
351  int nSplines_SingleEvent = 0;
352  int nPoints = 0;
353  // Loop over each pointer
354  for(unsigned int ParamNumber = 0; ParamNumber < MasterSpline[EventCounter].size(); ++ParamNumber) {
355  if (MasterSpline[EventCounter][ParamNumber]) {
356  if(SplineType[ParamNumber] == kTSpline3_red)
357  {
358  TResponseFunction_red* TespFunc = MasterSpline[EventCounter][ParamNumber];
359  TSpline3_red* CurrSpline = dynamic_cast<TSpline3_red*>(TespFunc);
360  if(CurrSpline){
361  nPoints = CurrSpline->GetNp();
362  }
363 
364  if (nPoints > MaxPoints) {
365  MaxPoints = static_cast<short int>(nPoints);
366  }
367  numKnots += nPoints;
368  nSplines_SingleEvent++;
369 
370  // Fill the SplineInfoArray entries with information on each splinified parameter
371  if (SplineInfoArray[ParamNumber].xPts.size() == 0)
372  {
373  // Fill the number of points
374  SplineInfoArray[ParamNumber].nPts = CurrSpline->GetNp();
375 
376  // Fill the x points
377  SplineInfoArray[ParamNumber].xPts.resize(SplineInfoArray[ParamNumber].nPts);
378  for (M3::int_t k = 0; k < SplineInfoArray[ParamNumber].nPts; ++k)
379  {
380  M3::float_t xtemp = M3::float_t(-999.99);
381  M3::float_t ytemp = M3::float_t(-999.99);
382  CurrSpline->GetKnot(k, xtemp, ytemp);
383  SplineInfoArray[ParamNumber].xPts[k] = xtemp;
384  }
385  }
386  NSplinesValid++;
387  }
388  else if (SplineType[ParamNumber] == kTF1_red)
389  {
390  TResponseFunction_red* TespFunc = MasterSpline[EventCounter][ParamNumber];
391  TF1_red* CurrSpline = dynamic_cast<TF1_red*>(TespFunc);
392  nPoints = CurrSpline->GetSize();
393  nTF1_coeff += nPoints;
394  nTF1Valid++;
395  }
396  } else {
397  // If NULL we don't have this spline for the event, so move to next spline
398  continue;
399  }
400  }
401  if (nSplines_SingleEvent > nMaxSplines_PerEvent) nMaxSplines_PerEvent = nSplines_SingleEvent;
402  }
403  nSplines = nMaxSplines_PerEvent;
404 
405  int Counter = 0;
406  //KS: Sanity check that everything was set correctly
407  for (M3::int_t i = 0; i < numParams; ++i)
408  {
409  // KS: We don't find segment for TF1, so ignore this
410  if (SplineType[i] == kTF1_red) continue;
411 
412  const M3::int_t nPoints = SplineInfoArray[i].nPts;
413  const std::vector<M3::float_t>& xArray = SplineInfoArray[i].xPts;
414  if (nPoints == -999 || xArray.size() == 0) {
415  Counter++;
416  if(Counter < 5) {
417  MACH3LOG_WARN("SplineInfoArray[{}] isn't set yet", i);
418  }
419  continue;
420  //throw MaCh3Exception(__FILE__ , __LINE__ );
421  }
422  }
423  MACH3LOG_WARN("In total SplineInfoArray for {} hasn't been initialised", Counter);
424 }
425 
426 // *****************************************
427 // Load SplineFile
429  : SplineBase() {
430 // *****************************************
431  Initialise();
432  MACH3LOG_INFO("-- GPUING WITH {X} and {Y,B,C,D} arrays and master spline containing TSpline3_red");
433  // Convert the TSpline3 pointers to the reduced form and call the reduced constructor
434  LoadSplineFile(FileName);
435 }
436 
437 // *****************************************
438 // Load SplineMonolith from ROOT file
439 void UnbinnedSplineHandler::LoadSplineFile(std::string FileName) {
440 // *****************************************
441  M3::AddPath(FileName);
442  auto SplineFile = std::make_unique<TFile>(FileName.c_str(), "OPEN");
443  TTree *Settings = SplineFile->Get<TTree>("Settings");
444  TTree *Monolith_TF1 = SplineFile->Get<TTree>("Monolith_TF1");
445  TTree *EventInfo = SplineFile->Get<TTree>("EventInfo");
446  TTree *SplineTree = SplineFile->Get<TTree>("SplineTree");
447 
448  unsigned int NEvents_temp;
449  short int nParams_temp;
450  int _max_knots_temp;
451  unsigned int nKnots_temp;
452  unsigned int NSplines_valid_temp;
453  unsigned int nTF1Valid_temp;
454  unsigned int nTF1coeff_temp;
455 
456  Settings->SetBranchAddress("NEvents", &NEvents_temp);
457  Settings->SetBranchAddress("nParams", &nParams_temp);
458  Settings->SetBranchAddress("_max_knots", &_max_knots_temp);
459  Settings->SetBranchAddress("nKnots", &nKnots_temp);
460  Settings->SetBranchAddress("NSplines_valid", &NSplines_valid_temp);
461  Settings->SetBranchAddress("NTF1_valid", &nTF1Valid_temp);
462  Settings->SetBranchAddress("nTF1coeff", &nTF1coeff_temp);
463 
464  Settings->GetEntry(0);
465 
466  NEvents = NEvents_temp;
467  nParams = nParams_temp;
468  _max_knots = static_cast<short int>(_max_knots_temp);
469  nKnots = nKnots_temp;
470  NSplines_valid = NSplines_valid_temp;
471  NTF1_valid = nTF1Valid_temp;
472  nTF1coeff = nTF1coeff_temp;
473 
474  cpu_nParamPerEvent.resize(2*NEvents);
477 
478  //KS: This is tricky as this variable use both by CPU and GPU, however if use CUDA we use cudaMallocHost
479 #ifndef MaCh3_CUDA
481  cpu_weights_spline_var = new float[NSplines_valid]();
482  cpu_weights_tf1_var = new float[NTF1_valid]();
483 #endif
484 
485  SplineTree->SetBranchAddress("SplineObject", &cpu_spline_handler);
486  SplineTree->GetEntry(0);
487 
488  float coeff_tf1 = 0.;
489  Monolith_TF1->SetBranchAddress("cpu_coeff_TF1_many", &coeff_tf1);
490  for(unsigned int i = 0; i < nTF1coeff; i++)
491  {
492  Monolith_TF1->GetEntry(i);
493  cpu_coeff_TF1_many[i] = coeff_tf1;
494  }
495 
496  unsigned int nParamPerEvent = 0;
497  unsigned int nParamPerEvent_tf1 = 0;
498 
499  EventInfo->SetBranchAddress("cpu_nParamPerEvent", &nParamPerEvent);
500  EventInfo->SetBranchAddress("cpu_nParamPerEvent_tf1", &nParamPerEvent_tf1);
501  for(unsigned int i = 0; i < 2*NEvents; i++)
502  {
503  EventInfo->GetEntry(i);
504  cpu_nParamPerEvent[i] = nParamPerEvent;
505  cpu_nParamPerEvent_tf1[i] = nParamPerEvent_tf1;
506  }
507 
508  LoadFastSplineInfoDir(SplineFile);
509 
510  SplineFile->Close();
511 
512  // Print some info; could probably make this to a separate function
514 
515  MoveToGPU();
516 
517  SetupSegments();
518 }
519 
520 // *****************************************
522 // *****************************************
523  //KS: Since we are going to copy it each step use fancy CUDA memory allocation
524  #ifdef MaCh3_CUDA
527  #else
528  SplineSegments = new short int[nParams]();
529  ParamValues = new float[nParams]();
530  #endif
531  for (M3::int_t j = 0; j < nParams; j++)
532  {
533  SplineSegments[j] = 0;
534  ParamValues[j] = -999;
535  }
536 }
537 
538 // *****************************************
539 // Save SplineMonolith into ROOT file
540 void UnbinnedSplineHandler::PrepareSplineFile(std::string FileName) {
541 // *****************************************
542  M3::AddPath(FileName);
543 
544  auto SplineFile = std::make_unique<TFile>(FileName.c_str(), "recreate");
545  TTree *Settings = new TTree("Settings", "Settings");
546  TTree *Monolith_TF1 = new TTree("Monolith_TF1", "Monolith_TF1");
547  TTree *XKnots = new TTree("XKnots", "XKnots");
548  TTree *EventInfo = new TTree("EventInfo", "EventInfo");
549 
550  unsigned int NEvents_temp = NEvents;
551  short int nParams_temp = nParams;
552  int _max_knots_temp = _max_knots;
553  unsigned int nKnots_temp = nKnots;
554  unsigned int NSplines_valid_temp = NSplines_valid;
555  unsigned int nTF1Valid_temp = NTF1_valid;
556  unsigned int nTF1coeff_temp = nTF1coeff;
557 
558  Settings->Branch("NEvents", &NEvents_temp, "NEvents/i");
559  Settings->Branch("nParams", &nParams_temp, "nParams/S");
560  Settings->Branch("_max_knots", &_max_knots_temp, "_max_knots/I");
561  Settings->Branch("nKnots", &nKnots_temp, "nKnots/i");
562  Settings->Branch("NSplines_valid", &NSplines_valid_temp, "NSplines_valid/i");
563  Settings->Branch("NTF1_valid", &nTF1Valid_temp, "NTF1_valid/i");
564  Settings->Branch("nTF1coeff", &nTF1coeff_temp, "nTF1coeff/i");
565 
566  Settings->Fill();
567 
568  SplineFile->cd();
569  Settings->Write();
570 
571  TTree *SplineTree = new TTree("SplineTree", "SplineTree");
572  // Create a branch for the SplineMonoStruct object
573  SplineTree->Branch("SplineObject", &cpu_spline_handler);
574  SplineTree->Fill();
575  SplineTree->Write();
576  delete SplineTree;
577 
578  float coeff_tf1 = 0.;
579  Monolith_TF1->Branch("cpu_coeff_TF1_many", &coeff_tf1, "cpu_coeff_TF1_many/F");
580  for(unsigned int i = 0; i < nTF1coeff; i++)
581  {
582  coeff_tf1 = cpu_coeff_TF1_many[i];
583  Monolith_TF1->Fill();
584  }
585  SplineFile->cd();
586  Monolith_TF1->Write();
587 
588  unsigned int nParamPerEvent = 0;
589  unsigned int nParamPerEvent_tf1 = 0;
590 
591  EventInfo->Branch("cpu_nParamPerEvent", &nParamPerEvent, "cpu_nParamPerEvent/i");
592  EventInfo->Branch("cpu_nParamPerEvent_tf1", &nParamPerEvent_tf1, "cpu_nParamPerEvent_tf1/i");
593 
594  for(unsigned int i = 0; i < 2*NEvents; i++)
595  {
596  nParamPerEvent = cpu_nParamPerEvent[i];
597  nParamPerEvent_tf1 = cpu_nParamPerEvent_tf1[i];
598  EventInfo->Fill();
599  }
600  SplineFile->cd();
601  EventInfo->Write();
602 
603  PrepareFastSplineInfoDir(SplineFile);
604 
605  delete Settings;
606  delete Monolith_TF1;
607  delete XKnots;
608  delete EventInfo;
609  SplineFile->Close();
610 }
611 
612 // *****************************************
613 // Destructor
614 // Cleans up the allocated GPU memory
616 // *****************************************
617  #ifdef MaCh3_CUDA
618  //KS: Since we declared them using CUDA alloc we have to free memory using also cuda functions
620  delete gpu_spline_handler;
621  #else
622  if(SplineSegments != nullptr) delete[] SplineSegments;
623  if(ParamValues != nullptr) delete[] ParamValues;
624  if(cpu_total_weights != nullptr) delete[] cpu_total_weights;
625  #endif
626 
627  if(cpu_weights_spline_var != nullptr) delete[] cpu_weights_spline_var;
628  if(cpu_weights_tf1_var != nullptr) delete[] cpu_weights_tf1_var;
629 
630  if(cpu_spline_handler != nullptr) delete cpu_spline_handler;
631 }
632 
633 // *****************************************
634 // Get the spline coefficients from the TSpline3 so that we can load ONLY these onto the GPU, not the whole TSpline3 object
635 // This loads up coefficients into two arrays: one x array and one yabcd array
636 // This should maximize our cache hits!
637 void UnbinnedSplineHandler::GetSplineCoeff_SepMany(TSpline3_red* &spl, int &nPoints, float *& xArray, float *& manyArray) const {
638 // *****************************************
639  // Initialise all arrays to 1.0
640  for (int i = 0; i < _max_knots; ++i) {
641  xArray[i] = 1.0;
642  for (int j = 0; j < _nCoeff_; j++) {
643  manyArray[i*_nCoeff_+j] = 1.0;
644  }
645  }
646  // Get number of points in spline
647  int Np = spl->GetNp();
648  // If spline is flat, set number of knots to 1.0,
649  // This is used later to expedite the calculations for flat splines
650  // tmpArray[0] is number of knots
651  nPoints = Np;
652  if (Np > _max_knots) {
653  MACH3LOG_ERROR("Error, number of points is greater than saved {}", _max_knots);
654  MACH3LOG_ERROR("This _WILL_ cause problems with GPU splines and _SHOULD_ be fixed!");
655  MACH3LOG_ERROR("nPoints = {}, _max_knots = {}", nPoints, _max_knots);
656  throw MaCh3Exception(__FILE__ , __LINE__ );
657  }
658 
659  // The coefficients we're writing to
660  M3::float_t x, y, b, c, d;
661  // TSpline3 can only take doubles, not floats
662  // But our GPU is slow with doubles, so need to cast to float
663  for(int i = 0; i < Np; i++) {
664  // Get the coefficients from the TSpline3 object
665  spl->GetCoeff(i, x, y, b, c, d);
666  // Write the arrays
667  xArray[i] = float(x);
668  manyArray[i*_nCoeff_] = float(y); // 4 because manyArray stores y,b,c,d
669  manyArray[i*_nCoeff_+1] = float(b);
670  manyArray[i*_nCoeff_+2] = float(c);
671  manyArray[i*_nCoeff_+3] = float(d);
672  if((xArray[i] == -999) || (manyArray[i*_nCoeff_] == -999) || (manyArray[i*_nCoeff_ +1] == -999) || (manyArray[i*_nCoeff_+2] == -999) || (manyArray[i*_nCoeff_+3] == -999)){
673  MACH3LOG_ERROR("*********** Bad params in {} ************", __func__);
674  MACH3LOG_ERROR("pre cast to float (x, y, b, c, d) = {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}", x, y, b, c, d);
675  MACH3LOG_ERROR("pre cast to float (x, y, b, c, d) = {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}", xArray[i], manyArray[i*_nCoeff_], manyArray[i*_nCoeff_+1], manyArray[i*_nCoeff_+2], manyArray[i*_nCoeff_+3]);
676  MACH3LOG_ERROR("This will cause problems when preparing for GPU");
677  MACH3LOG_ERROR("***************************************************************");
678  }
679  }
680 }
681 
682 #ifdef MaCh3_CUDA
683 // *****************************************
684 // Tell the GPU to evaluate the weights
685 // Load up the two x,{y,b,c,d} arrays into memory and have GPU read them with more coalescence instead of one monolithic array
686 // This should be used when we're using separate x,y,a,b,c,d arrays
687 // Also pass the segments for the parameter along with their parameter values
688 // This avoids doing lots of binary searches on the GPU
690 // *****************************************
691  // There's a parameter mapping that goes from spline parameter to a global parameter index
692  // Find the spline segments
694 
695  // The main call to the GPU
698  ParamValues,
700 }
701 #else
702 //If CUDA is not enabled do the same on CPU
703 // *****************************************
705 // *****************************************
706  // There's a parameter mapping that goes from spline parameter to a global parameter index
707  // Find the spline segments
709 
710  //KS: Huge MP loop over all valid splines
712 
713  //KS: Huge MP loop over all events calculating total weight per event
715 }
716 #endif
717 
718 //*********************************************************
720 //*********************************************************
721  #ifdef MULTITHREAD
722  //KS: Open parallel region
723  #pragma omp parallel
724  {
725  #endif
726  //KS: First we calculate
727  #ifdef MULTITHREAD
728  #pragma omp for simd nowait
729  #endif
730  for (unsigned int splineNum = 0; splineNum < NSplines_valid; ++splineNum)
731  {
732  //CW: Which Parameter we are accessing
733  const short int Param = cpu_spline_handler->paramNo_arr[splineNum];
734 
735  //CW: Avoids doing costly binary search on GPU
736  const short int segment = SplineSegments[Param];
737 
738  //KS: Segment for coeff_x is simply parameter*max knots + segment as each parameters has the same spacing
739  const short int segment_X = short(Param*_max_knots+segment);
740 
741  //KS: Find knot position in out monolithical structure
742  const unsigned int CurrentKnotPos = cpu_spline_handler->nKnots_arr[splineNum]*_nCoeff_+segment*_nCoeff_;
743 
744  // We've read the segment straight from CPU and is saved in segment_gpu
745  // polynomial parameters from the monolithic splineMonolith
746  const float fY = cpu_spline_handler->coeff_many[CurrentKnotPos];
747  const float fB = cpu_spline_handler->coeff_many[CurrentKnotPos + 1];
748  const float fC = cpu_spline_handler->coeff_many[CurrentKnotPos + 2];
749  const float fD = cpu_spline_handler->coeff_many[CurrentKnotPos + 3];
750  // The is the variation itself (needed to evaluate variation - stored spline point = dx)
751  const float dx = ParamValues[Param] - cpu_spline_handler->coeff_x[segment_X];
752 
753  //CW: Wooow, let's use some fancy intrinsic and pull down the processing time by <1% from normal multiplication! HURRAY
754  cpu_weights_spline_var[splineNum] = fmaf(dx, fmaf(dx, fmaf(dx, fD, fC), fB), fY);
755  // Or for the more "easy to read" version:
756  //cpu_weights_spline_var[splineNum] = (fY+dx*(fB+dx*(fC+dx*fD)));
757  }
758 
759  #ifdef MULTITHREAD
760  #pragma omp for simd
761  #endif
762  for (unsigned int tf1Num = 0; tf1Num < NTF1_valid; ++tf1Num)
763  {
764  // The is the variation itself (needed to evaluate variation - stored spline point = dx)
765  const float x = ParamValues[cpu_paramNo_TF1_arr[tf1Num]];
766 
767  // Read the coefficients
768  const unsigned int TF1_Index = tf1Num * _nTF1Coeff_;
769  const float a = cpu_coeff_TF1_many[TF1_Index];
770  const float b = cpu_coeff_TF1_many[TF1_Index + 1];
771 
772  cpu_weights_tf1_var[tf1Num] = fmaf(a, x, b);
773  // cpu_weights_tf1_var[tf1Num] = a*x + b;
774  //cpu_weights_tf1_var[splineNum] = 1 + a*x + b*x*x + c*x*x*x + d*x*x*x*x + e*x*x*x*x*x;
775  }
776  #ifdef MULTITHREAD
777  //KS: End parallel region
778  }
779  #endif
780 }
781 
782 //*********************************************************
783 //KS: Calc total event weight on CPU
785 //*********************************************************
786  #ifdef MULTITHREAD
787  #pragma omp parallel for
788  #endif
789  for (unsigned int EventNum = 0; EventNum < NEvents; ++EventNum)
790  {
791  float totalWeight = 1.0f; // Initialize total weight for each event
792 
793  const unsigned int Offset = 2 * EventNum;
794 
795  // Extract the parameters for the current event
796  const unsigned int startIndex = cpu_nParamPerEvent[Offset + 1];
797  const unsigned int numParams = cpu_nParamPerEvent[Offset];
798 
799  // Compute total weight for the current event
800  #ifdef MULTITHREAD
801  #pragma omp simd reduction(*:totalWeight)
802  #endif
803  for (unsigned int id = 0; id < numParams; ++id) {
804  totalWeight *= cpu_weights_spline_var[startIndex + id];
805  }
806  //Now TF1
807  // Extract the parameters for the current event
808  const unsigned int startIndex_tf1 = cpu_nParamPerEvent_tf1[Offset + 1];
809  const unsigned int numParams_tf1 = cpu_nParamPerEvent_tf1[Offset];
810 
811  // Compute total weight for the current event
812  #ifdef MULTITHREAD
813  #pragma omp simd reduction(*:totalWeight)
814  #endif
815  for (unsigned int id = 0; id < numParams_tf1; ++id) {
816  totalWeight *= cpu_weights_tf1_var[startIndex_tf1 + id];
817  }
818 
819  // Store the total weight for the current event
820  cpu_total_weights[EventNum] = static_cast<M3::float_t>(totalWeight);
821  }
822 }
823 
824 //*********************************************************
825 //KS: Print info about how much knots etc has been initialised
827 //*********************************************************
828  unsigned int event_size_max = _max_knots * nParams;
829 
830  MACH3LOG_INFO("--- INITIALISED Spline Monolith ---");
831  MACH3LOG_INFO("{} events with {} splines", NEvents, NSplines_valid);
832  MACH3LOG_INFO("On average {:.2f} splines per event ({}/{})", float(NSplines_valid)/float(NEvents), NSplines_valid, NEvents);
833  MACH3LOG_INFO("Size of x array = {:.4f} MB", double(sizeof(float)*event_size_max)/1.E6);
834  MACH3LOG_INFO("Size of coefficient (y,b,c,d) array = {:.2f} MB", double(sizeof(float)*nKnots*_nCoeff_)/1.E6);
835  MACH3LOG_INFO("Size of parameter # array = {:.2f} MB", double(sizeof(short int)*NSplines_valid)/1.E6);
836 
837  MACH3LOG_INFO("On average {:.2f} TF1 per event ({}/{})", float(NTF1_valid)/float(NEvents), NTF1_valid, NEvents);
838  MACH3LOG_INFO("Size of TF1 coefficient (a,b,c,d,e) array = {:.2f} MB", double(sizeof(float)*NTF1_valid*_nTF1Coeff_)/1.E6);
839 }
840 
841 //*********************************************************
842 //KS: After calculations are done on GPU we copy memory to CPU. This operation is asynchronous meaning while memory is being copied some operations are being carried. Memory must be copied before actual reweight. This function make sure all has been copied.
844 //*********************************************************
845  #ifdef MaCh3_CUDA
847  CudaCheckError();
848  #endif
849 }
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
@ kTF1_red
Uses TF1_red for interpolation.
@ kTSpline3_red
Uses TSpline3_red for interpolation.
void CleanVector(T &)
Base case: do nothing for non-vector types.
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
constexpr int _nTF1Coeff_
KS: For TF1 we store at most 5 coefficients, we could make it more flexible but for now define it her...
Definition: SplineCommon.h:20
Custom exception class used throughout MaCh3.
Base class for calculating weight from spline.
Definition: SplineBase.h:27
void GetTF1Coeff(TF1_red *&spl, int &nPoints, float *&coeffs) const
CW: Gets the polynomial coefficients for TF1.
Definition: SplineBase.cpp:115
short int nParams
Number of parameters that have splines.
Definition: SplineBase.h:81
void FindSplineSegment()
CW:Code used in step by step reweighting, Find Spline Segment for each param.
Definition: SplineBase.cpp:44
short int * SplineSegments
Definition: SplineBase.h:77
std::vector< FastSplineInfo > SplineInfoArray
Definition: SplineBase.h:74
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:79
void LoadFastSplineInfoDir(std::unique_ptr< TFile > &SplineFile)
KS: Load preprocessed FastSplineInfo.
Definition: SplineBase.cpp:167
void PrepareFastSplineInfoDir(std::unique_ptr< TFile > &SplineFile) const
KS: Prepare Fast Spline Info within SplineFile.
Definition: SplineBase.cpp:139
Class responsible for calculating spline weight on GPU.
__host__ void RunGPU_SplineMonolith(M3::float_t *cpu_total_weights, float *vals, short int *segment)
Run the GPU code for the separate many arrays. As in separate {x}, {y,b,c,d} arrays Pass the segment ...
__host__ void InitGPU_Vals(float **vals)
Allocate memory for spline segments.
__host__ void CleanupPinnedMemory(M3::float_t *cpu_total_weights, short int *segment, float *vals)
Clean up pinned variables at CPU.
__host__ void InitGPU_SplineMonolith(M3::float_t **cpu_total_weights, int n_events, unsigned int total_nknots, unsigned int n_splines, unsigned int n_tf1, int Eve_size)
Allocate memory on gpu for spline monolith.
__host__ void CopyToGPU_SplineMonolith(const SplineMonoStruct *cpu_spline_handler, const std::vector< float > &cpu_many_array_TF1, const std::vector< short int > &cpu_paramNo_arr_TF1, const int n_events, const std::vector< unsigned int > &cpu_nParamPerEvent, const std::vector< unsigned int > &cpu_nParamPerEvent_TF1, const int n_params, const unsigned int n_splines, const short int spline_size, const unsigned int total_nknots, const unsigned int n_tf1)
Copies data from CPU to GPU for the spline monolith.
__host__ void InitGPU_Segments(short int **segment)
Allocate memory for spline segments.
CW: A reduced TF1 class only. Only saves parameters for each TF1 and how many parameters each paramet...
int GetSize() const
Get the size.
KS: A reduced ResponseFunction Generic function used for evaluating weight.
CW: Reduced TSpline3 class.
void GetKnot(int i, M3::float_t &xtmp, M3::float_t &ytmp) const
void GetCoeff(int segment, M3::float_t &x, M3::float_t &y, M3::float_t &b, M3::float_t &c, M3::float_t &d) const
CW: Get the coefficient of a given segment.
M3::int_t GetNp() const override
CW: Get the number of points.
float * cpu_weights_tf1_var
CPU arrays to hold weight for each TF1.
float * cpu_weights_spline_var
CPU arrays to hold weight for each spline.
unsigned int nTF1coeff
Sum of all coefficients over all TF1.
unsigned int NTF1_valid
Number of valid TF1.
void ScanMasterSpline(std::vector< std::vector< TResponseFunction_red * > > &MasterSpline, unsigned int &nEvents, short int &MaxPoints, short int &numParams, int &nSplines, unsigned int &NSplinesValid, unsigned int &numKnots, unsigned int &nTF1Valid, unsigned int &nTF1_coeff, const std::vector< RespFuncType > &SplineType)
CW: Function to scan through the MasterSpline of TSpline3.
void PrepareSplineFile(std::string FileName) final
KS: Prepare spline file that can be used for fast loading.
std::vector< unsigned int > cpu_nParamPerEvent
KS: CPU map keeping track how many parameters applies to each event, we keep two numbers here {number...
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...
void SynchroniseMemTransfer() const final
KS: After calculations are done on GPU we copy memory to CPU. This operation is asynchronous meaning ...
std::vector< short int > cpu_paramNo_TF1_arr
CW: CPU array with the number of points per spline (not per spline point!)
void PrintInitialsiation() const
KS: Print info about how much knots etc has been initialised.
std::vector< unsigned int > cpu_nParamPerEvent_tf1
KS: CPU map keeping track how many parameters applies to each event, we keep two numbers here {number...
SplineMonolithGPU * gpu_spline_handler
KS: Store info about Spline monolith, this allow to obtain better step time. As all necessary informa...
unsigned int NSplines_valid
Number of valid splines.
void MoveToGPU()
CW: The shared initialiser from constructors of TResponseFunction_red.
unsigned int nKnots
Sum of all knots over all splines.
void CalcSplineWeights() final
CPU based code which eval weight for each spline.
short int _max_knots
Max knots for production.
std::vector< short int > cpu_nPoints_arr
CPU arrays to hold number of points.
void PrepareForGPU(std::vector< std::vector< TResponseFunction_red * > > &MasterSpline, const std::vector< RespFuncType > &SplineType)
CW: Prepare the TSpline3_red objects for the GPU.
void Initialise()
KS: Set everything to null etc.
SplineMonoStruct * cpu_spline_handler
KS: Store info about Spline monolith, this allow to obtain better step time. As all necessary informa...
void LoadSplineFile(std::string FileName) final
KS: Load preprocessed spline file.
virtual ~UnbinnedSplineHandler()
Destructor for UnbinnedSplineHandler class.
std::vector< float > cpu_coeff_TF1_many
CPU arrays to hold TF1 coefficients.
void GetSplineCoeff_SepMany(TSpline3_red *&spl, int &nPoints, float *&xArray, float *&manyArray) const
CW: This loads up coefficients into two arrays: one x array and one yabcd array.
M3::float_t * cpu_total_weights
KS: This holds the total CPU weights that gets read in SampleHandler.
UnbinnedSplineHandler(std::vector< std::vector< TResponseFunction_red * > > &MasterSpline, const std::vector< RespFuncType > &SplineType, const bool SaveFlatTree=false, const std::string &_FastSplineName="SplineFile.root")
Constructor.
bool SaveSplineFile
Flag telling whether we are saving spline monolith into handy root file.
unsigned int NEvents
Number of events.
std::string FastSplineName
Name of Fast Spline to which will be saved.
void CalcTotalEventWeight()
Calc total event weight.
__host__ void SynchroniseSplines()
Make sure all Cuda threads finished execution.
MaCh3 event-by-event cross-section spline code.
#define CudaCheckError()
Definition: gpuUtils.cuh:21
double float_t
Definition: Core.h:37
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
Stores info about each MC event used during reweighting routine.
Definition: EventInfo.h:13
KS: Struct storing information for spline monolith.
Definition: SplineCommon.h:61
std::vector< unsigned int > nKnots_arr
KS: CPU Number of knots per spline.
Definition: SplineCommon.h:73
std::vector< float > coeff_x
KS: CPU arrays to hold X coefficient.
Definition: SplineCommon.h:67
std::vector< float > coeff_many
CPU arrays to hold other coefficients.
Definition: SplineCommon.h:70
std::vector< short int > paramNo_arr
CW: CPU array with the number of points per spline (not per spline point!)
Definition: SplineCommon.h:76