Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-13 08:16:38

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/SpacePointFormation2/StripSpacePointBuilder.hpp"
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/SpacePointFormation2/PixelSpacePointBuilder.hpp"
0013 #include "Acts/SpacePointFormation2/SpacePointFormationError.hpp"
0014 #include "Acts/Utilities/MathHelpers.hpp"
0015 #include "Acts/Utilities/VectorHelpers.hpp"
0016 
0017 namespace Acts {
0018 
0019 Result<double> StripSpacePointBuilder::computeClusterPairDistance(
0020     const Vector3& globalCluster1, const Vector3& globalCluster2,
0021     const ClusterPairingOptions& options) {
0022   // Check if measurements are close enough to each other
0023   if ((globalCluster1 - globalCluster2).norm() > options.maxDistance) {
0024     return Result<double>::failure(
0025         SpacePointFormationError::ClusterPairDistanceExceeded);
0026   }
0027 
0028   const Vector3 vertexToCluster1 = globalCluster1 - options.vertex;
0029   const Vector3 vertexToCluster2 = globalCluster2 - options.vertex;
0030 
0031   // Calculate the angles of the vectors
0032   const double phi1 = VectorHelpers::phi(vertexToCluster1);
0033   const double theta1 = VectorHelpers::theta(vertexToCluster1);
0034   const double phi2 = VectorHelpers::phi(vertexToCluster2);
0035   const double theta2 = VectorHelpers::theta(vertexToCluster2);
0036 
0037   // Calculate the squared difference between the theta angles
0038   const double diffTheta = std::abs(theta1 - theta2);
0039   if (diffTheta > options.maxAngleTheta) {
0040     return Result<double>::failure(
0041         SpacePointFormationError::ClusterPairThetaDistanceExceeded);
0042   }
0043 
0044   // Calculate the squared difference between the phi angles
0045   const double diffPhi = std::abs(phi1 - phi2);
0046   if (diffPhi > options.maxAnglePhi) {
0047     return Result<double>::failure(
0048         SpacePointFormationError::ClusterPairPhiDistanceExceeded);
0049   }
0050 
0051   // Return the squared distance between both vector
0052   const double distance2 = square(diffTheta) + square(diffPhi);
0053   return Result<double>::success(distance2);
0054 }
0055 
0056 Result<Vector3> StripSpacePointBuilder::computeCosmicSpacePoint(
0057     const StripEnds& stripEnds1, const StripEnds& stripEnds2,
0058     const CosmicOptions& options) {
0059   // This approach assumes that no vertex is available. This option aims to
0060   // approximate the space points from cosmic data.
0061   // The underlying assumption is that the best point is given by the closest
0062   // distance between both lines describing the SDEs. The point x on the first
0063   // SDE is parametrized as a + lambda0 * q with the top end a of the strip and
0064   // the vector q = a - (bottom end of the strip). An analogous parametrization
0065   // is performed of the second SDE with y = c  + lambda1 * r. x get resolved by
0066   // resolving lambda0 from the condition that |x-y| is the shortest distance
0067   // between two skew lines.
0068 
0069   const Vector3 firstBtmToTop = stripEnds1.top - stripEnds1.bottom;
0070   const Vector3 secondBtmToTop = stripEnds2.top - stripEnds2.bottom;
0071 
0072   const Vector3 ac = stripEnds2.top - stripEnds1.top;
0073   const double qr = firstBtmToTop.dot(secondBtmToTop);
0074   const double denom = firstBtmToTop.dot(firstBtmToTop) - qr * qr;
0075   // Check for numerical stability
0076   if (std::abs(denom) < options.tolerance) {
0077     return Result<Vector3>::failure(
0078         SpacePointFormationError::CosmicToleranceNotMet);
0079   }
0080 
0081   const double lambda0 =
0082       (ac.dot(secondBtmToTop) * qr -
0083        ac.dot(firstBtmToTop) * secondBtmToTop.dot(secondBtmToTop)) /
0084       denom;
0085   const Vector3 spacePoint = stripEnds1.top + lambda0 * firstBtmToTop;
0086   return Result<Vector3>::success(spacePoint);
0087 }
0088 
0089 namespace {
0090 
0091 /// @brief Struct for variables related to the calculation of space points
0092 struct FormationState {
0093   /// Vector pointing from bottom to top end of first SDE
0094   Vector3 firstBtmToTop = Vector3::Zero();
0095   /// Vector pointing from bottom to top end of second SDE
0096   Vector3 secondBtmToTop = Vector3::Zero();
0097   /// Twice the vector pointing from vertex to to midpoint of first SDE
0098   Vector3 vtxToFirstMid2 = Vector3::Zero();
0099   /// Twice the vector pointing from vertex to to midpoint of second SDE
0100   Vector3 vtxToSecondMid2 = Vector3::Zero();
0101   /// Cross product between firstBtmToTop and vtxToFirstMid2
0102   Vector3 firstBtmToTopXvtxToFirstMid2 = Vector3::Zero();
0103   /// Cross product between secondBtmToTop and vtxToSecondMid2
0104   Vector3 secondBtmToTopXvtxToSecondMid2 = Vector3::Zero();
0105   /// Parameter that determines the hit position on the first SDE
0106   double m = 0;
0107   /// Parameter that determines the hit position on the second SDE
0108   double n = 0;
0109   /// Regular limit of the absolute values of `m` and `n`
0110   double limit = 1;
0111 };
0112 
0113 /// @brief This function performs a straight forward calculation of a space
0114 /// point and returns whether it was successful or not.
0115 ///
0116 /// @param stripEnds1 Top and bottom end of the first strip
0117 /// @param stripEnds2 Top and bottom end of the second strip
0118 /// @param vertex Position of the vertex
0119 /// @param spParams Data container of the calculations
0120 /// @param stripLengthTolerance Tolerance scaling factor on the strip detector element length
0121 ///
0122 /// @return whether the space point calculation was successful
0123 Result<void> computeConstrainedFormationState(
0124     const StripSpacePointBuilder::StripEnds& stripEnds1,
0125     const StripSpacePointBuilder::StripEnds& stripEnds2, const Vector3& vertex,
0126     FormationState& state, const double stripLengthTolerance) {
0127   /// The following algorithm is meant for finding the position on the first
0128   /// strip if there is a corresponding Measurement on the second strip. The
0129   /// resulting point is a point x on the first surfaces. This point is
0130   /// along a line between the points a (top end of the strip)
0131   /// and b (bottom end of the strip). The location can be parametrized as
0132   ///   2 * x = (1 + m) a + (1 - m) b
0133   /// as function of the scalar m. m is a parameter in the interval
0134   /// -1 < m < 1 since the hit was on the strip. Furthermore, the vector
0135   /// from the vertex to the Measurement on the second strip y is needed to be a
0136   /// multiple k of the vector from vertex to the hit on the first strip x.
0137   /// As a consequence of this demand y = k * x needs to be on the
0138   /// connecting line between the top (c) and bottom (d) end of
0139   /// the second strip. If both measurements correspond to each other, the
0140   /// condition
0141   ///   y * (c X d) = k * x (c X d) = 0 ("X" represents a cross product)
0142   /// needs to be fulfilled. Inserting the first equation into this
0143   /// equation leads to the condition for m as given in the following
0144   /// algorithm and therefore to the calculation of x.
0145   /// The same calculation can be repeated for y. Its corresponding
0146   /// parameter will be named n.
0147 
0148   state.firstBtmToTop = stripEnds1.top - stripEnds1.bottom;
0149   state.secondBtmToTop = stripEnds2.top - stripEnds2.bottom;
0150   state.vtxToFirstMid2 = stripEnds1.top + stripEnds1.bottom - 2 * vertex;
0151   state.vtxToSecondMid2 = stripEnds2.top + stripEnds2.bottom - 2 * vertex;
0152   state.firstBtmToTopXvtxToFirstMid2 =
0153       state.firstBtmToTop.cross(state.vtxToFirstMid2);
0154   state.secondBtmToTopXvtxToSecondMid2 =
0155       state.secondBtmToTop.cross(state.vtxToSecondMid2);
0156   state.m = -state.vtxToFirstMid2.dot(state.secondBtmToTopXvtxToSecondMid2) /
0157             state.firstBtmToTop.dot(state.secondBtmToTopXvtxToSecondMid2);
0158   state.n = -state.vtxToSecondMid2.dot(state.firstBtmToTopXvtxToFirstMid2) /
0159             state.secondBtmToTop.dot(state.firstBtmToTopXvtxToFirstMid2);
0160 
0161   // Set the limit for the parameter
0162   state.limit = 1 + stripLengthTolerance;
0163 
0164   // Check if m and n can be resolved in the interval (-1, 1)
0165   if (std::abs(state.m) > state.limit || std::abs(state.n) > state.limit) {
0166     return Result<void>::failure(SpacePointFormationError::OutsideLimits);
0167   }
0168   return Result<void>::success();
0169 }
0170 
0171 /// @brief This function tests if a space point can be estimated by a more
0172 /// tolerant treatment of construction. In fact, this function indirectly
0173 /// allows shifts of the vertex.
0174 ///
0175 /// @param state container that stores geometric parameters and rules of the space point formation
0176 /// @param stripLengthGapTolerance Tolerance scaling factor of the gap between strip detector elements
0177 ///
0178 /// @return whether the space point calculation was successful
0179 Result<void> recoverConstrainedFormationState(
0180     FormationState& state, const double stripLengthGapTolerance) {
0181   const double magFirstBtmToTop = state.firstBtmToTop.norm();
0182   // Increase the limits. This allows a check if the point is just slightly
0183   // outside the SDE
0184   const double relaxedLimit =
0185       state.limit + stripLengthGapTolerance / magFirstBtmToTop;
0186 
0187   // Check if m is just slightly outside
0188   if (std::abs(state.m) > relaxedLimit) {
0189     return Result<void>::failure(
0190         SpacePointFormationError::OutsideRelaxedLimits);
0191   }
0192   // Calculate n if not performed previously
0193   if (state.n == 0) {
0194     state.n = -state.vtxToSecondMid2.dot(state.firstBtmToTopXvtxToFirstMid2) /
0195               state.secondBtmToTop.dot(state.firstBtmToTopXvtxToFirstMid2);
0196   }
0197   // Check if n is just slightly outside
0198   if (std::abs(state.n) > relaxedLimit) {
0199     return Result<void>::failure(
0200         SpacePointFormationError::OutsideRelaxedLimits);
0201   }
0202 
0203   /// The following code considers an overshoot of m and n in the same direction
0204   /// of their SDE. The term "overshoot" represents the amount of m or n outside
0205   /// its regular interval (-1, 1).
0206   /// It calculates which overshoot is worse. In order to compare both, the
0207   /// overshoot in n is projected onto the first surface by considering the
0208   /// normalized projection of r onto q.
0209   /// This allows a rescaling of the overshoot. The worse overshoot will be set
0210   /// to +/-1, the parameter with less overshoot will be moved towards 0 by the
0211   /// worse overshoot.
0212   /// In order to treat both SDEs equally, the rescaling eventually needs to be
0213   /// performed several times. If these shifts allows m and n to be in the
0214   /// limits, the space point can be stored.
0215   /// @note This shift can be understood as a shift of the particle's
0216   /// trajectory. This is leads to a shift of the vertex. Since these two points
0217   /// are treated independently from other measurement, it is also possible to
0218   /// consider this as a change in the slope of the particle's trajectory.
0219   /// This would also move the vertex position.
0220 
0221   // Calculate the scaling factor to project lengths of the second SDE on the
0222   // first SDE
0223   const double secOnFirstScale =
0224       state.firstBtmToTop.dot(state.secondBtmToTop) / square(magFirstBtmToTop);
0225 
0226   // Check if both overshoots are in the same direction
0227   if (state.m > 1 && state.n > 1) {
0228     // Calculate the overshoots
0229     const double mOvershoot = state.m - 1;
0230     // Perform projection
0231     const double nOvershoot = (state.n - 1) * secOnFirstScale;
0232     // Resolve worse overshoot
0233     const double biggerOvershoot = std::max(mOvershoot, nOvershoot);
0234     // Move m and n towards 0
0235     state.m -= biggerOvershoot;
0236     state.n -= (biggerOvershoot / secOnFirstScale);
0237     // Check if this recovered the space point
0238     if (std::abs(state.m) > state.limit || std::abs(state.n) > state.limit) {
0239       return Result<void>::failure(SpacePointFormationError::OutsideLimits);
0240     }
0241     return Result<void>::success();
0242   }
0243 
0244   // Check if both overshoots are in the same direction
0245   if (state.m < -1 && state.n < -1) {
0246     // Calculate the overshoots
0247     const double mOvershoot = -(state.m + 1);
0248     // Perform projection
0249     const double nOvershoot = -(state.n + 1) * secOnFirstScale;
0250     // Resolve worse overshoot
0251     const double biggerOvershoot = std::max(mOvershoot, nOvershoot);
0252     // Move m and n towards 0
0253     state.m += biggerOvershoot;
0254     state.n += (biggerOvershoot / secOnFirstScale);
0255     // Check if this recovered the space point
0256     if (std::abs(state.m) > state.limit || std::abs(state.n) > state.limit) {
0257       return Result<void>::failure(SpacePointFormationError::OutsideLimits);
0258     }
0259     return Result<void>::success();
0260   }
0261 
0262   // No solution could be found
0263   return Result<void>::failure(SpacePointFormationError::NoSolutionFound);
0264 }
0265 
0266 }  // namespace
0267 
0268 Result<Vector3> StripSpacePointBuilder::computeConstrainedSpacePoint(
0269     const StripEnds& stripEnds1, const StripEnds& stripEnds2,
0270     const ConstrainedOptions& options) {
0271   FormationState state;
0272 
0273   Result<void> result =
0274       computeConstrainedFormationState(stripEnds1, stripEnds2, options.vertex,
0275                                        state, options.stripLengthTolerance);
0276 
0277   if (!result.ok()) {
0278     result = recoverConstrainedFormationState(state,
0279                                               options.stripLengthGapTolerance);
0280   }
0281 
0282   if (!result.ok()) {
0283     return Result<Vector3>::failure(result.error());
0284   }
0285 
0286   const Vector3 spacePoint = 0.5 * (stripEnds1.top + stripEnds1.bottom +
0287                                     state.m * state.firstBtmToTop);
0288   return Result<Vector3>::success(spacePoint);
0289 }
0290 
0291 Vector2 StripSpacePointBuilder::computeVarianceZR(const GeometryContext& gctx,
0292                                                   const Surface& surface1,
0293                                                   const Vector3& spacePoint,
0294                                                   const double localCov1,
0295                                                   const double localCov2,
0296                                                   const double theta) {
0297   const double sinThetaHalf = std::sin(0.5 * theta);
0298   const double cosThetaHalf = std::cos(0.5 * theta);
0299 
0300   // strip1 and strip2 are tilted at +/- theta/2
0301   const double var = fastHypot(localCov1, localCov2);
0302   const double varX = var / (2 * sinThetaHalf);
0303   const double varY = var / (2 * cosThetaHalf);
0304 
0305   // projection to the surface with strip1.
0306   const double varX1 = varX * cosThetaHalf + varY * sinThetaHalf;
0307   const double varY1 = varY * cosThetaHalf + varX * sinThetaHalf;
0308   const SquareMatrix2 localCov = Vector2(varX1, varY1).asDiagonal();
0309 
0310   return PixelSpacePointBuilder::computeVarianceZR(gctx, surface1, spacePoint,
0311                                                    localCov);
0312 }
0313 
0314 }  // namespace Acts