Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:11:31

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