File indexing completed on 2026-04-06 07:44:10
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Surfaces/PerigeeSurface.hpp"
0013 #include "Acts/Vertexing/LinearizedTrack.hpp"
0014 #include "Acts/Vertexing/TrackAtVertex.hpp"
0015 #include "Acts/Vertexing/Vertex.hpp"
0016
0017 namespace Acts::KalmanVertexUpdater::detail {
0018
0019
0020
0021
0022 template <unsigned int nDimVertex>
0023 struct Cache {
0024 using VertexVector = Vector<nDimVertex>;
0025 using VertexMatrix = SquareMatrix<nDimVertex>;
0026
0027 VertexVector newVertexPos = VertexVector::Zero();
0028
0029 VertexMatrix newVertexCov = VertexMatrix::Zero();
0030
0031 VertexMatrix newVertexWeight = VertexMatrix::Zero();
0032
0033 VertexMatrix oldVertexWeight = VertexMatrix::Zero();
0034
0035 SquareMatrix3 wMat = SquareMatrix3::Zero();
0036 };
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052 template <unsigned int nDimVertex>
0053 void calculateUpdate(const Vertex& vtx, const Acts::LinearizedTrack& linTrack,
0054 const double trackWeight, const int sign,
0055 Cache<nDimVertex>& cache) {
0056 constexpr unsigned int nBoundParams = nDimVertex + 2;
0057 using ParameterVector = Vector<nBoundParams>;
0058 using ParameterMatrix = SquareMatrix<nBoundParams>;
0059
0060
0061
0062 const Matrix<nBoundParams, nDimVertex> posJac =
0063 linTrack.positionJacobian.block<nBoundParams, nDimVertex>(0, 0);
0064
0065 const Matrix<nBoundParams, 3> momJac =
0066 linTrack.momentumJacobian.block<nBoundParams, 3>(0, 0);
0067
0068 const ParameterVector trkParams =
0069 linTrack.parametersAtPCA.head<nBoundParams>();
0070
0071 const ParameterVector constTerm = linTrack.constantTerm.head<nBoundParams>();
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081 const ParameterMatrix trkParamWeight =
0082 linTrack.covarianceAtPCA.block<nBoundParams, nBoundParams>(0, 0)
0083 .inverse();
0084
0085
0086 const Vector<nDimVertex> oldVtxPos =
0087 vtx.fullPosition().template head<nDimVertex>();
0088
0089 cache.oldVertexWeight =
0090 (vtx.fullCovariance().template block<nDimVertex, nDimVertex>(0, 0))
0091 .inverse();
0092
0093
0094 cache.wMat = (momJac.transpose() * (trkParamWeight * momJac)).inverse();
0095
0096
0097 ParameterMatrix gBMat = trkParamWeight - trkParamWeight * momJac *
0098 cache.wMat * momJac.transpose() *
0099 trkParamWeight;
0100
0101
0102 cache.newVertexWeight = cache.oldVertexWeight + sign * trackWeight *
0103 posJac.transpose() *
0104 gBMat * posJac;
0105
0106 cache.newVertexCov = cache.newVertexWeight.inverse();
0107
0108
0109 cache.newVertexPos =
0110 cache.newVertexCov * (cache.oldVertexWeight * oldVtxPos +
0111 sign * trackWeight * posJac.transpose() * gBMat *
0112 (trkParams - constTerm));
0113 }
0114
0115 template <unsigned int nDimVertex>
0116 double vertexPositionChi2Update(const Vector4& oldVtxPos,
0117 const Cache<nDimVertex>& cache) {
0118 Vector<nDimVertex> posDiff =
0119 cache.newVertexPos - oldVtxPos.template head<nDimVertex>();
0120
0121
0122 return posDiff.transpose() * (cache.oldVertexWeight * posDiff);
0123 }
0124
0125 template <unsigned int nDimVertex>
0126 double trackParametersChi2(const LinearizedTrack& linTrack,
0127 const Cache<nDimVertex>& cache) {
0128 constexpr unsigned int nBoundParams = nDimVertex + 2;
0129 using ParameterVector = Vector<nBoundParams>;
0130 using ParameterMatrix = SquareMatrix<nBoundParams>;
0131
0132 const Matrix<nBoundParams, nDimVertex> posJac =
0133 linTrack.positionJacobian.block<nBoundParams, nDimVertex>(0, 0);
0134
0135 const Matrix<nBoundParams, 3> momJac =
0136 linTrack.momentumJacobian.block<nBoundParams, 3>(0, 0);
0137
0138 const ParameterVector trkParams =
0139 linTrack.parametersAtPCA.head<nBoundParams>();
0140
0141 const ParameterVector constTerm = linTrack.constantTerm.head<nBoundParams>();
0142
0143
0144
0145 const ParameterMatrix trkParamWeight =
0146 linTrack.covarianceAtPCA.block<nBoundParams, nBoundParams>(0, 0)
0147 .inverse();
0148
0149
0150 const ParameterVector posJacVtxPos = posJac * cache.newVertexPos;
0151
0152
0153 Vector3 newTrkMom = cache.wMat * momJac.transpose() * trkParamWeight *
0154 (trkParams - constTerm - posJacVtxPos);
0155
0156
0157 ParameterVector linearizedTrackParameters =
0158 constTerm + posJacVtxPos + momJac * newTrkMom;
0159
0160
0161 ParameterVector paramDiff = trkParams - linearizedTrackParameters;
0162
0163
0164 return paramDiff.transpose() * (trkParamWeight * paramDiff);
0165 }
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177 template <unsigned int nDimVertex>
0178 Acts::BoundMatrix calculateTrackCovariance(
0179 const SquareMatrix3& wMat, const Matrix<nDimVertex, 3>& crossCovVP,
0180 const SquareMatrix<nDimVertex>& vtxCov, const BoundVector& newTrkParams) {
0181
0182 SquareMatrix<3> momCov =
0183 wMat + crossCovVP.transpose() * vtxCov.inverse() * crossCovVP;
0184
0185
0186
0187
0188
0189 constexpr unsigned int nFreeParams = nDimVertex + 3;
0190 SquareMatrix<nFreeParams> freeTrkCov(SquareMatrix<nFreeParams>::Zero());
0191
0192 freeTrkCov.template block<3, 3>(0, 0) = vtxCov.template block<3, 3>(0, 0);
0193 freeTrkCov.template block<3, 3>(0, 3) = crossCovVP.template block<3, 3>(0, 0);
0194 freeTrkCov.template block<3, 3>(3, 0) =
0195 crossCovVP.template block<3, 3>(0, 0).transpose();
0196 freeTrkCov.template block<3, 3>(3, 3) = momCov;
0197
0198
0199 if constexpr (nFreeParams == 7) {
0200 freeTrkCov.template block<3, 1>(0, 6) = vtxCov.template block<3, 1>(0, 3);
0201 freeTrkCov.template block<3, 1>(3, 6) =
0202 crossCovVP.template block<1, 3>(3, 0).transpose();
0203 freeTrkCov.template block<1, 3>(6, 0) = vtxCov.template block<1, 3>(3, 0);
0204 freeTrkCov.template block<1, 3>(6, 3) =
0205 crossCovVP.template block<1, 3>(3, 0);
0206 freeTrkCov(6, 6) = vtxCov(3, 3);
0207 }
0208
0209
0210 constexpr unsigned int nBoundParams = nDimVertex + 2;
0211 Matrix<nBoundParams, nFreeParams> freeToBoundJac(
0212 Matrix<nBoundParams, nFreeParams>::Zero());
0213
0214
0215
0216 freeToBoundJac(0, 0) = -std::sin(newTrkParams[2]);
0217 freeToBoundJac(0, 1) = std::cos(newTrkParams[2]);
0218
0219 double tanTheta = std::tan(newTrkParams[3]);
0220
0221
0222 freeToBoundJac(1, 0) = -freeToBoundJac(0, 1) / tanTheta;
0223 freeToBoundJac(1, 1) = freeToBoundJac(0, 0) / tanTheta;
0224
0225
0226 constexpr unsigned int nDimIdentity = nFreeParams - 2;
0227 freeToBoundJac.template block<nDimIdentity, nDimIdentity>(1, 2) =
0228 Matrix<nDimIdentity, nDimIdentity>::Identity();
0229
0230
0231 BoundMatrix boundTrackCov(BoundMatrix::Identity());
0232 boundTrackCov.block<nBoundParams, nBoundParams>(0, 0) =
0233 (freeToBoundJac * (freeTrkCov * freeToBoundJac.transpose()));
0234
0235 return boundTrackCov;
0236 }
0237
0238 template <unsigned int nDimVertex>
0239 void updateVertexWithTrackImpl(Vertex& vtx, TrackAtVertex& trk, int sign) {
0240 if constexpr (nDimVertex != 3 && nDimVertex != 4) {
0241 throw std::invalid_argument(
0242 "The vertex dimension must either be 3 (when fitting the spatial "
0243 "coordinates) or 4 (when fitting the spatial coordinates + time).");
0244 }
0245
0246 double trackWeight = trk.trackWeight;
0247
0248
0249 Cache<nDimVertex> cache;
0250
0251
0252 calculateUpdate(vtx, trk.linearizedState, trackWeight, sign, cache);
0253
0254
0255 auto [chi2, ndf] = vtx.fitQuality();
0256
0257
0258 double trkChi2 = trackParametersChi2(trk.linearizedState, cache);
0259
0260
0261 double vtxPosChi2Update = vertexPositionChi2Update(vtx.fullPosition(), cache);
0262
0263
0264 chi2 += sign * (vtxPosChi2Update + trackWeight * trkChi2);
0265
0266
0267 ndf += sign * trackWeight * 2.;
0268
0269
0270 if constexpr (nDimVertex == 3) {
0271 vtx.fullPosition().head<3>() = cache.newVertexPos.template head<3>();
0272 vtx.fullCovariance().template topLeftCorner<3, 3>() =
0273 cache.newVertexCov.template topLeftCorner<3, 3>();
0274 } else if constexpr (nDimVertex == 4) {
0275 vtx.fullPosition() = cache.newVertexPos;
0276 vtx.fullCovariance() = cache.newVertexCov;
0277 }
0278 vtx.setFitQuality(chi2, ndf);
0279
0280 if (sign == 1) {
0281
0282 trk.chi2Track = trkChi2;
0283 trk.ndf = 2 * trackWeight;
0284 }
0285
0286 else if (sign == -1) {
0287 trk.trackWeight = 0.;
0288 } else {
0289 throw std::invalid_argument(
0290 "Sign for adding/removing track must be +1 (add) or -1 (remove).");
0291 }
0292 }
0293
0294 template <unsigned int nDimVertex>
0295 void updateTrackWithVertexImpl(TrackAtVertex& track, const Vertex& vtx) {
0296 if constexpr (nDimVertex != 3 && nDimVertex != 4) {
0297 throw std::invalid_argument(
0298 "The vertex dimension must either be 3 (when fitting the spatial "
0299 "coordinates) or 4 (when fitting the spatial coordinates + time).");
0300 }
0301
0302 using VertexVector = Vector<nDimVertex>;
0303 using VertexMatrix = SquareMatrix<nDimVertex>;
0304 constexpr unsigned int nBoundParams = nDimVertex + 2;
0305 using ParameterVector = Vector<nBoundParams>;
0306 using ParameterMatrix = SquareMatrix<nBoundParams>;
0307
0308 if (!track.isLinearized) {
0309 throw std::invalid_argument("TrackAtVertex object must be linearized.");
0310 }
0311
0312
0313
0314 const VertexVector vtxPos = vtx.fullPosition().template head<nDimVertex>();
0315
0316 const VertexMatrix vtxCov =
0317 vtx.fullCovariance().template block<nDimVertex, nDimVertex>(0, 0);
0318
0319
0320 const LinearizedTrack& linTrack = track.linearizedState;
0321
0322
0323
0324 const Matrix<nBoundParams, nDimVertex> posJac =
0325 linTrack.positionJacobian.block<nBoundParams, nDimVertex>(0, 0);
0326
0327 const Matrix<nBoundParams, 3> momJac =
0328 linTrack.momentumJacobian.block<nBoundParams, 3>(0, 0);
0329
0330 const ParameterVector trkParams =
0331 linTrack.parametersAtPCA.head<nBoundParams>();
0332
0333 const ParameterVector constTerm = linTrack.constantTerm.head<nBoundParams>();
0334
0335
0336
0337 const ParameterMatrix trkParamWeight =
0338 linTrack.covarianceAtPCA.block<nBoundParams, nBoundParams>(0, 0)
0339 .inverse();
0340
0341
0342 Cache<nDimVertex> cache;
0343
0344
0345
0346 calculateUpdate(vtx, linTrack, track.trackWeight, -1, cache);
0347
0348
0349 Vector3 newTrkMomentum = cache.wMat * momJac.transpose() * trkParamWeight *
0350 (trkParams - constTerm - posJac * vtxPos);
0351
0352
0353
0354 BoundVector newTrkParams(BoundVector::Zero());
0355
0356
0357 const auto correctedPhiTheta =
0358 Acts::detail::normalizePhiTheta(newTrkMomentum(0), newTrkMomentum(1));
0359 newTrkParams(BoundIndices::eBoundPhi) = correctedPhiTheta.first;
0360 newTrkParams(BoundIndices::eBoundTheta) = correctedPhiTheta.second;
0361 newTrkParams(BoundIndices::eBoundQOverP) = newTrkMomentum(2);
0362
0363
0364 const Matrix<nDimVertex, 3> crossCovVP =
0365 -vtxCov * posJac.transpose() * trkParamWeight * momJac * cache.wMat;
0366
0367
0368 VertexVector posDiff =
0369 vtxPos - cache.newVertexPos.template head<nDimVertex>();
0370
0371
0372 ParameterVector paramDiff =
0373 trkParams - (constTerm + posJac * vtxPos + momJac * newTrkMomentum);
0374
0375
0376 double chi2 =
0377 posDiff.dot(
0378 cache.newVertexWeight.template block<nDimVertex, nDimVertex>(0, 0) *
0379 posDiff) +
0380 paramDiff.dot(trkParamWeight * paramDiff);
0381
0382 Acts::BoundMatrix newTrackCov = calculateTrackCovariance<nDimVertex>(
0383 cache.wMat, crossCovVP, vtxCov, newTrkParams);
0384
0385
0386 std::shared_ptr<PerigeeSurface> perigeeSurface =
0387 Surface::makeShared<PerigeeSurface>(vtxPos.template head<3>());
0388
0389 BoundTrackParameters refittedPerigee =
0390 BoundTrackParameters(perigeeSurface, newTrkParams, std::move(newTrackCov),
0391 track.fittedParams.particleHypothesis());
0392
0393
0394 track.fittedParams = refittedPerigee;
0395 track.chi2Track = chi2;
0396 track.ndf = 2 * track.trackWeight;
0397
0398 return;
0399 }
0400
0401 }