File indexing completed on 2026-09-02 08:17:59
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Surfaces/AnnulusBounds.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Surfaces/detail/VerticesHelper.hpp"
0013 #include "Acts/Utilities/VectorHelpers.hpp"
0014 #include "Acts/Utilities/detail/OstreamStateGuard.hpp"
0015 #include "Acts/Utilities/detail/periodic.hpp"
0016
0017 #include <algorithm>
0018 #include <cmath>
0019 #include <iomanip>
0020 #include <iostream>
0021 #include <limits>
0022
0023 namespace Acts {
0024
0025 namespace {
0026
0027 Vector2 closestOnSegment(const Vector2& a, const Vector2& b, const Vector2& p,
0028 const SquareMatrix2& metric) {
0029
0030 auto n = b - a;
0031
0032 auto f = (n.transpose() * metric * n).value();
0033
0034 auto u = ((p - a).transpose() * metric * n).value() / f;
0035
0036 return std::clamp(u, 0., 1.) * n + a;
0037 }
0038
0039 double squaredNorm(const Vector2& v, const SquareMatrix2& metric) {
0040 return (v.transpose() * metric * v).value();
0041 }
0042
0043 }
0044
0045 AnnulusBounds::AnnulusBounds(const std::array<double, eSize>& values) noexcept(
0046 false)
0047 : m_values(values), m_moduleOrigin({values[eOriginX], values[eOriginY]}) {
0048 checkConsistency();
0049 m_rotationStripPC = Translation2(Vector2(0, -get(eAveragePhi)));
0050 m_translation = Translation2(m_moduleOrigin);
0051
0052 m_shiftXY = m_moduleOrigin * -1;
0053 m_shiftPC =
0054 Vector2(VectorHelpers::perp(m_shiftXY), VectorHelpers::phi(m_shiftXY));
0055
0056
0057
0058
0059
0060 auto circIx = [](double O_x, double O_y, double r, double phi) -> Vector2 {
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070 double m = std::tan(phi);
0071 Vector2 dir(std::cos(phi), std::sin(phi));
0072 double x1 = (O_x + O_y * m -
0073 std::sqrt(-std::pow(O_x, 2) * std::pow(m, 2) +
0074 2 * O_x * O_y * m - std::pow(O_y, 2) +
0075 std::pow(m, 2) * std::pow(r, 2) + std::pow(r, 2))) /
0076 (std::pow(m, 2) + 1);
0077 double x2 = (O_x + O_y * m +
0078 std::sqrt(-std::pow(O_x, 2) * std::pow(m, 2) +
0079 2 * O_x * O_y * m - std::pow(O_y, 2) +
0080 std::pow(m, 2) * std::pow(r, 2) + std::pow(r, 2))) /
0081 (std::pow(m, 2) + 1);
0082
0083 Vector2 v1(x1, m * x1);
0084 if (v1.dot(dir) > 0) {
0085 return v1;
0086 }
0087 return {x2, m * x2};
0088 };
0089
0090
0091 m_outLeftStripXY =
0092 circIx(m_moduleOrigin[0], m_moduleOrigin[1], get(eMaxR), get(eMaxPhiRel));
0093 m_inLeftStripXY =
0094 circIx(m_moduleOrigin[0], m_moduleOrigin[1], get(eMinR), get(eMaxPhiRel));
0095 m_outRightStripXY =
0096 circIx(m_moduleOrigin[0], m_moduleOrigin[1], get(eMaxR), get(eMinPhiRel));
0097 m_inRightStripXY =
0098 circIx(m_moduleOrigin[0], m_moduleOrigin[1], get(eMinR), get(eMinPhiRel));
0099
0100 m_outLeftStripPC = {m_outLeftStripXY.norm(),
0101 VectorHelpers::phi(m_outLeftStripXY)};
0102 m_inLeftStripPC = {m_inLeftStripXY.norm(),
0103 VectorHelpers::phi(m_inLeftStripXY)};
0104 m_outRightStripPC = {m_outRightStripXY.norm(),
0105 VectorHelpers::phi(m_outRightStripXY)};
0106 m_inRightStripPC = {m_inRightStripXY.norm(),
0107 VectorHelpers::phi(m_inRightStripXY)};
0108
0109 m_outLeftModulePC = stripXYToModulePC(m_outLeftStripXY);
0110 m_inLeftModulePC = stripXYToModulePC(m_inLeftStripXY);
0111 m_outRightModulePC = stripXYToModulePC(m_outRightStripXY);
0112 m_inRightModulePC = stripXYToModulePC(m_inRightStripXY);
0113
0114
0115
0116 Vector2 centerXY = 0.25 * (m_outLeftStripXY + m_inLeftStripXY +
0117 m_outRightStripXY + m_inRightStripXY);
0118
0119
0120
0121 Vector2 centerPC = Vector2(centerXY.norm(), VectorHelpers::phi(centerXY));
0122 m_center = m_rotationStripPC.inverse() * centerPC;
0123 }
0124
0125 std::vector<double> AnnulusBounds::values() const {
0126 return {m_values.begin(), m_values.end()};
0127 }
0128
0129 void AnnulusBounds::checkConsistency() noexcept(false) {
0130 if (get(eMinR) < 0. || get(eMaxR) < 0. || get(eMinR) > get(eMaxR) ||
0131 std::abs(get(eMinR) - get(eMaxR)) < s_epsilon) {
0132 throw std::invalid_argument("AnnulusBounds: invalid radial setup.");
0133 }
0134 if (get(eMinPhiRel) != detail::radian_sym(get(eMinPhiRel)) ||
0135 get(eMaxPhiRel) != detail::radian_sym(get(eMaxPhiRel)) ||
0136 get(eMinPhiRel) > get(eMaxPhiRel)) {
0137 throw std::invalid_argument("AnnulusBounds: invalid phi boundary setup.");
0138 }
0139 if (get(eAveragePhi) != detail::radian_sym(get(eAveragePhi))) {
0140 throw std::invalid_argument("AnnulusBounds: invalid phi positioning.");
0141 }
0142 }
0143
0144 std::vector<Vector2> AnnulusBounds::corners() const {
0145 auto rot = m_rotationStripPC.inverse();
0146
0147 return {rot * m_outRightStripPC, rot * m_outLeftStripPC,
0148 rot * m_inLeftStripPC, rot * m_inRightStripPC};
0149 }
0150
0151 std::vector<Vector2> AnnulusBounds::vertices(
0152 unsigned int quarterSegments) const {
0153 if (quarterSegments > 0u) {
0154 using VectorHelpers::phi;
0155
0156 double phiMinInner = phi(m_inRightStripXY - m_moduleOrigin);
0157 double phiMaxInner = phi(m_inLeftStripXY - m_moduleOrigin);
0158
0159 double phiMinOuter = phi(m_outRightStripXY - m_moduleOrigin);
0160 double phiMaxOuter = phi(m_outLeftStripXY - m_moduleOrigin);
0161
0162
0163 std::vector<Vector2> rvertices =
0164 detail::VerticesHelper::segmentVertices<Vector2, Transform2>(
0165 {get(eMinR), get(eMinR)}, phiMinInner, phiMaxInner, {},
0166 quarterSegments);
0167 std::reverse(rvertices.begin(), rvertices.end());
0168
0169
0170 auto overtices =
0171 detail::VerticesHelper::segmentVertices<Vector2, Transform2>(
0172 {get(eMaxR), get(eMaxR)}, phiMinOuter, phiMaxOuter, {},
0173 quarterSegments);
0174 rvertices.insert(rvertices.end(), overtices.begin(), overtices.end());
0175
0176 std::ranges::for_each(rvertices,
0177 [&](Vector2& rv) { rv += m_moduleOrigin; });
0178 return rvertices;
0179 }
0180 return {m_inLeftStripXY, m_inRightStripXY, m_outRightStripXY,
0181 m_outLeftStripXY};
0182 }
0183
0184 SquareMatrix2 AnnulusBounds::boundToCartesianJacobian(
0185 const Vector2& lposition) const {
0186 SquareMatrix2 j;
0187 j(0, 0) = std::cos(lposition[1] - get(eAveragePhi));
0188 j(0, 1) = -lposition[0] * std::sin(lposition[1] - get(eAveragePhi));
0189 j(1, 0) = std::sin(lposition[1] - get(eAveragePhi));
0190 j(1, 1) = lposition[0] * std::cos(lposition[1] - get(eAveragePhi));
0191 return j;
0192 }
0193
0194 SquareMatrix2 AnnulusBounds::boundToCartesianMetric(
0195 const Vector2& lposition) const {
0196 SquareMatrix2 m;
0197 m(0, 0) = 1;
0198 m(0, 1) = 0;
0199 m(1, 0) = 0;
0200 m(1, 1) = lposition[0] * lposition[0];
0201 return m;
0202 }
0203
0204 bool AnnulusBounds::inside(const Vector2& lposition) const {
0205
0206
0207 Vector2 locpo_rotated = m_rotationStripPC * lposition;
0208 double phiLoc = locpo_rotated[1];
0209 double rLoc = locpo_rotated[0];
0210
0211 if (phiLoc < get(eMinPhiRel) || phiLoc > get(eMaxPhiRel)) {
0212 return false;
0213 }
0214
0215
0216
0217 double r_mod2 = m_shiftPC[0] * m_shiftPC[0] + rLoc * rLoc +
0218 2 * m_shiftPC[0] * rLoc * std::cos(phiLoc - m_shiftPC[1]);
0219
0220 if (r_mod2 < get(eMinR) * get(eMinR) || r_mod2 > get(eMaxR) * get(eMaxR)) {
0221 return false;
0222 }
0223
0224 return true;
0225 }
0226
0227 SquareMatrix2 AnnulusBounds::stripPCToModulePCJacobian(
0228 const Vector2& lpositionRotated) const {
0229 double dphi = get(eAveragePhi);
0230 double phi_strip = lpositionRotated[1];
0231 double r_strip = lpositionRotated[0];
0232 double O_x = m_shiftXY[0];
0233 double O_y = m_shiftXY[1];
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277
0278
0279
0280
0281
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292 double cosDPhiPhiStrip = std::cos(dphi - phi_strip);
0293 double sinDPhiPhiStrip = std::sin(dphi - phi_strip);
0294
0295 double A = O_x * O_x + 2 * O_x * r_strip * cosDPhiPhiStrip + O_y * O_y -
0296 2 * O_y * r_strip * sinDPhiPhiStrip + r_strip * r_strip;
0297 double sqrtA = std::sqrt(A);
0298
0299 double B = cosDPhiPhiStrip;
0300 double C = -sinDPhiPhiStrip;
0301
0302 SquareMatrix2 j;
0303 j(0, 0) = (B * O_x + C * O_y + r_strip) / sqrtA;
0304 j(0, 1) = r_strip * (B * O_y + O_x * sinDPhiPhiStrip) / sqrtA;
0305 j(1, 0) = -(B * O_y - C * O_x) / A;
0306 j(1, 1) = r_strip * (B * O_x + C * O_y + r_strip) / A;
0307 return j;
0308 }
0309
0310 Vector2 AnnulusBounds::closestPoint(const Vector2& lposition,
0311 const SquareMatrix2& metric) const {
0312
0313
0314 Vector2 lpositionRotated = m_rotationStripPC * lposition;
0315
0316 SquareMatrix2 jacobianStripPCToModulePC =
0317 stripPCToModulePCJacobian(lpositionRotated);
0318
0319
0320 SquareMatrix2 metricStripPC = metric;
0321 SquareMatrix2 metricModulePC = jacobianStripPCToModulePC.transpose() *
0322 metricStripPC * jacobianStripPCToModulePC;
0323
0324
0325 double minDist = std::numeric_limits<double>::max();
0326 Vector2 closest = Vector2::Zero();
0327
0328
0329
0330 {
0331 Vector2 currentClosest = closestOnSegment(m_inLeftStripPC, m_outLeftStripPC,
0332 lpositionRotated, metricStripPC);
0333 double currentDist =
0334 squaredNorm(lpositionRotated - currentClosest, metricStripPC);
0335 if (currentDist < minDist) {
0336 minDist = currentDist;
0337 closest = m_rotationStripPC.inverse() * currentClosest;
0338 }
0339 }
0340
0341 {
0342 Vector2 currentClosest = closestOnSegment(
0343 m_inRightStripPC, m_outRightStripPC, lpositionRotated, metricStripPC);
0344 double currentDist =
0345 squaredNorm(lpositionRotated - currentClosest, metricStripPC);
0346 if (currentDist < minDist) {
0347 minDist = currentDist;
0348 closest = m_rotationStripPC.inverse() * currentClosest;
0349 }
0350 }
0351
0352
0353
0354 Vector2 lpositionModulePC = stripPCToModulePC(lpositionRotated);
0355
0356
0357
0358 {
0359 Vector2 currentClosest = closestOnSegment(
0360 m_inLeftModulePC, m_inRightModulePC, lpositionModulePC, metricModulePC);
0361 double currentDist =
0362 squaredNorm(lpositionModulePC - currentClosest, metricModulePC);
0363 if (currentDist < minDist) {
0364 minDist = currentDist;
0365 closest = m_rotationStripPC.inverse() * modulePCToStripPC(currentClosest);
0366 }
0367 }
0368
0369 {
0370 Vector2 currentClosest =
0371 closestOnSegment(m_outLeftModulePC, m_outRightModulePC,
0372 lpositionModulePC, metricModulePC);
0373 double currentDist =
0374 squaredNorm(lpositionModulePC - currentClosest, metricModulePC);
0375 if (currentDist < minDist) {
0376 closest = m_rotationStripPC.inverse() * modulePCToStripPC(currentClosest);
0377 }
0378 }
0379
0380 return closest;
0381 }
0382
0383 Vector2 AnnulusBounds::stripXYToModulePC(const Vector2& vStripXY) const {
0384 Vector2 vModuleXY = vStripXY + m_shiftXY;
0385 return {vModuleXY.norm(), VectorHelpers::phi(vModuleXY)};
0386 }
0387
0388 Vector2 AnnulusBounds::stripPCToModulePC(const Vector2& vStripPC) const {
0389 Vector2 vStripXY = {vStripPC[0] * std::cos(vStripPC[1]),
0390 vStripPC[0] * std::sin(vStripPC[1])};
0391 return stripXYToModulePC(vStripXY);
0392 }
0393
0394 Vector2 AnnulusBounds::modulePCToStripPC(const Vector2& vModulePC) const {
0395 Vector2 vModuleXY = {vModulePC[0] * std::cos(vModulePC[1]),
0396 vModulePC[0] * std::sin(vModulePC[1])};
0397 Vector2 vStripXY = vModuleXY - m_shiftXY;
0398 return {vStripXY.norm(), VectorHelpers::phi(vStripXY)};
0399 }
0400
0401 Vector2 AnnulusBounds::moduleOrigin() const {
0402 return Eigen::Rotation2D<double>(get(eAveragePhi)) * m_moduleOrigin;
0403 }
0404
0405 Vector2 AnnulusBounds::center() const {
0406
0407
0408 return m_center;
0409 }
0410
0411 std::ostream& AnnulusBounds::toStream(std::ostream& sl) const {
0412 detail::OstreamStateGuard guard{sl};
0413 sl << std::fixed << std::setprecision(7);
0414 sl << "Acts::AnnulusBounds: (innerRadius, outerRadius, minPhi, maxPhi) = ";
0415 sl << "(" << get(eMinR) << ", " << get(eMaxR) << ", " << phiMin() << ", "
0416 << phiMax() << ")" << '\n';
0417 sl << " - shift xy = " << m_shiftXY.x() << ", " << m_shiftXY.y() << '\n';
0418 sl << " - shift pc = " << m_shiftPC.x() << ", " << m_shiftPC.y() << '\n';
0419 return sl;
0420 }
0421
0422 }