diff --git a/Core/include/Acts/Vertexing/ImpactPointEstimator.hpp b/Core/include/Acts/Vertexing/ImpactPointEstimator.hpp index e6334c73c85..4c4d7a75228 100644 --- a/Core/include/Acts/Vertexing/ImpactPointEstimator.hpp +++ b/Core/include/Acts/Vertexing/ImpactPointEstimator.hpp @@ -143,6 +143,38 @@ class ImpactPointEstimator { const BoundTrackParameters& track, const Vertex& vtx, const GeometryContext& gctx, const MagneticFieldContext& mctx) const; + /// @brief Estimates the sign of the 2D and Z lifetime of a given track + /// w.r.t. a vertex and a direction (e.g. a jet direction) + /// by propagating the trajectory state towards the vertex position + /// and computing the scalar product with the direction vector + /// + /// @param track Track to estimate the IP from + /// @param vtx Vertex the track belongs to + /// @param direction The direction + /// @param gctx The geometry context + /// @param mctx The magnetic field context + /// + /// @return A pair holding the sign for the 2D an Z lifetimes + Result> getLifetimesSignOfTrack( + const BoundTrackParameters& track, const Vertex& vtx, + const Acts::Vector3& direction, const GeometryContext& gctx, + const MagneticFieldContext& mctx) const; + + /// @brief Estimates the sign of the 3D lifetime of a given track + /// w.r.t. a vertex and a direction (e.g. a jet direction) + /// + /// @param track Track to estimate the IP from + /// @param vtx Vertex the track belongs to + /// @param direction The direction + /// @param gctx The geometry context + /// @param mctx The magnetic field context + /// + /// @return The value of the 3D lifetime + Result get3DLifetimeSignOfTrack( + const BoundTrackParameters& track, const Vertex& vtx, + const Acts::Vector3& direction, const GeometryContext& gctx, + const MagneticFieldContext& mctx) const; + private: /// Configuration object const Config m_cfg; diff --git a/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp b/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp index 685c85702a5..acacd9e29d5 100644 --- a/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp +++ b/Core/include/Acts/Vertexing/ImpactPointEstimator.ipp @@ -355,3 +355,81 @@ Acts::ImpactPointEstimator:: return newIPandSigma; } + +template +Acts::Result> +Acts::ImpactPointEstimator:: + getLifetimesSignOfTrack(const BoundTrackParameters& track, + const Vertex& vtx, + const Acts::Vector3& direction, + const GeometryContext& gctx, + const MagneticFieldContext& mctx) const { + const std::shared_ptr perigeeSurface = + Surface::makeShared(vtx.position()); + + // Create propagator options + propagator_options_t pOptions(gctx, mctx); + pOptions.direction = NavigationDirection::Backward; + + // Do the propagation to the perigeee + auto result = m_cfg.propagator->propagate(track, *perigeeSurface, pOptions); + + if (!result.ok()) { + return result.error(); + } + + const auto& propRes = *result; + const auto& params = propRes.endParameters->parameters(); + const double d0 = params[BoundIndices::eBoundLoc0]; + const double z0 = params[BoundIndices::eBoundLoc1]; + const double phi = params[BoundIndices::eBoundPhi]; + const double theta = params[BoundIndices::eBoundTheta]; + + double vs = std::sin(std::atan2(direction[1], direction[0]) - phi) * d0; + double eta = -std::log(std::tan(theta / 2.)); + double dir_eta = VectorHelpers::eta(direction); + + double zs = (dir_eta - eta) * z0; + + std::pair vszs; + + vszs.first = vs >= 0. ? 1. : -1.; + vszs.second = zs >= 0. ? 1. : -1.; + + return vszs; +} + +template +Acts::Result +Acts::ImpactPointEstimator:: + get3DLifetimeSignOfTrack(const BoundTrackParameters& track, + const Vertex& vtx, + const Acts::Vector3& direction, + const GeometryContext& gctx, + const MagneticFieldContext& mctx) const { + const std::shared_ptr perigeeSurface = + Surface::makeShared(vtx.position()); + + // Create propagator options + propagator_options_t pOptions(gctx, mctx); + pOptions.direction = NavigationDirection::Backward; + + // Do the propagation to the perigeee + auto result = m_cfg.propagator->propagate(track, *perigeeSurface, pOptions); + + if (!result.ok()) { + return result.error(); + } + + const auto& propRes = *result; + const auto& params = propRes.endParameters; + const Vector3 trkpos = params->position(gctx); + const Vector3 trkmom = params->momentum(); + + double sign = + (direction.cross(trkmom)).dot(trkmom.cross(vtx.position() - trkpos)); + + return sign >= 0. ? 1. : -1.; +} diff --git a/Tests/UnitTests/Core/Vertexing/ImpactPointEstimatorTests.cpp b/Tests/UnitTests/Core/Vertexing/ImpactPointEstimatorTests.cpp index 8a9b40b6621..50b313778d3 100644 --- a/Tests/UnitTests/Core/Vertexing/ImpactPointEstimatorTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/ImpactPointEstimatorTests.cpp @@ -196,6 +196,54 @@ BOOST_AUTO_TEST_CASE(SingleTrackDistanceParametersAthenaRegression) { BOOST_CHECK_EQUAL(surfaceCenter, vtxPos); } +// Test the Impact3d Point estimator 2d and 3d lifetimes sign +// on a single track. + +BOOST_AUTO_TEST_CASE(Lifetimes2d3d) { + Estimator ipEstimator = makeEstimator(2_T); + + // Create a track from a decay + BoundVector trk_par; + trk_par[eBoundLoc0] = 200_um; + trk_par[eBoundLoc1] = 300_um; + trk_par[eBoundTime] = 1_ns; + trk_par[eBoundPhi] = 45_degree; + trk_par[eBoundTheta] = 45_degree; + trk_par[eBoundQOverP] = 1_e / 10_GeV; + + Vector4 ip_pos{0., 0., 0., 0.}; + Vertex ip_vtx(ip_pos, makeVertexCovariance(), {}); + + // Form the bound track parameters at the ip + auto perigeeSurface = Surface::makeShared(ip_pos.head<3>()); + BoundTrackParameters track(perigeeSurface, trk_par, + makeBoundParametersCovariance()); + + Vector3 direction{0., 1., 0.}; + auto lifetimes_signs = ipEstimator.getLifetimesSignOfTrack( + track, ip_vtx, direction, geoContext, magFieldContext); + + // Check if the result is OK + BOOST_CHECK(lifetimes_signs.ok()); + + // Check that d0 sign is positive + BOOST_CHECK((*lifetimes_signs).first > 0.); + + // Check that z0 sign is negative + BOOST_CHECK((*lifetimes_signs).second < 0.); + + // Check the 3d sign + + auto sign3d = ipEstimator.get3DLifetimeSignOfTrack( + track, ip_vtx, direction, geoContext, magFieldContext); + + // Check result is OK + BOOST_CHECK(sign3d.ok()); + + // Check 3D sign (should be positive) + BOOST_CHECK((*sign3d) > 0.); +} + // Check `.estimateImpactParameters`. BOOST_DATA_TEST_CASE(SingeTrackImpactParameters, tracks* vertices, d0, l0, t0, phi, theta, p, q, vx0, vy0, vz0, vt0) {