Back to home page

sPhenix code displayed by LXR

 
 

    


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

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2022 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/Utilities/SpacePointUtility.hpp"
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Common.hpp"
0013 #include "Acts/EventData/SourceLink.hpp"
0014 #include "Acts/Geometry/TrackingGeometry.hpp"
0015 #include "Acts/SpacePointFormation/SpacePointBuilderOptions.hpp"
0016 #include "Acts/Surfaces/Surface.hpp"
0017 #include "Acts/Utilities/Helpers.hpp"
0018 
0019 #include <algorithm>
0020 #include <cmath>
0021 #include <memory>
0022 
0023 namespace Acts {
0024 
0025 Result<double> SpacePointUtility::differenceOfMeasurementsChecked(
0026     const Vector3& pos1, const Vector3& pos2, const Vector3& posVertex,
0027     const double maxDistance, const double maxAngleTheta2,
0028     const double maxAnglePhi2) const {
0029   // Check if measurements are close enough to each other
0030   if ((pos1 - pos2).norm() > maxDistance) {
0031     return Result<double>::failure(m_error);
0032   }
0033 
0034   // Calculate the angles of the vectors
0035   double phi1 = VectorHelpers::phi(pos1 - posVertex);
0036   double theta1 = VectorHelpers::theta(pos1 - posVertex);
0037   double phi2 = VectorHelpers::phi(pos2 - posVertex);
0038   double theta2 = VectorHelpers::theta(pos2 - posVertex);
0039   // Calculate the squared difference between the theta angles
0040   double diffTheta2 = (theta1 - theta2) * (theta1 - theta2);
0041   if (diffTheta2 > maxAngleTheta2) {
0042     return Result<double>::failure(m_error);
0043   }
0044   // Calculate the squared difference between the phi angles
0045   double diffPhi2 = (phi1 - phi2) * (phi1 - phi2);
0046   if (diffPhi2 > maxAnglePhi2) {
0047     return Result<double>::failure(m_error);
0048   }
0049   // Return the squared distance between both vector
0050   return Result<double>::success(diffTheta2 + diffPhi2);
0051 }
0052 
0053 std::tuple<Vector3, std::optional<ActsScalar>, Vector2,
0054            std::optional<ActsScalar>>
0055 SpacePointUtility::globalCoords(
0056     const GeometryContext& gctx, const SourceLink& slink,
0057     const SourceLinkSurfaceAccessor& surfaceAccessor, const BoundVector& par,
0058     const BoundSquareMatrix& cov) const {
0059   const Surface* surface = surfaceAccessor(slink);
0060   Vector2 localPos(par[eBoundLoc0], par[eBoundLoc1]);
0061   SquareMatrix2 localCov = cov.block<2, 2>(eBoundLoc0, eBoundLoc0);
0062   Vector3 globalPos = surface->localToGlobal(gctx, localPos, Vector3());
0063   RotationMatrix3 rotLocalToGlobal =
0064       surface->referenceFrame(gctx, globalPos, Vector3());
0065 
0066   // the space point requires only the variance of the transverse and
0067   // longitudinal position. reduce computations by transforming the
0068   // covariance directly from local to rho/z.
0069   //
0070   // compute Jacobian from global coordinates to rho/z
0071   //
0072   //         rho = sqrt(x² + y²)
0073   // drho/d{x,y} = (1 / sqrt(x² + y²)) * 2 * {x,y}
0074   //             = 2 * {x,y} / r
0075   //       dz/dz = 1
0076   //
0077   auto x = globalPos[ePos0];
0078   auto y = globalPos[ePos1];
0079   auto scale = 2 / std::hypot(x, y);
0080   ActsMatrix<2, 3> jacXyzToRhoZ = ActsMatrix<2, 3>::Zero();
0081   jacXyzToRhoZ(0, ePos0) = scale * x;
0082   jacXyzToRhoZ(0, ePos1) = scale * y;
0083   jacXyzToRhoZ(1, ePos2) = 1;
0084   // compute Jacobian from local coordinates to rho/z
0085   ActsMatrix<2, 2> jac =
0086       jacXyzToRhoZ * rotLocalToGlobal.block<3, 2>(ePos0, ePos0);
0087   // compute rho/z variance
0088   ActsVector<2> var = (jac * localCov * jac.transpose()).diagonal();
0089 
0090   auto gcov = Vector2(var[0], var[1]);
0091 
0092   // optionally set time
0093   // TODO the current condition of checking the covariance is not optional but
0094   // should do for now
0095   std::optional<ActsScalar> globalTime = par[eBoundTime];
0096   std::optional<ActsScalar> tcov = cov(eBoundTime, eBoundTime);
0097   if (tcov.value() <= 0) {
0098     globalTime = std::nullopt;
0099     tcov = std::nullopt;
0100   }
0101 
0102   return std::make_tuple(globalPos, globalTime, gcov, tcov);
0103 }
0104 
0105 Vector2 SpacePointUtility::calcRhoZVars(
0106     const GeometryContext& gctx, const SourceLink& slinkFront,
0107     const SourceLink& slinkBack,
0108     const SourceLinkSurfaceAccessor& surfaceAccessor,
0109     const ParamCovAccessor& paramCovAccessor, const Vector3& globalPos,
0110     const double theta) const {
0111   const auto var1 = paramCovAccessor(slinkFront).second(0, 0);
0112   const auto var2 = paramCovAccessor(slinkBack).second(0, 0);
0113 
0114   // strip1 and strip2 are tilted at +/- theta/2
0115   double sigma_x = std::hypot(var1, var2) / (2 * sin(theta * 0.5));
0116   double sigma_y = std::hypot(var1, var2) / (2 * cos(theta * 0.5));
0117 
0118   // projection to the surface with strip1.
0119   double sig_x1 = sigma_x * cos(0.5 * theta) + sigma_y * sin(0.5 * theta);
0120   double sig_y1 = sigma_y * cos(0.5 * theta) + sigma_x * sin(0.5 * theta);
0121   SquareMatrix2 lcov;
0122   lcov << sig_x1, 0, 0, sig_y1;
0123 
0124   const Surface& surface = *surfaceAccessor(slinkFront);
0125 
0126   auto gcov = rhoZCovariance(gctx, surface, globalPos, lcov);
0127   return gcov;
0128 }
0129 
0130 Vector2 SpacePointUtility::rhoZCovariance(const GeometryContext& gctx,
0131                                           const Surface& surface,
0132                                           const Vector3& globalPos,
0133                                           const SquareMatrix2& localCov) const {
0134   Vector3 globalFakeMom(1, 1, 1);
0135 
0136   RotationMatrix3 rotLocalToGlobal =
0137       surface.referenceFrame(gctx, globalPos, globalFakeMom);
0138 
0139   auto x = globalPos[ePos0];
0140   auto y = globalPos[ePos1];
0141   auto scale = 2 / std::hypot(x, y);
0142   ActsMatrix<2, 3> jacXyzToRhoZ = ActsMatrix<2, 3>::Zero();
0143   jacXyzToRhoZ(0, ePos0) = scale * x;
0144   jacXyzToRhoZ(0, ePos1) = scale * y;
0145   jacXyzToRhoZ(1, ePos2) = 1;
0146   // compute Jacobian from local coordinates to rho/z
0147   ActsMatrix<2, 2> jac =
0148       jacXyzToRhoZ * rotLocalToGlobal.block<3, 2>(ePos0, ePos0);
0149   // compute rho/z variance
0150   ActsVector<2> var = (jac * localCov * jac.transpose()).diagonal();
0151 
0152   auto gcov = Vector2(var[0], var[1]);
0153 
0154   return gcov;
0155 }
0156 
0157 Result<void> SpacePointUtility::calculateStripSPPosition(
0158     const std::pair<Vector3, Vector3>& stripEnds1,
0159     const std::pair<Vector3, Vector3>& stripEnds2, const Vector3& posVertex,
0160     SpacePointParameters& spParams, const double stripLengthTolerance) const {
0161   /// The following algorithm is meant for finding the position on the first
0162   /// strip if there is a corresponding Measurement on the second strip. The
0163   /// resulting point is a point x on the first surfaces. This point is
0164   /// along a line between the points a (top end of the strip)
0165   /// and b (bottom end of the strip). The location can be parametrized as
0166   ///   2 * x = (1 + m) a + (1 - m) b
0167   /// as function of the scalar m. m is a parameter in the interval
0168   /// -1 < m < 1 since the hit was on the strip. Furthermore, the vector
0169   /// from the vertex to the Measurement on the second strip y is needed to be a
0170   /// multiple k of the vector from vertex to the hit on the first strip x.
0171   /// As a consequence of this demand y = k * x needs to be on the
0172   /// connecting line between the top (c) and bottom (d) end of
0173   /// the second strip. If both measurements correspond to each other, the
0174   /// condition
0175   ///   y * (c X d) = k * x (c X d) = 0 ("X" represents a cross product)
0176   /// needs to be fulfilled. Inserting the first equation into this
0177   /// equation leads to the condition for m as given in the following
0178   /// algorithm and therefore to the calculation of x.
0179   /// The same calculation can be repeated for y. Its corresponding
0180   /// parameter will be named n.
0181 
0182   spParams.firstBtmToTop = stripEnds1.first - stripEnds1.second;
0183   spParams.secondBtmToTop = stripEnds2.first - stripEnds2.second;
0184   spParams.vtxToFirstMid2 =
0185       stripEnds1.first + stripEnds1.second - 2 * posVertex;
0186   spParams.vtxToSecondMid2 =
0187       stripEnds2.first + stripEnds2.second - 2 * posVertex;
0188   spParams.firstBtmToTopXvtxToFirstMid2 =
0189       spParams.firstBtmToTop.cross(spParams.vtxToFirstMid2);
0190   spParams.secondBtmToTopXvtxToSecondMid2 =
0191       spParams.secondBtmToTop.cross(spParams.vtxToSecondMid2);
0192   spParams.m =
0193       -spParams.vtxToFirstMid2.dot(spParams.secondBtmToTopXvtxToSecondMid2) /
0194       spParams.firstBtmToTop.dot(spParams.secondBtmToTopXvtxToSecondMid2);
0195   spParams.n =
0196       -spParams.vtxToSecondMid2.dot(spParams.firstBtmToTopXvtxToFirstMid2) /
0197       spParams.secondBtmToTop.dot(spParams.firstBtmToTopXvtxToFirstMid2);
0198 
0199   // Set the limit for the parameter
0200   if (spParams.limit == 1. && stripLengthTolerance != 0.) {
0201     spParams.limit = 1. + stripLengthTolerance;
0202   }
0203 
0204   // Check if m and n can be resolved in the interval (-1, 1)
0205   if (fabs(spParams.m) <= spParams.limit &&
0206       fabs(spParams.n) <= spParams.limit) {
0207     return Result<void>::success();
0208   }
0209   return Result<void>::failure(m_error);
0210 }
0211 
0212 Result<void> SpacePointUtility::recoverSpacePoint(
0213     SpacePointParameters& spParams, double stripLengthGapTolerance) const {
0214   // Consider some cases that would allow an easy exit
0215   // Check if the limits are allowed to be increased
0216   if (stripLengthGapTolerance <= 0.) {
0217     return Result<void>::failure(m_error);
0218   }
0219 
0220   spParams.mag_firstBtmToTop = spParams.firstBtmToTop.norm();
0221   // Increase the limits. This allows a check if the point is just slightly
0222   // outside the SDE
0223   spParams.limitExtended =
0224       spParams.limit + stripLengthGapTolerance / spParams.mag_firstBtmToTop;
0225 
0226   // Check if m is just slightly outside
0227   if (fabs(spParams.m) > spParams.limitExtended) {
0228     return Result<void>::failure(m_error);
0229   }
0230   // Calculate n if not performed previously
0231   if (spParams.n == 0.) {
0232     spParams.n =
0233         -spParams.vtxToSecondMid2.dot(spParams.firstBtmToTopXvtxToFirstMid2) /
0234         spParams.secondBtmToTop.dot(spParams.firstBtmToTopXvtxToFirstMid2);
0235   }
0236   // Check if n is just slightly outside
0237   if (fabs(spParams.n) > spParams.limitExtended) {
0238     return Result<void>::failure(m_error);
0239   }
0240   /// The following code considers an overshoot of m and n in the same direction
0241   /// of their SDE. The term "overshoot" represents the amount of m or n outside
0242   /// its regular interval (-1, 1).
0243   /// It calculates which overshoot is worse. In order to compare both, the
0244   /// overshoot in n is projected onto the first surface by considering the
0245   /// normalized projection of r onto q.
0246   /// This allows a rescaling of the overshoot. The worse overshoot will be set
0247   /// to +/-1, the parameter with less overshoot will be moved towards 0 by the
0248   /// worse overshoot.
0249   /// In order to treat both SDEs equally, the rescaling eventually needs to be
0250   /// performed several times. If these shifts allows m and n to be in the
0251   /// limits, the space point can be stored.
0252   /// @note This shift can be understood as a shift of the particle's
0253   /// trajectory. This is leads to a shift of the vertex. Since these two points
0254   /// are treated independently from other measurement, it is also possible to
0255   /// consider this as a change in the slope of the particle's trajectory.
0256   ///  The would also move the vertex position.
0257 
0258   // Calculate the scaling factor to project lengths of the second SDE on the
0259   // first SDE
0260   double secOnFirstScale =
0261       spParams.firstBtmToTop.dot(spParams.secondBtmToTop) /
0262       (spParams.mag_firstBtmToTop * spParams.mag_firstBtmToTop);
0263   // Check if both overshoots are in the same direction
0264   if (spParams.m > 1. && spParams.n > 1.) {
0265     // Calculate the overshoots
0266     double mOvershoot = spParams.m - 1.;
0267     double nOvershoot =
0268         (spParams.n - 1.) * secOnFirstScale;  // Perform projection
0269     // Resolve worse overshoot
0270     double biggerOvershoot = std::max(mOvershoot, nOvershoot);
0271     // Move m and n towards 0
0272     spParams.m -= biggerOvershoot;
0273     spParams.n -= (biggerOvershoot / secOnFirstScale);
0274     // Check if this recovered the space point
0275 
0276     if (fabs(spParams.m) < spParams.limit &&
0277         fabs(spParams.n) < spParams.limit) {
0278       return Result<void>::success();
0279     } else {
0280       return Result<void>::failure(m_error);
0281     }
0282   }
0283   // Check if both overshoots are in the same direction
0284   if (spParams.m < -1. && spParams.n < -1.) {
0285     // Calculate the overshoots
0286     double mOvershoot = -(spParams.m + 1.);
0287     double nOvershoot =
0288         -(spParams.n + 1.) * secOnFirstScale;  // Perform projection
0289     // Resolve worse overshoot
0290     double biggerOvershoot = std::max(mOvershoot, nOvershoot);
0291     // Move m and n towards 0
0292     spParams.m += biggerOvershoot;
0293     spParams.n += (biggerOvershoot / secOnFirstScale);
0294     // Check if this recovered the space point
0295     if (fabs(spParams.m) < spParams.limit &&
0296         fabs(spParams.n) < spParams.limit) {
0297       return Result<void>::success();
0298     }
0299   }
0300   // No solution could be found
0301   return Result<void>::failure(m_error);
0302 }
0303 
0304 Result<double> SpacePointUtility::calcPerpendicularProjection(
0305     const std::pair<Vector3, Vector3>& stripEnds1,
0306     const std::pair<Vector3, Vector3>& stripEnds2,
0307     SpacePointParameters& spParams) const {
0308   /// This approach assumes that no vertex is available. This option aims to
0309   /// approximate the space points from cosmic data.
0310   /// The underlying assumption is that the best point is given by the  closest
0311   /// distance between both lines describing the SDEs.
0312   /// The point x on the first SDE is parametrized as a + lambda0 * q with  the
0313   /// top end a of the strip and the vector q = a - b(ottom end of the  strip).
0314   /// An analogous parametrization is performed of the second SDE with y = c  +
0315   /// lambda1 * r.
0316   /// x get resolved by resolving lambda0 from the condition that |x-y| is  the
0317   /// shortest distance between two skew lines.
0318 
0319   spParams.firstBtmToTop = stripEnds1.first - stripEnds1.second;
0320   spParams.secondBtmToTop = stripEnds2.first - stripEnds2.second;
0321 
0322   Vector3 ac = stripEnds2.first - stripEnds1.first;
0323   double qr = (spParams.firstBtmToTop).dot(spParams.secondBtmToTop);
0324   double denom = spParams.firstBtmToTop.dot(spParams.firstBtmToTop) - qr * qr;
0325   // Check for numerical stability
0326   if (fabs(denom) > 1e-6) {
0327     // Return lambda0
0328     return Result<double>::success(
0329         (ac.dot(spParams.secondBtmToTop) * qr -
0330          ac.dot(spParams.firstBtmToTop) *
0331              (spParams.secondBtmToTop).dot(spParams.secondBtmToTop)) /
0332         denom);
0333   }
0334   return Result<double>::failure(m_error);
0335 }
0336 
0337 }  // namespace Acts