File indexing completed on 2025-08-05 08:10:22
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Plugins/TGeo/TGeoSurfaceConverter.hpp"
0010
0011 #include "Acts/Definitions/Tolerance.hpp"
0012 #include "Acts/Plugins/TGeo/TGeoPrimitivesHelper.hpp"
0013 #include "Acts/Surfaces/AnnulusBounds.hpp"
0014 #include "Acts/Surfaces/ConvexPolygonBounds.hpp"
0015 #include "Acts/Surfaces/CylinderBounds.hpp"
0016 #include "Acts/Surfaces/CylinderSurface.hpp"
0017 #include "Acts/Surfaces/DiscSurface.hpp"
0018 #include "Acts/Surfaces/PlaneSurface.hpp"
0019 #include "Acts/Surfaces/RadialBounds.hpp"
0020 #include "Acts/Surfaces/RectangleBounds.hpp"
0021 #include "Acts/Surfaces/Surface.hpp"
0022 #include "Acts/Surfaces/TrapezoidBounds.hpp"
0023 #include "Acts/Utilities/Helpers.hpp"
0024
0025 #include <algorithm>
0026 #include <array>
0027 #include <cctype>
0028 #include <cstddef>
0029 #include <memory>
0030 #include <stdexcept>
0031 #include <tuple>
0032 #include <utility>
0033 #include <vector>
0034
0035 #include <boost/algorithm/string.hpp>
0036 #include <boost/algorithm/string/predicate.hpp>
0037
0038 #include "RtypesCore.h"
0039 #include "TGeoArb8.h"
0040 #include "TGeoBBox.h"
0041 #include "TGeoBoolNode.h"
0042 #include "TGeoCompositeShape.h"
0043 #include "TGeoMatrix.h"
0044 #include "TGeoShape.h"
0045 #include "TGeoTrd1.h"
0046 #include "TGeoTrd2.h"
0047 #include "TGeoTube.h"
0048
0049 std::tuple<std::shared_ptr<const Acts::CylinderBounds>, const Acts::Transform3,
0050 double>
0051 Acts::TGeoSurfaceConverter::cylinderComponents(const TGeoShape& tgShape,
0052 const Double_t* rotation,
0053 const Double_t* translation,
0054 const std::string& axes,
0055 double scalor) noexcept(false) {
0056 std::shared_ptr<const CylinderBounds> bounds = nullptr;
0057 Transform3 transform = Transform3::Identity();
0058 double thickness = 0.;
0059
0060
0061 auto tube = dynamic_cast<const TGeoTube*>(&tgShape);
0062 if (tube != nullptr) {
0063 if (!boost::istarts_with(axes, "XY") && !boost::istarts_with(axes, "YX")) {
0064 throw std::invalid_argument(
0065 "TGeoShape -> CylinderSurface (full): can only be converted with "
0066 "'(x/X)(y/Y)(*)' or '(y/Y)(x/X)(*) axes.");
0067 }
0068
0069
0070 int xs = std::islower(axes.at(0)) != 0 ? -1 : 1;
0071 int ys = std::islower(axes.at(1)) != 0 ? -1 : 1;
0072
0073
0074 Vector3 t(scalor * translation[0], scalor * translation[1],
0075 scalor * translation[2]);
0076 bool flipxy = !boost::istarts_with(axes, "X");
0077 Vector3 ax = flipxy ? xs * Vector3(rotation[1], rotation[4], rotation[7])
0078 : xs * Vector3(rotation[0], rotation[3], rotation[6]);
0079 Vector3 ay = flipxy ? ys * Vector3(rotation[0], rotation[3], rotation[6])
0080 : ys * Vector3(rotation[1], rotation[4], rotation[7]);
0081 Vector3 az = ax.cross(ay);
0082
0083 double minR = tube->GetRmin() * scalor;
0084 double maxR = tube->GetRmax() * scalor;
0085 double deltaR = maxR - minR;
0086 double medR = 0.5 * (minR + maxR);
0087 double halfZ = tube->GetDz() * scalor;
0088 if (halfZ > deltaR) {
0089 transform = TGeoPrimitivesHelper::makeTransform(ax, ay, az, t);
0090 double halfPhi = M_PI;
0091 double avgPhi = 0.;
0092
0093 auto tubeSeg = dynamic_cast<const TGeoTubeSeg*>(tube);
0094 if (tubeSeg != nullptr) {
0095 double phi1 = toRadian(tubeSeg->GetPhi1());
0096 double phi2 = toRadian(tubeSeg->GetPhi2());
0097 if (std::abs(phi2 - phi1) < M_PI * (1. - s_epsilon)) {
0098 if (!boost::starts_with(axes, "X")) {
0099 throw std::invalid_argument(
0100 "TGeoShape -> CylinderSurface (sectorial): can only be "
0101 "converted "
0102 "with "
0103 "'(X)(y/Y)(*)' axes.");
0104 }
0105 halfPhi = 0.5 * (std::max(phi1, phi2) - std::min(phi1, phi2));
0106 avgPhi = 0.5 * (phi1 + phi2);
0107 }
0108 }
0109 bounds = std::make_shared<CylinderBounds>(medR, halfZ, halfPhi, avgPhi);
0110 thickness = deltaR;
0111 }
0112 }
0113 return {bounds, transform, thickness};
0114 }
0115
0116 std::tuple<std::shared_ptr<const Acts::DiscBounds>, const Acts::Transform3,
0117 double>
0118 Acts::TGeoSurfaceConverter::discComponents(const TGeoShape& tgShape,
0119 const Double_t* rotation,
0120 const Double_t* translation,
0121 const std::string& axes,
0122 double scalor) noexcept(false) {
0123 using Line2D = Eigen::Hyperplane<double, 2>;
0124 std::shared_ptr<const DiscBounds> bounds = nullptr;
0125 Transform3 transform = Transform3::Identity();
0126
0127 double thickness = 0.;
0128
0129 auto compShape = dynamic_cast<const TGeoCompositeShape*>(&tgShape);
0130 if (compShape != nullptr) {
0131 if (!boost::istarts_with(axes, "XY")) {
0132 throw std::invalid_argument(
0133 "TGeoShape -> DiscSurface (Annulus): can only be converted with "
0134 "'(x/X)(y/Y)(*)' "
0135 "axes");
0136 }
0137
0138
0139 Vector3 t(scalor * translation[0], scalor * translation[1],
0140 scalor * translation[2]);
0141 Vector3 ax(rotation[0], rotation[3], rotation[6]);
0142 Vector3 ay(rotation[1], rotation[4], rotation[7]);
0143 Vector3 az(rotation[2], rotation[5], rotation[8]);
0144
0145 transform = TGeoPrimitivesHelper::makeTransform(ax, ay, az, t);
0146
0147 auto interNode = dynamic_cast<TGeoIntersection*>(compShape->GetBoolNode());
0148 if (interNode != nullptr) {
0149 auto baseTube = dynamic_cast<TGeoTubeSeg*>(interNode->GetLeftShape());
0150 if (baseTube != nullptr) {
0151 double rMin = baseTube->GetRmin() * scalor;
0152 double rMax = baseTube->GetRmax() * scalor;
0153 auto maskShape = dynamic_cast<TGeoArb8*>(interNode->GetRightShape());
0154 if (maskShape != nullptr) {
0155 auto maskTransform = interNode->GetRightMatrix();
0156
0157 const Double_t* polyVrt = maskShape->GetVertices();
0158
0159
0160
0161
0162
0163 std::vector<Vector2> vertices;
0164 for (unsigned int v = 0; v < 8; v += 2) {
0165 std::array<double, 3> local{polyVrt[v + 0], polyVrt[v + 1], 0.};
0166 std::array<double, 3> global{};
0167 maskTransform->LocalToMaster(local.data(), global.data());
0168 Vector2 vtx = Vector2(global[0] * scalor, global[1] * scalor);
0169 vertices.push_back(vtx);
0170 }
0171
0172 std::vector<std::pair<Vector2, Vector2>> boundLines;
0173 for (std::size_t i = 0; i < vertices.size(); ++i) {
0174 Vector2 a = vertices.at(i);
0175 Vector2 b = vertices.at((i + 1) % vertices.size());
0176 Vector2 ab = b - a;
0177 double phi = VectorHelpers::phi(ab);
0178
0179 if (std::abs(phi) > 3 * M_PI / 4. || std::abs(phi) < M_PI / 4.) {
0180 if (a.norm() < b.norm()) {
0181 boundLines.push_back(std::make_pair(a, b));
0182 } else {
0183 boundLines.push_back(std::make_pair(b, a));
0184 }
0185 }
0186 }
0187
0188 if (boundLines.size() != 2) {
0189 throw std::logic_error(
0190 "Input DiscPoly bounds type does not have sensible edges.");
0191 }
0192
0193 Line2D lA =
0194 Line2D::Through(boundLines[0].first, boundLines[0].second);
0195 Line2D lB =
0196 Line2D::Through(boundLines[1].first, boundLines[1].second);
0197 Vector2 ix = lA.intersection(lB);
0198
0199 const Eigen::Translation3d originTranslation(ix.x(), ix.y(), 0.);
0200 const Vector2 originShift = -ix;
0201
0202
0203 transform = transform * originTranslation;
0204
0205 double phi1 =
0206 VectorHelpers::phi(boundLines[0].second - boundLines[0].first);
0207 double phi2 =
0208 VectorHelpers::phi(boundLines[1].second - boundLines[1].first);
0209 double phiMax = std::max(phi1, phi2);
0210 double phiMin = std::min(phi1, phi2);
0211 double phiShift = 0.;
0212
0213
0214 auto annulusBounds = std::make_shared<const AnnulusBounds>(
0215 rMin, rMax, phiMin, phiMax, originShift, phiShift);
0216
0217 thickness = maskShape->GetDZ() * scalor;
0218 bounds = annulusBounds;
0219 }
0220 }
0221 }
0222 } else {
0223
0224 auto tube = dynamic_cast<const TGeoTube*>(&tgShape);
0225 if (tube != nullptr) {
0226 if (!boost::istarts_with(axes, "XY") &&
0227 !boost::istarts_with(axes, "YX")) {
0228 throw std::invalid_argument(
0229 "TGeoShape -> DiscSurface: can only be converted with "
0230 "'(x/X)(y/Y)(*)' or '(y/Y)(x/X)(*) axes.");
0231 }
0232
0233
0234 int xs = std::islower(axes.at(0)) != 0 ? -1 : 1;
0235 int ys = std::islower(axes.at(1)) != 0 ? -1 : 1;
0236
0237
0238 Vector3 t(scalor * translation[0], scalor * translation[1],
0239 scalor * translation[2]);
0240 Vector3 ax = xs * Vector3(rotation[0], rotation[3], rotation[6]);
0241 Vector3 ay = ys * Vector3(rotation[1], rotation[4], rotation[7]);
0242 Vector3 az = ax.cross(ay);
0243 transform = TGeoPrimitivesHelper::makeTransform(ax, ay, az, t);
0244
0245 double minR = tube->GetRmin() * scalor;
0246 double maxR = tube->GetRmax() * scalor;
0247 double halfZ = tube->GetDz() * scalor;
0248 double halfPhi = M_PI;
0249 double avgPhi = 0.;
0250
0251 auto tubeSeg = dynamic_cast<const TGeoTubeSeg*>(tube);
0252 if (tubeSeg != nullptr) {
0253 double phi1 = toRadian(tubeSeg->GetPhi1());
0254 double phi2 = toRadian(tubeSeg->GetPhi2());
0255 if (std::abs(phi2 - phi1) < 2 * M_PI * (1. - s_epsilon)) {
0256 if (!boost::starts_with(axes, "X")) {
0257 throw std::invalid_argument(
0258 "TGeoShape -> CylinderSurface (sectorial): can only be "
0259 "converted "
0260 "with "
0261 "'(X)(y/Y)(*)' axes.");
0262 }
0263 halfPhi = 0.5 * (std::max(phi1, phi2) - std::min(phi1, phi2));
0264 avgPhi = 0.5 * (phi1 + phi2);
0265 }
0266 }
0267 bounds = std::make_shared<RadialBounds>(minR, maxR, halfPhi, avgPhi);
0268 thickness = 2 * halfZ;
0269 }
0270 }
0271 return {bounds, transform, thickness};
0272 }
0273
0274 std::tuple<std::shared_ptr<const Acts::PlanarBounds>, const Acts::Transform3,
0275 double>
0276 Acts::TGeoSurfaceConverter::planeComponents(const TGeoShape& tgShape,
0277 const Double_t* rotation,
0278 const Double_t* translation,
0279 const std::string& axes,
0280 double scalor) noexcept(false) {
0281
0282 Vector3 t(scalor * translation[0], scalor * translation[1],
0283 scalor * translation[2]);
0284 Vector3 ax(rotation[0], rotation[3], rotation[6]);
0285 Vector3 ay(rotation[1], rotation[4], rotation[7]);
0286 Vector3 az(rotation[2], rotation[5], rotation[8]);
0287
0288 std::shared_ptr<const PlanarBounds> bounds = nullptr;
0289
0290
0291 const TGeoBBox* box = dynamic_cast<const TGeoBBox*>(&tgShape);
0292
0293
0294 const TGeoTrd1* trapezoid1 = dynamic_cast<const TGeoTrd1*>(&tgShape);
0295 if ((trapezoid1 != nullptr) && !boost::istarts_with(axes, "XZ")) {
0296 throw std::invalid_argument(
0297 "TGeoTrd1 -> PlaneSurface: can only be converted with '(x/X)(z/Z)(*)' "
0298 "axes");
0299 }
0300
0301
0302 const TGeoTrd2* trapezoid2 = dynamic_cast<const TGeoTrd2*>(&tgShape);
0303 if (trapezoid2 != nullptr) {
0304 if (!boost::istarts_with(axes, "X") &&
0305 std::abs(trapezoid2->GetDx1() - trapezoid2->GetDx2()) > s_epsilon) {
0306 throw std::invalid_argument(
0307 "TGeoTrd2 -> PlaneSurface: dx1 must be be equal to dx2 if not taken "
0308 "as trapezoidal side.");
0309 } else if (!boost::istarts_with(axes, "Y") &&
0310 std::abs(trapezoid2->GetDy1() - trapezoid2->GetDy2()) >
0311 s_epsilon) {
0312 throw std::invalid_argument(
0313 "TGeoTrd2 -> PlaneSurface: dy1 must be be equal to dy2 if not taken "
0314 "as trapezoidal side.");
0315 }
0316
0317 if (boost::istarts_with(axes, "XY") || boost::istarts_with(axes, "YX")) {
0318 throw std::invalid_argument(
0319 "TGeoTrd2 -> PlaneSurface: only works with (x/X)(z/Z) and "
0320 "(y/Y)(z/Z).");
0321 }
0322 }
0323
0324
0325 const TGeoArb8* polygon8c = dynamic_cast<const TGeoArb8*>(&tgShape);
0326 TGeoArb8* polygon8 = nullptr;
0327 if (polygon8c != nullptr) {
0328
0329 polygon8 = const_cast<TGeoArb8*>(polygon8c);
0330 }
0331
0332 if ((polygon8c != nullptr) &&
0333 !(boost::istarts_with(axes, "XY") || boost::istarts_with(axes, "YX"))) {
0334 throw std::invalid_argument(
0335 "TGeoArb8 -> PlaneSurface: dz must be normal component of Surface.");
0336 }
0337
0338
0339 double thickness = 0.;
0340
0341
0342 int xs = std::islower(axes.at(0)) != 0 ? -1 : 1;
0343 int ys = std::islower(axes.at(1)) != 0 ? -1 : 1;
0344
0345
0346 Vector3 cx = xs * ax;
0347 Vector3 cy = ys * ay;
0348 if (boost::istarts_with(axes, "XY")) {
0349 if (trapezoid2 != nullptr) {
0350 double dx1 = (ys < 0) ? trapezoid1->GetDx2() : trapezoid1->GetDx1();
0351 double dx2 = (ys < 0) ? trapezoid1->GetDx1() : trapezoid1->GetDx2();
0352 bounds = std::make_shared<const TrapezoidBounds>(
0353 scalor * dx1, scalor * dx2, scalor * trapezoid2->GetDy1());
0354 thickness = 2 * scalor * trapezoid2->GetDz();
0355 } else if (polygon8 != nullptr) {
0356 Double_t* tgverts = polygon8->GetVertices();
0357 std::vector<Vector2> pVertices;
0358 for (unsigned int ivtx = 0; ivtx < 4; ++ivtx) {
0359 pVertices.push_back(Vector2(scalor * xs * tgverts[ivtx * 2],
0360 scalor * ys * tgverts[ivtx * 2 + 1]));
0361 }
0362 bounds = std::make_shared<ConvexPolygonBounds<4>>(pVertices);
0363 thickness = 2 * scalor * polygon8->GetDz();
0364 } else if (box != nullptr) {
0365 bounds = std::make_shared<const RectangleBounds>(scalor * box->GetDX(),
0366 scalor * box->GetDY());
0367 thickness = 2 * scalor * box->GetDZ();
0368 }
0369 } else if (boost::istarts_with(axes, "YZ")) {
0370 cx = xs * ay;
0371 cy = ys * az;
0372 if (trapezoid1 != nullptr) {
0373 throw std::invalid_argument(
0374 "TGeoTrd1 can only be converted with '(x/X)(z/Z)(y/Y)' axes");
0375 } else if (trapezoid2 != nullptr) {
0376 double dx1 = (ys < 0) ? trapezoid2->GetDy2() : trapezoid2->GetDy1();
0377 double dx2 = (ys < 0) ? trapezoid2->GetDy1() : trapezoid2->GetDy2();
0378 bounds = std::make_shared<const TrapezoidBounds>(
0379 scalor * dx1, scalor * dx2, scalor * trapezoid2->GetDz());
0380 thickness = 2 * scalor * trapezoid2->GetDx1();
0381 } else if (box != nullptr) {
0382 bounds = std::make_shared<const RectangleBounds>(scalor * box->GetDY(),
0383 scalor * box->GetDZ());
0384 thickness = 2 * scalor * box->GetDX();
0385 }
0386 } else if (boost::istarts_with(axes, "ZX")) {
0387 cx = xs * az;
0388 cy = ys * ax;
0389 if (box != nullptr) {
0390 bounds = std::make_shared<const RectangleBounds>(scalor * box->GetDZ(),
0391 scalor * box->GetDX());
0392 thickness = 2 * scalor * box->GetDY();
0393 }
0394 } else if (boost::istarts_with(axes, "XZ")) {
0395 cx = xs * ax;
0396 cy = ys * az;
0397 if (trapezoid1 != nullptr) {
0398 double dx1 = (ys < 0) ? trapezoid1->GetDx2() : trapezoid1->GetDx1();
0399 double dx2 = (ys < 0) ? trapezoid1->GetDx1() : trapezoid1->GetDx2();
0400 bounds = std::make_shared<const TrapezoidBounds>(
0401 scalor * dx1, scalor * dx2, scalor * trapezoid1->GetDz());
0402 thickness = 2 * scalor * trapezoid1->GetDy();
0403 } else if (trapezoid2 != nullptr) {
0404 double dx1 = (ys < 0) ? trapezoid2->GetDx2() : trapezoid2->GetDx1();
0405 double dx2 = (ys < 0) ? trapezoid2->GetDx1() : trapezoid2->GetDx2();
0406 bounds = std::make_shared<const TrapezoidBounds>(
0407 scalor * dx1, scalor * dx2, scalor * trapezoid2->GetDz());
0408 thickness = 2 * scalor * trapezoid2->GetDy1();
0409 } else if (box != nullptr) {
0410 bounds = std::make_shared<const RectangleBounds>(scalor * box->GetDX(),
0411 scalor * box->GetDZ());
0412 thickness = 2 * scalor * box->GetDY();
0413 }
0414 } else if (boost::istarts_with(axes, "YX")) {
0415 cx = xs * ay;
0416 cy = ys * ax;
0417 if (trapezoid2 != nullptr) {
0418 double dx1 = (ys < 0) ? trapezoid2->GetDy2() : trapezoid2->GetDy1();
0419 double dx2 = (ys < 0) ? trapezoid2->GetDy1() : trapezoid2->GetDy2();
0420 bounds = std::make_shared<const TrapezoidBounds>(
0421 scalor * dx1, scalor * dx2, scalor * trapezoid2->GetDx1());
0422 thickness = 2 * scalor * trapezoid2->GetDz();
0423 } else if (polygon8 != nullptr) {
0424 const Double_t* tgverts = polygon8->GetVertices();
0425 std::vector<Vector2> pVertices;
0426 for (unsigned int ivtx = 0; ivtx < 4; ++ivtx) {
0427 pVertices.push_back(Vector2(scalor * xs * tgverts[ivtx * 2 + 1],
0428 scalor * ys * tgverts[ivtx * 2]));
0429 }
0430 bounds = std::make_shared<ConvexPolygonBounds<4>>(pVertices);
0431 thickness = 2 * scalor * polygon8->GetDz();
0432 } else if (box != nullptr) {
0433 bounds = std::make_shared<const RectangleBounds>(scalor * box->GetDY(),
0434 scalor * box->GetDX());
0435 thickness = 2 * scalor * box->GetDZ();
0436 }
0437 } else if (boost::istarts_with(axes, "ZY")) {
0438 cx = xs * az;
0439 cy = ys * ay;
0440 if (box != nullptr) {
0441 bounds = std::make_shared<const RectangleBounds>(scalor * box->GetDZ(),
0442 scalor * box->GetDY());
0443 thickness = 2 * scalor * box->GetDX();
0444 }
0445 } else {
0446 throw std::invalid_argument(
0447 "TGeoConverter: axes definition must be permutation of "
0448 "'(x/X)(y/Y)(z/Z)'");
0449 }
0450
0451
0452 auto cz = cx.cross(cy);
0453 auto transform = TGeoPrimitivesHelper::makeTransform(cx, cy, cz, t);
0454
0455 return {bounds, transform, thickness};
0456 }
0457
0458 std::tuple<std::shared_ptr<Acts::Surface>, Acts::ActsScalar>
0459 Acts::TGeoSurfaceConverter::toSurface(const TGeoShape& tgShape,
0460 const TGeoMatrix& tgMatrix,
0461 const std::string& axes,
0462 double scalor) noexcept(false) {
0463
0464 const Double_t* rotation = tgMatrix.GetRotationMatrix();
0465 const Double_t* translation = tgMatrix.GetTranslation();
0466
0467 auto [cBounds, cTransform, cThickness] =
0468 cylinderComponents(tgShape, rotation, translation, axes, scalor);
0469 if (cBounds != nullptr) {
0470 return {Surface::makeShared<CylinderSurface>(cTransform, cBounds),
0471 cThickness};
0472 }
0473
0474 auto [dBounds, dTransform, dThickness] =
0475 discComponents(tgShape, rotation, translation, axes, scalor);
0476 if (dBounds != nullptr) {
0477 return {Surface::makeShared<DiscSurface>(dTransform, dBounds), dThickness};
0478 }
0479
0480 auto [pBounds, pTransform, pThickness] =
0481 planeComponents(tgShape, rotation, translation, axes, scalor);
0482 if (pBounds != nullptr) {
0483 return {Surface::makeShared<PlaneSurface>(pTransform, pBounds), pThickness};
0484 }
0485
0486 return {nullptr, 0.};
0487 }