File indexing completed on 2026-05-08 08:00:23
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Definitions/Units.hpp"
0010 #include "Acts/Geometry/Blueprint.hpp"
0011 #include "Acts/Geometry/BlueprintOptions.hpp"
0012 #include "Acts/Geometry/ContainerBlueprintNode.hpp"
0013 #include "Acts/Geometry/NavigationPolicyFactory.hpp"
0014 #include "Acts/Geometry/StaticBlueprintNode.hpp"
0015 #include "Acts/Geometry/TrackingVolume.hpp"
0016 #include "Acts/Geometry/VolumeAttachmentStrategy.hpp"
0017 #include "Acts/Geometry/VolumeResizeStrategy.hpp"
0018 #include "Acts/Navigation/CylinderNavigationPolicy.hpp"
0019 #include "Acts/Navigation/SurfaceArrayNavigationPolicy.hpp"
0020 #include "Acts/Surfaces/CylinderBounds.hpp"
0021 #include "ActsPlugins/DD4hep/OpenDataDetectorBuilder.hpp"
0022 #include "ActsPlugins/Root/BlueprintBuilder.hpp"
0023 #include "ActsPlugins/Root/TGeoSurfaceConverter.hpp"
0024
0025 #include <array>
0026 #include <format>
0027 #include <memory>
0028 #include <optional>
0029 #include <regex>
0030 #include <span>
0031 #include <stdexcept>
0032 #include <string>
0033 #include <string_view>
0034
0035 #include <DD4hep/Detector.h>
0036
0037 #include "TGeoMaterial.h"
0038 #include "TGeoMedium.h"
0039 #include "TGeoNode.h"
0040 #include "TGeoVolume.h"
0041
0042 namespace ActsPlugins::DD4hep {
0043
0044 namespace {
0045
0046 const std::regex kTGeoPixelLayerFilter{"(?:PixelBarrel|PixelEndcap[NP])(\\d)"};
0047 const std::regex kTGeoShortStripLayerFilter{
0048 "(?:ShortStripBarrel|ShortStripEndcap[NP])(\\d)"};
0049 const std::regex kTGeoLongStripLayerFilter{
0050 "(?:LongStripBarrel|LongStripEndcap[NP])(\\d)"};
0051
0052 const std::regex kTGeoPixelBarrelLayerFilter{"PixelBarrel\\d"};
0053 const std::regex kTGeoShortStripBarrelLayerFilter{"ShortStripBarrel\\d"};
0054 const std::regex kTGeoLongStripBarrelLayerFilter{"LongStripBarrel\\d"};
0055 constexpr std::string_view kTGeoBeampipeName = "BeamPipe";
0056
0057 struct TGeoLayerBinning {
0058 std::span<const std::size_t> barrelPhiBins = {};
0059 std::size_t barrelZBins = 0u;
0060 std::array<std::size_t, 2> endcapBins = {0u, 0u};
0061
0062 std::array<std::size_t, 2> binsFor(bool isBarrelLayer, int layerIdx) const {
0063 if (!isBarrelLayer) {
0064 return endcapBins;
0065 }
0066
0067 if (layerIdx < 0 ||
0068 static_cast<std::size_t>(layerIdx) >= barrelPhiBins.size()) {
0069 throw std::runtime_error(std::format(
0070 "No TGeo barrel binning configured for layer {}", layerIdx));
0071 }
0072
0073 return {barrelPhiBins[static_cast<std::size_t>(layerIdx)], barrelZBins};
0074 }
0075 };
0076
0077 constexpr std::array<std::size_t, 4> kPixelBarrelPhiBins = {16u, 32u, 52u, 78u};
0078 constexpr TGeoLayerBinning kPixelLayerBinning{
0079 .barrelPhiBins = kPixelBarrelPhiBins,
0080 .barrelZBins = 14u,
0081 .endcapBins = {2u, 36u},
0082 };
0083
0084 constexpr std::array<std::size_t, 4> kShortStripBarrelPhiBins = {40u, 56u, 78u,
0085 102u};
0086 constexpr TGeoLayerBinning kShortStripLayerBinning{
0087 .barrelPhiBins = kShortStripBarrelPhiBins,
0088 .barrelZBins = 21u,
0089 .endcapBins = {3u, 42u},
0090 };
0091
0092 constexpr std::array<std::size_t, 2> kLongStripBarrelPhiBins = {60u, 80u};
0093 constexpr TGeoLayerBinning kLongStripLayerBinning{
0094 .barrelPhiBins = kLongStripBarrelPhiBins,
0095 .barrelZBins = 21u,
0096 .endcapBins = {2u, 48u},
0097 };
0098
0099 bool hasSensitiveMaterial(const TGeoBlueprintBuilderBackend::Element& element) {
0100 if (element.context == nullptr || element.context->node == nullptr) {
0101 return false;
0102 }
0103
0104 const auto* volume = element.context->node->GetVolume();
0105 if (volume == nullptr) {
0106 return false;
0107 }
0108
0109 const auto* medium = volume->GetMedium();
0110 if (medium == nullptr || medium->GetMaterial() == nullptr ||
0111 medium->GetMaterial()->GetName() == nullptr) {
0112 return false;
0113 }
0114
0115 return std::string_view{medium->GetMaterial()->GetName()}.find("Silicon") !=
0116 std::string_view::npos;
0117 }
0118
0119 auto makeTGeoLayerCustomizer(const BlueprintBuilder& builder,
0120 const TGeoLayerBinning& binning,
0121 const std::regex& layerFilter) {
0122 return [&builder, &binning, layerFilter](
0123 const std::optional<TGeoBlueprintBuilderBackend::Element>& elem,
0124 Acts::Experimental::LayerBlueprintNode& layer) {
0125 layer.setEnvelope(detail::kLayerEnvelope);
0126
0127 const std::string elemName =
0128 elem.has_value() ? std::string{builder.backend().nameOf(*elem)}
0129 : layer.name();
0130 const int layerIdx = detail::layerIndexFromName(elemName, layerFilter);
0131
0132 using SrfArrayNavPol = Acts::SurfaceArrayNavigationPolicy;
0133 using enum SrfArrayNavPol::LayerType;
0134
0135 SrfArrayNavPol::Config navCfg;
0136 const bool isBarrelLayer =
0137 layer.layerType() ==
0138 Acts::Experimental::LayerBlueprintNode::LayerType::Cylinder;
0139 navCfg.layerType = isBarrelLayer ? Cylinder : Disc;
0140
0141 const auto bins = binning.binsFor(isBarrelLayer, layerIdx);
0142 navCfg.bins = {bins[0], bins[1]};
0143
0144 layer.setNavigationPolicyFactory(Acts::NavigationPolicyFactory{}
0145 .add<Acts::CylinderNavigationPolicy>()
0146 .add<SrfArrayNavPol>(navCfg)
0147 .asUniquePtr());
0148 };
0149 }
0150
0151 std::shared_ptr<Acts::Experimental::StaticBlueprintNode> makeTGeoBeampipeNode(
0152 const BlueprintBuilder& builder) {
0153 const auto beampipeElement =
0154 builder.findDetElementByName(std::string{kTGeoBeampipeName});
0155 if (!beampipeElement.has_value()) {
0156 throw std::runtime_error(
0157 std::format("Could not find beampipe element '{}'", kTGeoBeampipeName));
0158 }
0159
0160 const auto& node = builder.backend().nodeOf(*beampipeElement);
0161 const auto tgTransform = builder.backend().transformOf(*beampipeElement);
0162 auto [bounds, transform, thickness] =
0163 ActsPlugins::TGeoSurfaceConverter::cylinderComponents(
0164 *node.GetVolume()->GetShape(), tgTransform.GetRotationMatrix(),
0165 tgTransform.GetTranslation(), "XYZ", Acts::UnitConstants::cm);
0166 (void)thickness;
0167
0168 if (bounds == nullptr) {
0169 throw std::runtime_error(
0170 "Beampipe element shape could not be converted to cylinder.");
0171 }
0172
0173 auto volumeBounds = std::make_shared<Acts::CylinderVolumeBounds>(
0174 0., bounds->get(Acts::CylinderBounds::eR),
0175 bounds->get(Acts::CylinderBounds::eHalfLengthZ));
0176 auto volume = std::make_unique<Acts::TrackingVolume>(
0177 transform, volumeBounds, std::string{kTGeoBeampipeName});
0178 return std::make_shared<Acts::Experimental::StaticBlueprintNode>(
0179 std::move(volume));
0180 }
0181
0182 void configureSubsystemNode(Acts::Experimental::ContainerBlueprintNode& node) {
0183 node.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::Gap);
0184 node.setResizeStrategies(Acts::VolumeResizeStrategy::Gap,
0185 Acts::VolumeResizeStrategy::Gap);
0186 }
0187
0188 struct TGeoSubsystemSpec {
0189 std::string_view assembly;
0190 std::string_view barrelName;
0191 std::string_view negativeEndcapName;
0192 std::string_view positiveEndcapName;
0193 const TGeoLayerBinning& binning;
0194 const std::regex& layerFilter;
0195 const std::regex& barrelLayerFilter;
0196 const std::regex& negativeEndcapLayerFilter;
0197 const std::regex& positiveEndcapLayerFilter;
0198 };
0199
0200 void addTGeoSubsystem(const BlueprintBuilder& builder,
0201 Acts::Experimental::BlueprintNode& parent,
0202 const TGeoSubsystemSpec& spec) {
0203 const auto assemblyElement =
0204 builder.findDetElementByName(std::string{spec.assembly});
0205 if (!assemblyElement.has_value()) {
0206 throw std::runtime_error(
0207 std::format("Could not find assembly '{}'", spec.assembly));
0208 }
0209 const auto barrelElement = builder.findDetElementByName(
0210 *assemblyElement, std::string{spec.barrelName});
0211 if (!barrelElement.has_value()) {
0212 throw std::runtime_error(
0213 std::format("Could not find barrel '{}'", spec.barrelName));
0214 }
0215 const auto negativeEndcapElement = builder.findDetElementByName(
0216 *assemblyElement, std::string{spec.negativeEndcapName});
0217 if (!negativeEndcapElement.has_value()) {
0218 throw std::runtime_error(std::format("Could not find negative endcap '{}'",
0219 spec.negativeEndcapName));
0220 }
0221 const auto positiveEndcapElement = builder.findDetElementByName(
0222 *assemblyElement, std::string{spec.positiveEndcapName});
0223 if (!positiveEndcapElement.has_value()) {
0224 throw std::runtime_error(std::format("Could not find positive endcap '{}'",
0225 spec.positiveEndcapName));
0226 }
0227
0228 auto subsystemNode =
0229 std::make_shared<Acts::Experimental::CylinderContainerBlueprintNode>(
0230 builder.backend().nameOf(*assemblyElement),
0231 Acts::AxisDirection::AxisZ);
0232
0233 auto barrelNode = builder.layers()
0234 .barrel()
0235 .setSensorAxes("XYZ")
0236 .setLayerFilter(spec.barrelLayerFilter)
0237 .setContainer(*barrelElement)
0238 .setContainerName(std::string{spec.barrelName})
0239 .onLayer(makeTGeoLayerCustomizer(builder, spec.binning,
0240 spec.layerFilter))
0241 .build();
0242 configureSubsystemNode(*barrelNode);
0243 subsystemNode->addChild(std::move(barrelNode));
0244
0245 auto negativeEndcapNode =
0246 builder.layers()
0247 .endcap()
0248 .setSensorAxes("XZY")
0249 .setLayerFilter(spec.negativeEndcapLayerFilter)
0250 .setContainer(*negativeEndcapElement)
0251 .setContainerName(std::string{spec.negativeEndcapName})
0252 .onLayer(
0253 makeTGeoLayerCustomizer(builder, spec.binning, spec.layerFilter))
0254 .build();
0255 configureSubsystemNode(*negativeEndcapNode);
0256 subsystemNode->addChild(std::move(negativeEndcapNode));
0257
0258 auto positiveEndcapNode =
0259 builder.layers()
0260 .endcap()
0261 .setSensorAxes("XZY")
0262 .setLayerFilter(spec.positiveEndcapLayerFilter)
0263 .setContainer(*positiveEndcapElement)
0264 .setContainerName(std::string{spec.positiveEndcapName})
0265 .onLayer(
0266 makeTGeoLayerCustomizer(builder, spec.binning, spec.layerFilter))
0267 .build();
0268 configureSubsystemNode(*positiveEndcapNode);
0269 subsystemNode->addChild(std::move(positiveEndcapNode));
0270
0271 parent.addChild(std::move(subsystemNode));
0272 }
0273
0274 }
0275
0276 std::unique_ptr<Acts::TrackingGeometry>
0277 buildOpenDataDetectorBarrelEndcapViaTGeo(const TGeoNode& rootNode,
0278 const Acts::GeometryContext& gctx,
0279 const Acts::Logger& logger) {
0280 using namespace Acts::Experimental;
0281 using namespace Acts;
0282 using enum AxisDirection;
0283
0284 TGeoBlueprintBuilderBackend::Config cfg;
0285 cfg.root = &rootNode;
0286 cfg.lengthScale = Acts::UnitConstants::cm;
0287 cfg.sensitivePredicate = hasSensitiveMaterial;
0288
0289 BlueprintBuilder builder{cfg, logger.cloneWithSuffix("TGeoBlpBld")};
0290
0291 Blueprint::Config blueprintCfg;
0292 blueprintCfg.envelope = ActsPlugins::DD4hep::detail::kBlueprintEnvelope;
0293 Blueprint root{blueprintCfg};
0294
0295 auto& outer = root.addCylinderContainer("OpenDataDetector", AxisR);
0296 outer.setAttachmentStrategy(VolumeAttachmentStrategy::Gap);
0297
0298 outer.addChild(makeTGeoBeampipeNode(builder));
0299 addTGeoSubsystem(
0300 builder, outer,
0301 {.assembly = "Pixels",
0302 .barrelName = "PixelBarrel",
0303 .negativeEndcapName = "PixelEndcapN",
0304 .positiveEndcapName = "PixelEndcapP",
0305 .binning = kPixelLayerBinning,
0306 .layerFilter = kTGeoPixelLayerFilter,
0307 .barrelLayerFilter = kTGeoPixelBarrelLayerFilter,
0308 .negativeEndcapLayerFilter = detail::kPixelNegativeEndcapLayerFilter,
0309 .positiveEndcapLayerFilter = detail::kPixelPositiveEndcapLayerFilter});
0310 addTGeoSubsystem(builder, outer,
0311 {.assembly = "ShortStrips",
0312 .barrelName = "ShortStripBarrel",
0313 .negativeEndcapName = "ShortStripEndcapN",
0314 .positiveEndcapName = "ShortStripEndcapP",
0315 .binning = kShortStripLayerBinning,
0316 .layerFilter = kTGeoShortStripLayerFilter,
0317 .barrelLayerFilter = kTGeoShortStripBarrelLayerFilter,
0318 .negativeEndcapLayerFilter =
0319 detail::kShortStripNegativeEndcapLayerFilter,
0320 .positiveEndcapLayerFilter =
0321 detail::kShortStripPositiveEndcapLayerFilter});
0322 addTGeoSubsystem(
0323 builder, outer,
0324 {.assembly = "LongStrips",
0325 .barrelName = "LongStripBarrel",
0326 .negativeEndcapName = "LongStripEndcapN",
0327 .positiveEndcapName = "LongStripEndcapP",
0328 .binning = kLongStripLayerBinning,
0329 .layerFilter = kTGeoLongStripLayerFilter,
0330 .barrelLayerFilter = kTGeoLongStripBarrelLayerFilter,
0331 .negativeEndcapLayerFilter = detail::kLongStripNegativeEndcapLayerFilter,
0332 .positiveEndcapLayerFilter =
0333 detail::kLongStripPositiveEndcapLayerFilter});
0334
0335 return root.construct(BlueprintOptions{}, gctx, logger);
0336 }
0337
0338 }