ATLAS Offline Software
Loading...
Searching...
No Matches
interpolate.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
10
11
13#include <vector>
14#include <cassert>
15#include <cmath>
16#include <algorithm>
17
18
19namespace {
20
21
22// Test to see if two numbers are significantly different.
23bool 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.
32struct 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
44namespace 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
static Double_t a
static const std::vector< std::string > regions
#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)