File indexing completed on 2025-07-11 07:50:58
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Plugins/Geant4/Geant4Converters.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0013 #include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
0014 #include "Acts/Material/Material.hpp"
0015 #include "Acts/Material/MaterialSlab.hpp"
0016 #include "Acts/Surfaces/CylinderBounds.hpp"
0017 #include "Acts/Surfaces/CylinderSurface.hpp"
0018 #include "Acts/Surfaces/DiscSurface.hpp"
0019 #include "Acts/Surfaces/LineBounds.hpp"
0020 #include "Acts/Surfaces/PlaneSurface.hpp"
0021 #include "Acts/Surfaces/RadialBounds.hpp"
0022 #include "Acts/Surfaces/RectangleBounds.hpp"
0023 #include "Acts/Surfaces/StrawSurface.hpp"
0024 #include "Acts/Surfaces/TrapezoidBounds.hpp"
0025
0026 #include <algorithm>
0027 #include <cmath>
0028 #include <cstdlib>
0029 #include <iterator>
0030 #include <numbers>
0031 #include <stdexcept>
0032 #include <utility>
0033 #include <vector>
0034
0035 #include "G4Box.hh"
0036 #include "G4LogicalVolume.hh"
0037 #include "G4Material.hh"
0038 #include "G4RotationMatrix.hh"
0039 #include "G4ThreeVector.hh"
0040 #include "G4Transform3D.hh"
0041 #include "G4Trap.hh"
0042 #include "G4Trd.hh"
0043 #include "G4Tubs.hh"
0044 #include "G4VPhysicalVolume.hh"
0045 #include "G4VSolid.hh"
0046
0047 Acts::Transform3 Acts::Geant4AlgebraConverter::transform(
0048 const G4ThreeVector& g4Trans) {
0049 Transform3 gTransform = Transform3::Identity();
0050 Vector3 scaledTrans =
0051 Vector3(scale * g4Trans[0], scale * g4Trans[1], scale * g4Trans[2]);
0052 gTransform.pretranslate(scaledTrans);
0053 return gTransform;
0054 }
0055
0056 Acts::Transform3 Acts::Geant4AlgebraConverter::transform(
0057 const G4RotationMatrix& g4Rot, const G4ThreeVector& g4Trans) {
0058
0059 Vector3 translation(scale * g4Trans[0], scale * g4Trans[1],
0060 scale * g4Trans[2]);
0061
0062 RotationMatrix3 rotation;
0063 rotation << g4Rot.xx(), g4Rot.xy(), g4Rot.xz(), g4Rot.yx(), g4Rot.yy(),
0064 g4Rot.yz(), g4Rot.zx(), g4Rot.zy(), g4Rot.zz();
0065 Transform3 transform = Transform3::Identity();
0066 transform.rotate(rotation);
0067 transform.pretranslate(translation);
0068 return transform;
0069 }
0070
0071 Acts::Transform3 Acts::Geant4AlgebraConverter::transform(
0072 const G4Transform3D& g4Trf) {
0073 auto g4Rot = g4Trf.getRotation();
0074 auto g4Trans = g4Trf.getTranslation();
0075 return transform(g4Rot, g4Trans);
0076 }
0077
0078 Acts::Transform3 Acts::Geant4AlgebraConverter::transform(
0079 const G4VPhysicalVolume& g4PhysVol) {
0080
0081 auto g4Translation = g4PhysVol.GetTranslation();
0082 auto g4Rotation = g4PhysVol.GetRotation();
0083
0084 G4Transform3D g4Transform =
0085 (g4Rotation == nullptr)
0086 ? G4Transform3D(CLHEP::HepRotation(), g4Translation)
0087 : G4Transform3D(*g4Rotation, g4Translation);
0088
0089 return transform(g4Transform);
0090 }
0091
0092 std::tuple<std::shared_ptr<Acts::CylinderBounds>, double>
0093 Acts::Geant4ShapeConverter::cylinderBounds(const G4Tubs& g4Tubs) {
0094 using B = Acts::CylinderBounds;
0095
0096 std::array<double, B::eSize> tArray = {};
0097 tArray[B::eR] =
0098 static_cast<double>(g4Tubs.GetInnerRadius() + g4Tubs.GetOuterRadius()) *
0099 0.5;
0100 tArray[B::eHalfLengthZ] = static_cast<double>(g4Tubs.GetZHalfLength());
0101 tArray[B::eHalfPhiSector] =
0102 0.5 * static_cast<double>(g4Tubs.GetDeltaPhiAngle());
0103
0104
0105 if (std::abs(tArray[B::eHalfPhiSector] - std::numbers::pi) <
0106 std::numeric_limits<double>::epsilon()) {
0107 tArray[B::eAveragePhi] = 0.;
0108 } else {
0109 tArray[B::eAveragePhi] = static_cast<double>(g4Tubs.GetStartPhiAngle()) +
0110 tArray[B::eHalfPhiSector];
0111 }
0112 double thickness = g4Tubs.GetOuterRadius() - g4Tubs.GetInnerRadius();
0113 auto cBounds = std::make_shared<CylinderBounds>(tArray);
0114 return {std::move(cBounds), thickness};
0115 }
0116
0117 std::tuple<std::shared_ptr<Acts::RadialBounds>, double>
0118 Acts::Geant4ShapeConverter::radialBounds(const G4Tubs& g4Tubs) {
0119 using B = Acts::RadialBounds;
0120
0121 std::array<double, B::eSize> tArray = {};
0122 tArray[B::eMinR] = static_cast<double>(g4Tubs.GetInnerRadius());
0123 tArray[B::eMaxR] = static_cast<double>(g4Tubs.GetOuterRadius());
0124 tArray[B::eHalfPhiSector] =
0125 0.5 * static_cast<double>(g4Tubs.GetDeltaPhiAngle());
0126
0127
0128 if (std::abs(tArray[B::eHalfPhiSector] - std::numbers::pi) <
0129 std::numeric_limits<double>::epsilon()) {
0130 tArray[B::eAveragePhi] = 0.;
0131 } else {
0132 tArray[B::eAveragePhi] = static_cast<double>(g4Tubs.GetStartPhiAngle()) +
0133 tArray[B::eHalfPhiSector];
0134 }
0135 double thickness = g4Tubs.GetZHalfLength() * 2;
0136 auto rBounds = std::make_shared<RadialBounds>(tArray);
0137 return {std::move(rBounds), thickness};
0138 }
0139
0140 std::shared_ptr<Acts::LineBounds> Acts::Geant4ShapeConverter::lineBounds(
0141 const G4Tubs& g4Tubs) {
0142 auto r = static_cast<double>(g4Tubs.GetOuterRadius());
0143 auto hlZ = static_cast<double>(g4Tubs.GetZHalfLength());
0144 return std::make_shared<LineBounds>(r, hlZ);
0145 }
0146
0147 std::tuple<std::shared_ptr<Acts::RectangleBounds>, std::array<int, 2u>, double>
0148 Acts::Geant4ShapeConverter::rectangleBounds(const G4Box& g4Box) {
0149 std::vector<double> hG4XYZ = {static_cast<double>(g4Box.GetXHalfLength()),
0150 static_cast<double>(g4Box.GetYHalfLength()),
0151 static_cast<double>(g4Box.GetZHalfLength())};
0152
0153 auto minAt = std::min_element(hG4XYZ.begin(), hG4XYZ.end());
0154 std::size_t minPos = std::distance(hG4XYZ.begin(), minAt);
0155 double thickness = 2. * hG4XYZ[minPos];
0156
0157 std::array<int, 2u> rAxes = {};
0158 switch (minPos) {
0159 case 0: {
0160 rAxes = {1, 2};
0161 } break;
0162 case 1: {
0163 if (keepAxisOrder) {
0164 rAxes = {0, -2};
0165 } else {
0166 rAxes = {2, 0};
0167 }
0168 } break;
0169 case 2: {
0170 rAxes = {0, 1};
0171 } break;
0172 default:
0173 break;
0174 }
0175 auto rBounds = std::make_shared<RectangleBounds>(hG4XYZ[std::abs(rAxes[0u])],
0176 hG4XYZ[std::abs(rAxes[1u])]);
0177 return {std::move(rBounds), rAxes, thickness};
0178 }
0179
0180 std::tuple<std::shared_ptr<Acts::TrapezoidBounds>, std::array<int, 2u>, double>
0181 Acts::Geant4ShapeConverter::trapezoidBounds(const G4Trd& g4Trd) {
0182
0183 double hlX0 = static_cast<double>(g4Trd.GetXHalfLength1());
0184 double hlX1 = static_cast<double>(g4Trd.GetXHalfLength2());
0185 double hlY0 = static_cast<double>(g4Trd.GetYHalfLength1());
0186 double hlY1 = static_cast<double>(g4Trd.GetYHalfLength2());
0187 double hlZ = static_cast<double>(g4Trd.GetZHalfLength());
0188
0189 std::vector<double> dXYZ = {(hlX0 + hlX1) * 0.5, (hlY0 + hlY1) * 0.5, hlZ};
0190
0191 auto minAt = std::min_element(dXYZ.begin(), dXYZ.end());
0192 std::size_t minPos = std::distance(dXYZ.begin(), minAt);
0193 double thickness = 2. * dXYZ[minPos];
0194
0195 double halfLengthXminY = 0.;
0196 double halfLengthXmaxY = 0.;
0197 double halfLengthY = 0.;
0198
0199 std::array<int, 2u> rAxes = {};
0200 switch (minPos) {
0201 case 0: {
0202 halfLengthXminY = hlY0;
0203 halfLengthXmaxY = hlY1;
0204 halfLengthY = hlZ;
0205 rAxes = {1, 2};
0206 } break;
0207 case 1: {
0208 halfLengthXminY = hlX0;
0209 halfLengthXmaxY = hlX1;
0210 halfLengthY = hlZ;
0211 rAxes = {0, -2};
0212 } break;
0213 case 2: {
0214 if (std::abs(hlY0 - hlY1) < std::abs(hlX0 - hlX1)) {
0215 halfLengthXminY = hlX0;
0216 halfLengthXmaxY = hlX1;
0217 halfLengthY = (hlY0 + hlY1) * 0.5;
0218 rAxes = {0, 1};
0219 } else {
0220 halfLengthXminY = hlY0;
0221 halfLengthXmaxY = hlY1;
0222 halfLengthY = (hlX0 + hlX1) * 0.5;
0223 rAxes = {-1, 0};
0224 }
0225 } break;
0226 }
0227
0228 auto tBounds = std::make_shared<TrapezoidBounds>(
0229 halfLengthXminY, halfLengthXmaxY, halfLengthY);
0230 return {std::move(tBounds), rAxes, thickness};
0231 }
0232
0233 std::tuple<std::shared_ptr<Acts::TrapezoidBounds>, std::array<int, 2u>, double>
0234 Acts::Geant4ShapeConverter::trapezoidBounds(const G4Trap& g4Trap) {
0235
0236 auto y1 = static_cast<double>(g4Trap.GetYHalfLength1());
0237 auto y2 = static_cast<double>(g4Trap.GetYHalfLength2());
0238 auto x1 = static_cast<double>(g4Trap.GetXHalfLength1());
0239 auto x2 = static_cast<double>(g4Trap.GetXHalfLength2());
0240 auto x3 = static_cast<double>(g4Trap.GetXHalfLength3());
0241 auto x4 = static_cast<double>(g4Trap.GetXHalfLength4());
0242 auto phi = static_cast<double>(g4Trap.GetPhi());
0243 auto theta = static_cast<double>(g4Trap.GetTheta());
0244 auto z = static_cast<double>(g4Trap.GetZHalfLength());
0245
0246 double hlX0 = (x1 + x2) * 0.5;
0247 double hlX1 = 2 * z * tan(theta) * cos(phi) + (x3 + x4) * 0.5;
0248 double hlY0 = y1;
0249 double hlY1 = y2 + 2 * z * tan(theta) * sin(phi);
0250 double hlZ = z;
0251
0252 std::vector<double> dXYZ = {(hlX0 + hlX1) * 0.5, (hlY0 + hlY1) * 0.5, hlZ};
0253
0254 auto minAt = std::ranges::min_element(dXYZ);
0255 std::size_t minPos = std::distance(dXYZ.begin(), minAt);
0256 double thickness = 2. * dXYZ[minPos];
0257
0258 double halfLengthXminY = 0.;
0259 double halfLengthXmaxY = 0.;
0260 double halfLengthY = 0.;
0261
0262 std::array<int, 2u> rAxes = {};
0263 switch (minPos) {
0264 case 0: {
0265 halfLengthXminY = std::min(hlY0, hlY1);
0266 halfLengthXmaxY = std::max(hlY0, hlY1);
0267 halfLengthY = hlZ;
0268 rAxes = {1, 2};
0269 } break;
0270 case 1: {
0271 halfLengthXminY = std::min(hlX0, hlX1);
0272 halfLengthXmaxY = std::max(hlX0, hlX1);
0273 halfLengthY = hlZ;
0274 rAxes = {0, -2};
0275 } break;
0276 case 2: {
0277 if (std::abs(hlY0 - hlY1) < std::abs(hlX0 - hlX1)) {
0278 halfLengthXminY = std::min(hlX0, hlX1);
0279 halfLengthXmaxY = std::max(hlX0, hlX1);
0280 halfLengthY = (hlY0 + hlY1) * 0.5;
0281 rAxes = {0, 1};
0282 } else {
0283 halfLengthXminY = std::min(hlY0, hlY1);
0284 halfLengthXmaxY = std::max(hlY0, hlY1);
0285 halfLengthY = (hlX0 + hlX1) * 0.5;
0286 rAxes = {-1, 0};
0287 }
0288 } break;
0289 default: {
0290 throw std::runtime_error("Geant4Converters: could not convert G4Trap.");
0291 }
0292 }
0293
0294 auto tBounds = std::make_shared<TrapezoidBounds>(
0295 halfLengthXminY, halfLengthXmaxY, halfLengthY);
0296 return std::make_tuple(std::move(tBounds), rAxes, thickness);
0297 }
0298
0299 std::tuple<std::shared_ptr<Acts::PlanarBounds>, std::array<int, 2u>, double>
0300 Acts::Geant4ShapeConverter::planarBounds(const G4VSolid& g4Solid) {
0301 const G4Box* box = dynamic_cast<const G4Box*>(&g4Solid);
0302 if (box != nullptr) {
0303 auto [rBounds, axes, thickness] = rectangleBounds(*box);
0304 return {std::move(rBounds), axes, thickness};
0305 }
0306
0307 const G4Trd* trd = dynamic_cast<const G4Trd*>(&g4Solid);
0308 if (trd != nullptr) {
0309 auto [tBounds, axes, thickness] = trapezoidBounds(*trd);
0310 return {std::move(tBounds), axes, thickness};
0311 }
0312
0313 std::shared_ptr<Acts::PlanarBounds> pBounds = nullptr;
0314 std::array<int, 2u> rAxes = {};
0315 double rThickness = 0.;
0316 return {std::move(pBounds), rAxes, rThickness};
0317 }
0318
0319 namespace {
0320 Acts::Transform3 axesOriented(const Acts::Transform3& toGlobalOriginal,
0321 const std::array<int, 2u>& axes) {
0322 auto originalRotation = toGlobalOriginal.rotation();
0323 auto colX = originalRotation.col(std::abs(axes[0u]));
0324 auto colY = originalRotation.col(std::abs(axes[1u]));
0325 colX *= std::copysign(1, axes[0u]);
0326 colY *= std::copysign(1, axes[1u]);
0327 Acts::Vector3 colZ = colX.cross(colY);
0328
0329 Acts::Transform3 orientedTransform = Acts::Transform3::Identity();
0330 orientedTransform.matrix().block(0, 0, 3, 1) = colX;
0331 orientedTransform.matrix().block(0, 1, 3, 1) = colY;
0332 orientedTransform.matrix().block(0, 2, 3, 1) = colZ;
0333 orientedTransform.matrix().block(0, 3, 3, 1) = toGlobalOriginal.translation();
0334
0335 return orientedTransform;
0336 }
0337 }
0338
0339 std::shared_ptr<Acts::Surface> Acts::Geant4PhysicalVolumeConverter::surface(
0340 const G4VPhysicalVolume& g4PhysVol, const Transform3& toGlobal,
0341 bool convertMaterial, double compressed) {
0342
0343 auto g4LogVol = g4PhysVol.GetLogicalVolume();
0344 auto g4Solid = g4LogVol->GetSolid();
0345
0346 auto assignMaterial = [&](Acts::Surface& sf, double moriginal,
0347 double mcompressed) -> void {
0348 auto g4Material = g4LogVol->GetMaterial();
0349 if (convertMaterial && g4Material != nullptr) {
0350 if (compressed < 0.) {
0351 mcompressed = moriginal;
0352 }
0353 auto surfaceMaterial = Geant4MaterialConverter{}.surfaceMaterial(
0354 *g4Material, moriginal, mcompressed);
0355 sf.assignSurfaceMaterial(std::move(surfaceMaterial));
0356 }
0357 };
0358
0359
0360 std::shared_ptr<Surface> surface = nullptr;
0361
0362
0363 auto g4Box = dynamic_cast<const G4Box*>(g4Solid);
0364 if (g4Box != nullptr) {
0365 if (forcedType == Surface::SurfaceType::Other ||
0366 forcedType == Surface::SurfaceType::Plane) {
0367 auto [bounds, axes, original] =
0368 Geant4ShapeConverter{}.rectangleBounds(*g4Box);
0369 auto orientedToGlobal = axesOriented(toGlobal, axes);
0370 surface = Acts::Surface::makeShared<PlaneSurface>(orientedToGlobal,
0371 std::move(bounds));
0372 assignMaterial(*surface, original, compressed);
0373 return surface;
0374 } else {
0375 throw std::runtime_error("Can not convert 'G4Box' into forced shape.");
0376 }
0377 }
0378
0379
0380 auto g4Trd = dynamic_cast<const G4Trd*>(g4Solid);
0381 if (g4Trd != nullptr) {
0382 if (forcedType == Surface::SurfaceType::Other ||
0383 forcedType == Surface::SurfaceType::Plane) {
0384 auto [bounds, axes, original] =
0385 Geant4ShapeConverter{}.trapezoidBounds(*g4Trd);
0386 auto orientedToGlobal = axesOriented(toGlobal, axes);
0387 surface = Acts::Surface::makeShared<PlaneSurface>(orientedToGlobal,
0388 std::move(bounds));
0389 assignMaterial(*surface, original, compressed);
0390 return surface;
0391 } else {
0392 throw std::runtime_error("Can not convert 'G4Trd' into forced shape.");
0393 }
0394 }
0395
0396
0397 auto g4Trap = dynamic_cast<const G4Trap*>(g4Solid);
0398 if (g4Trap != nullptr) {
0399 if (forcedType == Surface::SurfaceType::Other ||
0400 forcedType == Surface::SurfaceType::Plane) {
0401 auto [bounds, axes, original] =
0402 Geant4ShapeConverter{}.trapezoidBounds(*g4Trap);
0403 auto orientedToGlobal = axesOriented(toGlobal, axes);
0404 surface = Acts::Surface::makeShared<PlaneSurface>(orientedToGlobal,
0405 std::move(bounds));
0406 assignMaterial(*surface, original, compressed);
0407 return surface;
0408 } else {
0409 throw std::runtime_error("Can not convert 'G4Trap' into forced shape.");
0410 }
0411 }
0412
0413
0414 auto g4Tubs = dynamic_cast<const G4Tubs*>(g4Solid);
0415 if (g4Tubs != nullptr) {
0416 double diffR = g4Tubs->GetOuterRadius() - g4Tubs->GetInnerRadius();
0417 double diffZ = 2 * g4Tubs->GetZHalfLength();
0418
0419 double original = 0.;
0420 if (forcedType == Surface::SurfaceType::Cylinder ||
0421 (diffR < diffZ && forcedType == Surface::SurfaceType::Other)) {
0422 auto [bounds, originalT] = Geant4ShapeConverter{}.cylinderBounds(*g4Tubs);
0423 original = originalT;
0424 surface = Acts::Surface::makeShared<CylinderSurface>(toGlobal,
0425 std::move(bounds));
0426 } else if (forcedType == Surface::SurfaceType::Disc ||
0427 forcedType == Surface::SurfaceType::Other) {
0428 auto [bounds, originalT] = Geant4ShapeConverter{}.radialBounds(*g4Tubs);
0429 original = originalT;
0430 surface =
0431 Acts::Surface::makeShared<DiscSurface>(toGlobal, std::move(bounds));
0432 } else if (forcedType == Surface::SurfaceType::Straw) {
0433 auto bounds = Geant4ShapeConverter{}.lineBounds(*g4Tubs);
0434 surface =
0435 Acts::Surface::makeShared<StrawSurface>(toGlobal, std::move(bounds));
0436
0437 } else {
0438 throw std::runtime_error("Can not convert 'G4Tubs' into forced shape.");
0439 }
0440 assignMaterial(*surface, original, compressed);
0441 return surface;
0442 }
0443
0444 return nullptr;
0445 }
0446
0447 Acts::Material Acts::Geant4MaterialConverter::material(
0448 const G4Material& g4Material, double compression) {
0449 auto X0 = g4Material.GetRadlen();
0450 auto L0 = g4Material.GetNuclearInterLength();
0451 auto Rho = g4Material.GetDensity();
0452
0453
0454
0455 auto g4Elements = g4Material.GetElementVector();
0456 auto g4Fraction = g4Material.GetFractionVector();
0457 auto g4NElements = g4Material.GetNumberOfElements();
0458 double Ar = 0;
0459 double Z = 0;
0460 if (g4NElements == 1) {
0461 Ar = g4Elements->at(0)->GetN();
0462 Z = g4Material.GetZ();
0463 } else {
0464 for (std::size_t i = 0; i < g4NElements; i++) {
0465 Ar += g4Elements->at(i)->GetN() * g4Fraction[i];
0466 Z += g4Elements->at(i)->GetZ() * g4Fraction[i];
0467 }
0468 }
0469
0470 return Material::fromMassDensity(X0 / compression, L0 / compression, Ar, Z,
0471 compression * Rho);
0472 }
0473
0474 std::shared_ptr<Acts::HomogeneousSurfaceMaterial>
0475 Acts::Geant4MaterialConverter::surfaceMaterial(const G4Material& g4Material,
0476 double original,
0477 double compressed) {
0478 double compression = original / compressed;
0479 return std::make_shared<HomogeneousSurfaceMaterial>(
0480 MaterialSlab(material(g4Material, compression), compressed));
0481 }
0482
0483 std::shared_ptr<Acts::CylinderVolumeBounds>
0484 Acts::Geant4VolumeConverter::cylinderBounds(const G4Tubs& g4Tubs) {
0485 using C = Acts::CylinderVolumeBounds;
0486
0487 std::array<double, C::eSize> tArray = {};
0488 tArray[C::eMinR] = static_cast<double>(g4Tubs.GetInnerRadius());
0489 tArray[C::eMaxR] = static_cast<double>(g4Tubs.GetOuterRadius());
0490 tArray[C::eHalfLengthZ] = static_cast<double>(g4Tubs.GetZHalfLength());
0491 tArray[C::eHalfPhiSector] =
0492 0.5 * static_cast<double>(g4Tubs.GetDeltaPhiAngle());
0493 tArray[C::eAveragePhi] = static_cast<double>(g4Tubs.GetStartPhiAngle());
0494
0495 return std::make_shared<CylinderVolumeBounds>(tArray);
0496 }