Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:09:42

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2019-2023 CERN for the benefit of the Acts project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Vertexing/HelicalTrackLinearizer.hpp"
0010 
0011 #include "Acts/Surfaces/PerigeeSurface.hpp"
0012 #include "Acts/Vertexing/LinearizerTrackParameters.hpp"
0013 
0014 Acts::Result<Acts::LinearizedTrack>
0015 Acts::HelicalTrackLinearizer::linearizeTrack(
0016     const BoundTrackParameters& params, double linPointTime,
0017     const Surface& perigeeSurface, const Acts::GeometryContext& gctx,
0018     const Acts::MagneticFieldContext& mctx,
0019     MagneticFieldProvider::Cache& fieldCache) const {
0020   // Create propagator options
0021   PropagatorOptions<> pOptions(gctx, mctx);
0022 
0023   // Length scale at which we consider to be sufficiently close to the Perigee
0024   // surface to skip the propagation.
0025   pOptions.surfaceTolerance = m_cfg.targetTolerance;
0026 
0027   // Get intersection of the track with the Perigee if the particle would
0028   // move on a straight line.
0029   // This allows us to determine whether we need to propagate the track
0030   // forward or backward to arrive at the PCA.
0031   auto intersection = perigeeSurface
0032                           .intersect(gctx, params.position(gctx),
0033                                      params.direction(), BoundaryCheck(false))
0034                           .closest();
0035 
0036   // Setting the propagation direction using the intersection length from
0037   // above
0038   // We handle zero path length as forward propagation, but we could actually
0039   // skip the whole propagation in this case
0040   pOptions.direction =
0041       Direction::fromScalarZeroAsPositive(intersection.pathLength());
0042 
0043   // Propagate to the PCA of the reference point
0044   const auto res =
0045       m_cfg.propagator->propagateToSurface(params, perigeeSurface, pOptions);
0046   if (!res.ok()) {
0047     return res.error();
0048   }
0049   const auto& endParams = *res;
0050 
0051   // Extracting the track parameters at said PCA - this corresponds to the
0052   // Perigee representation of the track wrt the reference point
0053   BoundVector paramsAtPCA = endParams.parameters();
0054 
0055   // Extracting the 4D position of the PCA in global coordinates
0056   Vector4 pca = Vector4::Zero();
0057   {
0058     auto pos = endParams.position(gctx);
0059     pca[ePos0] = pos[ePos0];
0060     pca[ePos1] = pos[ePos1];
0061     pca[ePos2] = pos[ePos2];
0062     pca[eTime] = endParams.time();
0063   }
0064   BoundSquareMatrix parCovarianceAtPCA = endParams.covariance().value();
0065 
0066   // Extracting Perigee parameters and compute functions of them for later
0067   // usage
0068   ActsScalar d0 = paramsAtPCA(BoundIndices::eBoundLoc0);
0069 
0070   ActsScalar phi = paramsAtPCA(BoundIndices::eBoundPhi);
0071   ActsScalar sinPhi = std::sin(phi);
0072   ActsScalar cosPhi = std::cos(phi);
0073 
0074   ActsScalar theta = paramsAtPCA(BoundIndices::eBoundTheta);
0075   ActsScalar sinTheta = std::sin(theta);
0076   ActsScalar tanTheta = std::tan(theta);
0077 
0078   // q over p
0079   ActsScalar qOvP = paramsAtPCA(BoundIndices::eBoundQOverP);
0080   // Rest mass
0081   ActsScalar m0 = params.particleHypothesis().mass();
0082   // Momentum
0083   ActsScalar p = params.particleHypothesis().extractMomentum(qOvP);
0084 
0085   // Speed in units of c
0086   ActsScalar beta = p / std::hypot(p, m0);
0087   // Transverse speed (i.e., speed in the x-y plane)
0088   ActsScalar betaT = beta * sinTheta;
0089 
0090   // Momentum direction at the PCA
0091   Vector3 momentumAtPCA(phi, theta, qOvP);
0092 
0093   // Particle charge
0094   ActsScalar absoluteCharge = params.particleHypothesis().absoluteCharge();
0095 
0096   // get the z-component of the B-field at the PCA
0097   auto field = m_cfg.bField->getField(VectorHelpers::position(pca), fieldCache);
0098   if (!field.ok()) {
0099     return field.error();
0100   }
0101   ActsScalar Bz = (*field)[eZ];
0102 
0103   // Complete Jacobian (consists of positionJacobian and momentumJacobian)
0104   ActsMatrix<eBoundSize, eLinSize> completeJacobian =
0105       ActsMatrix<eBoundSize, eLinSize>::Zero(eBoundSize, eLinSize);
0106 
0107   // The particle moves on a straight trajectory if its charge is 0 or if there
0108   // is no B field. Conversely, if it has a charge and the B field is constant,
0109   // it moves on a helical trajectory.
0110   if (absoluteCharge == 0. || Bz == 0.) {
0111     // Derivatives can be found in Eqs. 5.39 and 5.40 of Ref. (1).
0112     // Since we propagated to the PCA (point P in Ref. (1)), we evaluate the
0113     // Jacobians there. One can show that, in this case, RTilde = 0 and QTilde =
0114     // -d0.
0115 
0116     // Derivatives of d0
0117     completeJacobian(eBoundLoc0, eLinPos0) = -sinPhi;
0118     completeJacobian(eBoundLoc0, eLinPos1) = cosPhi;
0119 
0120     // Derivatives of z0
0121     completeJacobian(eBoundLoc1, eLinPos0) = -cosPhi / tanTheta;
0122     completeJacobian(eBoundLoc1, eLinPos1) = -sinPhi / tanTheta;
0123     completeJacobian(eBoundLoc1, eLinPos2) = 1.;
0124     completeJacobian(eBoundLoc1, eLinPhi) = -d0 / tanTheta;
0125 
0126     // Derivatives of phi
0127     completeJacobian(eBoundPhi, eLinPhi) = 1.;
0128 
0129     // Derivatives of theta
0130     completeJacobian(eBoundTheta, eLinTheta) = 1.;
0131 
0132     // Derivatives of q/p
0133     completeJacobian(eBoundQOverP, eLinQOverP) = 1.;
0134 
0135     // Derivatives of time
0136     completeJacobian(eBoundTime, eLinPos0) = -cosPhi / betaT;
0137     completeJacobian(eBoundTime, eLinPos1) = -sinPhi / betaT;
0138     completeJacobian(eBoundTime, eLinTime) = 1.;
0139     completeJacobian(eBoundTime, eLinPhi) = -d0 / betaT;
0140   } else {
0141     // Helix radius
0142     ActsScalar rho = sinTheta * (1. / qOvP) / Bz;
0143     // Sign of helix radius
0144     ActsScalar h = (rho < 0.) ? -1 : 1;
0145 
0146     // Quantities from Eq. 5.34 in Ref. (1) (see .hpp)
0147     ActsScalar X = pca(0) - perigeeSurface.center(gctx).x() + rho * sinPhi;
0148     ActsScalar Y = pca(1) - perigeeSurface.center(gctx).y() - rho * cosPhi;
0149     ActsScalar S2 = (X * X + Y * Y);
0150     // S is the 2D distance from the helix center to the reference point
0151     // in the x-y plane
0152     ActsScalar S = std::sqrt(S2);
0153 
0154     ActsScalar XoverS2 = X / S2;
0155     ActsScalar YoverS2 = Y / S2;
0156     ActsScalar rhoCotTheta = rho / tanTheta;
0157     ActsScalar rhoOverBetaT = rho / betaT;
0158     // Absolute value of rho over S
0159     ActsScalar absRhoOverS = h * rho / S;
0160 
0161     // Derivatives can be found in Eq. 5.36 in Ref. (1)
0162     // Since we propagated to the PCA (point P in Ref. (1)), the points
0163     // P and V coincide, and thus deltaPhi = 0.
0164     // One can show that if deltaPhi = 0 --> R = 0 and Q = h * S.
0165     // As a consequence, many terms of the B matrix vanish.
0166 
0167     // Derivatives of d0
0168     completeJacobian(eBoundLoc0, eLinPos0) = -h * X / S;
0169     completeJacobian(eBoundLoc0, eLinPos1) = -h * Y / S;
0170 
0171     // Derivatives of z0
0172     completeJacobian(eBoundLoc1, eLinPos0) = rhoCotTheta * YoverS2;
0173     completeJacobian(eBoundLoc1, eLinPos1) = -rhoCotTheta * XoverS2;
0174     completeJacobian(eBoundLoc1, eLinPos2) = 1.;
0175     completeJacobian(eBoundLoc1, eLinPhi) = rhoCotTheta * (1. - absRhoOverS);
0176 
0177     // Derivatives of phi
0178     completeJacobian(eBoundPhi, eLinPos0) = -YoverS2;
0179     completeJacobian(eBoundPhi, eLinPos1) = XoverS2;
0180     completeJacobian(eBoundPhi, eLinPhi) = absRhoOverS;
0181 
0182     // Derivatives of theta
0183     completeJacobian(eBoundTheta, eLinTheta) = 1.;
0184 
0185     // Derivatives of q/p
0186     completeJacobian(eBoundQOverP, eLinQOverP) = 1.;
0187 
0188     // Derivatives of time
0189     completeJacobian(eBoundTime, eLinPos0) = rhoOverBetaT * YoverS2;
0190     completeJacobian(eBoundTime, eLinPos1) = -rhoOverBetaT * XoverS2;
0191     completeJacobian(eBoundTime, eLinTime) = 1.;
0192     completeJacobian(eBoundTime, eLinPhi) = rhoOverBetaT * (1. - absRhoOverS);
0193   }
0194 
0195   // Extracting positionJacobian and momentumJacobian from the complete Jacobian
0196   ActsMatrix<eBoundSize, eLinPosSize> positionJacobian =
0197       completeJacobian.block<eBoundSize, eLinPosSize>(0, 0);
0198   ActsMatrix<eBoundSize, eLinMomSize> momentumJacobian =
0199       completeJacobian.block<eBoundSize, eLinMomSize>(0, eLinPosSize);
0200 
0201   // const term in Taylor expansion from Eq. 5.38 in Ref. (1)
0202   BoundVector constTerm =
0203       paramsAtPCA - positionJacobian * pca - momentumJacobian * momentumAtPCA;
0204 
0205   // The parameter weight
0206   BoundSquareMatrix weightAtPCA = parCovarianceAtPCA.inverse();
0207 
0208   Vector4 linPoint;
0209   linPoint.head<3>() = perigeeSurface.center(gctx);
0210   linPoint[3] = linPointTime;
0211 
0212   return LinearizedTrack(paramsAtPCA, parCovarianceAtPCA, weightAtPCA, linPoint,
0213                          positionJacobian, momentumJacobian, pca, momentumAtPCA,
0214                          constTerm);
0215 }