laura is hosted by Hepforge, IPPP Durham
Laura++  v3r4
A maximum likelihood fitting package for performing Dalitz-plot analysis.
Lau2DCubicSpline.cc
Go to the documentation of this file.
1 
2 /*
3 Copyright 2013 University of Warwick
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9  http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 */
17 
18 /*
19 Laura++ package authors:
20 John Back
21 Paul Harrison
22 Thomas Latham
23 */
24 
32 #include <cmath>
33 #include <cstdlib>
34 #include <iostream>
35 
36 #include <TH2.h>
37 #include <TSystem.h>
38 
39 #include "Lau2DCubicSpline.hh"
40 
42 { }
43 
45  const TH2& h, Int_t xbin, Int_t ybin) const
46 {
47  //reflect until we're in range
48  while(xbin < 0 || xbin >= nBinsX - 1) {
49  if(xbin < 0) xbin = -xbin-1;
50  if(xbin >= nBinsX -1) xbin = 2*(nBinsX-1) - xbin - 1;
51  }
52  while(ybin < 0 || ybin >= nBinsY - 1) {
53  if(ybin < 0) ybin = -ybin-1;
54  if(ybin >= nBinsY -1) ybin = 2*(nBinsY-1) - ybin - 1;
55  }
56 
57  return h.GetBinContent(1 + xbin, 1 + ybin);
58 }
59 
60 inline Double_t Lau2DCubicSpline::dhistdx(
61  const TH2& h, Int_t xbin, Int_t ybin) const
62 {
63  return 0.5 * (histcont(h, xbin + 1, ybin) -
64  histcont(h, xbin - 1, ybin));
65 }
66 
67 inline Double_t Lau2DCubicSpline::dhistdy(
68  const TH2& h, Int_t xbin, Int_t ybin) const
69 {
70  return 0.5 * (histcont(h, xbin, ybin + 1) -
71  histcont(h, xbin, ybin - 1));
72 }
73 
75  const TH2& h, Int_t xbin, Int_t ybin) const
76 {
77  return 0.5 * (histcont(h, xbin - 1, ybin - 1) -
78  histcont(h, xbin + 1, ybin - 1) +
79  histcont(h, xbin + 1, ybin + 1) -
80  histcont(h, xbin - 1, ybin + 1));
81 }
82 
84  nBinsX(1 + h.GetNbinsX()), nBinsY(1 + h.GetNbinsY()),
85  binSizeX(h.GetXaxis()->GetBinWidth(1)),
86  binSizeY(h.GetYaxis()->GetBinWidth(1)),
87  xmin(h.GetXaxis()->GetBinCenter(1) - binSizeX),
88  xmax(h.GetXaxis()->GetBinCenter(nBinsX - 1) + binSizeX),
89  ymin(h.GetYaxis()->GetBinCenter(1) - binSizeY),
90  ymax(h.GetYaxis()->GetBinCenter(nBinsY - 1) + binSizeY),
91  coeffs(CoeffRecLen * nBinsX * nBinsY)
92 {
93  const TAxis* xaxis = h.GetXaxis();
94  const TAxis* yaxis = h.GetYaxis();
95  // verify that all bins have same size
96  for (Int_t i = 1; i < nBinsX; ++i) {
97  if (std::abs(xaxis->GetBinWidth(i) / binSizeX - 1.) > 1e-9) {
98  std::cerr << "ERROR in Lau2DCubicSpline constructor : the histogram has variable bin sizes." << std::endl;
99  gSystem->Exit(EXIT_FAILURE);
100  }
101  }
102  for (Int_t i = 1; i < nBinsY; ++i) {
103  if (std::abs(yaxis->GetBinWidth(i) / binSizeY - 1.) > 1e-9) {
104  std::cerr << "ERROR in Lau2DCubicSpline constructor : the histogram has variable bin sizes." << std::endl;
105  gSystem->Exit(EXIT_FAILURE);
106  }
107  }
108  // ok, go through histogram to precalculate the interpolation coefficients
109  // in rectangles between bin centres
110  //
111  // for that purpose, we map each of those rectangles to the unit square
112  for (Int_t j = -1; j < nBinsY - 1; ++j) {
113  for (Int_t i = -1; i < nBinsX - 1; ++i) {
114  const Double_t rhs[NCoeff] = {
115  // function values in bin centres
116  histcont(h, i, j),
117  histcont(h, i + 1, j),
118  histcont(h, i, j + 1),
119  histcont(h, i + 1, j + 1),
120  // df/dx in bin centres (finite difference approximation)
121  dhistdx(h, i, j),
122  dhistdx(h, i + 1, j),
123  dhistdx(h, i, j + 1),
124  dhistdx(h, i + 1, j + 1),
125  // df/dy in bin centres (finite difference approximation)
126  dhistdy(h, i, j),
127  dhistdy(h, i + 1, j),
128  dhistdy(h, i, j + 1),
129  dhistdy(h, i + 1, j + 1),
130  // d^2f/dxdy in bin centres (finite difference approximation)
131  d2histdxdy(h, i, j),
132  d2histdxdy(h, i + 1, j),
133  d2histdxdy(h, i, j + 1),
134  d2histdxdy(h, i + 1, j + 1)
135  };
136  // work out solution - strange array placement is due to the fact
137  // that terms with x/y to high powers can be small, so they should
138  // be added up first during evaluation to avoid cancellation
139  // issues; at the same time you want to access them in order to
140  // not confuse the CPU cache, so they're stored back to front
141  //
142  // a_00 ... a_30
143  coeff(1 + i, 1 + j, 15) = rhs[0];
144  coeff(1 + i, 1 + j, 14) = rhs[4];
145  coeff(1 + i, 1 + j, 13) =
146  3. * (-rhs[0] + rhs[1]) - 2. * rhs[4] - rhs[5];
147  coeff(1 + i, 1 + j, 12) =
148  2. * (rhs[0] - rhs[1]) + rhs[4] + rhs[5];
149  // a_31 ... a_31
150  coeff(1 + i, 1 + j, 11) = rhs[8];
151  coeff(1 + i, 1 + j, 10) = rhs[12];
152  coeff(1 + i, 1 + j, 9) =
153  3. * (-rhs[8] + rhs[9]) - 2. * rhs[12] - rhs[13];
154  coeff(1 + i, 1 + j, 8) =
155  2. * (rhs[8] - rhs[9]) + rhs[12] + rhs[13];
156  // a_02 ... a_32
157  coeff(1 + i, 1 + j, 7) =
158  3. * (-rhs[0] + rhs[2]) - 2. * rhs[8] - rhs[10];
159  coeff(1 + i, 1 + j, 6) =
160  3. * (-rhs[4] + rhs[6]) - 2. * rhs[12] - rhs[14];
161  coeff(1 + i, 1 + j, 5) =
162  9. * (rhs[0] - rhs[1] - rhs[2] + rhs[3]) +
163  6. * (rhs[4] - rhs[6] + rhs[8] - rhs[9]) + 4. * rhs[12] +
164  3. * (rhs[5] - rhs[7] + rhs[10] - rhs[11]) +
165  2. * (rhs[13] + rhs[14]) + rhs[15];
166  coeff(1 + i, 1 + j, 4) =
167  6. * (-rhs[0] + rhs[1] + rhs[2] - rhs[3]) +
168  4. * (-rhs[8] + rhs[9]) +
169  3. * (-rhs[4] - rhs[5] + rhs[6] + rhs[7]) +
170  2. * (-rhs[10] + rhs[11] - rhs[12] - rhs[13]) -
171  rhs[14] - rhs[15];
172  // a_03 ... a_33
173  coeff(1 + i, 1 + j, 3) =
174  2. * (rhs[0] - rhs[2]) + rhs[8] + rhs[10];
175  coeff(1 + i, 1 + j, 2) =
176  2. * (rhs[4] - rhs[6]) + rhs[12] + rhs[14];
177  coeff(1 + i, 1 + j, 1) =
178  6. * (-rhs[0] + rhs[1] + rhs[2] - rhs[3]) +
179  4. * (-rhs[4] + rhs[6]) +
180  3. * (-rhs[8] + rhs[9] - rhs[10] + rhs[11]) +
181  2. * (- rhs[5] + rhs[7] - rhs[12] - rhs[14]) -
182  rhs[13] - rhs[15];
183  coeff(1 + i, 1 + j, 0) =
184  4. * (rhs[0] - rhs[1] - rhs[2] + rhs[3]) +
185  2. * (rhs[4] + rhs[5] - rhs[6] - rhs[7] +
186  rhs[8] - rhs[9] + rhs[10] - rhs[11]) +
187  rhs[12] + rhs[13] + rhs[14] + rhs[15];
188  // coeff(1 + i, 1 + j, 17) contains integral of function over the
189  // square in "unit square coordinates", i.e. neglecting bin widths
190  // this is done to help speed up calculations of 2D integrals
191  Double_t sum = 0.;
192  for (Int_t k = 0; k < NCoeff; ++k)
193  sum += coeff(1 + i, 1 + j, k) /
194  Double_t((4 - (k % 4)) * (4 - (k / 4)));
195  coeff(1 + i, 1 + j, NCoeff) = sum;
196  }
197  }
198 }
199 
200 Double_t Lau2DCubicSpline::evaluate(Double_t x, Double_t y) const
201 {
202  // protect against NaN and out of range
203  if (x <= xmin || x >= xmax || y <= ymin || y >= ymax || x != x || y != y)
204  return 0.;
205  // find the bin in question
206  const Int_t binx = Int_t(Double_t(nBinsX) * (x - xmin) / (xmax - xmin));
207  const Int_t biny = Int_t(Double_t(nBinsY) * (y - ymin) / (ymax - ymin));
208  // get low edge of bin
209  const Double_t xlo = Double_t(nBinsX - binx) / Double_t(nBinsX) * xmin +
210  Double_t(binx) / Double_t(nBinsX) * xmax;
211  const Double_t ylo = Double_t(nBinsY - biny) / Double_t(nBinsY) * ymin +
212  Double_t(biny) / Double_t(nBinsY) * ymax;
213  // normalise to coordinates in unit sqare
214  const Double_t hx = (x - xlo) / binSizeX;
215  const Double_t hy = (y - ylo) / binSizeY;
216  // monomials
217  const Double_t hxton[4] = { hx * hx * hx, hx * hx, hx, 1. };
218  const Double_t hyton[4] = { hy * hy * hy, hy * hy, hy, 1. };
219  // sum up
220  Double_t retVal = 0.;
221  for (Int_t k = 0; k < NCoeff; ++k)
222  retVal += coeff(binx, biny, k) * hxton[k % 4] * hyton[k / 4];
223 
224  return retVal;
225 }
226 
228 {
229  return evalXY(xmin,xmax,ymin,ymax);
230 }
231 
232 Double_t Lau2DCubicSpline::analyticalIntegral(Double_t x1, Double_t x2, Double_t y1, Double_t y2) const
233 {
234  if(y1==y2) return evalX(x1, x2, y1);
235  if(x1==x2) return evalY(x1, y1, y2);
236  return evalXY(x1, x2, y1, y2);
237 }
238 
239 Double_t Lau2DCubicSpline::evalX(Double_t x1, Double_t x2, Double_t y) const
240 {
241  // protect against NaN
242  if (x1 != x1 || x2 != x2 || y != y) return 0.;
243  // find the bin in question
244  const Int_t biny = Int_t(Double_t(nBinsY) * (y - ymin) / (ymax - ymin));
245  // get low edge of bin
246  const Double_t ylo = Double_t(nBinsY - biny) / Double_t(nBinsY) * ymin +
247  Double_t(biny) / Double_t(nBinsY) * ymax;
248  // normalise to coordinates in unit sqare
249  const Double_t hy = (y - ylo) / binSizeY;
250  // monomials
251  const Double_t hyton[4] = { hy * hy * hy, hy * hy, hy, 1. };
252  // integral
253  Double_t sum = 0.;
254  for (Int_t binx = 0; binx < nBinsX; ++binx) {
255  // get low/high edge of bin
256  const Double_t xhi = Double_t(nBinsX - binx - 1) / Double_t(nBinsX) * xmin +
257  Double_t(binx + 1) / Double_t(nBinsX) * xmax;
258  if (xhi < x1) continue;
259  const Double_t xlo = Double_t(nBinsX - binx) / Double_t(nBinsX) * xmin +
260  Double_t(binx) / Double_t(nBinsX) * xmax;
261  if (xlo > x2) break;
262  // work out integration range
263  const Double_t a = ((xlo > x1) ? 0. : (x1 - xlo)) / binSizeX;
264  const Double_t b = ((xhi < x2) ? binSizeX : (x2 - xlo)) / binSizeX;
265  // integrated monomials
266  const Double_t hxton[4] = { 0.25 * (b * b * b * b - a * a * a * a),
267  (b * b * b - a * a * a) / 3., 0.5 * (b * b - a * a), b - a };
268  Double_t lsum = 0.;
269  for (Int_t k = 0; k < NCoeff; ++k)
270  lsum += coeff(binx, biny, k) * hxton[k % 4] * hyton[k / 4];
271  sum += lsum;
272  }
273  // move from unit square coordinates to user coordinates
274  return sum * binSizeX;
275 }
276 
277 Double_t Lau2DCubicSpline::evalY(Double_t x, Double_t y1, Double_t y2) const
278 {
279  // protect against NaN
280  if (x != x || y1 != y1 || y2 != y2) return 0.;
281  // find the bin in question
282  const Int_t binx = Int_t(Double_t(nBinsX) * (x - xmin) / (xmax - xmin));
283  // get low edge of bin
284  const Double_t xlo = Double_t(nBinsX - binx) / Double_t(nBinsX) * xmin +
285  Double_t(binx) / Double_t(nBinsX) * xmax;
286  // normalise to coordinates in unit sqare
287  const Double_t hx = (x - xlo) / binSizeX;
288  // monomials
289  const Double_t hxton[4] = { hx * hx * hx, hx * hx, hx, 1. };
290  // integral
291  Double_t sum = 0.;
292  for (Int_t biny = 0; biny < nBinsY; ++biny) {
293  // get low/high edge of bin
294  const Double_t yhi = Double_t(nBinsY - biny - 1) / Double_t(nBinsY) * ymin +
295  Double_t(biny + 1) / Double_t(nBinsY) * ymax;
296  if (yhi < y1) continue;
297  const Double_t ylo = Double_t(nBinsY - biny) / Double_t(nBinsY) * ymin +
298  Double_t(biny) / Double_t(nBinsY) * ymax;
299  if (ylo > y2) break;
300  // work out integration range
301  const Double_t a = ((ylo > y1) ? 0. : (y1 - ylo)) / binSizeY;
302  const Double_t b = ((yhi < y2) ? binSizeY : (y2 - ylo)) / binSizeY;
303  // integrated monomials
304  const Double_t hyton[4] = { 0.25 * (b * b * b * b - a * a * a * a),
305  (b * b * b - a * a * a) / 3., 0.5 * (b * b - a * a), b - a };
306  Double_t lsum = 0.;
307  for (Int_t k = 0; k < NCoeff; ++k)
308  lsum += coeff(binx, biny, k) * hxton[k % 4] * hyton[k / 4];
309  sum += lsum;
310  }
311  // move from unit square coordinates to user coordinates
312  return sum * binSizeY;
313 }
314 
316  Double_t x1, Double_t x2, Double_t y1, Double_t y2) const
317 {
318  // protect against NaN
319  if (x1 != x1 || x2 != x2 || y1 != y1 || y2 != y2) return 0.;
320  // integral
321  Double_t sum = 0.;
322  for (Int_t biny = 0; biny < nBinsY; ++biny) {
323  // get low/high edge of bin
324  const Double_t yhi = Double_t(nBinsY - biny - 1) / Double_t(nBinsY) * ymin +
325  Double_t(biny + 1) / Double_t(nBinsY) * ymax;
326  if (yhi < y1) continue;
327  const Double_t ylo = Double_t(nBinsY - biny) / Double_t(nBinsY) * ymin +
328  Double_t(biny) / Double_t(nBinsY) * ymax;
329  if (ylo > y2) break;
330  // work out integration range
331  const Double_t ay = ((ylo > y1) ? 0. : (y1 - ylo)) / binSizeY;
332  const Double_t by = ((yhi < y2) ? binSizeY : (y2 - ylo)) / binSizeY;
333  const Bool_t yFullyContained = std::abs(by - ay - 1.0) < 1e-15;
334  // integrated monomials
335  const Double_t hyton[4] = {
336  0.25 * (by * by * by * by - ay * ay * ay * ay),
337  (by * by * by - ay * ay * ay) / 3., 0.5 * (by * by - ay * ay),
338  by - ay };
339  for (Int_t binx = 0; binx < nBinsX; ++binx) {
340  // get low/high edge of bin
341  const Double_t xhi = Double_t(nBinsX - binx - 1) / Double_t(nBinsX) * xmin +
342  Double_t(binx + 1) / Double_t(nBinsX) * xmax;
343  if (xhi < x1) continue;
344  const Double_t xlo = Double_t(nBinsX - binx) / Double_t(nBinsX) * xmin +
345  Double_t(binx) / Double_t(nBinsX) * xmax;
346  if (xlo > x2) break;
347  // work out integration range
348  const Double_t ax = ((xlo > x1) ? 0. : (x1 - xlo)) / binSizeX;
349  const Double_t bx = ((xhi < x2) ? binSizeX : (x2 - xlo)) / binSizeX;
350  const Bool_t xFullyContained = std::abs(bx - ax - 1.0) < 1e-15;
351  if (xFullyContained && yFullyContained) {
352  // for fully contained bins, we have cached the integral
353  sum += coeff(binx, biny, NCoeff);
354  continue;
355  }
356  // integrated monomials
357  const Double_t hxton[4] = {
358  0.25 * (bx * bx * bx * bx - ax * ax * ax * ax),
359  (bx * bx * bx - ax * ax * ax) / 3., 0.5 * (bx * bx - ax * ax),
360  bx - ax };
361  // integrate over bin
362 
363  Double_t lsum = 0.;
364  for (Int_t k = 0; k < NCoeff; ++k)
365  lsum += coeff(binx, biny, k) * hxton[k % 4] * hyton[k / 4];
366  sum += lsum;
367  }
368  }
369  // move from unit square coordinates to user coordinates
370  return sum * binSizeX * binSizeY;
371 }
Double_t dhistdx(const TH2 &h, Int_t xbin, Int_t ybin) const
Get d/dx finite difference in a given bin from a histogram.
Double_t histcont(const TH2 &h, Int_t xbin, Int_t ybin) const
Get contents of a given bin from a histogram.
Double_t xmax
Maximum x value.
virtual Double_t analyticalIntegral() const
Evaluate analytical integral across the whole function.
Double_t d2histdxdy(const TH2 &h, Int_t xbin, Int_t ybin) const
Get d^2/dydx finite difference in a given bin from a histogram.
Int_t nBinsX
Number of bins in x axis.
Double_t evalX(Double_t x1, Double_t x2, Double_t y) const
Evaluate integral over x at a given y from (x1, y) to (x2, y)
Int_t nBinsY
Number of bins in y axis.
virtual Double_t evaluate(Double_t x, Double_t y) const
Evaluate the function at given point.
Double_t xmin
Minimum x value.
Lau2DCubicSpline(const TH2 &h)
Constructor from histogram.
Double_t binSizeX
Bin size in x.
virtual ~Lau2DCubicSpline()
Destructor.
Double_t evalXY(Double_t x1, Double_t x2, Double_t y1, Double_t y2) const
Evaluate integral over x and y from (x1, y1) to (x2, y2)
Double_t ymax
Maximum y value.
Double_t dhistdy(const TH2 &h, Int_t xbin, Int_t ybin) const
Get d/dy finite difference in a given bin from a histogram.
Double_t ymin
Minimum y value.
Double_t binSizeY
Bin size in y.
File containing declaration of Lau2DCubicSpline class.
Double_t evalY(Double_t x, Double_t y1, Double_t y2) const
Evaluate integral over y at a given x from (x, y1) to (x, y2)
const Double_t & coeff(Int_t binx, Int_t biny, Int_t theCoeff) const
Const access to coefficients.