ATLAS Offline Software
Loading...
Searching...
No Matches
PufitUtils.cxx
Go to the documentation of this file.
1/*
2 * Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3 */
4
6#include <algorithm>
7#include <numeric>
8#include <stdexcept>
9
10namespace HLT
11{
12 namespace MET
13 {
14 namespace PufitUtils
15 {
16
17 CovarianceSum::CovarianceSum() : sum(Eigen::Vector2d::Zero()),
18 covariance(Eigen::Matrix2d::Zero())
19 {
20 }
21
23 const Eigen::Vector2d &sum,
24 const Eigen::Matrix2d &covariance) : sum(sum), covariance(covariance) {}
25
27 const SignedKinematics &kin,
28 double sigma)
29 {
30 Eigen::Vector2d cosSin(kin.cosPhi(), kin.sinPhi());
31 sum += Eigen::Vector2d(kin.px(), kin.py());
32 covariance += sigma * cosSin * cosSin.transpose();
33 return *this;
34 }
35
37 const std::vector<double> &sorted,
38 double trimFraction,
39 double &mean,
40 double &variance)
41 {
42 // The number to trim from each side
43 std::size_t nTrim = sorted.size() * (1 - trimFraction) / 2;
44
45 mean = std::accumulate(sorted.begin() + nTrim, sorted.end() - nTrim, 0.);
46 mean /= (sorted.size() - 2 * nTrim);
47
48 // Now get the variance
49 variance = 0;
50 for (std::size_t ii = 0; ii < sorted.size() - nTrim; ++ii)
51 {
52 double val = sorted.at(ii) - mean;
53 variance += val * val * (ii < nTrim ? 2 : 1);
54 }
55 variance /= sorted.size();
56 }
57
59 const PufitGrid &grid,
60 double trimFraction,
61 double &mean,
62 double &variance)
63 {
64 std::vector<double> sorted;
65 sorted.reserve(grid.nTowers());
66 for (const PufitGrid::Tower &tower : grid)
67 sorted.insert(
68 std::lower_bound(sorted.begin(), sorted.end(), tower.sumEt()),
69 tower.sumEt());
70 trimmedMeanAndVariance(sorted, trimFraction, mean, variance);
71 }
72
74 const PufitGrid &grid,
75 double &mean,
76 double &variance)
77 {
78 double sum = 0;
79 double squaredSum = 0;
80 std::size_t n = 0;
81 for (const PufitGrid::Tower &tower : grid)
82 {
83 if (tower.masked())
84 continue;
85 ++n;
86 sum += tower.sumEt();
87 squaredSum += tower.sumEt() * tower.sumEt();
88 }
89 //if n is still zero here, something went very wrong
90 if (n == 0)[[unlikely]]{
91 throw std::runtime_error("unmaskedMeanAndVariance: n is zero in denominator.");
92 }
93 mean = sum / n;
94 // Note that this could result in catastrophic cancellation in some cases.
95 // However the amount of precision present in a double is around 10^15 so we
96 // can afford to lose some significant figures
97 variance = squaredSum / n - mean * mean;
98 }
99
101 {
103 double maxSum = 0;
104 for (std::size_t d = 0; d < 4; ++d)
105 {
106 double sum = 0;
107 for (const PufitGrid::Tower &tower : grids[GridDisplacement(d)])
108 if (tower.masked())
109 sum += tower.sumEt();
110 if (sum > maxSum)
111 {
112 maximum = GridDisplacement(d);
113 maxSum = sum;
114 }
115 }
116 return maximum;
117 }
118
119 Eigen::VectorXd pufit(
120 const Eigen::Vector2d &pileupSum,
121 const Eigen::Matrix2d &pileupCovariance,
122 const Eigen::VectorXd &towerExpectations,
123 const Eigen::VectorXd &towerVariances,
124 const Eigen::VectorXd &correctionDirections,
125 double constraintImportance)
126 {
127 // Keep track of the number of corrections to derive
128 std::size_t nCorr = correctionDirections.size();
129 if (nCorr == 0)
130 // No need to do anything to derive corrections to nothing
131 return {};
132 // Turn the directions into cos/sin pairs
133 Eigen::Matrix<double, 2, Eigen::Dynamic> cosSin(2, nCorr);
134 cosSin.row(0) = correctionDirections.array().cos();
135 cosSin.row(1) = correctionDirections.array().sin();
136 return pufit(
137 pileupSum,
138 pileupCovariance,
139 towerExpectations,
140 towerVariances,
141 cosSin,
142 constraintImportance);
143 }
144
145 Eigen::VectorXd pufit(
146 const Eigen::Vector2d &pileupSum,
147 const Eigen::Matrix2d &pileupCovariance,
148 double towerMean,
149 double towerVariance,
150 const Eigen::VectorXd &correctionDirections,
151 double constraintImportance)
152 {
153 std::size_t nCorr = correctionDirections.size();
154 return pufit(
155 pileupSum,
156 pileupCovariance,
157 Eigen::VectorXd::Constant(nCorr, towerMean),
158 Eigen::VectorXd::Constant(nCorr, towerVariance),
159 correctionDirections,
160 constraintImportance);
161 }
162
163 Eigen::VectorXd pufit(
164 const Eigen::Vector2d &pileupSum,
165 const Eigen::Matrix2d &pileupCovariance,
166 const Eigen::VectorXd &towerExpectations,
167 const Eigen::VectorXd &towerVariances,
168 const Eigen::Matrix<double, 2, Eigen::Dynamic> &cosSin,
169 double constraintImportance)
170 {
171 // Keep track of the number of corrections to derive
172 std::size_t nCorr = cosSin.cols();
173 if (nCorr == 0)
174 // No need to do anything to derive corrections to nothing
175 return {};
176 // Eigen internally uses somewhat complicated 'expression templates' to hold
177 // the mathematics of a matrix calculation without actually doing the loop.
178 // This allows it to avoid doing more loops than it actually needs to. It
179 // will do this by default unless you force it to evaluate an expression by
180 // binding it to a vector matrix type. Therefore we use 'auto' to store the
181 // individual expression pieces, rather than forcing an early evaluation.
182 if (pileupCovariance.determinant() == 0)
183 {
184 // In very rare circumstances the pileup covariance can be 0. This essentially means that
185 // there is nothing interpreted as pileup. The calculation will fail in this case so we
186 // cannot run the fit. Just return the expectations
187 return towerExpectations;
188 }
189 auto constants =
190 // Constant part derived from the uniformity constraint
191 towerExpectations.cwiseQuotient(towerVariances) -
192 // Constant part derived from the pileup balance constraint
193 constraintImportance * cosSin.transpose() * pileupCovariance.inverse() * pileupSum;
194 Eigen::MatrixXd diagonal = towerVariances.cwiseInverse().asDiagonal();
195 auto coeffMatrix =
196 // Matrix part derived from the uniformity constraint
197 diagonal +
198 // Matrix part derived from the pileup balance constraint
199 constraintImportance * (cosSin.transpose() * pileupCovariance.inverse() * cosSin);
200 // Now return the actual corrections
201 return coeffMatrix.inverse() * constants;
202 }
203
204 Eigen::VectorXd pufit(
205 const Eigen::Vector2d &pileupSum,
206 const Eigen::Matrix2d &pileupCovariance,
207 double towerMean,
208 double towerVariance,
209 const Eigen::Matrix<double, 2, Eigen::Dynamic> &cosSin,
210 double constraintImportance)
211 {
212 std::size_t nCorr = cosSin.cols();
213 return pufit(
214 pileupSum,
215 pileupCovariance,
216 Eigen::VectorXd::Constant(nCorr, towerMean),
217 Eigen::VectorXd::Constant(nCorr, towerVariance),
218 cosSin,
219 constraintImportance);
220 }
221
222 std::vector<SignedKinematics> pufit(
223 const Eigen::Vector2d &pileupSum,
224 const Eigen::Matrix2d &pileupCovariance,
225 const std::vector<double> &towerExpectations,
226 const std::vector<double> &towerVariances,
227 const std::vector<SignedKinematics> &toCorrect,
228 double constraintImportance)
229 {
230 // Keep track of the number of corrections to derive
231 std::size_t nCorr = toCorrect.size();
232 if (nCorr == 0)
233 // No need to do anything to derive corrections to nothing
234 return {};
235
236 // Get the cos-sin matrix
237 Eigen::Matrix<double, 2, Eigen::Dynamic> cosSin(2, nCorr);
238 Eigen::VectorXd vecTowerExpectations(nCorr);
239 Eigen::VectorXd vecTowerVariances(nCorr);
240 for (std::size_t ii = 0; ii < nCorr; ++ii)
241 {
242 cosSin(0, ii) = toCorrect.at(ii).cosPhi();
243 cosSin(1, ii) = toCorrect.at(ii).sinPhi();
244 vecTowerExpectations(ii) = towerExpectations.at(ii);
245 vecTowerVariances(ii) = towerVariances.at(ii);
246 }
247 Eigen::VectorXd corrections = pufit(
248 pileupSum,
249 pileupCovariance,
250 vecTowerExpectations,
251 vecTowerVariances,
252 cosSin,
253 constraintImportance);
254 // Now convert these into kinematics
255 std::vector<SignedKinematics> kinCorrections;
256 kinCorrections.reserve(nCorr);
257 for (std::size_t ii = 0; ii < nCorr; ++ii)
258 kinCorrections.push_back(SignedKinematics::fromEtEtaPhi(
259 -corrections.coeff(ii),
260 toCorrect.at(ii).eta(),
261 toCorrect.at(ii).phi()));
262 return kinCorrections;
263 }
264 std::vector<SignedKinematics> pufit(
265 const Eigen::Vector2d &pileupSum,
266 const Eigen::Matrix2d &pileupCovariance,
267 double towerMean,
268 double towerVariance,
269 const std::vector<SignedKinematics> &toCorrect,
270 double constraintImportance)
271 {
272 // Keep track of the number of corrections to derive
273 std::size_t nCorr = toCorrect.size();
274 if (nCorr == 0)
275 // No need to do anything to derive corrections to nothing
276 return {};
277
278 return pufit(
279 pileupSum,
280 pileupCovariance,
281 std::vector<double>(nCorr, towerMean),
282 std::vector<double>(nCorr, towerVariance),
283 toCorrect,
284 constraintImportance);
285 }
286 } // namespace PufitUtils
287 } // namespace MET
288} // namespace HLT
std::size_t nTowers() const
The number of bins.
Describes a single element of the grid.
Definition PufitGrid.h:48
Bins energy deposits into a grid.
Definition PufitGrid.h:38
Class to describe the kinematics of an object that can have negative energies.
static SignedKinematics fromEtEtaPhi(double et, double eta, double phi)
Factory function to construct from et, eta, phi (massless)
double sinPhi() const
Provide accessors for sin and cos phi.
void mean(std::vector< double > &bins, std::vector< double > &values, const std::vector< std::string > &files, const std::string &histname, const std::string &tplotname, const std::string &label="")
void trimmedMeanAndVariance(const std::vector< double > &sorted, double trimFraction, double &mean, double &variance)
Calculate the trimmed mean and variance for a vector of tower sumEts.
Eigen::VectorXd pufit(const Eigen::Vector2d &pileupSum, const Eigen::Matrix2d &pileupCovariance, const Eigen::VectorXd &towerExpectations, const Eigen::VectorXd &towerVariances, const Eigen::VectorXd &correctionDirections, double constraintImportance)
Perform the pile-up fit.
void unmaskedMeanAndVariance(const PufitGrid &grid, double &mean, double &variance)
Calculate the mean and variance of unmasked towers.
GridDisplacement selectGrid(const PufitGridSet &grids)
Select the grid with the highest masked sumEt.
GridDisplacement
Enum to describe the positioning of the grid.
@ NoDisplacement
The grid is not shifted.
It used to be useful piece of code for replacing actual SG with other store of similar functionality ...
#define unlikely(x)
Helper struct to contain a full set of grids.
Definition PufitGrid.h:240
Eigen::Matrix2d covariance
The covariance matrix.
Definition PufitUtils.h:43
CovarianceSum & add(const SignedKinematics &kin, double sigma)
Add a new contribution to the sum.
Eigen::Vector2d sum
The sum.
Definition PufitUtils.h:41
CovarianceSum()
Default constructor - zero initialize everything.