ATLAS Offline Software
Loading...
Searching...
No Matches
Chi2Fitter.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
5
6#include "Acts/Definitions/TrackParametrization.hpp"
7#include "Acts/Definitions/Units.hpp"
8
9using namespace Acts::UnitLiterals;
10
11namespace MuonExperimental {
12
13// A projector used for scattering. By using Jacobian * phiThetaProjector one
14// gets only the derivatives for the variables phi and theta.
15const Acts::Matrix<eBoundSize, 2> phiThetaProjector = [] {
16 Acts::Matrix<eBoundSize, 2> m = Acts::Matrix<eBoundSize, 2>::Zero();
17 m(eBoundPhi, 0) = 1.0;
18 m(eBoundTheta, 1) = 1.0;
19 return m;
20}();
21
22// A projector used for scattering and energy loss. By using Jacobian *
23// phiThetaQOverPProjector one gets the derivatives for the variables phi,
24// theta, and q/p.
25const Acts::Matrix<eBoundSize, 3> phiThetaQOverPProjector = [] {
26 Acts::Matrix<eBoundSize, 3> m = Acts::Matrix<eBoundSize, 3>::Zero();
27 m(eBoundPhi, 0) = 1.0;
28 m(eBoundTheta, 1) = 1.0;
29 m(eBoundQOverP, 2) = 1.0;
30 return m;
31}();
32
33const Acts::Matrix<eBoundSize, 1> qOverPProjector = [] {
34 Acts::Matrix<eBoundSize, 1> m = Acts::Matrix<eBoundSize, 1>::Zero();
35 m(eBoundQOverP, 0) = 1.0;
36 return m;
37}();
38
40 const Eigen::VectorXd& deltaParamsExtended, const std::size_t stateIdx) {
41 if (m_scatterer.isValid()) {
42 m_scatPhi += deltaParamsExtended[stateIdx];
43 m_scatTheta += deltaParamsExtended[stateIdx + 1ul];
44 }
45 if (m_eloss.isValid()) {
46 const std::size_t eLossIdx = stateIdx + (nDim() - 1ul);
47 m_lostEnergy += deltaParamsExtended[eLossIdx];
48 }
49}
50
52 BoundTrackParameters& trackPars) {
53 if (m_scatterer.isValid()) {
54 trackPars.parameters()[eBoundPhi] += deltaPhi();
55 trackPars.parameters()[eBoundTheta] += deltaTheta();
56 }
57 if (m_eloss.isValid()) {
58 const ParticleHypothesis& hypot = trackPars.particleHypothesis();
59 const double nextE =
60 fastHypot(hypot.mass(), trackPars.absoluteMomentum()) - m_lostEnergy;
61 const double qOverPBefore = trackPars.parameters()[eBoundQOverP];
62 const double pAfter =
63 (hypot.mass() < nextE) ? fastCathetus(nextE, hypot.mass()) : 0;
64
65 if (pAfter <= Acts::s_epsilon) {
66 // Fitted loss exceeds the available momentum: leave the parameters
67 // untouched, and degrade to no contribution rather than a division by
68 // zero.
69 // ACTS_WARNING("Fitted energy loss exceeds available momentum. "
70 // << "Momentum before: " << trackPars.absoluteMomentum()
71 // << ", fitted loss: " << m_lostEnergy
72 // << ", fitted momentum after: " << pAfter);
73 m_qOverPderiv = 0.;
74 m_qOverPratio = 1.;
75 return;
76 }
77
78 trackPars.parameters()[eBoundQOverP] =
79 hypot.qOverP(pAfter, trackPars.charge());
80 // std::cout<<"Update q/p from " << qOverPBefore << " to " <<
81 // trackPars.parameters()[eBoundQOverP] << std::endl; cache the derivative
82 // of q/p with respect to energy loss for the Gx2f system and the qoverp
83 // ratio before and after
84 // we are gonna need them for the bVecror and aMatrix for measurements
85 // after material surfaces the residuals of which depend on the updated q/p
86 // value after the energy loss
87 m_qOverPderiv = trackPars.charge() / Acts::square(pAfter);
89 Acts::square(trackPars.parameters()[eBoundQOverP] / qOverPBefore);
90 }
91}
92
94 Gx2fSystem& extendedSystem, const double theta, const std::size_t stateIdx,
95 const Logger& logger) const {
96 if (m_scatterer.isValid()) {
97 const double invCovTheta = Acts::square(1. / m_scatterer.sigma());
98 const double sinThetaLoc = std::sin(theta);
99 const double invCovPhi = invCovTheta * Acts::square(sinThetaLoc);
100
101 // Phi contribution
102 extendedSystem.aMatrix()(stateIdx, stateIdx) += invCovPhi;
103 extendedSystem.bVector()(stateIdx, 0) -= invCovPhi * deltaPhi();
104 extendedSystem.chi2() += invCovPhi * Acts::square(deltaPhi());
105
106 // Theta Contribution
107 extendedSystem.aMatrix()(stateIdx + 1, stateIdx + 1) += invCovTheta;
108 extendedSystem.bVector()(stateIdx + 1, 0) -= invCovTheta * deltaTheta();
109 extendedSystem.chi2() += invCovTheta * Acts::square(deltaTheta());
110 ACTS_VERBOSE("Scattering contributions in contributionToGx2fSums:\n"
111 << " index: " << stateIdx << "-" << (stateIdx + 1) << " \n"
112 << " invCov: " << invCovPhi << "\n"
113 << " sinThetaLoc: " << sinThetaLoc << "\n"
114 << " Phi:\n"
115 << " scattering angle: " << deltaPhi() << "\n"
116 << " aMatrix contribution: " << invCovPhi << "\n"
117 << " bVector contribution: " << invCovPhi * deltaPhi()
118 << "\n"
119 << " chi2sum contribution: "
120 << invCovPhi * Acts::square(deltaPhi()) << "\n"
121 << " Theta:\n"
122 << " scattering angle: " << deltaTheta() << "\n"
123 << " aMatrix contribution: " << invCovTheta << "\n"
124 << " bVector contribution: "
125 << invCovTheta * deltaTheta() << "\n"
126 << " chi2sum contribution: "
127 << invCovTheta * Acts::square(deltaTheta()));
128 }
129
130 if (m_eloss.isValid()) {
131 const std::size_t eLossIdx = stateIdx + (nDim() - 1ul);
132 extendedSystem.aMatrix()(eLossIdx, eLossIdx) +=
133 Acts::square(1. / m_eloss.lostSigma());
134 extendedSystem.bVector()(eLossIdx, 0) -=
135 Acts::square(1. / m_eloss.lostSigma()) *
136 (m_lostEnergy - m_eloss.lostEnergy());
137 extendedSystem.chi2() += Acts::square(
138 (m_lostEnergy - m_eloss.lostEnergy()) / m_eloss.lostSigma());
139 // The energy loss constraint to require only positive energy loss for the
140 // free parameter
141 constexpr double Nepsilon = Acts::square(1. / 10._keV);
142 if (m_lostEnergy < 10._keV) {
143 ACTS_VERBOSE(
144 "Add penalty term to the chi2 to push the energy loss above zero");
145 extendedSystem.chi2() += Nepsilon * Acts::square(m_lostEnergy);
146 extendedSystem.bVector()(eLossIdx, 0) -= Nepsilon * m_lostEnergy;
147 extendedSystem.aMatrix()(eLossIdx, eLossIdx) += Nepsilon;
148 }
149 ACTS_VERBOSE("Energy loss contributions in contributionToGx2fSums:\n"
150 << " index: " << eLossIdx << " \n"
151 << " measured loss: " << m_eloss.lostEnergy() << "+-"
152 << (m_eloss.lostSigma()) << ", \n"
153 << " currently estimated loss: " << m_lostEnergy << "\n"
154 << " --> chi2 contribution: "
155 << Acts::square((m_lostEnergy - m_eloss.lostEnergy()) /
156 m_eloss.lostSigma())
157 << "\n"
158 << " --> aMatrix contribution: "
159 << Acts::square(1. / m_eloss.lostSigma()) << "\n"
160 << " --> bVector contribution: "
161 << Acts::square(1. / m_eloss.lostSigma()) *
162 (m_lostEnergy - m_eloss.lostEnergy()));
163 }
164}
165
168 const CalibrationContext&,
169 const Vector3&,
170 const Vector3&,
171 const Surface&) {
172 return ELossAtSurface{};
173}
174
176 BoundTrackParameters& params, const Eigen::VectorXd& deltaParamsExtended,
177 std::unordered_map<GeometryIdentifier, Gx2fMaterialProperties>& materialMap,
178 const std::vector<GeometryIdentifier>& geoIdVector) {
179 // update params
180 params.parameters() +=
181 deltaParamsExtended.topLeftCorner<eBoundSize, 1>().eval();
182
183 std::size_t deltaPosition{eBoundSize};
184 // update the scattering angles and energy loss.
185 for (const GeometryIdentifier& geoId : geoIdVector) {
186 const auto materialMapId = materialMap.find(geoId);
187 assert(materialMapId != materialMap.end() &&
188 "No material properties found for material surface.");
189
190 materialMapId->second.updateParameters(deltaParamsExtended, deltaPosition);
191 deltaPosition += materialMapId->second.nDim();
192 }
193}
194
195void updateGx2fCovarianceParams(BoundMatrix& fullCovariancePredicted,
196 Gx2fSystem& extendedSystem) {
197 const std::size_t nDof = extendedSystem.findRequiredNdf();
198 // make invertible
199 for (std::size_t i = 0; i < extendedSystem.nDims(); ++i) {
200 if (extendedSystem.aMatrix()(i, i) == 0.) {
201 extendedSystem.aMatrix()(i, i) = 1.;
202 }
203 }
204
205 visit_measurement(nDof, [&](auto N) {
206 fullCovariancePredicted.topLeftCorner<N, N>() =
207 extendedSystem.aMatrix().inverse().topLeftCorner<N, N>();
208 });
209
210 return;
211}
212
214 Gx2fSystem& extendedSystem,
215 const std::vector<BoundMatrix>& jacobianFromStart,
216 const std::vector<std::size_t>& materialIndices,
217 const std::vector<double>& qOverPderiv,
218 const Eigen::MatrixXd& covarianceMeasurement, const BoundVector& predicted,
219 const Eigen::VectorXd& measurement, const Eigen::MatrixXd& projector,
220 const Logger& logger) {
221 // First, we try to invert the covariance matrix. If the inversion fails, we
222 // can already abort.
223 const auto safeInvCovMeasurement = safeInverse(covarianceMeasurement);
224 if (!safeInvCovMeasurement) {
225 ACTS_WARNING("addMeasurementToGx2fSums: safeInvCovMeasurement failed.");
226 ACTS_VERBOSE(" covarianceMeasurement:\n" << covarianceMeasurement);
227 return;
228 }
229 ACTS_VERBOSE("Jacobian size: " << jacobianFromStart.size());
230 // Create an extended Jacobian. This one contains only eBoundSize rows,
231 // because the rest is irrelevant. We fill it in the next steps.
232 // TODO make dimsExtendedParams template with unrolling
233 Eigen::MatrixXd extendedJacobian =
234 Eigen::MatrixXd::Zero(eBoundSize, extendedSystem.nDims());
235
236 // This part of the Jacobian comes from the material-less propagation
237 extendedJacobian.topLeftCorner<eBoundSize, eBoundSize>() =
238 jacobianFromStart[0];
239
240 // If we have material, loop here over all Jacobians. We add extra columns for
241 // their phi-theta projections. These parts account for the propagation of the
242 // scattering angles. if energy loss is enabled, we also add the q/p
243 // projection.
244 for (std::size_t matSurface = 1; matSurface < jacobianFromStart.size();
245 matSurface++) {
246 const BoundMatrix& jac = jacobianFromStart[matSurface];
247
248 // The position, where we need to insert the values in the extended Jacobian
249 // accounting for material effects
250 const std::size_t deltaPosition = materialIndices.at(matSurface - 1);
251 ACTS_DEBUG("Update material jacobian " << deltaPosition << ", "
252 << extendedSystem.nDims());
253
254 // check the dimension of the material map: 2 means only scattering, 3 means
255 // scattering and energy loss amd 1 only energy loss
256 const std::size_t matDim = materialIndices.at(matSurface) - deltaPosition;
257
258 // check if energy loss is enabled, if yes, we need to add the q/p
259 // projection in the extended jacobian
260 switch (matDim) {
261 case 1: { // energy loss only
262 Acts::Matrix<eBoundSize, 1> proj = qOverPProjector;
263 proj(eBoundQOverP, 0) = qOverPderiv.at(matSurface - 1);
264 extendedJacobian.block<eBoundSize, 1>(0, deltaPosition) = jac * proj;
265 break;
266 }
267 case 2: // scattering only
268 extendedJacobian.block<eBoundSize, 2>(0, deltaPosition) =
269 jac * phiThetaProjector;
270 break;
271 case 3: { // scattering and energy loss
272 Acts::Matrix<eBoundSize, 3> proj = phiThetaQOverPProjector;
273 proj(eBoundQOverP, 2) = qOverPderiv.at(matSurface - 1);
274 extendedJacobian.block<eBoundSize, 3>(0, deltaPosition) = jac * proj;
275 break;
276 }
277 default:
278 ACTS_ERROR("Invalid material dimension: "
279 << matDim << " - this should be 1, 2, or 3.");
280 throw std::domain_error("Invalid material dimension.");
281 }
282 }
283
284 const Eigen::MatrixXd projJacobian = projector * extendedJacobian;
285
286 const Eigen::VectorXd projPredicted = projector * predicted;
287
288 const Eigen::VectorXd residual = measurement - projPredicted;
289
290 // Finally contribute to chi2sum, aMatrix, and bVector
291 extendedSystem.chi2() +=
292 (residual.transpose() * (*safeInvCovMeasurement) * residual)(0, 0);
293
294 extendedSystem.aMatrix() +=
295 (projJacobian.transpose() * (*safeInvCovMeasurement) * projJacobian)
296 .eval();
297
298 extendedSystem.bVector() +=
299 (residual.transpose() * (*safeInvCovMeasurement) * projJacobian)
300 .eval()
301 .transpose();
302
303 ACTS_VERBOSE(
304 "Contributions in addMeasurementToGx2fSums:\n"
305 << " predicted: " << predicted.transpose() << "\n"
306 << " measurement: " << measurement.transpose() << "\n"
307 << " covarianceMeasurement:\n"
308 << covarianceMeasurement << "\n"
309 << " projector:\n"
310 << projector.eval() << "\n"
311 << " projJacobian:\n"
312 << projJacobian.eval() << "\n"
313 << " projPredicted: " << (projPredicted.transpose()).eval() << "\n"
314 << " residual: " << (residual.transpose()).eval() << "\n"
315 << " extendedJacobian:\n"
316 << extendedJacobian << "\n"
317 << " aMatrix contribution:\n"
318 << (projJacobian.transpose() * (*safeInvCovMeasurement) * projJacobian)
319 .eval()
320 << "\n"
321 << " bVector contribution: "
322 << (residual.transpose() * (*safeInvCovMeasurement) * projJacobian).eval()
323 << "\n"
324 << " chi2sum contribution: "
325 << (residual.transpose() * (*safeInvCovMeasurement) * residual)(0, 0)
326 << "\n"
327 << " safeInvCovMeasurement:\n"
328 << (*safeInvCovMeasurement));
329}
330
331Eigen::VectorXd computeGx2fDeltaParams(const Gx2fSystem& extendedSystem) {
332 return extendedSystem.aMatrix().colPivHouseholderQr().solve(
333 extendedSystem.bVector());
334}
335
336} // namespace Acts::Experimental
Scalar theta() const
theta method
void updateGx2fParams(BoundTrackParameters &params, const Eigen::VectorXd &deltaParamsExtended, std::unordered_map< GeometryIdentifier, Gx2fMaterialProperties > &materialMap, const std::vector< GeometryIdentifier > &geoIdVector)
Update parameters (and scattering angles if applicable).
Eigen::VectorXd computeGx2fDeltaParams(const Gx2fSystem &extendedSystem)
Solve the gx2f system to get the delta parameters for the update.
void addMeasurementToGx2fSumsBackend(Gx2fSystem &extendedSystem, const std::vector< BoundMatrix > &jacobianFromStart, const std::vector< std::size_t > &materialIndices, const std::vector< double > &qOverPderiv, const Eigen::MatrixXd &covarianceMeasurement, const BoundVector &predicted, const Eigen::VectorXd &measurement, const Eigen::MatrixXd &projector, const Logger &logger)
Adds a measurement to the GX2F equation system in a modular backend function.
void updateGx2fCovarianceParams(BoundMatrix &fullCovariancePredicted, Gx2fSystem &extendedSystem)
Calculate and update the covariance of the fitted parameters.
static Root::TMsgLogger logger("iLumiCalc")
const Acts::Matrix< eBoundSize, 2 > phiThetaProjector
const Acts::Matrix< eBoundSize, 3 > phiThetaQOverPProjector
const Acts::Matrix< eBoundSize, 1 > qOverPProjector
Auxiliary struct to include the measured energy loss at a surface.
Definition Chi2Fitter.h:69
ELossAtSurface()=default
Default constructor to construct an invalid energy loss.
static ELossAtSurface invalidELoss(const GeometryContext &geoContext, const CalibrationContext &calibContext, const Vector3 &pos, const Vector3 &dir, const Surface &matSurface)
Function that satisfies the eLossAccumulator delegate pattern and the produces an invalid energy loss...
double m_scatPhi
Fitted delta phi scattering.
Definition Chi2Fitter.h:219
constexpr double qOverPderiv() const
Definition Chi2Fitter.h:187
ELossAtSurface m_eloss
Description of the energy loss.
Definition Chi2Fitter.h:208
void updateParameters(const Eigen::VectorXd &deltaParamsExtended, const std::size_t firstParamIdx)
constexpr std::size_t nDim() const
Number of dimensions from the material scatterer.
Definition Chi2Fitter.h:172
double m_lostEnergy
Fitted parameters.
Definition Chi2Fitter.h:215
double m_scatTheta
Fitted delta theta scattering.
Definition Chi2Fitter.h:217
double m_qOverPderiv
qOverP derivative with respect to eloss
Definition Chi2Fitter.h:221
void contributionToGx2fSums(Gx2fSystem &extendedSystem, const double theta, const std::size_t firstParamIdx, const Logger &logger) const
ScatteringAtSurface m_scatterer
Description of the scattering angles.
Definition Chi2Fitter.h:206
constexpr double deltaPhi() const
Definition Chi2Fitter.h:183
constexpr double deltaTheta() const
Definition Chi2Fitter.h:181
void updateTrackParameters(BoundTrackParameters &trackPars)
Update the track parameters with the scattering angles and.
A container to manage all properties of a gx2f system.
Definition Chi2Fitter.h:421
double chi2() const
Accessor for the accumulated chi-squared value (const version).
Definition Chi2Fitter.h:433
std::size_t findRequiredNdf()
Determines the minimum number of degrees of freedom required for the fit.
Definition Chi2Fitter.h:472
std::size_t nDims() const
Accessor for the number of dimensions of the extended system.
Definition Chi2Fitter.h:429
const Eigen::VectorXd & bVector() const
Accessor for the extended system vector (const version).
Definition Chi2Fitter.h:449
const Eigen::MatrixXd & aMatrix() const
Accessor for the extended system matrix (const version).
Definition Chi2Fitter.h:441