MaCh3  2.8.0
Reference Guide
SplineBase.cpp
Go to the documentation of this file.
1 #include "Splines/SplineBase.h"
2 
3 #ifdef MaCh3_CUDA
5 #endif
6 
7 #pragma GCC diagnostic ignored "-Wuseless-cast"
8 #pragma GCC diagnostic ignored "-Wfloat-conversion"
9 
10 // *****************************************
11 SplineBase::SplineBase([[maybe_unused]] bool Use_GPU) {
12 // *****************************************
13  #ifdef MaCh3_CUDA
14  useGPU = Use_GPU;
15  if(useGPU) MACH3LOG_INFO("Using GPU version of {}", GetName());
16  gpu_monolith = nullptr;
17  #else
18  // if we don't have GPU simply
19  useGPU = false;
20  #endif
21 
22  NSplines_valid = 0;
23  nParams = 0;
24  _max_knots = 0;
25  SplineSegments = nullptr;
26  ParamValues = nullptr;
27 }
28 
29 // *****************************************
31 // *****************************************
32 }
33 
34 // *************************
36  const M3::int_t segment,
37  const M3::float_t xvar,
38  const std::vector<M3::float_t>& xPts) const {
39 // *************************
40  if (xPts[segment] > xvar && segment != 0) {
41  MACH3LOG_ERROR("Found a segment which is _ABOVE_ the variation!");
42  MACH3LOG_ERROR("IT SHOULD ALWAYS BE BELOW! (except when segment 0)");
43  MACH3LOG_ERROR("Spline: {}", i);
44  MACH3LOG_ERROR("Found segment = {}", segment);
45  MACH3LOG_ERROR("Doing variation = {}", xvar);
46  MACH3LOG_ERROR("x in spline = {}", xPts[segment]);
47  for (size_t j = 0; j < xPts.size(); ++j) {
48  MACH3LOG_ERROR(" {} = {}", j, xPts[j]);
49  }
50  throw MaCh3Exception(__FILE__, __LINE__);
51  }
52 }
53 
54 // *************************
55 // CW: Only need to do the binary search once per parameter, not once per event!
56 // Takes down the number of binary searches from 1.2M to 17, ha!
57 // For ROOT version see root/hist/hist/src/TSpline3.cxx TSpline3::FindX(double)
59 // *************************
60  // Loop over the splines
61  //KS: Tried multithreading here with 48 splines and it is faster with one thread, maybe in future multithreading will be worth revisiting
62  for (M3::int_t i = 0; i < nParams; ++i)
63  {
64  const M3::int_t nPoints = SplineInfoArray[i].nPts;
65  const std::vector<M3::float_t>& xArray = SplineInfoArray[i].xPts;
66 
67  // Get the variation for this reconfigure for the ith parameter
68  const float xvar = float(*SplineInfoArray[i].splineParsPointer);
69  ParamValues[i] = xvar;
70 
71  // EM: if we have a parameter that has no response for any event (i.e. all splines have just one knot), then skip it and avoid a seg fault here
72  // In principle, such parameters shouldn't really be included in the first place, but with new det syst splines this
73  // could happen if say you were just running on one FHC run, then all RHC parameters would be flat and the code below would break.
74  if(xArray.size() == 0) continue;
75 
76  // The segment we're interested in (klow in ROOT code)
77  M3::int_t segment = 0;
78  M3::int_t kHigh = nPoints-1;
79  //KS: We expect new segment is very close to previous
80  const M3::int_t PreviousSegment = SplineInfoArray[i].CurrSegment;
81 
82  // If the variation is below the lowest saved spline point
83  if (xvar <= xArray[0]) {
84  segment = 0;
85  // If the variation is above the highest saved spline point
86  } else if (xvar >= xArray[nPoints-1]) {
87  //CW: Yes, the -2 is indeed correct, see TSpline.cxx:814 and //see: https://savannah.cern.ch/bugs/?71651
88  segment = kHigh;
89  //KS: It is quite probable the new segment is same as in previous step so try to avoid binary search, first we have to check if it is in bounds to avoid seg fault
90  } else if( xArray[PreviousSegment+1] > xvar && xvar >= xArray[PreviousSegment] ) {
91  segment = PreviousSegment;
92  // If the variation is between the maximum and minimum, perform a binary search
93  } else {
94  // The top point we've got
95  M3::int_t kHalf = 0;
96  // While there is still a difference in the points (we haven't yet found the segment)
97  // This is a binary search, incrementing segment and decrementing kHalf until we've found the segment
98  while (kHigh - segment > 1) {
99  // Increment the half-step
100  kHalf = M3::int_t((segment + kHigh)/2);
101  // If our variation is above the kHalf, set the segment to kHalf
102  if (xvar > xArray[kHalf]) {
103  segment = kHalf;
104  // Else move kHigh down
105  } else {
106  kHigh = kHalf;
107  }
108  } // End the while: we've now done our binary search
109  } // End the else: we've now found our point
110 
111  if (segment >= nPoints-1 && nPoints > 1) segment = nPoints-2;
112 
113  // CW: This way we avoid doing 1.2M+ binary searches on the GPU
114  // and literally just multiply lots of numbers together on the GPU without any algorithm
115  // Update the values and which segment it belongs to
116  SplineInfoArray[i].CurrSegment = segment;
117  SplineSegments[i] = short(SplineInfoArray[i].CurrSegment);
118 
119  #ifdef MACH3_DEBUG
120  CheckSegmentValidity(i, segment, xvar, SplineInfoArray[i].xPts);
121  #endif
122  } //end loop over params
123 }
124 
125 // *****************************************
126 // Get the spline coefficients from the TSpline3 so that we can load ONLY these onto the GPU, not the whole TSpline3 object
127 // This loads up coefficients into two arrays: one x array and one yabcd array
128 // This should maximize our cache hits!
129 void SplineBase::GetTF1Coeff(TF1_red* &spl, int &nPoints, float *& coeffs) const {
130 // *****************************************
131  // Initialise all arrays to 1.0
132  for (int i = 0; i < _nTF1Coeff_; ++i) {
133  coeffs[i] = 0.0;
134  }
135 
136  // Get number of points in spline
137  nPoints = spl->GetSize();
138 
139  if(nPoints > _nTF1Coeff_)
140  {
141  MACH3LOG_ERROR("Too big number of points for TF1");
142  throw MaCh3Exception(__FILE__ , __LINE__ );
143  }
144  // TSpline3 can only take doubles, not floats
145  // But our GPU is slow with doubles, so need to cast to float
146  for (int i = 0; i < nPoints; i++) {
147  coeffs[i] = float(spl->GetParameter(M3::int_t(i)));
148  }
149  // The structure is now coeffs = {a,b,c,d,e}
150 }
151 
152 // *****************************************
153 void SplineBase::PrepareFastSplineInfoDir(std::unique_ptr<TFile>& SplineFile) const {
154 // *****************************************
155  TTree *FastSplineInfoTree = new TTree("FastSplineInfoTree", "FastSplineInfoTree");
156 
157  M3::int_t nPoints = 0;
158  float xtemp[20];
159  FastSplineInfoTree->Branch("nPts", &nPoints, "nPts/I");
160  FastSplineInfoTree->Branch("xPts", xtemp, "xPts[nPts]/F");
161 
162  for (M3::int_t i = 0; i < nParams; ++i)
163  {
164  nPoints = SplineInfoArray[i].nPts;
165 
166  for (M3::int_t k = 0; k < SplineInfoArray[i].nPts; ++k)
167  {
168  xtemp[k] = float(SplineInfoArray[i].xPts[k]);
169  }
170  FastSplineInfoTree->Fill();
171  }
172 
173  SplineFile->cd();
174  FastSplineInfoTree->Write();
175 
176  delete FastSplineInfoTree;
177 }
178 
179 // *****************************************
180 // KS: Prepare Fast Spline Info within SplineFile
181 void SplineBase::LoadFastSplineInfoDir(std::unique_ptr<TFile>& SplineFile) {
182 // *****************************************
183  TTree *FastSplineInfoTree = SplineFile->Get<TTree>("FastSplineInfoTree");
184  M3::int_t nPoints = 0;
185  float xtemp[20];
186  FastSplineInfoTree->SetBranchAddress("nPts", &nPoints);
187  FastSplineInfoTree->SetBranchAddress("xPts", &xtemp);
188 
189  if(nParams == 0){
190  MACH3LOG_WARN("Calling {} with {} number of params", __func__, nParams);
191  }
192  SplineInfoArray.resize(nParams);
193  for (M3::int_t i = 0; i < nParams; ++i) {
194  FastSplineInfoTree->GetEntry(i);
195 
196  // Fill the number of points
197  SplineInfoArray[i].nPts = nPoints;
198  if(nPoints == -999) continue;
199  SplineInfoArray[i].xPts.resize(SplineInfoArray[i].nPts);
200  for (M3::int_t k = 0; k < SplineInfoArray[i].nPts; ++k)
201  {
202  SplineInfoArray[i].xPts[k] = xtemp[k];
203  }
204  }
205 }
206 
207 // *****************************************
209 // *****************************************
210  //KS: Since we are going to copy it each step use fancy CUDA memory allocation
211  #ifdef MaCh3_CUDA
212  if (useGPU) {
215  } else
216  #endif
217  {
218  SplineSegments = new short int[nParams]();
219  ParamValues = new float[nParams]();
220  }
221  //ETA - let this just be set as the first segment by default
222  for (M3::int_t j = 0; j < nParams; j++)
223  {
224  SplineSegments[j] = 0;
225  ParamValues[j] = -999;
226  }
227 }
228 
229 //*********************************************************
230 //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.
232 //*********************************************************
233  #ifdef MaCh3_CUDA
234  if(useGPU) {
236  CudaCheckError();
237  }
238  #endif
239 }
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
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.
SplineMonolithGPU * gpu_monolith
KS: Store info about Spline monolith, this allow to obtain better step time. As all necessary informa...
Definition: SplineBase.h:120
virtual ~SplineBase()
Destructor.
Definition: SplineBase.cpp:30
void CheckSegmentValidity(const M3::int_t i, const M3::int_t segment, const M3::float_t xvar, const std::vector< M3::float_t > &xPts) const
Validates that the spline segment is correct for the given variation.
Definition: SplineBase.cpp:35
void GetTF1Coeff(TF1_red *&spl, int &nPoints, float *&coeffs) const
CW: Gets the polynomial coefficients for TF1.
Definition: SplineBase.cpp:129
void SynchroniseMemTransfer() const
KS: After calculations are done on GPU we copy memory to CPU. This operation is asynchronous meaning ...
Definition: SplineBase.cpp:231
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
virtual std::string GetName() const
Get class name.
Definition: SplineBase.h:41
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
SplineBase(const bool Use_GPU=true)
Constructor.
Definition: SplineBase.cpp:11
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
__host__ void InitGPU_Vals(float **vals)
Allocate memory for spline segments.
__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...
double GetParameter(M3::int_t Parameter) const
Get a parameter value.
int GetSize() const
Get the size.
__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
int int_t
Definition: Core.h:38