File indexing completed on 2025-08-05 08:09:43
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Vertexing/KalmanVertexUpdater.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Surfaces/PerigeeSurface.hpp"
0013 #include "Acts/Vertexing/LinearizedTrack.hpp"
0014 #include "Acts/Vertexing/Vertex.hpp"
0015
0016 namespace Acts {
0017 namespace {
0018
0019
0020
0021
0022 template <unsigned int nDimVertex>
0023 struct Cache {
0024 using VertexVector = ActsVector<nDimVertex>;
0025 using VertexMatrix = ActsSquareMatrix<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 = ActsVector<nBoundParams>;
0058 using ParameterMatrix = ActsSquareMatrix<nBoundParams>;
0059
0060
0061
0062 const ActsMatrix<nBoundParams, nDimVertex> posJac =
0063 linTrack.positionJacobian.block<nBoundParams, nDimVertex>(0, 0);
0064
0065 const ActsMatrix<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 ActsVector<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 ActsVector<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 = ActsVector<nBoundParams>;
0130 using ParameterMatrix = ActsSquareMatrix<nBoundParams>;
0131
0132 const ActsMatrix<nBoundParams, nDimVertex> posJac =
0133 linTrack.positionJacobian.block<nBoundParams, nDimVertex>(0, 0);
0134
0135 const ActsMatrix<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 template <unsigned int nDimVertex>
0168 void updateVertexWithTrackImpl(Vertex& vtx, TrackAtVertex& trk, int sign) {
0169 if constexpr (nDimVertex != 3 && nDimVertex != 4) {
0170 throw std::invalid_argument(
0171 "The vertex dimension must either be 3 (when fitting the spatial "
0172 "coordinates) or 4 (when fitting the spatial coordinates + time).");
0173 }
0174
0175 double trackWeight = trk.trackWeight;
0176
0177
0178 Cache<nDimVertex> cache;
0179
0180
0181 calculateUpdate(vtx, trk.linearizedState, trackWeight, sign, cache);
0182
0183
0184 double chi2 = 0.;
0185 double ndf = 0.;
0186 std::tie(chi2, ndf) = vtx.fitQuality();
0187
0188
0189 double trkChi2 = trackParametersChi2(trk.linearizedState, cache);
0190
0191
0192 double vtxPosChi2Update = vertexPositionChi2Update(vtx.fullPosition(), cache);
0193
0194
0195 chi2 += sign * (vtxPosChi2Update + trackWeight * trkChi2);
0196
0197
0198 ndf += sign * trackWeight * 2.;
0199
0200
0201 if constexpr (nDimVertex == 3) {
0202 vtx.fullPosition().head<3>() = cache.newVertexPos.template head<3>();
0203 vtx.fullCovariance().template topLeftCorner<3, 3>() =
0204 cache.newVertexCov.template topLeftCorner<3, 3>();
0205 } else if constexpr (nDimVertex == 4) {
0206 vtx.fullPosition() = cache.newVertexPos;
0207 vtx.fullCovariance() = cache.newVertexCov;
0208 }
0209 vtx.setFitQuality(chi2, ndf);
0210
0211 if (sign == 1) {
0212
0213 trk.chi2Track = trkChi2;
0214 trk.ndf = 2 * trackWeight;
0215 }
0216
0217 else if (sign == -1) {
0218 trk.trackWeight = 0.;
0219 } else {
0220 throw std::invalid_argument(
0221 "Sign for adding/removing track must be +1 (add) or -1 (remove).");
0222 }
0223 }
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234
0235 template <unsigned int nDimVertex>
0236 Acts::BoundMatrix calculateTrackCovariance(
0237 const SquareMatrix3& wMat, const ActsMatrix<nDimVertex, 3>& crossCovVP,
0238 const ActsSquareMatrix<nDimVertex>& vtxCov,
0239 const BoundVector& newTrkParams) {
0240
0241 ActsSquareMatrix<3> momCov =
0242 wMat + crossCovVP.transpose() * vtxCov.inverse() * crossCovVP;
0243
0244
0245
0246
0247
0248 constexpr unsigned int nFreeParams = nDimVertex + 3;
0249 ActsSquareMatrix<nFreeParams> freeTrkCov(
0250 ActsSquareMatrix<nFreeParams>::Zero());
0251
0252 freeTrkCov.template block<3, 3>(0, 0) = vtxCov.template block<3, 3>(0, 0);
0253 freeTrkCov.template block<3, 3>(0, 3) = crossCovVP.template block<3, 3>(0, 0);
0254 freeTrkCov.template block<3, 3>(3, 0) =
0255 crossCovVP.template block<3, 3>(0, 0).transpose();
0256 freeTrkCov.template block<3, 3>(3, 3) = momCov;
0257
0258
0259 if constexpr (nFreeParams == 7) {
0260 freeTrkCov.template block<3, 1>(0, 6) = vtxCov.template block<3, 1>(0, 3);
0261 freeTrkCov.template block<3, 1>(3, 6) =
0262 crossCovVP.template block<1, 3>(3, 0).transpose();
0263 freeTrkCov.template block<1, 3>(6, 0) = vtxCov.template block<1, 3>(3, 0);
0264 freeTrkCov.template block<1, 3>(6, 3) =
0265 crossCovVP.template block<1, 3>(3, 0);
0266 freeTrkCov(6, 6) = vtxCov(3, 3);
0267 }
0268
0269
0270 constexpr unsigned int nBoundParams = nDimVertex + 2;
0271 ActsMatrix<nBoundParams, nFreeParams> freeToBoundJac(
0272 ActsMatrix<nBoundParams, nFreeParams>::Zero());
0273
0274
0275
0276 freeToBoundJac(0, 0) = -std::sin(newTrkParams[2]);
0277 freeToBoundJac(0, 1) = std::cos(newTrkParams[2]);
0278
0279 double tanTheta = std::tan(newTrkParams[3]);
0280
0281
0282 freeToBoundJac(1, 0) = -freeToBoundJac(0, 1) / tanTheta;
0283 freeToBoundJac(1, 1) = freeToBoundJac(0, 0) / tanTheta;
0284
0285
0286 constexpr unsigned int nDimIdentity = nFreeParams - 2;
0287 freeToBoundJac.template block<nDimIdentity, nDimIdentity>(1, 2) =
0288 ActsMatrix<nDimIdentity, nDimIdentity>::Identity();
0289
0290
0291 BoundMatrix boundTrackCov(BoundMatrix::Identity());
0292 boundTrackCov.block<nBoundParams, nBoundParams>(0, 0) =
0293 (freeToBoundJac * (freeTrkCov * freeToBoundJac.transpose()));
0294
0295 return boundTrackCov;
0296 }
0297
0298 template <unsigned int nDimVertex>
0299 void updateTrackWithVertexImpl(TrackAtVertex& track, const Vertex& vtx) {
0300 if constexpr (nDimVertex != 3 && nDimVertex != 4) {
0301 throw std::invalid_argument(
0302 "The vertex dimension must either be 3 (when fitting the spatial "
0303 "coordinates) or 4 (when fitting the spatial coordinates + time).");
0304 }
0305
0306 using VertexVector = ActsVector<nDimVertex>;
0307 using VertexMatrix = ActsSquareMatrix<nDimVertex>;
0308 constexpr unsigned int nBoundParams = nDimVertex + 2;
0309 using ParameterVector = ActsVector<nBoundParams>;
0310 using ParameterMatrix = ActsSquareMatrix<nBoundParams>;
0311
0312 if (!track.isLinearized) {
0313 throw std::invalid_argument("TrackAtVertex object must be linearized.");
0314 }
0315
0316
0317
0318 const VertexVector vtxPos = vtx.fullPosition().template head<nDimVertex>();
0319
0320 const VertexMatrix vtxCov =
0321 vtx.fullCovariance().template block<nDimVertex, nDimVertex>(0, 0);
0322
0323
0324 const LinearizedTrack& linTrack = track.linearizedState;
0325
0326
0327
0328 const ActsMatrix<nBoundParams, nDimVertex> posJac =
0329 linTrack.positionJacobian.block<nBoundParams, nDimVertex>(0, 0);
0330
0331 const ActsMatrix<nBoundParams, 3> momJac =
0332 linTrack.momentumJacobian.block<nBoundParams, 3>(0, 0);
0333
0334 const ParameterVector trkParams =
0335 linTrack.parametersAtPCA.head<nBoundParams>();
0336
0337 const ParameterVector constTerm = linTrack.constantTerm.head<nBoundParams>();
0338
0339
0340
0341 const ParameterMatrix trkParamWeight =
0342 linTrack.covarianceAtPCA.block<nBoundParams, nBoundParams>(0, 0)
0343 .inverse();
0344
0345
0346 Cache<nDimVertex> cache;
0347
0348
0349
0350 calculateUpdate(vtx, linTrack, track.trackWeight, -1, cache);
0351
0352
0353 Vector3 newTrkMomentum = cache.wMat * momJac.transpose() * trkParamWeight *
0354 (trkParams - constTerm - posJac * vtxPos);
0355
0356
0357
0358 BoundVector newTrkParams(BoundVector::Zero());
0359
0360
0361 const auto correctedPhiTheta =
0362 Acts::detail::normalizePhiTheta(newTrkMomentum(0), newTrkMomentum(1));
0363 newTrkParams(BoundIndices::eBoundPhi) = correctedPhiTheta.first;
0364 newTrkParams(BoundIndices::eBoundTheta) = correctedPhiTheta.second;
0365 newTrkParams(BoundIndices::eBoundQOverP) = newTrkMomentum(2);
0366
0367
0368 const ActsMatrix<nDimVertex, 3> crossCovVP =
0369 -vtxCov * posJac.transpose() * trkParamWeight * momJac * cache.wMat;
0370
0371
0372 VertexVector posDiff =
0373 vtxPos - cache.newVertexPos.template head<nDimVertex>();
0374
0375
0376 ParameterVector paramDiff =
0377 trkParams - (constTerm + posJac * vtxPos + momJac * newTrkMomentum);
0378
0379
0380 double chi2 =
0381 posDiff.dot(
0382 cache.newVertexWeight.template block<nDimVertex, nDimVertex>(0, 0) *
0383 posDiff) +
0384 paramDiff.dot(trkParamWeight * paramDiff);
0385
0386 Acts::BoundMatrix newTrackCov = calculateTrackCovariance<nDimVertex>(
0387 cache.wMat, crossCovVP, vtxCov, newTrkParams);
0388
0389
0390 std::shared_ptr<PerigeeSurface> perigeeSurface =
0391 Surface::makeShared<PerigeeSurface>(vtxPos.template head<3>());
0392
0393 BoundTrackParameters refittedPerigee =
0394 BoundTrackParameters(perigeeSurface, newTrkParams, std::move(newTrackCov),
0395 track.fittedParams.particleHypothesis());
0396
0397
0398 track.fittedParams = refittedPerigee;
0399 track.chi2Track = chi2;
0400 track.ndf = 2 * track.trackWeight;
0401
0402 return;
0403 }
0404
0405 }
0406
0407
0408
0409
0410
0411 void KalmanVertexUpdater::updateVertexWithTrack(Vertex& vtx, TrackAtVertex& trk,
0412 unsigned int nDimVertex) {
0413 if (nDimVertex == 3) {
0414 updateVertexWithTrackImpl<3>(vtx, trk, 1);
0415 } else if (nDimVertex == 4) {
0416 updateVertexWithTrackImpl<4>(vtx, trk, 1);
0417 } else {
0418 throw std::invalid_argument(
0419 "The vertex dimension must either be 3 (when fitting the spatial "
0420 "coordinates) or 4 (when fitting the spatial coordinates + time).");
0421 }
0422 }
0423
0424 void Acts::KalmanVertexUpdater::updateTrackWithVertex(TrackAtVertex& track,
0425 const Vertex& vtx,
0426 unsigned int nDimVertex) {
0427 if (nDimVertex == 3) {
0428 updateTrackWithVertexImpl<3>(track, vtx);
0429 } else if (nDimVertex == 4) {
0430 updateTrackWithVertexImpl<4>(track, vtx);
0431 } else {
0432 throw std::invalid_argument(
0433 "The vertex dimension must either be 3 (when fitting the spatial "
0434 "coordinates) or 4 (when fitting the spatial coordinates + time).");
0435 }
0436 }
0437
0438 }