Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-26 08:19:03

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/Surfaces/PointSurface.hpp"
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Tolerance.hpp"
0013 #include "Acts/Geometry/GeometryObject.hpp"
0014 #include "Acts/Surfaces/InfiniteBounds.hpp"
0015 #include "Acts/Surfaces/PointBounds.hpp"
0016 #include "Acts/Surfaces/SurfaceBounds.hpp"
0017 #include "Acts/Surfaces/SurfaceError.hpp"
0018 #include "Acts/Utilities/Intersection.hpp"
0019 #include "Acts/Utilities/VectorHelpers.hpp"
0020 #include "Acts/Utilities/detail/OstreamStateGuard.hpp"
0021 
0022 #include <cmath>
0023 #include <iomanip>
0024 #include <iostream>
0025 #include <limits>
0026 #include <utility>
0027 #include <vector>
0028 
0029 namespace Acts {
0030 
0031 namespace {
0032 /// Build the curvilinear measurement frame from the momentum @p direction.
0033 /// Columns are (U, V, T) with T the (normalized) direction, U built from global
0034 /// Z (falling back to global X when the direction is (anti-)parallel to Z).
0035 RotationMatrix3 pointReferenceFrame(const Vector3& direction) {
0036   const Vector3 T = direction.normalized();
0037   const bool standard =
0038       std::abs(T.dot(Vector3::UnitZ())) < s_curvilinearProjTolerance;
0039   Vector3 U = (standard ? Vector3::UnitZ() : Vector3::UnitX()).cross(T);
0040   U.normalize();
0041   const Vector3 V = T.cross(U);
0042 
0043   RotationMatrix3 rframe;
0044   rframe << U, V, T;
0045   return rframe;
0046 }
0047 }  // namespace
0048 
0049 PointSurface::PointSurface(const Vector3& center)
0050     : Surface(Transform3(Translation3(center))), m_bounds(nullptr) {}
0051 
0052 PointSurface::PointSurface(const Vector3& center, double maxDistance)
0053     : Surface(Transform3(Translation3(center))),
0054       m_bounds(std::make_shared<const PointBounds>(maxDistance)) {}
0055 
0056 PointSurface::PointSurface(const Transform3& transform,
0057                            std::shared_ptr<const PointBounds> pbounds)
0058     : Surface(transform), m_bounds(std::move(pbounds)) {}
0059 
0060 PointSurface::PointSurface(const GeometryContext& gctx,
0061                            const PointSurface& other, const Transform3& shift)
0062     : GeometryObject{}, Surface(gctx, other, shift), m_bounds(other.m_bounds) {}
0063 
0064 Vector3 PointSurface::normal(const GeometryContext& /*gctx*/,
0065                              const Vector3& /*pos*/,
0066                              const Vector3& direction) const {
0067   return direction.normalized();
0068 }
0069 
0070 Vector3 PointSurface::referencePosition(const GeometryContext& gctx,
0071                                         AxisDirection /*aDir*/) const {
0072   return center(gctx);
0073 }
0074 
0075 RotationMatrix3 PointSurface::referenceFrame(const GeometryContext& /*gctx*/,
0076                                              const Vector3& /*position*/,
0077                                              const Vector3& direction) const {
0078   return pointReferenceFrame(direction);
0079 }
0080 
0081 Vector3 PointSurface::localToGlobal(const GeometryContext& gctx,
0082                                     const Vector2& lposition,
0083                                     const Vector3& direction) const {
0084   const RotationMatrix3 rframe = pointReferenceFrame(direction);
0085   return center(gctx) + lposition[0] * rframe.col(0) +
0086          lposition[1] * rframe.col(1);
0087 }
0088 
0089 Result<Vector2> PointSurface::globalToLocal(const GeometryContext& gctx,
0090                                             const Vector3& position,
0091                                             const Vector3& direction,
0092                                             double tolerance) const {
0093   // Bring the global position into the measurement frame relative to the point.
0094   const Vector3 localPosition =
0095       referenceFrame(gctx, position, direction).inverse() *
0096       (position - center(gctx));
0097 
0098   // `localPosition.z()` is the distance along the direction. It must vanish for
0099   // `position` to be the point of closest approach (i.e. on the measurement
0100   // plane through the center).
0101   if (std::abs(localPosition.z()) > std::abs(tolerance)) {
0102     return Result<Vector2>::failure(SurfaceError::GlobalPositionNotOnSurface);
0103   }
0104 
0105   return Result<Vector2>::success(localPosition.head<2>());
0106 }
0107 
0108 MultiIntersection3D PointSurface::intersect(
0109     const GeometryContext& gctx, const Vector3& position,
0110     const Vector3& direction, const BoundaryTolerance& boundaryTolerance,
0111     double tolerance) const {
0112   // The point (center) and the track ray (position, direction)
0113   const Vector3 pc = center(gctx);
0114 
0115   // Path length along the track to the point of closest approach. Since the
0116   // measurement plane normal equals the track direction there is always a
0117   // unique crossing, hence no degeneracy check is needed.
0118   const double u = (pc - position).dot(direction);
0119 
0120   IntersectionStatus status = std::abs(u) > std::abs(tolerance)
0121                                   ? IntersectionStatus::reachable
0122                                   : IntersectionStatus::onSurface;
0123   const Vector3 result = position + u * direction;
0124 
0125   // Evaluate the boundary check if requested. m_bounds == nullptr means an
0126   // unbounded point surface, which skips the check.
0127   if (m_bounds != nullptr && !boundaryTolerance.isInfinite()) {
0128     const RotationMatrix3 rframe = pointReferenceFrame(direction);
0129     const Vector3 vecLocal = result - pc;
0130     const Vector2 local(vecLocal.dot(rframe.col(0)),
0131                         vecLocal.dot(rframe.col(1)));
0132     if (!m_bounds->inside(local, boundaryTolerance)) {
0133       status = IntersectionStatus::unreachable;
0134     }
0135   }
0136 
0137   return MultiIntersection3D(Intersection3D(result, u, status));
0138 }
0139 
0140 BoundToFreeMatrix PointSurface::boundToFreeJacobian(
0141     const GeometryContext& gctx, const Vector3& position,
0142     const Vector3& direction) const {
0143   assert(isOnSurface(gctx, position, direction, BoundaryTolerance::Infinite()));
0144 
0145   const RotationMatrix3 rframe = referenceFrame(gctx, position, direction);
0146   const Vector3 U = rframe.col(0);
0147   const Vector3 V = rframe.col(1);
0148   const Vector3 T = rframe.col(2);
0149 
0150   // The local (x, y) position on the measurement plane
0151   const Vector2 local = *globalToLocal(gctx, position, direction,
0152                                        std::numeric_limits<double>::max());
0153   const double loc0 = local.x();
0154   const double loc1 = local.y();
0155 
0156   // Start from the generic jacobian (correct local, direction, time, q/p
0157   // blocks) and add the position/angle coupling that arises because the
0158   // measurement frame rotates with the direction.
0159   BoundToFreeMatrix jacToGlobal =
0160       Surface::boundToFreeJacobian(gctx, position, direction);
0161 
0162   // For the global-Z frame branch the frame axes are
0163   //   U = (-sinPhi, cosPhi, 0)
0164   //   V = (-cosTheta cosPhi, -cosTheta sinPhi, sinTheta)
0165   // with derivatives
0166   //   dU/dPhi = cosTheta V - sinTheta T,   dU/dTheta = 0
0167   //   dV/dPhi = -cosTheta U,               dV/dTheta = T
0168   // and the free position on surface is P = c + loc0 U + loc1 V, so
0169   //   dP/dPhi   = loc0 (cosTheta V - sinTheta T) - loc1 cosTheta U
0170   //   dP/dTheta = loc1 T
0171   // This generalizes the LineSurface patch, which keeps only the loc0 term
0172   // (its second axis is fixed to the line). The closed form assumes the
0173   // standard (Z-based) branch; near direction parallel to Z the frame uses the
0174   // X-based fall-back and this correction is only approximate (as with the
0175   // documented forward-eta limitation of the LineSurface).
0176   const double cosTheta = direction.z();
0177   const double sinTheta = VectorHelpers::perp(direction);
0178 
0179   jacToGlobal.block<3, 1>(eFreePos0, eBoundPhi) =
0180       loc0 * (cosTheta * V - sinTheta * T) - loc1 * cosTheta * U;
0181   jacToGlobal.block<3, 1>(eFreePos0, eBoundTheta) = loc1 * T;
0182 
0183   return jacToGlobal;
0184 }
0185 
0186 FreeToPathMatrix PointSurface::freeToPathDerivative(
0187     const GeometryContext& gctx, const Vector3& position,
0188     const Vector3& direction) const {
0189   assert(isOnSurface(gctx, position, direction, BoundaryTolerance::Infinite()));
0190 
0191   // Path to the PCA: u = (c - position) . direction, with the measurement plane
0192   // normal equal to the direction (so the usual 1/cos term is unity).
0193   FreeToPathMatrix freeToPath = FreeToPathMatrix::Zero();
0194   // d u / d position = -direction
0195   freeToPath.segment<3>(eFreePos0) = -direction.transpose();
0196   // d u / d direction = (center - position); nonzero at the PCA since the
0197   // residual is generally nonzero (unlike the curvilinear surface).
0198   freeToPath.segment<3>(eFreeDir0) = (center(gctx) - position).transpose();
0199 
0200   return freeToPath;
0201 }
0202 
0203 AlignmentToPathMatrix PointSurface::alignmentToPathDerivative(
0204     const GeometryContext& gctx, const Vector3& position,
0205     const Vector3& direction) const {
0206   static_cast<void>(gctx);
0207   static_cast<void>(position);
0208 
0209   assert(isOnSurface(gctx, position, direction, BoundaryTolerance::Infinite()));
0210 
0211   // u = (c - position) . direction, so d u / d center = direction. A point has
0212   // no orientation, hence the rotation block is zero.
0213   AlignmentToPathMatrix alignToPath = AlignmentToPathMatrix::Zero();
0214   alignToPath.segment<3>(eAlignmentCenter0) = direction.transpose();
0215 
0216   return alignToPath;
0217 }
0218 
0219 Matrix<2, 3> PointSurface::localCartesianToBoundLocalDerivative(
0220     const GeometryContext& /*gctx*/, const Vector3& /*position*/) const {
0221   // The bound local coordinates are the (x, y) components of the local 3D
0222   // Cartesian position, so the derivative selects the first two rows. Note the
0223   // local frame here is the transform frame; the direction-dependent
0224   // measurement frame is handled in the jacobians above.
0225   Matrix<2, 3> loc3DToLocBound = Matrix<2, 3>::Zero();
0226   loc3DToLocBound << 1, 0, 0, 0, 1, 0;
0227   return loc3DToLocBound;
0228 }
0229 
0230 double PointSurface::pathCorrection(const GeometryContext& /*gctx*/,
0231                                     const Vector3& /*pos*/,
0232                                     const Vector3& /*mom*/) const {
0233   return 1.;
0234 }
0235 
0236 const SurfaceBounds& PointSurface::bounds() const {
0237   if (m_bounds != nullptr) {
0238     return *m_bounds;
0239   }
0240   return s_noBounds;
0241 }
0242 
0243 const std::shared_ptr<const PointBounds>& PointSurface::boundsPtr() const {
0244   return m_bounds;
0245 }
0246 
0247 void PointSurface::assignSurfaceBounds(
0248     std::shared_ptr<const PointBounds> newBounds) {
0249   m_bounds = std::move(newBounds);
0250 }
0251 
0252 std::string PointSurface::name() const {
0253   return "Acts::PointSurface";
0254 }
0255 
0256 Polyhedron PointSurface::polyhedronRepresentation(
0257     const GeometryContext& gctx, unsigned int /*quarterSegments*/) const {
0258   // Non-physical marker: a small cross centered on the point in the transform
0259   // frame (a point has no extent, this is for display/debugging only).
0260   std::vector<Vector3> vertices;
0261   std::vector<Polyhedron::FaceType> faces;
0262   std::vector<Polyhedron::FaceType> triangularMesh;
0263 
0264   const Transform3& ctransform = localToGlobalTransform(gctx);
0265   const double d = 1.;
0266   vertices.push_back(ctransform * Vector3(-d, 0., 0.));
0267   vertices.push_back(ctransform * Vector3(d, 0., 0.));
0268   vertices.push_back(ctransform * Vector3(0., -d, 0.));
0269   vertices.push_back(ctransform * Vector3(0., d, 0.));
0270   faces.push_back({0, 1});
0271   faces.push_back({2, 3});
0272   triangularMesh.push_back({0, 1, 2});
0273 
0274   return Polyhedron(vertices, faces, triangularMesh);
0275 }
0276 
0277 std::ostream& PointSurface::toStreamImpl(const GeometryContext& gctx,
0278                                          std::ostream& sl) const {
0279   detail::OstreamStateGuard guard{sl};
0280   sl << std::fixed << std::setprecision(7);
0281   sl << "Acts::PointSurface:" << std::endl;
0282   const Vector3& sfCenter = center(gctx);
0283   sl << "     Center position  (x, y, z) = (" << sfCenter.x() << ", "
0284      << sfCenter.y() << ", " << sfCenter.z() << ")";
0285   return sl;
0286 }
0287 
0288 }  // namespace Acts