ATLAS Offline Software
interpolate.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 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 {
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  if (n_points < 2 || degree < 1) {
89  std::abort();
90  }
91  degree = std::min (degree, static_cast<unsigned int> (n_points) - 1);
92 
93  // Find subscripts of the input value in the input arrays.
94  unsigned int ix = std::lower_bound (a.begin(), a.begin()+n_points, x,
95  xcompare()) -
96  a.begin();
97  unsigned int ir = std::lower_bound (regions.begin(), regions.end(), x) -
98  regions.begin();
99 
100  // Number of points to try for.
101  // Either degree+1 or degree+2, whichever is even,
102  // to give the same number of points on each side.
103  // If we run up against an edge or a boundary, we'll
104  // fall back to using just degree+1 points (or fewer if we can't
105  // even get that many).
106  // If we end up using degree+2 points, we'll do two interpolations
107  // of degree degree and average them.
108  unsigned int npts = degree + 2 - (degree%2);
109 
110  // If we run up against the edge of a region boundary,
111  // we'll want to add a pseudopoint right at the boundary
112  // (copying the point closest to the boundary) instead of the
113  // point farthest away from it.
114  bool extralo = false;
115  bool extrahi = false;
116 
117  // Starting point index, not considering edges or boundaries.
118  int ilo = ix - npts/2;
119 
120  // Make sure this point is within the array range and has not
121  // crossed a region boundary.
122  if (ilo < 0) {
123  ilo = 0;
124  npts = degree+1;
125  }
126  while (ilo < n_points &&
127  ir > 0 && a[ilo][xcol] < regions[ir-1])
128  {
129  ++ilo;
130  npts = degree+1;
131  extralo = true;
132  }
133 
134  // Same deal for the right hand edge.
135  // ihi is one past the last point to use.
136  bool himoved = false;
137  int ihi = ilo + npts;
138  if (ihi > n_points) {
139  ihi = n_points;
140  npts = degree+1;
141  himoved = true;
142  }
143  while (ihi > 0 && ir < regions.size() && a[ihi-1][xcol] >= regions[ir]) {
144  --ihi;
145  npts = degree+1;
146  himoved = true;
147  extrahi = true;
148  }
149 
150  // The adjustment due to ihi may have moved the low point of the range
151  // back over a boundary. If so, we lose points from the fit.
152  bool lomoved = false;
153  ilo = ihi - npts;
154  if (ilo < 0) {
155  ilo = 0;
156  lomoved = true;
157  }
158  while (ilo < n_points &&
159  ir > 0 && a[ilo][xcol] < regions[ir-1])
160  {
161  ++ilo;
162  extralo = true;
163  lomoved = true;
164  }
165 
166  // Copy the points we're going to use to arrays t and d.
167  // t gets the x coordinates, d gets the y coordinates.
168  // Note that the order doesn't matter.
169  // Reserve two extra points in case we end up adding some.
170  npts = ihi - ilo;
171  std::vector<float> t;
172  t.reserve (npts+2);
173  std::vector<float> d;
174  d.reserve (npts+2);
175 
176  // Add the pseudopoints, if needed, removing a point from the other end.
177  // Be careful not to duplicate a point if there's already one
178  // right at the boundary.
179  if (extralo && is_different (a[ilo][xcol], regions[ir-1])) {
180  if (!himoved)
181  --ihi;
182  else
183  ++npts;
184  t.push_back (regions[ir-1]);
185  d.push_back (a[ilo][ycol]);
186  }
187  if (extrahi && is_different (a[ihi-1][xcol], regions[ir])) {
188  if (!lomoved)
189  ++ilo;
190  else
191  ++npts;
192  t.push_back (regions[ir]);
193  d.push_back (a[ihi-1][ycol]);
194  }
195 
196  // Add the rest if the points.
197  for (; ilo < ihi; ++ilo) {
198  t.push_back (a[ilo][xcol]);
199  d.push_back (a[ilo][ycol]);
200  }
201 
202  // Now figure out the interpolation degree we're really going to use.
203  assert (t.size() == npts);
204  assert (d.size() == npts);
205  degree = std::min (degree, npts-1);
206 
207  // Option to remove zeros in the interpolation table, by averaging
208  // adjacent points. Used to handle a couple bad points in the rfac-v5
209  // table.
210  if (fixZero) {
211  for (size_t i = 1; i < npts-1; i++) {
212  if (d[i] == 0) {
213  d[i] = (d[i-1] + d[i+1])/2;
214  }
215  }
216  }
217 
218  // True if we're averaging together two interpolations
219  // (to get a symmetric range).
220  bool extra = npts != degree+1;
221 
222  // If we're averaging two interpolations, we need to be sure the
223  // two extreme points are at the end of the table.
224  // (If extra is true, extrahi and extralo must be false.)
225  if (extra) {
226  std::swap (t[0], t[npts-2]);
227  std::swap (d[0], d[npts-2]);
228  }
229 
230  // Here we start the actual interpolation.
231  // Replace d by the leading diagonal of a divided-difference table,
232  // supplemented by an extra line if extra is true.
233  for (unsigned int l=0; l<degree; l++) {
234  if (extra)
235  d[degree+1] = (d[degree+1]-d[degree-1])/(t[degree+1]-t[degree-1-l]);
236  for (unsigned int i = degree; i > l; --i)
237  d[i] = (d[i]-d[i-1])/(t[i]-t[i-1-l]);
238  }
239 
240  // Evaluate the Newton interpolation formula at x, averaging
241  // two values of the last difference if extra is true.
242  float sum = d[degree];
243  if (extra)
244  sum = 0.5*(sum+d[degree+1]);
245  for (int j = degree-1; j >= 0; --j)
246  sum = d[j] + (x - t[j]) * sum;
247 
248  return sum;
249 }
250 
251 
252 } // namespace CaloClusterCorrection
253 
AllowedVariables::e
e
Definition: AsgElectronSelectorTool.cxx:37
hist_file_dump.d
d
Definition: hist_file_dump.py:142
min
constexpr double min()
Definition: ap_fixedTest.cxx:26
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:157
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:75
convertTimingResiduals.sum
sum
Definition: convertTimingResiduals.py:55
lumiFormat.i
int i
Definition: lumiFormat.py:85
CxxUtils::Array
Read-only multidimensional array.
Definition: Control/CxxUtils/CxxUtils/Array.h:135
WriteCalibToCool.swap
swap
Definition: WriteCalibToCool.py:94
python.handimod.extra
int extra
Definition: handimod.py:521
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:76
ir
int ir
counter of the current depth
Definition: fastadd.cxx:49
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.
columnar::operator()
decltype(auto) operator()(ObjectId< CI, CM > id) const noexcept
Definition: ColumnAccessor.h:173
python.SystemOfUnits.degree
tuple degree
Definition: SystemOfUnits.py:121