ATLAS Offline Software
interpolate.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
3 */
13 #include <vector>
14 #include <cassert>
15 #include <cmath>
16 #include <algorithm>
17 
18 
19 namespace {
20 
21 
22 // Test to see if two numbers are significantly different.
23 bool is_different (float a, float b)
24 {
25  float den = std::abs (a) + std::abs (b);
26  if (den == 0) return false;
27  return std::abs ((a-b)/den) > 1e-6;
28 }
29 
30 
31 // Compare the first column of an array with an element.
32 struct xcompare
33 {
34  bool operator() (const CaloRec::Array<1>& a, CaloRec::Arrayelt b)
35  {
36  return a[0] < b;
37  }
38 };
39 
40 
41 } // anonymous namespace.
42 
43 
44 namespace CaloClusterCorr {
45 
46 
76  float x,
77  unsigned int degree,
78  unsigned int ycol /*= 1*/,
79  const CaloRec::Array<1>& regions /*= CaloRec::Array<1>()*/,
80  int n_points /*= -1*/,
81  bool fixZero /*= false*/)
82 {
83  const int xcol = 0;
84 
85  // Check arguments.
86  if (n_points < 0)
87  n_points = static_cast<int> (a.size());
88  assert (n_points >= 2 && degree >= 1);
89  degree = std::min (degree, static_cast<unsigned int> (n_points) - 1);
90 
91  // Find subscripts of the input value in the input arrays.
92  unsigned int ix = std::lower_bound (a.begin(), a.begin()+n_points, x,
93  xcompare()) -
94  a.begin();
95  unsigned int ir = std::lower_bound (regions.begin(), regions.end(), x) -
96  regions.begin();
97 
98  // Number of points to try for.
99  // Either degree+1 or degree+2, whichever is even,
100  // to give the same number of points on each side.
101  // If we run up against an edge or a boundary, we'll
102  // fall back to using just degree+1 points (or fewer if we can't
103  // even get that many).
104  // If we end up using degree+2 points, we'll do two interpolations
105  // of degree degree and average them.
106  unsigned int npts = degree + 2 - (degree%2);
107 
108  // If we run up against the edge of a region boundary,
109  // we'll want to add a pseudopoint right at the boundary
110  // (copying the point closest to the boundary) instead of the
111  // point farthest away from it.
112  bool extralo = false;
113  bool extrahi = false;
114 
115  // Starting point index, not considering edges or boundaries.
116  int ilo = ix - npts/2;
117 
118  // Make sure this point is within the array range and has not
119  // crossed a region boundary.
120  if (ilo < 0) {
121  ilo = 0;
122  npts = degree+1;
123  }
124  while (ilo < n_points &&
125  ir > 0 && a[ilo][xcol] < regions[ir-1])
126  {
127  ++ilo;
128  npts = degree+1;
129  extralo = true;
130  }
131 
132  // Same deal for the right hand edge.
133  // ihi is one past the last point to use.
134  bool himoved = false;
135  int ihi = ilo + npts;
136  if (ihi > n_points) {
137  ihi = n_points;
138  npts = degree+1;
139  himoved = true;
140  }
141  while (ihi > 0 && ir < regions.size() && a[ihi-1][xcol] >= regions[ir]) {
142  --ihi;
143  npts = degree+1;
144  himoved = true;
145  extrahi = true;
146  }
147 
148  // The adjustment due to ihi may have moved the low point of the range
149  // back over a boundary. If so, we lose points from the fit.
150  bool lomoved = false;
151  ilo = ihi - npts;
152  if (ilo < 0) {
153  ilo = 0;
154  lomoved = true;
155  }
156  while (ilo < n_points &&
157  ir > 0 && a[ilo][xcol] < regions[ir-1])
158  {
159  ++ilo;
160  extralo = true;
161  lomoved = true;
162  }
163 
164  // Copy the points we're going to use to arrays t and d.
165  // t gets the x coordinates, d gets the y coordinates.
166  // Note that the order doesn't matter.
167  // Reserve two extra points in case we end up adding some.
168  npts = ihi - ilo;
169  std::vector<float> t;
170  t.reserve (npts+2);
171  std::vector<float> d;
172  d.reserve (npts+2);
173 
174  // Add the pseudopoints, if needed, removing a point from the other end.
175  // Be careful not to duplicate a point if there's already one
176  // right at the boundary.
177  if (extralo && is_different (a[ilo][xcol], regions[ir-1])) {
178  if (!himoved)
179  --ihi;
180  else
181  ++npts;
182  t.push_back (regions[ir-1]);
183  d.push_back (a[ilo][ycol]);
184  }
185  if (extrahi && is_different (a[ihi-1][xcol], regions[ir])) {
186  if (!lomoved)
187  ++ilo;
188  else
189  ++npts;
190  t.push_back (regions[ir]);
191  d.push_back (a[ihi-1][ycol]);
192  }
193 
194  // Add the rest if the points.
195  for (; ilo < ihi; ++ilo) {
196  t.push_back (a[ilo][xcol]);
197  d.push_back (a[ilo][ycol]);
198  }
199 
200  // Now figure out the interpolation degree we're really going to use.
201  assert (t.size() == npts);
202  assert (d.size() == npts);
203  degree = std::min (degree, npts-1);
204 
205  // Option to remove zeros in the interpolation table, by averaging
206  // adjacent points. Used to handle a couple bad points in the rfac-v5
207  // table.
208  if (fixZero) {
209  for (size_t i = 1; i < npts-1; i++) {
210  if (d[i] == 0) {
211  d[i] = (d[i-1] + d[i+1])/2;
212  }
213  }
214  }
215 
216  // True if we're averaging together two interpolations
217  // (to get a symmetric range).
218  bool extra = npts != degree+1;
219 
220  // If we're averaging two interpolations, we need to be sure the
221  // two extreme points are at the end of the table.
222  // (If extra is true, extrahi and extralo must be false.)
223  if (extra) {
224  std::swap (t[0], t[npts-2]);
225  std::swap (d[0], d[npts-2]);
226  }
227 
228  // Here we start the actual interpolation.
229  // Replace d by the leading diagonal of a divided-difference table,
230  // supplemented by an extra line if extra is true.
231  for (unsigned int l=0; l<degree; l++) {
232  if (extra)
233  d[degree+1] = (d[degree+1]-d[degree-1])/(t[degree+1]-t[degree-1-l]);
234  for (unsigned int i = degree; i > l; --i)
235  d[i] = (d[i]-d[i-1])/(t[i]-t[i-1-l]);
236  }
237 
238  // Evaluate the Newton interpolation formula at x, averaging
239  // two values of the last difference if extra is true.
240  float sum = d[degree];
241  if (extra)
242  sum = 0.5*(sum+d[degree+1]);
243  for (int j = degree-1; j >= 0; --j)
244  sum = d[j] + (x - t[j]) * sum;
245 
246  return sum;
247 }
248 
249 
250 } // namespace CaloClusterCorrection
251 
hist_file_dump.d
d
Definition: hist_file_dump.py:137
CxxUtils::Array::size
unsigned int size(unsigned int dim=0) const
Return the size of the array along one dimension.
UploadAMITag.l
list l
Definition: UploadAMITag.larcaf.py:158
read_hist_ntuple.t
t
Definition: read_hist_ntuple.py:5
CaloClusterCorr
Definition: CaloClusterCorrectionCommon.h:22
x
#define x
CxxUtils::Array::end
const_iterator end() const
Return an iterator pointing past the end of the container.
CalibDbCompareRT.n_points
n_points
Definition: CalibDbCompareRT.py:76
convertTimingResiduals.sum
sum
Definition: convertTimingResiduals.py:55
lumiFormat.i
int i
Definition: lumiFormat.py:92
CxxUtils::Array
Read-only multidimensional array.
Definition: Control/CxxUtils/CxxUtils/Array.h:138
WriteCalibToCool.swap
swap
Definition: WriteCalibToCool.py:94
min
#define min(a, b)
Definition: cfImp.cxx:40
python.handimod.extra
int extra
Definition: handimod.py:522
CaloRec::Arrayelt
float Arrayelt
The type of an element of an Array.
Definition: Control/CxxUtils/CxxUtils/Arrayrep.h:26
plotBeamSpotMon.b
b
Definition: plotBeamSpotMon.py:77
ir
int ir
counter of the current depth
Definition: fastadd.cxx:49
DiTauMassTools::MaxHistStrategyV2::e
e
Definition: PhysicsAnalysis/TauID/DiTauMassTools/DiTauMassTools/HelperFunctions.h:26
a
TList * a
Definition: liststreamerinfos.cxx:10
CaloClusterCorr::interpolate
float interpolate(const CaloRec::Array< 2 > &a, float x, unsigned int degree, unsigned int ycol=1, const CaloRec::Array< 1 > &regions=CaloRec::Array< 1 >(), int n_points=-1, bool fixZero=false)
Polynomial interpolation in a table.
Definition: interpolate.cxx:75
CxxUtils::Array::begin
const_iterator begin() const
Return an iterator pointing at the beginning of the container.
interpolate.h
Polynomial interpolation in a table.
python.SystemOfUnits.degree
tuple degree
Definition: SystemOfUnits.py:106