MaCh3  2.2.3
Reference Guide
SplineStructs.h
Go to the documentation of this file.
1 #pragma once
2 
3 // MaCh3 includes
6 
7 #include <cmath>
8 
9 #pragma GCC diagnostic push
10 #pragma GCC diagnostic ignored "-Wuseless-cast"
11 #pragma GCC diagnostic ignored "-Wfloat-conversion"
12 #pragma GCC diagnostic ignored "-Wnull-dereference"
13 
18 
19 // *******************
22 // *******************
25  nPts = -999;
26  CurrSegment = 0;
27  splineParsPointer = nullptr;
28  }
29 
31  virtual ~FastSplineInfo() {
32 
33  }
34 
37 
39  std::vector<M3::float_t> xPts;
40 
43 
45  const double* splineParsPointer;
46 };
47 
48 
49 // ***************************************************************************
51 inline void ApplyKnotWeightCap(TGraph* xsecgraph, const int splineParsIndex, ParameterHandlerGeneric* XsecCov) {
52 // ***************************************************************************
53  if(xsecgraph == nullptr){
54  MACH3LOG_ERROR("hmmm looks like you're trying to apply capping for spline parameter {} but it hasn't been set in xsecgraph yet", XsecCov->GetParFancyName(splineParsIndex));
55  throw MaCh3Exception(__FILE__ , __LINE__ );
56  }
57 
58  // EM: cap the weights of the knots if specified in the config
59  if(
60  (XsecCov->GetParSplineKnotUpperBound(splineParsIndex) != M3::DefSplineKnotUpBound)
61  || (XsecCov->GetParSplineKnotLowerBound(splineParsIndex) != M3::DefSplineKnotLowBound))
62  {
63  for(int knotId = 0; knotId < xsecgraph->GetN(); knotId++){
64  double x,y;
65 
66  // EM: get the x and y of the point. Also double check that the requested point was valid just to be super safe
67  if( xsecgraph->GetPoint(knotId, x, y) == -1) {
68  MACH3LOG_ERROR("Invalid knot requested: {}", knotId);
69  throw MaCh3Exception(__FILE__ , __LINE__ );
70  }
71 
72  y = std::min(y, XsecCov->GetParSplineKnotUpperBound(splineParsIndex));
73  y = std::max(y, XsecCov->GetParSplineKnotLowerBound(splineParsIndex));
74 
75  xsecgraph->SetPoint(knotId, x, y);
76  }
77 
78  // EM: check if our cap made the spline flat, if so we set to 1 knot to avoid problems later on
79  bool isFlat = true;
80  for(int knotId = 0; knotId < xsecgraph->GetN(); knotId++){
81  double x,y;
82 
83  // EM: get the x and y of the point. Also double check that the requested point was valid just to be super safe
84  if( xsecgraph->GetPoint(knotId, x, y) == -1) {
85  MACH3LOG_ERROR("Invalid knot requested: {}", knotId);
86  throw MaCh3Exception(__FILE__ , __LINE__ );
87  }
88  if(std::abs(y - 1.0) > 1e-5) isFlat = false;
89  }
90 
91  if(isFlat){
92  xsecgraph->Set(1);
93  xsecgraph->SetPoint(0, 0.0, 1.0);
94  }
95  }
96 }
97 
98 // ***************************************************************************
100 inline void ApplyKnotWeightCapTSpline3(TSpline3* &Spline, const int splineParsIndex, ParameterHandlerGeneric* XsecCov) {
101 // ***************************************************************************
102  if(Spline == nullptr) {
103  MACH3LOG_ERROR("hmmm looks like you're trying to apply capping for spline parameter {} but it hasn't been set in Spline yet", XsecCov->GetParFancyName(splineParsIndex));
104  throw MaCh3Exception(__FILE__ , __LINE__ );
105  }
106 
107  std::string oldName = Spline->GetName();
108  // EM: cap the weights of the knots if specified in the config
109  if((XsecCov->GetParSplineKnotUpperBound(splineParsIndex) != M3::DefSplineKnotUpBound) || (XsecCov->GetParSplineKnotLowerBound(splineParsIndex) != M3::DefSplineKnotLowBound))
110  {
111  const int NValues = Spline->GetNp();
112  std::vector<double> XVals(NValues);
113  std::vector<double> YVals(NValues);
114  for(int knotId = 0; knotId < NValues; knotId++){
115  double x,y;
116  // Extract X and Y
117  Spline->GetKnot(knotId, x, y);
118 
119  y = std::min(y, XsecCov->GetParSplineKnotUpperBound(splineParsIndex));
120  y = std::max(y, XsecCov->GetParSplineKnotLowerBound(splineParsIndex));
121 
122  XVals[knotId] = x;
123  YVals[knotId] = y;
124  }
125  delete Spline;
126  // If we capped we have to make new TSpline3 to recalculate coefficients
127  Spline = new TSpline3(oldName.c_str(), XVals.data(), YVals.data(), NValues);
128  }
129 }
130 
131 // ************************
134 // ************************
135 public:
141  virtual double Eval(const double var)=0;
143  virtual void Print()=0;
145  virtual M3::int_t GetNp()=0;
146 };
147 
148 // ************************
151 // ************************
152 public:
155  length = 0;
156  Par = nullptr;
157  }
158 
160  virtual ~TF1_red() {
161  if (Par != NULL) {
162  delete[] Par;
163  Par = nullptr;
164  }
165  }
166 
169  length = nSize;
170  for (int i = 0; i < length; ++i) {
171  Par[i] = Array[i];
172  }
173  }
174 
176  TF1_red(TF1* &Function) : TResponseFunction_red() {
177  Par = nullptr;
178  SetFunc(Function);
179  }
180 
182  inline void SetFunc(TF1* &Func) {
183  length = M3::int_t(Func->GetNpar());
184  if (Par != nullptr) delete[] Par;
185  Par = new M3::float_t[length];
186  for (int i = 0; i < length; ++i) {
187  Par[i] = M3::float_t(Func->GetParameter(i));
188  }
189  delete Func;
190  Func = nullptr;
191  }
192 
194  inline double Eval(const double var) override {
195  return Par[1]+Par[0]*var;
196 
197  /* FIXME in future we might introduce more TF1
198  //If we have 5 parameters we're using a fifth order polynomial
199  if (Type == kFifthOrderPolynomial) {
200  return 1+Par[0]*var+Par[1]*var*var+Par[2]*var*var*var+Par[3]*var*var*var*var+Par[4]*var*var*var*var*var;
201  } else if (Type == kTwoLinears) {
202  return (var <= 0)*(Par[2]+Par[0]*var)+(var > 0)*(Par[2]+Par[1]*var);
203  } else if (Type == kLinear) {
204  return (Par[1]+Par[0]*var);
205  } else if (Type == kPseudoHeaviside) {
206  return (var <= 0)*(1+Par[0]*var) + (1 >= var)*(var > 0)*(1+Par[1]*var) + (var > 1)*(Par[3]+Par[2]*var);
207  }else {
208  MACH3LOG_ERROR(" Class only knows about 5th order polynomial, two superposed linear functions, linear function, or pseudo Heaviside.");
209  MACH3LOG_ERROR(" You have tried something else than this, which remains unimplemented.");
210  MACH3LOG_ERROR("{}: {}", __FILE__, __LINE__);
211  throw MaCh3Exception(__FILE__ , __LINE__ );
212  }
213  */
214  }
215 
217  inline void SetParameter(M3::int_t Parameter, M3::float_t Value) {
218  Par[Parameter] = Value;
219  }
220 
222  double GetParameter(M3::int_t Parameter) {
223  if (Parameter > length) {
224  MACH3LOG_ERROR("You requested parameter number {} but length is {} parameters", Parameter, length);
225  throw MaCh3Exception(__FILE__ , __LINE__ );
226  return -999.999;
227  }
228  return Par[Parameter];
229  }
230 
232  inline void SetSize(M3::int_t nSpline) {
233  length = nSpline;
234  Par = new M3::float_t[length];
235  }
237  inline int GetSize() { return length; }
239  inline void Print() override {
240  MACH3LOG_INFO("Printing TF1_red:");
241  MACH3LOG_INFO(" Length = {}", length);
242  for (int i = 0; i < length; i++) {
243  MACH3LOG_INFO(" Coeff {} = {}", i, Par[i]);
244  }
245  }
246 
248  inline TF1* ConstructTF1(const std::string& function, const int xmin, const int xmax) {
249  TF1 *func = new TF1("TF1", function.c_str(), xmin, xmax);
250  for(int i = 0; i < length; ++i) {
251  func->SetParameter(i, Par[i]);
252  }
253  return func;
254  }
255 
257  inline M3::int_t GetNp() override { return length; }
258 
259 private:
263 };
264 
265 // ************************
268 // ************************
269 public:
272  nPoints = 0;
273  Par = nullptr;
274  XPos = nullptr;
275  YResp = nullptr;
276  }
277 
279  TSpline3_red(TSpline3* &spline, SplineInterpolation InterPolation = kTSpline3) : TResponseFunction_red() {
280  Par = nullptr;
281  XPos = nullptr;
282  YResp = nullptr;
283  SetFunc(spline, InterPolation);
284  }
285 
288  nPoints = N;
289  // Save the parameters for each knot
290  Par = new M3::float_t*[nPoints];
291  // Save the positions of the knots
292  XPos = new M3::float_t[nPoints];
293  // Save the y response at each knot
294  YResp = new M3::float_t[nPoints];
295  for(int j = 0; j < N; ++j){
296  Par[j] = new M3::float_t[3];
297  Par[j][0] = P[j][0];
298  Par[j][1] = P[j][1];
299  Par[j][2] = P[j][2];
300  XPos[j] = X[j];
301  YResp[j] = Y[j];
302 
303  if((Par[j][0] == -999) | (Par[j][1] ==-999) | (Par[j][2] ==-999) | (XPos[j] ==-999) | (YResp[j] ==-999)){
304  MACH3LOG_ERROR("******************* Bad parameter values when constructing TSpline3_red *********************");
305  MACH3LOG_ERROR("Passed values (i, x, y, b, c, d): {}, {}, {}, {}, {}, {}", j, X[j], Y[j], P[j][0], P[j][1], P[j][2]);
306  MACH3LOG_ERROR("Set values (i, x, y, b, c, d): {}, {}, {}, {}, {}, {}", j, XPos[j], YResp[j], Par[j][0], Par[j][1], Par[j][2]);
307  MACH3LOG_ERROR("*********************************************************************************************");
308  }
309  }
310  }
313  inline void SetFunc(TSpline3* &spline, SplineInterpolation InterPolation = kTSpline3) {
314  nPoints = M3::int_t(spline->GetNp());
315  if (Par != nullptr) {
316  for (int i = 0; i < nPoints; ++i) {
317  delete[] Par[i];
318  Par[i] = nullptr;
319  }
320  delete[] Par;
321  Par = nullptr;
322  }
323  if (XPos != nullptr) delete[] XPos;
324  if (YResp != nullptr) delete[] YResp;
325  // Save the parameters for each knot
326  Par = new M3::float_t*[nPoints];
327  // Save the positions of the knots
328  XPos = new M3::float_t[nPoints];
329  // Save the y response at each knot
330  YResp = new M3::float_t[nPoints];
331 
332  //KS: Default TSpline3 ROOT implementation
333  if(InterPolation == kTSpline3)
334  {
335  for (int i = 0; i < nPoints; ++i) {
336  // 3 is the size of the TSpline3 coefficients
337  Par[i] = new M3::float_t[3];
338  double x = -999.99, y = -999.99, b = -999.99, c = -999.99, d = -999.99;
339  spline->GetCoeff(i, x, y, b, c, d);
340  XPos[i] = M3::float_t(x);
341  YResp[i] = M3::float_t(y);
342  Par[i][0] = M3::float_t(b);
343  Par[i][1] = M3::float_t(c);
344  Par[i][2] = M3::float_t(d);
345  }
346  }
347  //CW: Reduce to use linear spline interpolation for certain parameters
348  // Not the most elegant way: use TSpline3 object but set coefficients to zero and recalculate spline points; the smart way (but more human intensive) would be to save memory here and simply not store the zeros at all
349  // Get which parameters should be linear from the fit manager
350  // Convert the spline number to global xsec parameter
351  // Loop over the splines points
352  // KS: kLinearFunc should be used with TF1, this is just as safety
353  else if(InterPolation == kLinear || InterPolation == kLinearFunc)
354  {
355  for (int k = 0; k < nPoints; ++k) {
356  Par[k] = new M3::float_t[3];
357 
358  Double_t x1, y1, b1, c1, d1, x2, y2, b2, c2, d2 = 0;
359  spline->GetCoeff(k, x1, y1, b1, c1, d1);
360 
361  double tempb = 0;
362  if (k == nPoints - 1) {
363  tempb = Par[k-1][0];
364  } else {
365  spline->GetCoeff(k + 1, x2, y2, b2, c2, d2);
366  tempb = (y2-y1)/(x2-x1);
367  }
368  XPos[k] = M3::float_t(x1);
369  YResp[k] = M3::float_t(y1);
370  Par[k][0] = M3::float_t(tempb); // linear slope
371  Par[k][1] = M3::float_t(0);
372  Par[k][2] = M3::float_t(0);
373  }
374  }
375  //EM: Akima spline is similar to regular cubic spline but is allowed to be discontinuous in 2nd derivative and coefficients in any segment
376  // only depend on th 2 nearest points on either side
377  else if(InterPolation == kAkima)
378  {
379  // get the knot values for the spline
380  for (int i = 0; i < nPoints; ++i) {
381  // 3 is the size of the TSpline3 coefficients
382  Par[i] = new M3::float_t[3];
383 
384  double x = -999.99, y = -999.99;
385  spline->GetKnot(i, x, y);
386 
387  XPos[i] = M3::float_t(x);
388  YResp[i] = M3::float_t(y);
389  }
390 
391  M3::float_t* mvals = new M3::float_t[nPoints + 3];
392  M3::float_t* svals = new M3::float_t[nPoints + 1];
393 
394  for (int i = -2; i <= nPoints; ++i) {
395  // if segment is first or last or 2nd to first or last, needs to be dealt with slightly differently;
396  // need to estimate the values for additinal points which would lie outside of the spline
397  if(i ==-2){
398  mvals[i+2] = M3::float_t(3.0 * (YResp[1] - YResp[0]) / (XPos[1] - XPos[0]) - 2.0*(YResp[2] - YResp[1]) / (XPos[2] - XPos[1]));
399  }
400  else if(i==-1){
401  mvals[i+2] = M3::float_t(2.0 * (YResp[1] - YResp[0]) / (XPos[1] - XPos[0]) - (YResp[2] - YResp[1]) / (XPos[2] - XPos[1]));
402  }
403  else if(i==nPoints){
404  mvals[i+2] = M3::float_t(3.0 * (YResp[nPoints-1] - YResp[nPoints-2]) / (XPos[nPoints-1] - XPos[nPoints-2]) - 2.0*(YResp[nPoints-2] - YResp[nPoints-3]) / (XPos[nPoints-2] - XPos[nPoints-3]));
405  }
406  else if(i == nPoints - 1){
407  mvals[i+2] = M3::float_t(2.0 * (YResp[nPoints-1] - YResp[nPoints-2]) / (XPos[nPoints-1] - XPos[nPoints-2]) - (YResp[nPoints-2] - YResp[nPoints-3]) / (XPos[nPoints-2] - XPos[nPoints-3]));
408  }
409  //standard internal segment
410  else{
411  mvals[i+2] = (YResp[i+1] - YResp[i])/ (XPos[i+1] - XPos[i]);
412  }
413  }
414 
415  for(int i =2; i<=nPoints+2; i++){
416  if (std::abs(mvals[i+1] - mvals[i]) + std::abs(mvals[i-1] - mvals[i-2]) != 0.0){
417  svals[i-2] = (std::abs(mvals[i+1] - mvals[i]) * mvals[i-1] + std::abs(mvals[i-1] - mvals[i-2]) *mvals[i]) / (std::abs(mvals[i+1] - mvals[i]) + std::abs(mvals[i-1] - mvals[i-2]));
418  }
419  else{svals[i-2] = mvals[i];}
420  }
421 
422  // calculate the coefficients for the spline
423  for(int i = 0; i <nPoints; i++){
424  M3::float_t b, c, d = M3::float_t(-999.999);
425 
426  b = svals[i];
427  c = M3::float_t(3.0* (YResp[i+1] - YResp[i]) / (XPos[i+1] - XPos[i]) -2.0 *svals[i] - svals[i +1]) /(XPos[i+1] - XPos[i]);
428  d = M3::float_t((svals[i + 1] +svals[i]) - 2.0*(YResp[i+1] - YResp[i]) / (XPos[i+1] - XPos[i])) / ((XPos[i+1] - XPos[i]) * (XPos[i+1] - XPos[i]));
429 
430  Par[i][0] = b;
431  Par[i][1] = c;
432  Par[i][2] = d;
433  }
434 
435  // check the input spline for linear segments, if there are any then overwrite the calculated coefficients
436  // this will pretty much only ever be the case if they are set to be linear in samplePDFND i.e. the user wants it to be linear
437  for(int i = 0; i <nPoints-1; i++){
438  double x = -999.99, y = -999.99, b = -999.99, c = -999.99, d = -999.99;
439  spline->GetCoeff(i, x, y, b, c, d);
440 
441  if((c == 0.0 && d == 0.0)){
442  Par[i][0] = M3::float_t(b);
443  Par[i][1] = M3::float_t(0.0);
444  Par[i][2] = M3::float_t(0.0);
445  }
446  }
447  delete[] mvals;
448  delete[] svals;
449  }
450  //EM: Monotone spline is similar to regular cubic spline but enforce the condition that the interpolated value at any point
451  // must be between its two nearest knots, DOES NOT make the entire spline monotonic, only the segments
452  else if(InterPolation == kMonotonic)
453  {
454  // values of the secants at each point (for calculating monotone spline)
455  M3::float_t * Secants = new M3::float_t[nPoints -1];
456  // values of the tangens at each point (for calculating monotone spline)
457  M3::float_t * Tangents = new M3::float_t[nPoints];
458 
459  // get the knot values for the spline
460  for (int i = 0; i < nPoints; ++i) {
461  // 3 is the size of the TSpline3 coefficients
462  Par[i] = new M3::float_t[3];
463 
464  double x = -999.99, y = -999.99;
465  spline->GetKnot(i, x, y);
466 
467  XPos[i] = M3::float_t(x);
468  YResp[i] = M3::float_t(y);
469 
470  Tangents[i] = 0.0;
471  }
472 
473  // deal with the case of two points (just do linear interpolation between them)
474  if (nPoints == 2){
475  Par[0][0] = (YResp[1] - YResp[0]) / (XPos[1] - XPos[0]);
476  Par[0][1] = 0.0;
477  Par[0][2] = 0.0;
478  // extra "virtual" segment at end to make Par array shape fit with knot arrays shapes
479  Par[1][1] = 0.0;
480  Par[1][2] = 0.0;
481 
482  return;
483  } // if nPoints !=2 do full monotonic spline treatment:
484  else
485  {
486  // first pass over knots to calculate the secants
487  for (int i = 0; i < nPoints-1; ++i) {
488  Secants[i] = (YResp[i+1] - YResp[i]) / (XPos[i+1] - XPos[i]);
489  MACH3LOG_TRACE("Secant {}: {}", i, Secants[i]);
490  }
491 
492  Tangents[0] = Secants[0];
493  Tangents[nPoints-1] = Secants[nPoints -2];
494 
495  M3::float_t alpha;
496  M3::float_t beta;
497 
498  // second pass over knots to calculate tangents
499  for (int i = 1; i < nPoints-1; ++i) {
500  if ((Secants[i-1] >= 0.0 && Secants[i] >= 0.0) | (Secants[i-1] < 0.0 && Secants[i] < 0.0)){ //check for same sign
501  Tangents[i] = M3::float_t((Secants[i-1] + Secants[i]) /2.0);
502  }
503  }
504 
505  // third pass over knots to rescale tangents
506  for (int i = 0; i < nPoints-1; ++i) {
507  if (Secants[i] == 0.0){
508  Tangents[i] = 0.0;
509  Tangents[i+1] = 0.0;
510  }
511 
512  else{
513  alpha = Tangents[i] / Secants[i];
514  beta = Tangents[i+1] / Secants[i];
515 
516  if (alpha <0.0){
517  Tangents[i] = 0.0;
518  }
519  if (beta < 0.0){
520  Tangents[i+1] = 0.0;
521  }
522 
523  if (alpha * alpha + beta * beta >9.0){
524  M3::float_t tau = M3::float_t(3.0 / std::sqrt(alpha * alpha + beta * beta));
525  Tangents[i] = tau * alpha * Secants[i];
526  Tangents[i+1] = tau * beta * Secants[i];
527  }
528  }
529  } // finished rescaling tangents
530  // fourth pass over knots to calculate the coefficients for the spline
531  M3::float_t dx;
532  for(int i = 0; i <nPoints-1; i++){
533  M3::float_t b, c, d = M3::float_t(-999.999);
534  dx = XPos[i+1] - XPos[i];
535 
536  b = Tangents[i] * dx;
537  c = M3::float_t(3.0* (YResp[i+1] - YResp[i]) -2.0 *dx * Tangents[i] - dx * Tangents[i +1]);
538  d = M3::float_t(2.0* (YResp[i] - YResp[i+1]) + dx * (Tangents[i] + Tangents[i+1]));
539 
540  Par[i][0] = b / dx;
541  Par[i][1] = c / (dx * dx);
542  Par[i][2] = d / (dx * dx * dx);
543 
544  if((Par[i][0] == -999) | (Par[i][1] == -999) | (Par[i][2] ==-999) | (Par[i][0] == -999.999) | (Par[i][1] == -999.999) | (Par[i][2] ==-999.999)){
545  MACH3LOG_INFO("Bad spline parameters for segment {}: (b, c, d) = {}, {}, {}. This will cause problems with GPU.",
546  i, Par[i][0], Par[i][1], Par[i][2]);
547  }
548  MACH3LOG_TRACE("b: {}", b);
549  MACH3LOG_TRACE("dx: {}, x_0: {}, x_1: {}", dx, XPos[i], XPos[i+1]);
550  MACH3LOG_TRACE(" y_0: {}, y_1: {}", YResp[i], YResp[i+1]);
551  }
552 
553  // include params for final "segment" outside of the spline so that par array fits with x and y arrays,
554  // should never actually get used but if not set then the GPU code gets very angry
555  Par[nPoints-1][0] = 0.0;
556  Par[nPoints-1][1] = 0.0;
557  Par[nPoints-1][2] = 0.0;
558 
559  // check the input spline for linear segments, if there are any then overwrite the calculated coefficients
560  // this will pretty much only ever be the case if they are set to be linear in samplePDFND i.e. the user wants it to be linear
561  for(int i = 0; i <nPoints-1; i++){
562  double x = -999.99, y = -999.99, b = -999.99, c = -999.99, d = -999.99;
563  spline->GetCoeff(i, x, y, b, c, d);
564 
565  if((c == 0.0 && d == 0.0)){
566  Par[i][0] = M3::float_t(b);
567  Par[i][1] = 0.0;
568  Par[i][2] = 0.0;
569  }
570  }
571  delete[] Secants;
572  delete[] Tangents;
573  } // end of if(nPoints !=2)
574  }
575  else if (InterPolation == kKochanekBartels)
576  {
577  // Allocate memory for tangents and coefficients
578  M3::float_t* Tangents = new M3::float_t[nPoints];
579  for (int i = 0; i < nPoints; ++i)
580  {
581  Par[i] = new M3::float_t[3];
582  double x = -999.99, y = -999.99;
583  spline->GetKnot(i, x, y);
584  XPos[i] = M3::float_t(x);
585  YResp[i] = M3::float_t(y);
586  Tangents[i] = 0.0;
587  }
588 
589  // KS: Setting all parameters to 0 gives a Catmull-Rom spline.
590  // Tension (T):
591  // - T = 0.0: Default smooth interpolation (Catmull-Rom).
592  // - T = 1.0: Maximum tension; curve becomes linear between knots (no curvature).
593  // - T = -1.0: Overshooting; creates loops or "bouncy" effects around knots.
594  constexpr M3::float_t T = 0.0;
595 
596  // Continuity (C):
597  // - C = 0.0: Default smooth transition (continuous first derivative).
598  // - C = 1.0: Sharp corner/crease at knot (discontinuous first derivative).
599  // - C = -1.0: Inverted curve; creates loops or kinks at knot.
600  constexpr M3::float_t C = 0.0;
601 
602  // Bias (B):
603  // - B = 0.0: Default symmetric curve around knot.
604  // - B = 1.0: Curve is "pulled" toward the next knot (leading effect).
605  // - B = -1.0: Curve is "pulled" toward the previous knot (lagging effect).
606  constexpr M3::float_t B = 0.0;
607 
608  // Calculate tangents for internal knots
609  for (int i = 1; i < nPoints - 1; ++i)
610  {
611  M3::float_t d0 = (YResp[i] - YResp[i-1]) / (XPos[i] - XPos[i-1]);
612  M3::float_t d1 = (YResp[i+1] - YResp[i]) / (XPos[i+1] - XPos[i]);
613 
614  M3::float_t term1 = (1.0 - T) * (1.0 + B) * (1.0 + C) * 0.5 * d0;
615  M3::float_t term2 = (1.0 - T) * (1.0 - B) * (1.0 - C) * 0.5 * d1;
616 
617  Tangents[i] = term1 + term2;
618  }
619 
620  // Boundary conditions (simple choice: secant slopes)
621  Tangents[0] = (YResp[1] - YResp[0]) / (XPos[1] - XPos[0]);
622  Tangents[nPoints-1]= (YResp[nPoints-1] - YResp[nPoints-2]) / (XPos[nPoints-1] - XPos[nPoints-2]);
623 
624  // Compute cubic coefficients for each segment
625  for (int i = 0; i < nPoints - 1; ++i)
626  {
627  M3::float_t dx = XPos[i+1] - XPos[i];
628  M3::float_t dy = YResp[i+1] - YResp[i];
629  M3::float_t m0 = Tangents[i];
630  M3::float_t m1 = Tangents[i+1];
631 
632  Par[i][0] = m0; // b
633  Par[i][1] = (3*dy/(dx*dx)) - (2*m0 + m1)/dx; // c
634  Par[i][2] = (m0 + m1 - 2*dy/dx) / (dx*dx); // d
635 
636  MACH3LOG_TRACE("KB segment {}: dx={}, dy={}, b={}, c={}, d={}",
637  i, dx, dy, Par[i][0], Par[i][1], Par[i][2]);
638  }
639  // Last "virtual" segment
640  Par[nPoints-1][0] = 0.0;
641  Par[nPoints-1][1] = 0.0;
642  Par[nPoints-1][2] = 0.0;
643 
644  delete[] Tangents;
645  }
646  else
647  {
648  MACH3LOG_ERROR("Unsupported interpolation type {}", static_cast<int>(InterPolation));
649  throw MaCh3Exception(__FILE__ , __LINE__ );
650  }
651 
652  delete spline;
653  spline = nullptr;
654  }
655 
657  virtual ~TSpline3_red() {
658  if(Par != nullptr) {
659  for (int i = 0; i < nPoints; ++i) {
660  if (Par[i] != nullptr) {
661  delete[] Par[i];
662  }
663  }
664  delete[] Par;
665  }
666  if(XPos != nullptr) delete[] XPos;
667  if(YResp != nullptr) delete[] YResp;
668  Par = nullptr;
669  XPos = YResp = nullptr;
670  }
671 
674  inline int FindX(double x) {
675  // The segment we're interested in (klow in ROOT code)
676  int segment = 0;
677  int kHigh = nPoints-1;
678  // If the variation is below the lowest saved spline point
679  if (x <= XPos[0]){
680  segment = 0;
681  // If the variation is above the highest saved spline point
682  } else if (x >= XPos[nPoints-1]) {
683  // Yes, the -2 is indeed correct, see TSpline.cxx:814 and //see: https://savannah.cern.ch/bugs/?71651
684  segment = kHigh;
685  // If the variation is between the maximum and minimum, perform a binary search
686  } else {
687  // The top point we've got
688  int kHalf = 0;
689  // While there is still a difference in the points (we haven't yet found the segment)
690  // This is a binary search, incrementing segment and decrementing kHalf until we've found the segment
691  while (kHigh - segment > 1) {
692  // Increment the half-step
693  kHalf = (segment + kHigh)/2;
694  // If our variation is above the kHalf, set the segment to kHalf
695  if (x > XPos[kHalf]) {
696  segment = kHalf;
697  // Else move kHigh down
698  } else {
699  kHigh = kHalf;
700  }
701  } // End the while: we've now done our binary search
702  } // End the else: we've now found our point
703  if (segment >= nPoints-1 && nPoints > 1) segment = nPoints-2;
704  return segment;
705  }
706 
708  inline double Eval(double var) override {
709  // Get the segment for this variation
710  int segment = FindX(var);
711  // The get the coefficients for this variation
712  M3::float_t x = M3::float_t(-999.99), y = M3::float_t(-999.99), b = M3::float_t(-999.99), c = M3::float_t(-999.99), d = M3::float_t(-999.99);
713  GetCoeff(segment, x, y, b, c, d);
714  double dx = var - x;
715  // Evaluate the third order polynomial
716  double weight = y+dx*(b+dx*(c+d*dx));
717  return weight;
718  }
719 
721  inline M3::int_t GetNp() override { return nPoints; }
722  // Get the ith knot's x and y position
723  inline void GetKnot(int i, M3::float_t &xtmp, M3::float_t &ytmp) {
724  xtmp = XPos[i];
725  ytmp = YResp[i];
726  }
727 
729  inline void GetCoeff(int segment, M3::float_t &x, M3::float_t &y, M3::float_t &b, M3::float_t &c, M3::float_t &d) {
730  b = Par[segment][0];
731  c = Par[segment][1];
732  d = Par[segment][2];
733  x = XPos[segment];
734  y = YResp[segment];
735  }
736 
738  inline TSpline3* ConstructTSpline3() {
739  // KS: Sadly ROOT only accepts double...
740  #ifdef _LOW_MEMORY_STRUCTS_
741  std::vector<Double_t> xPosDoubles(nPoints);
742  std::vector<Double_t> yPosDoubles(nPoints);
743  for (Int_t i = 0; i < nPoints; ++i) {
744  xPosDoubles[i] = static_cast<Double_t>(XPos[i]); // Convert float to double
745  yPosDoubles[i] = static_cast<Double_t>(YResp[i]); // Convert float to double
746  }
747  TSpline3 *spline = new TSpline3("Spline", xPosDoubles.data(), yPosDoubles.data(), static_cast<int>(nPoints));
748  #else
749  TSpline3 *spline = new TSpline3("Spline", XPos, YResp, nPoints);
750  #endif
751  for (Int_t i = 0; i < nPoints; ++i) {
752  spline->SetPointCoeff(i, Par[i][0], Par[i][1], Par[i][2]);
753  }
754 
755  return spline;
756  }
757 
759  inline void Print() override {
760  MACH3LOG_INFO("Printing TSpline_red:");
761  MACH3LOG_INFO(" Nknots = {}", nPoints);
762  for (int i = 0; i < nPoints; ++i) {
763  MACH3LOG_INFO(" i = {} x = {} y = {} b = {} c = {} d = {}",
764  i, XPos[i], YResp[i], Par[i][0], Par[i][1], Par[i][2]);
765  }
766  }
767 
768  protected: //changed to protected from private so can be accessed by derived classes
777 };
778 
779 // *****************************************
782 inline bool isFlat(TSpline3_red* &spl) {
783 // *****************************************
784  int Np = spl->GetNp();
785  M3::float_t x, y, b, c, d;
786  // Go through spline segment parameters,
787  // Get y values for each spline knot,
788  // Every knot must evaluate to 1.0 to create a flat spline
789  for(int i = 0; i < Np; i++) {
790  spl->GetCoeff(i, x, y, b, c, d);
791  if (y != 1) {
792  return false;
793  }
794  }
795  return true;
796 }
797 
798 // *********************************
801 inline std::vector<std::vector<TSpline3_red*> > ReduceTSpline3(std::vector<std::vector<TSpline3*> > &MasterSpline) {
802 // *********************************
803  std::vector<std::vector<TSpline3*> >::iterator OuterIt;
804  std::vector<TSpline3*>::iterator InnerIt;
805 
806  // The return vector
807  std::vector<std::vector<TSpline3_red*> > ReducedVector;
808  ReducedVector.reserve(MasterSpline.size());
809 
810  // Loop over each parameter
811  int OuterCounter = 0;
812  for (OuterIt = MasterSpline.begin(); OuterIt != MasterSpline.end(); ++OuterIt, ++OuterCounter) {
813  // Make the temp vector
814  std::vector<TSpline3_red*> TempVector;
815  TempVector.reserve(OuterIt->size());
816  int InnerCounter = 0;
817  // Loop over each TSpline3 pointer
818  for (InnerIt = OuterIt->begin(); InnerIt != OuterIt->end(); ++InnerIt, ++InnerCounter) {
819  // Here's our delicious TSpline3 object
820  TSpline3 *spline = (*InnerIt);
821  // Now make the reduced TSpline3 pointer
822  TSpline3_red *red = nullptr;
823  if (spline != NULL) {
824  red = new TSpline3_red(spline);
825  (*InnerIt) = spline;
826  }
827  // Push back onto new vector
828  TempVector.push_back(red);
829  } // End inner for loop
830  ReducedVector.push_back(TempVector);
831  } // End outer for loop
832  // Now have the reduced vector
833  return ReducedVector;
834 }
835 
836 // *********************************
839 inline std::vector<std::vector<TF1_red*> > ReduceTF1(std::vector<std::vector<TF1*> > &MasterSpline) {
840 // *********************************
841  std::vector<std::vector<TF1*> >::iterator OuterIt;
842  std::vector<TF1*>::iterator InnerIt;
843 
844  // The return vector
845  std::vector<std::vector<TF1_red*> > ReducedVector;
846  ReducedVector.reserve(MasterSpline.size());
847 
848  // Loop over each parameter
849  int OuterCounter = 0;
850  for (OuterIt = MasterSpline.begin(); OuterIt != MasterSpline.end(); ++OuterIt, ++OuterCounter) {
851  // Make the temp vector
852  std::vector<TF1_red*> TempVector;
853  TempVector.reserve(OuterIt->size());
854  int InnerCounter = 0;
855  // Loop over each TSpline3 pointer
856  for (InnerIt = OuterIt->begin(); InnerIt != OuterIt->end(); ++InnerIt, ++InnerCounter) {
857  // Here's our delicious TSpline3 object
858  TF1* spline = (*InnerIt);
859  // Now make the reduced TSpline3 pointer (which deleted TSpline3)
860  TF1_red* red = nullptr;
861  if (spline != NULL) {
862  red = new TF1_red(spline);
863  (*InnerIt) = spline;
864  }
865  // Push back onto new vector
866  TempVector.push_back(red);
867  } // End inner for loop
868  ReducedVector.push_back(TempVector);
869  } // End outer for loop
870  // Now have the reduced vector
871  return ReducedVector;
872 }
873 
874 // *********************************
881  const RespFuncType SplineRespFuncType,
882  const SplineInterpolation SplineInterpolationType,
883  const std::string& Title) {
884 // *********************************
885  TResponseFunction_red* RespFunc = nullptr;
886 
887  if (graph && graph->GetN() > 1)
888  {
889  if(SplineRespFuncType == kTSpline3_red)
890  {
891  // Here's the TSpline3
892  TSpline3* spline = nullptr;
893  TSpline3_red *spline_red = nullptr;
894 
895  // Create the TSpline3* from the TGraph* and build the coefficients
896  spline = new TSpline3(Title.c_str(), graph);
897  spline->SetNameTitle(Title.c_str(), Title.c_str());
898 
899  // Make the reduced TSpline3 format and delete the old spline
900  spline_red = new TSpline3_red(spline, SplineInterpolationType);
901 
902  RespFunc = spline_red;
903  }
904  else if(SplineRespFuncType == kTF1_red)
905  {
906  // The TF1 object we build from fitting the TGraph
907  TF1 *Fitter = nullptr;
908  TF1_red *tf1_red = nullptr;
909 
910  if(graph->GetN() != 2) {
911  MACH3LOG_ERROR("Trying to make TF1 from more than 2 knots. Knots = {}", graph->GetN());
912  MACH3LOG_ERROR("Currently support only linear with 2 knots :(");
913  throw MaCh3Exception(__FILE__ , __LINE__ );
914  }
915 
916  // Try simple linear function
917  Fitter = new TF1(Title.c_str(), "([1]+[0]*x)", graph->GetX()[0], graph->GetX()[graph->GetN()-1]);
918  //CW: For 2p2h shape C and O we can't fit a polynomial: try a linear combination of two linear functions around 0
919  //Fitter = new TF1(Title.c_str(), "(x<=0)*(1+[0]*x)+(x>0)*([1]*x+1)", graph->GetX()[0], graph->GetX()[graph->GetN()-1]);
920  // Fit 5hd order polynomial for all other parameters
921  //Fitter = new TF1(Title.c_str(), "1+[0]*x+[1]*x*x+[2]*x*x*x+[3]*x*x*x*x+[4]*x*x*x*x*x", graph->GetX()[0], graph->GetX()[graph->GetN()-1]);
922  //Pseudo Heaviside for Pauli Blocking
923  //Fitter = new TF1(Title.c_str(), "(x <= 0)*(1+[0]*x) + (1 >= x)*(x > 0)*(1+[1]*x) + (x > 1)*([3]+[2]*x)", graph->GetX()[0], graph->GetX()[graph->GetN()-1]);
924 
925  // Fit the TF1 to the graph
926  graph->Fit(Fitter, "Q0");
927  // Make the reduced TF1 if we want
928  tf1_red = new TF1_red(Fitter);
929 
930  RespFunc = tf1_red;
931  }
932  else
933  {
934  MACH3LOG_ERROR("Unsupported response function type");
935  throw MaCh3Exception(__FILE__ , __LINE__ );
936  }
937  }
938  else
939  {
940  RespFunc = nullptr;
941  }
942  return RespFunc;
943 }
944 
945 #pragma GCC diagnostic pop
946 
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:27
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:25
#define MACH3LOG_TRACE
Definition: MaCh3Logger.h:23
SplineInterpolation
Make an enum of the spline interpolation type.
@ kTSpline3
Default TSpline3 interpolation.
@ kMonotonic
EM: DOES NOT make the entire spline monotonic, only the segments.
@ kKochanekBartels
KS: Kochanek-Bartels spline: allows local control of tension, continuity, and bias.
@ kLinear
Linear interpolation between knots.
@ kLinearFunc
Liner interpolation using TF1 not spline.
@ kAkima
EM: Akima spline iis allowed to be discontinuous in 2nd derivative and coefficients in any segment.
RespFuncType
Make an enum of the spline interpolation type.
@ kTF1_red
Uses TF1_red for interpolation.
@ kTSpline3_red
Uses TSpline3_red for interpolation.
TResponseFunction_red * CreateResponseFunction(TGraph *&graph, const RespFuncType SplineRespFuncType, const SplineInterpolation SplineInterpolationType, const std::string &Title)
KS: Create Response Function using TGraph.
void ApplyKnotWeightCap(TGraph *xsecgraph, const int splineParsIndex, ParameterHandlerGeneric *XsecCov)
EM: Apply capping to knot weight for specified spline parameter. param graph needs to have been set i...
Definition: SplineStructs.h:51
void ApplyKnotWeightCapTSpline3(TSpline3 *&Spline, const int splineParsIndex, ParameterHandlerGeneric *XsecCov)
EM: Apply capping to knot weight for specified spline parameter. param graph needs to have been set i...
std::vector< std::vector< TF1_red * > > ReduceTF1(std::vector< std::vector< TF1 * > > &MasterSpline)
CW: Reduced the TF1 to TF1_red.
std::vector< std::vector< TSpline3_red * > > ReduceTSpline3(std::vector< std::vector< TSpline3 * > > &MasterSpline)
CW: Reduced the TSpline3 to TSpline3_red.
bool isFlat(TSpline3_red *&spl)
CW: Helper function used in the constructor, tests to see if the spline is flat.
Custom exception class for MaCh3 errors.
Class responsible for handling of systematic error parameters with different types defined in the con...
CW: A reduced TF1 class only. Only saves parameters for each TF1 and how many parameters each paramet...
void SetSize(M3::int_t nSpline)
Set the size.
M3::int_t length
void SetFunc(TF1 *&Func)
Set the function.
TF1_red()
Empty constructor.
void Print() override
Print detailed info.
M3::int_t GetNp() override
DL: Get number of points.
TF1 * ConstructTF1(const std::string &function, const int xmin, const int xmax)
KS: Make a TF1 from the reduced TF1.
TF1_red(TF1 *&Function)
The TF1 constructor with deep copy.
M3::float_t * Par
The parameters.
virtual ~TF1_red()
Empty destructor.
double Eval(const double var) override
Evaluate a variation.
TF1_red(M3::int_t nSize, M3::float_t *Array)
The useful constructor with deep copy.
double GetParameter(M3::int_t Parameter)
Get a parameter value.
void SetParameter(M3::int_t Parameter, M3::float_t Value)
Set a parameter to a value.
int GetSize()
Get the size.
KS: A reduced ResponseFunction Generic function used for evaluating weight.
virtual ~TResponseFunction_red()
Empty destructor.
TResponseFunction_red()
Empty constructor.
virtual M3::int_t GetNp()=0
DL: Get number of points.
virtual double Eval(const double var)=0
Evaluate a variation.
virtual void Print()=0
KS: Printer.
CW: Reduced TSpline3 class.
double Eval(double var) override
CW: Evaluate the weight from a variation.
void GetKnot(int i, M3::float_t &xtmp, M3::float_t &ytmp)
int FindX(double x)
Find the segment relevant to this variation in x.
virtual ~TSpline3_red()
Empty destructor.
M3::float_t ** Par
Always uses a third order polynomial, so hard-code the number of coefficients in implementation.
TSpline3_red(M3::float_t *X, M3::float_t *Y, M3::int_t N, M3::float_t **P)
constructor taking parameters
M3::int_t nPoints
Number of points/knot in TSpline3.
TSpline3_red(TSpline3 *&spline, SplineInterpolation InterPolation=kTSpline3)
The constructor that takes a TSpline3 pointer and copies in to memory.
M3::int_t GetNp() override
CW: Get the number of points.
TSpline3 * ConstructTSpline3()
CW: Make a TSpline3 from the reduced splines.
void GetCoeff(int segment, M3::float_t &x, M3::float_t &y, M3::float_t &b, M3::float_t &c, M3::float_t &d)
CW: Get the coefficient of a given segment.
void Print() override
Print detailed info.
void SetFunc(TSpline3 *&spline, SplineInterpolation InterPolation=kTSpline3)
Set the function .
M3::float_t * XPos
Positions of each x for each knot.
TSpline3_red()
Empty constructor.
M3::float_t * YResp
y-value for each knot
double GetParSplineKnotUpperBound(const int i) const
EM: value at which we cap spline knot weight.
std::string GetParFancyName(const int i) const
Get fancy name of the Parameter.
double GetParSplineKnotLowerBound(const int i) const
EM: value at which we cap spline knot weight.
constexpr static const double DefSplineKnotUpBound
Default value for spline knot capping, default mean not capping is being applied.
Definition: Core.h:79
double float_t
Definition: Core.h:30
constexpr static const double DefSplineKnotLowBound
Default value for spline knot capping, default mean not capping is being applied.
Definition: Core.h:81
int int_t
Definition: Core.h:31
CW: Add a struct to hold info about the splinified xsec parameters and help with FindSplineSegment.
Definition: SplineStructs.h:21
virtual ~FastSplineInfo()
Destructor.
Definition: SplineStructs.h:31
std::vector< M3::float_t > xPts
Array of the knots positions.
Definition: SplineStructs.h:39
const double * splineParsPointer
Array of the knots positions.
Definition: SplineStructs.h:45
M3::int_t CurrSegment
Array of what segment of spline we're currently interested in. Gets updated once per MCMC iteration.
Definition: SplineStructs.h:42
FastSplineInfo()
Constructor.
Definition: SplineStructs.h:24
M3::int_t nPts
Number of points in spline.
Definition: SplineStructs.h:36