File indexing completed on 2026-07-31 08:18:45
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Alignment.hpp"
0013 #include "Acts/Definitions/TrackParametrization.hpp"
0014 #include "Acts/Geometry/GeometryContext.hpp"
0015 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0016 #include "Acts/Surfaces/PointSurface.hpp"
0017 #include "Acts/Surfaces/Surface.hpp"
0018 #include "Acts/Utilities/Intersection.hpp"
0019 #include "Acts/Utilities/Result.hpp"
0020 #include "Acts/Utilities/UnitVectors.hpp"
0021 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0022
0023 #include <memory>
0024 #include <numbers>
0025 #include <tuple>
0026 #include <vector>
0027
0028 using namespace Acts;
0029
0030 namespace ActsTests {
0031
0032
0033 GeometryContext tgContext = GeometryContext::dangerouslyDefaultConstruct();
0034
0035 BOOST_AUTO_TEST_SUITE(SurfacesSuite)
0036
0037
0038 BOOST_AUTO_TEST_CASE(PointSurfaceConstruction) {
0039 Vector3 center{1., 2., 3.};
0040
0041
0042 auto unbounded = Surface::makeShared<PointSurface>(center);
0043 BOOST_CHECK_EQUAL(unbounded->type(), Surface::Point);
0044 BOOST_CHECK_EQUAL(unbounded->name(), "Acts::PointSurface");
0045 CHECK_CLOSE_ABS(unbounded->center(tgContext), center, 1e-12);
0046
0047 BOOST_CHECK_EQUAL(unbounded->bounds().type(), SurfaceBounds::eBoundless);
0048 BOOST_CHECK(unbounded->boundsPtr() == nullptr);
0049
0050
0051 auto bounded = Surface::makeShared<PointSurface>(center, 5.);
0052 BOOST_CHECK_EQUAL(bounded->type(), Surface::Point);
0053 BOOST_CHECK_EQUAL(bounded->bounds().type(), SurfaceBounds::ePoint);
0054
0055
0056 auto copied = Surface::makeShared<PointSurface>(*bounded);
0057 BOOST_CHECK_EQUAL(copied->type(), Surface::Point);
0058 BOOST_CHECK(*copied == *bounded);
0059 }
0060
0061
0062 BOOST_AUTO_TEST_CASE(PointSurfaceNormalIsDirection) {
0063 auto surface = Surface::makeShared<PointSurface>(Vector3(0., 0., 0.));
0064 Vector3 dir = Vector3(1., 2., 3.).normalized();
0065 CHECK_CLOSE_ABS(surface->normal(tgContext, Vector3::Zero(), dir), dir, 1e-12);
0066
0067 auto rframe = surface->referenceFrame(tgContext, Vector3::Zero(), dir);
0068 CHECK_CLOSE_ABS(rframe.col(2), dir, 1e-12);
0069
0070 CHECK_CLOSE_ABS(rframe.col(0).dot(rframe.col(1)), 0., 1e-12);
0071 CHECK_CLOSE_ABS(rframe.col(0).norm(), 1., 1e-12);
0072 CHECK_CLOSE_ABS(rframe.col(1).norm(), 1., 1e-12);
0073 }
0074
0075
0076 BOOST_AUTO_TEST_CASE(PointSurfaceRoundTrip) {
0077 Vector3 center{2., -1., 0.5};
0078 auto surface = Surface::makeShared<PointSurface>(center);
0079
0080 auto roundTrip = [&](const Vector3& pos, const Vector3& dir) {
0081 Intersection3D intersection =
0082 surface->intersect(tgContext, pos, dir).closest();
0083 Vector3 global = intersection.position();
0084 Vector2 local = *surface->globalToLocal(tgContext, global, dir);
0085 Vector3 global2 = surface->localToGlobal(tgContext, local, dir);
0086 return std::make_tuple(global, local, global2);
0087 };
0088
0089 {
0090 Vector3 pos = {-0.5, 0.3, 1.2};
0091 Vector3 dir = Vector3(-0.2, -0.7, 0.4).normalized();
0092 auto [global, local, global2] = roundTrip(pos, dir);
0093 CHECK_CLOSE_ABS(global, global2, 1e-10);
0094 }
0095 {
0096 Vector3 pos = {5., 5., 5.};
0097 Vector3 dir = Vector3(1., -1., 0.2).normalized();
0098 auto [global, local, global2] = roundTrip(pos, dir);
0099 CHECK_CLOSE_ABS(global, global2, 1e-10);
0100 }
0101 }
0102
0103
0104 BOOST_AUTO_TEST_CASE(PointSurfaceRoundTripEtaStability) {
0105 Vector3 center = {3., 0., 0.};
0106 auto surface = Surface::makeShared<PointSurface>(center);
0107
0108 const std::vector<double> etas = {0, 1, 2, 3, 5, 10};
0109
0110 for (double eta : etas) {
0111 Vector3 dir = makeDirectionFromPhiEta(std::numbers::pi / 4., eta);
0112 Vector3 pos = center + Vector3(0.7, -0.4, 0.9);
0113
0114 Intersection3D intersection =
0115 surface->intersect(tgContext, pos, dir).closest();
0116 Vector3 global = intersection.position();
0117 Vector2 local = *surface->globalToLocal(tgContext, global, dir);
0118 Vector3 global2 = surface->localToGlobal(tgContext, local, dir);
0119
0120 CHECK_CLOSE_ABS(global, global2, 1e-9);
0121
0122 CHECK_CLOSE_ABS((global - center).dot(dir), 0., 1e-9);
0123 }
0124
0125
0126 for (Vector3 dir : {Vector3(0., 0., 1.), Vector3(0., 0., -1.)}) {
0127 Vector3 pos = center + Vector3(1., 2., 4.);
0128 Intersection3D intersection =
0129 surface->intersect(tgContext, pos, dir).closest();
0130 Vector3 global = intersection.position();
0131 Vector2 local = *surface->globalToLocal(tgContext, global, dir);
0132 Vector3 global2 = surface->localToGlobal(tgContext, local, dir);
0133 CHECK_CLOSE_ABS(global, global2, 1e-9);
0134 CHECK_CLOSE_ABS((global - center).dot(dir), 0., 1e-9);
0135 }
0136 }
0137
0138
0139 BOOST_AUTO_TEST_CASE(PointSurfaceIntersection) {
0140 Vector3 center{1., 1., 1.};
0141 auto surface = Surface::makeShared<PointSurface>(center);
0142
0143 Vector3 pos{0., 0., 0.};
0144
0145
0146 const std::vector<Vector3> dirs = {
0147 Vector3(1., 0., 0.), Vector3(0., 1., 0.), Vector3(0., 0., 1.),
0148 Vector3(1., 1., 1.).normalized(), Vector3(-1., 2., -0.5).normalized()};
0149
0150 for (const Vector3& dir : dirs) {
0151 Intersection3D intersection =
0152 surface->intersect(tgContext, pos, dir).closest();
0153 BOOST_CHECK(intersection.isValid());
0154
0155 double u = (center - pos).dot(dir);
0156 Vector3 expected = pos + u * dir;
0157 CHECK_CLOSE_ABS(intersection.position(), expected, 1e-10);
0158 CHECK_CLOSE_ABS(intersection.pathLength(), u, 1e-10);
0159
0160 CHECK_CLOSE_ABS((intersection.position() - center).dot(dir), 0., 1e-10);
0161 }
0162
0163
0164 Vector3 dir = Vector3(1., 0., 0.);
0165 Vector3 pca = pos + (center - pos).dot(dir) * dir;
0166 Intersection3D onSurf = surface->intersect(tgContext, pca, dir).closest();
0167 BOOST_CHECK_EQUAL(onSurf.status(), IntersectionStatus::onSurface);
0168 }
0169
0170
0171 BOOST_AUTO_TEST_CASE(PointSurfaceIntersectionBounds) {
0172 Vector3 center{0., 0., 0.};
0173
0174 auto bounded = Surface::makeShared<PointSurface>(center, 1.);
0175 auto unbounded = Surface::makeShared<PointSurface>(center);
0176
0177
0178 Vector3 pos{2., 0., 0.};
0179 Vector3 dir = Vector3(0., 0., 1.);
0180
0181 auto within = BoundaryTolerance::None();
0182
0183 Intersection3D boundedHit =
0184 bounded->intersect(tgContext, pos, dir, within).closest();
0185 BOOST_CHECK_EQUAL(boundedHit.status(), IntersectionStatus::unreachable);
0186
0187 Intersection3D unboundedHit =
0188 unbounded->intersect(tgContext, pos, dir, within).closest();
0189 BOOST_CHECK(unboundedHit.isValid());
0190
0191
0192 Vector3 posClose{0.3, 0., 0.};
0193 Intersection3D closeHit =
0194 bounded->intersect(tgContext, posClose, dir, within).closest();
0195 BOOST_CHECK(closeHit.isValid());
0196 }
0197
0198
0199 FreeVector boundToFree(const PointSurface& surface, const BoundVector& b) {
0200 Vector3 dir = makeDirectionFromPhiTheta(b[eBoundPhi], b[eBoundTheta]);
0201 Vector3 pos = surface.localToGlobal(
0202 tgContext, Vector2(b[eBoundLoc0], b[eBoundLoc1]), dir);
0203 FreeVector f = FreeVector::Zero();
0204 f.segment<3>(eFreePos0) = pos;
0205 f[eFreeTime] = b[eBoundTime];
0206 f.segment<3>(eFreeDir0) = dir;
0207 f[eFreeQOverP] = b[eBoundQOverP];
0208 return f;
0209 }
0210
0211
0212 BOOST_AUTO_TEST_CASE(PointSurfaceBoundToFreeJacobian) {
0213 auto surface = Surface::makeShared<PointSurface>(Vector3(1., -2., 0.5));
0214
0215
0216
0217 double phi = 0.6;
0218 double theta = std::numbers::pi / 3.;
0219 BoundVector b;
0220 b << 0.7, -0.4, phi, theta, 0.5, 1.5;
0221
0222 FreeVector f0 = boundToFree(*surface, b);
0223 Vector3 pos0 = f0.segment<3>(eFreePos0);
0224 Vector3 dir0 = f0.segment<3>(eFreeDir0);
0225
0226 BoundToFreeMatrix jac = surface->boundToFreeJacobian(tgContext, pos0, dir0);
0227
0228 double h = 1e-6;
0229 for (int i = 0; i < static_cast<int>(eBoundSize); ++i) {
0230 BoundVector bp = b;
0231 BoundVector bm = b;
0232 bp[i] += h;
0233 bm[i] -= h;
0234 FreeVector numeric =
0235 (boundToFree(*surface, bp) - boundToFree(*surface, bm)) / (2 * h);
0236 for (int j = 0; j < static_cast<int>(eFreeSize); ++j) {
0237 BOOST_CHECK_SMALL(numeric[j] - jac(j, i), 1e-6);
0238 }
0239 }
0240 }
0241
0242
0243 BOOST_AUTO_TEST_CASE(PointSurfaceFreeToPathDerivative) {
0244 Vector3 center{1., -2., 0.5};
0245 auto surface = Surface::makeShared<PointSurface>(center);
0246
0247 Vector3 dir = Vector3(0.3, -0.5, 0.8).normalized();
0248
0249 Vector3 pos = center + Vector3(0.4, 0.1, -0.2);
0250
0251 pos -= (pos - center).dot(dir) * dir;
0252
0253 FreeToPathMatrix analytic =
0254 surface->freeToPathDerivative(tgContext, pos, dir);
0255
0256 auto pathU = [&](const Vector3& p, const Vector3& d) {
0257 return (center - p).dot(d);
0258 };
0259
0260 double h = 1e-7;
0261 for (int k = 0; k < 3; ++k) {
0262 Vector3 pp = pos, pm = pos;
0263 pp[k] += h;
0264 pm[k] -= h;
0265 double numeric = (pathU(pp, dir) - pathU(pm, dir)) / (2 * h);
0266 BOOST_CHECK_SMALL(numeric - analytic(0, eFreePos0 + k), 1e-6);
0267 }
0268 for (int k = 0; k < 3; ++k) {
0269 Vector3 dp = dir, dm = dir;
0270 dp[k] += h;
0271 dm[k] -= h;
0272 double numeric = (pathU(pos, dp) - pathU(pos, dm)) / (2 * h);
0273 BOOST_CHECK_SMALL(numeric - analytic(0, eFreeDir0 + k), 1e-6);
0274 }
0275 }
0276
0277
0278 BOOST_AUTO_TEST_CASE(PointSurfaceAlignmentToPathDerivative) {
0279 Vector3 center{1., -2., 0.5};
0280 auto surface = Surface::makeShared<PointSurface>(center);
0281 Vector3 dir = Vector3(0.3, -0.5, 0.8).normalized();
0282 Vector3 pos = center + Vector3(0.4, 0.1, -0.2);
0283 pos -= (pos - center).dot(dir) * dir;
0284
0285 AlignmentToPathMatrix analytic =
0286 surface->alignmentToPathDerivative(tgContext, pos, dir);
0287
0288
0289 CHECK_CLOSE_ABS(analytic.segment<3>(eAlignmentCenter0).transpose(), dir,
0290 1e-10);
0291
0292 CHECK_CLOSE_ABS(analytic.segment<3>(eAlignmentRotation0).norm(), 0., 1e-12);
0293 }
0294
0295 BOOST_AUTO_TEST_SUITE_END()
0296
0297 }