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