From adb987426a55e3c8d8130f38c16be3499d364415 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Mon, 1 Dec 2025 16:24:24 +0100 Subject: [PATCH 01/64] feat: First draft for partial DD4hep Gen3 support --- Plugins/DD4hep/CMakeLists.txt | 1 + .../ActsPlugins/DD4hep/BlueprintBuilder.hpp | 96 ++++++++++++ .../ActsPlugins/DD4hep/LayerBlueprintNode.hpp | 28 ++++ Plugins/DD4hep/src/BlueprintBuilder.cpp | 145 ++++++++++++++++++ 4 files changed, 270 insertions(+) create mode 100644 Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp create mode 100644 Plugins/DD4hep/include/ActsPlugins/DD4hep/LayerBlueprintNode.hpp create mode 100644 Plugins/DD4hep/src/BlueprintBuilder.cpp diff --git a/Plugins/DD4hep/CMakeLists.txt b/Plugins/DD4hep/CMakeLists.txt index 24c93ca6163..bea55cbf6fe 100644 --- a/Plugins/DD4hep/CMakeLists.txt +++ b/Plugins/DD4hep/CMakeLists.txt @@ -10,6 +10,7 @@ acts_add_library( src/DD4hepLayerBuilder.cpp src/DD4hepVolumeBuilder.cpp src/DD4hepFieldAdapter.cpp + src/BlueprintBuilder.cpp ACTS_INCLUDE_FOLDER include/ActsPlugins ) diff --git a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp new file mode 100644 index 00000000000..1d5d9eed62c --- /dev/null +++ b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp @@ -0,0 +1,96 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Utilities/Logger.hpp" +#include "ActsPlugins/DD4hep/DD4hepDetectorElement.hpp" +#include "ActsPlugins/DD4hep/LayerBlueprintNode.hpp" + +#include +#include +#include +#include + +namespace dd4hep { +class DetElement; +} + +namespace Acts { +namespace Experimental { +class CylinderContainerBlueprintNode; +} // namespace Experimental +} // namespace Acts + +namespace ActsPlugins { + +namespace DD4hep { + +class LayerBlueprintNode; + +class BlueprintBuilder { + public: + using ElementFactory = std::function( + const dd4hep::DetElement& detElement, const std::string& axes, + double lengthScale)>; + + static std::shared_ptr defaultElementFactory( + const dd4hep::DetElement& detElement, const std::string& axes, + double lengthScale); + + struct Config { + ElementFactory elementFactory = defaultElementFactory; + const dd4hep::Detector* dd4hepDetector; + double lengthScale = 1.0; + }; + + explicit BlueprintBuilder(const Config& cfg, + std::unique_ptr logger_ = + Acts::getDefaultLogger("BlueprintBuilder", + Acts::Logging::INFO)) + : m_cfg(cfg), m_logger{std::move(logger_)} { + if (m_cfg.dd4hepDetector == nullptr) { + throw std::invalid_argument( + "BlueprintBuilder: dd4hepDetector in config is null"); + } + } + + std::shared_ptr createDetectorElement( + const dd4hep::DetElement& detElement, const std::string& axes) const; + + std::shared_ptr addLayer( + const dd4hep::DetElement& detElement, const std::string& axes); + + std::shared_ptr addLayers( + const dd4hep::DetElement& container, const std::string& axes, + Acts::AxisDirection direction, const std::regex& layerPattern); + + static std::optional findDetElementByName( + const dd4hep::DetElement& parent, const std::string& name); + + std::optional findDetElementByName( + const std::string& name); + + static std::vector findDetElementByNamePattern( + const dd4hep::DetElement& parent, const std::regex& pattern); + + private: + static std::vector resolveSensitives( + const dd4hep::DetElement& detElement); + + dd4hep::DetElement world() const; + + Config m_cfg; + + std::unique_ptr m_logger; + const Acts::Logger& logger() const { return *m_logger; } +}; + +} // namespace DD4hep + +} // namespace ActsPlugins diff --git a/Plugins/DD4hep/include/ActsPlugins/DD4hep/LayerBlueprintNode.hpp b/Plugins/DD4hep/include/ActsPlugins/DD4hep/LayerBlueprintNode.hpp new file mode 100644 index 00000000000..9162bb804c5 --- /dev/null +++ b/Plugins/DD4hep/include/ActsPlugins/DD4hep/LayerBlueprintNode.hpp @@ -0,0 +1,28 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Geometry/LayerBlueprintNode.hpp" + +#include + +#include + +namespace ActsPlugins::DD4hep { + +class LayerBlueprintNode : public Acts::Experimental::LayerBlueprintNode { + public: + // explicit LayerBlueprintNode(const dd4hep::DetElement& detElement, + // std::string_view axes) + // : Acts::Experimental::LayerBlueprintNode(detElement.name()) {} + + using Acts::Experimental::LayerBlueprintNode::LayerBlueprintNode; +}; + +} // namespace ActsPlugins::DD4hep \ No newline at end of file diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp new file mode 100644 index 00000000000..293b505d5c0 --- /dev/null +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -0,0 +1,145 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsPlugins/DD4hep/BlueprintBuilder.hpp" + +#include "Acts/Definitions/Units.hpp" +#include "Acts/Geometry/ContainerBlueprintNode.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "ActsPlugins/DD4hep/DD4hepDetectorElement.hpp" +#include "ActsPlugins/DD4hep/LayerBlueprintNode.hpp" + +#include +#include +#include + +namespace ActsPlugins::DD4hep { + +std::shared_ptr BlueprintBuilder::defaultElementFactory( + const dd4hep::DetElement& detElement, const std::string& axes, + double lengthScale) { + return std::make_shared(detElement, axes, lengthScale); +} + +std::shared_ptr BlueprintBuilder::createDetectorElement( + const dd4hep::DetElement& detElement, const std::string& axes) const { + auto elem = m_cfg.elementFactory(detElement, axes, m_cfg.lengthScale); + + detElement.addExtension( + new dd4hep::rec::StructExtension(DD4hepDetectorElementExtension(elem))); + + return elem; +} + +namespace { +void visitSubtree( + const dd4hep::DetElement& detElement, + const std::function& visitor) { + visitor(detElement); + + for (auto& [name, child] : detElement.children()) { + visitSubtree(child, visitor); + } +} + +} // namespace + +std::optional BlueprintBuilder::findDetElementByName( + const dd4hep::DetElement& parent, const std::string& name) { + if (parent.name() == name) { + return parent; + } + + for (const auto& [childName, child] : parent.children()) { + auto result = findDetElementByName(child, name); + if (result.has_value()) { + return result; + } + } + + return std::nullopt; +} + +std::optional BlueprintBuilder::findDetElementByName( + const std::string& name) { + return findDetElementByName(world(), name); +} + +std::vector findDetElementByNamePattern( + const dd4hep::DetElement& parent, const std::regex& pattern) { + std::vector matches; + + visitSubtree(parent, [&](const auto& elem) { + if (std::regex_match(elem.name(), pattern)) { + matches.push_back(elem); + } + }); + + return matches; +} + +dd4hep::DetElement BlueprintBuilder::world() const { + return m_cfg.dd4hepDetector->world(); +} + +std::vector BlueprintBuilder::resolveSensitives( + const dd4hep::DetElement& detElement) { + std::vector sensitives; + visitSubtree(detElement, [&](const auto& elem) { + if (elem.volume().isSensitive()) { + sensitives.push_back(elem); + } + }); + return sensitives; +} + +std::shared_ptr BlueprintBuilder::addLayer( + const dd4hep::DetElement& detElement, const std::string& axes) { + auto node = std::make_shared(detElement.name()); + + auto sensitives = resolveSensitives(detElement); + + std::vector> surfaces; + surfaces.reserve(sensitives.size()); + + for (const auto& sensitive : sensitives) { + auto elem = createDetectorElement(sensitive, axes); + surfaces.push_back(elem->surface().getSharedPtr()); + } + + node->setSurfaces(std::move(surfaces)); + + // @TODO: Try to auto-detect what kind of layer this is + + return node; +} + +std::shared_ptr +BlueprintBuilder::addLayers(const dd4hep::DetElement& container, + const std::string& axes, + Acts::AxisDirection direction, + const std::regex& layerPattern) { + using enum Acts::AxisDirection; + using namespace Acts::UnitLiterals; + auto node = + std::make_shared( + container.name(), direction); + for (const auto& element : + findDetElementByNamePattern(container, layerPattern)) { + auto layer = addLayer(element, axes); + layer->setEnvelope(Acts::ExtentEnvelope{} + .set(AxisZ, {-5_mm, 5_mm}) + .set(AxisR, {-5_mm, 5_mm})); + + node->addChild(layer); + } + + return node; +} + +} // namespace ActsPlugins::DD4hep From a201866ad04261ea8072f47bc770050e1e7ba48c Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Mon, 1 Dec 2025 16:24:24 +0100 Subject: [PATCH 02/64] feat: Test Gen3 DD4hep code on ODD --- .../DD4hepDetector/OpenDataDetector.hpp | 17 +++++- .../DD4hepDetector/src/OpenDataDetector.cpp | 60 ++++++++++++++++--- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/OpenDataDetector.hpp b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/OpenDataDetector.hpp index 5bff8f538f8..b6894c3ccac 100644 --- a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/OpenDataDetector.hpp +++ b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/OpenDataDetector.hpp @@ -14,11 +14,26 @@ namespace Acts { class GeometryContext; } +namespace ActsPlugins { +class DD4hepDetectorElement; +} + namespace ActsExamples { class OpenDataDetector final : public DD4hepDetectorBase { public: - struct Config : public DD4hepDetectorBase::Config {}; + struct Config : public DD4hepDetectorBase::Config { + using ElementFactory = + std::function( + const dd4hep::DetElement& element, const std::string& axes, + double scale)>; + + ElementFactory detectorElementFactory = defaultDetectorElementFactory; + }; + + static std::shared_ptr + defaultDetectorElementFactory(const dd4hep::DetElement& element, + const std::string& axes, double scale); explicit OpenDataDetector(const Config& cfg, const Acts::GeometryContext& gctx); diff --git a/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp b/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp index 5ef0714c4e8..a1810e7c56b 100644 --- a/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp +++ b/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp @@ -10,10 +10,23 @@ #include "Acts/Geometry/Blueprint.hpp" #include "Acts/Geometry/BlueprintOptions.hpp" +#include "Acts/Geometry/ContainerBlueprintNode.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/Extent.hpp" +#include "Acts/Utilities/AxisDefinitions.hpp" +#include "ActsPlugins/DD4hep/BlueprintBuilder.hpp" +#include +#include +#include +#include -namespace ActsExamples { +#include +#include + +#include +#include +namespace ActsExamples { OpenDataDetector::OpenDataDetector(const Config& cfg, const Acts::GeometryContext& gctx) : DD4hepDetectorBase{cfg}, m_cfg{cfg} { @@ -25,21 +38,54 @@ auto OpenDataDetector::config() const -> const Config& { return m_cfg; } +std::shared_ptr +OpenDataDetector::defaultDetectorElementFactory( + const dd4hep::DetElement& element, const std::string& axes, double scale) { + return std::make_shared(element, axes, + scale); +} + void OpenDataDetector::construct(const Acts::GeometryContext& gctx) { using namespace Acts::Experimental; using namespace Acts; using namespace Acts::UnitLiterals; + using enum AxisDirection; + + ActsPlugins::DD4hep::BlueprintBuilder builder{ + { + .dd4hepDetector = &dd4hepDetector(), + .lengthScale = Acts::UnitConstants::cm, + }, + logger().cloneWithSuffix("BlpBld")}; + + // func(world); + + // ACTS_VERBOSE("Found detelement: " << pixelBrl0.name()); + + // std::vector sensitives = resolveSensitives(pixelBrl0); + + // std::vector surfaces; + // surfaces.reserve(sensitives.size()); + // std::vector> + // detectorElements; + // BARREL: XYZ + // ENDCAP: XZY + // for (const auto& sensitive : sensitives) { + // builder.createDetectorElement(sensitive, "XYZ"); + // } Blueprint::Config cfg; - cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm}; - cfg.envelope[AxisDirection::AxisR] = {0_mm, 20_mm}; + cfg.envelope[AxisZ] = {20_mm, 20_mm}; + cfg.envelope[AxisR] = {0_mm, 20_mm}; Blueprint root{cfg}; - auto volBounds = std::make_shared(0_mm, 100_mm, 1_m); - auto vol = - std::make_unique(Transform3::Identity(), volBounds); + dd4hep::DetElement pixelBarrelElem = + builder.findDetElementByName("PixelBarrel").value(); + + auto pixelBarrel = builder.addLayers(pixelBarrelElem, "XYZ", AxisR, + std::regex{"PixelLayer\\d"}); - root.addStaticVolume(std::move(vol)); + root.addChild(pixelBarrel); BlueprintOptions options; From 8049c2ead9c8640b602d74c7c88d8f0427c7ec3e Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Tue, 2 Dec 2025 13:45:36 +0100 Subject: [PATCH 03/64] fix: Add missing symbol --- Plugins/DD4hep/src/BlueprintBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index 293b505d5c0..ffbc122962e 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -70,7 +70,7 @@ std::optional BlueprintBuilder::findDetElementByName( return findDetElementByName(world(), name); } -std::vector findDetElementByNamePattern( +std::vector BlueprintBuilder::findDetElementByNamePattern( const dd4hep::DetElement& parent, const std::regex& pattern) { std::vector matches; From 71277c4c94186bd54b14bbc890a78263b37c0c53 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Tue, 2 Dec 2025 14:11:59 +0100 Subject: [PATCH 04/64] fix: dd4hep -> DD4hep on disk --- Plugins/DD4hep/src/BlueprintBuilder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index ffbc122962e..15f3bcef651 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -14,9 +14,9 @@ #include "ActsPlugins/DD4hep/DD4hepDetectorElement.hpp" #include "ActsPlugins/DD4hep/LayerBlueprintNode.hpp" +#include +#include #include -#include -#include namespace ActsPlugins::DD4hep { From 025fd22faa87d78709df50d781e49043ad087491 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Tue, 2 Dec 2025 13:40:37 +0100 Subject: [PATCH 05/64] layer helper API and use it --- .../DD4hepDetector/src/OpenDataDetector.cpp | 269 ++++++++++++++++-- .../ActsPlugins/DD4hep/BlueprintBuilder.hpp | 80 +++++- .../ActsPlugins/DD4hep/LayerBlueprintNode.hpp | 28 -- Plugins/DD4hep/src/BlueprintBuilder.cpp | 85 +++++- 4 files changed, 402 insertions(+), 60 deletions(-) delete mode 100644 Plugins/DD4hep/include/ActsPlugins/DD4hep/LayerBlueprintNode.hpp diff --git a/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp b/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp index a1810e7c56b..37379c4080e 100644 --- a/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp +++ b/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp @@ -13,15 +13,27 @@ #include "Acts/Geometry/ContainerBlueprintNode.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Geometry/Extent.hpp" +#include "Acts/Geometry/LayerBlueprintNode.hpp" #include "Acts/Utilities/AxisDefinitions.hpp" #include "ActsPlugins/DD4hep/BlueprintBuilder.hpp" +#include "ActsPlugins/DD4hep/DD4hepDetectorElement.hpp" #include #include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include #include #include @@ -45,6 +57,22 @@ OpenDataDetector::defaultDetectorElementFactory( scale); } +namespace { + +std::optional parseLayerNumber(const dd4hep::DetElement& elem, + const std::regex& pattern) { + std::cmatch match; + + if (!std::regex_match(elem.name(), match, pattern)) { + return std::nullopt; + } + + int n = std::stoi(match[1]); + return n; +} + +} // namespace + void OpenDataDetector::construct(const Acts::GeometryContext& gctx) { using namespace Acts::Experimental; using namespace Acts; @@ -58,34 +86,239 @@ void OpenDataDetector::construct(const Acts::GeometryContext& gctx) { }, logger().cloneWithSuffix("BlpBld")}; - // func(world); - - // ACTS_VERBOSE("Found detelement: " << pixelBrl0.name()); - - // std::vector sensitives = resolveSensitives(pixelBrl0); - - // std::vector surfaces; - // surfaces.reserve(sensitives.size()); - // std::vector> - // detectorElements; // BARREL: XYZ // ENDCAP: XZY - // for (const auto& sensitive : sensitives) { - // builder.createDetectorElement(sensitive, "XYZ"); - // } Blueprint::Config cfg; cfg.envelope[AxisZ] = {20_mm, 20_mm}; cfg.envelope[AxisR] = {0_mm, 20_mm}; Blueprint root{cfg}; - dd4hep::DetElement pixelBarrelElem = - builder.findDetElementByName("PixelBarrel").value(); + auto& outer = root.addCylinderContainer("OpenDataDetector", AxisR); + outer.addStaticVolume( + Transform3::Identity(), + std::make_unique(0_mm, 20_mm, 1000_mm), "Beampipe"); + + using AttachmentStrategy = Acts::VolumeAttachmentStrategy; + using SrfArrayNavPol = Acts::SurfaceArrayNavigationPolicy; + + auto constant = [this](const std::string& name) -> int { + return dd4hepDetector().constant(name); + }; + + auto makeBinningFromConstants = + [&](const dd4hep::DetElement& elem, const std::regex& pattern, + const std::string& constant0, const std::string& constant1) { + std::cmatch match; + + if (!std::regex_match(elem.name(), match, pattern)) { + throw std::runtime_error(std::format( + "Could not extract layer number from {}", elem.name())); + } + + if (auto n = parseLayerNumber(elem, pattern)) { + return std::pair{ + constant(std::vformat(constant0, std::make_format_args(*n))), + constant(std::vformat(constant1, std::make_format_args(*n)))}; + } else { + throw std::runtime_error(std::format( + "Could not extract layer number from {}", elem.name())); + } + }; + + outer.addCylinderContainer("Pixel", AxisZ, [&](auto& pixel) { + auto envelope = + ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + auto barrel = + builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("PixelLayer\\d") + .setContainer("PixelBarrel") + .setEnvelope(envelope) + .customize([&](const dd4hep::DetElement& elem, auto& layer) { + layer.setNavigationPolicyFactory( + NavigationPolicyFactory{} + .add() + .add(SrfArrayNavPol::Config{ + .layerType = SrfArrayNavPol::LayerType::Cylinder, + .bins = makeBinningFromConstants( + elem, std::regex{"PixelLayer(\\d+)"}, + "pix_b{}_sf_b_phi", "pix_b_sf_b_z")}) + .asUniquePtr()); + }) + .build(); + barrel->setAttachmentStrategy(AttachmentStrategy::First); + + std::shared_ptr endcapPolicyFactory = + NavigationPolicyFactory{} + .add() + .add(SrfArrayNavPol::Config{ + .layerType = SrfArrayNavPol::LayerType::Disc, + .bins = {constant("pix_e_sf_b_r"), constant("pix_e_sf_b_phi")}}) + .asUniquePtr(); + + auto negEndcap = + builder.layerHelper() + .endcap() + .setAxes("XZY") + .setPattern("PixelEndcapN\\d") + .setContainer("PixelEndcapN") + .setEnvelope(envelope) + .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { + layer.setNavigationPolicyFactory(endcapPolicyFactory); + }) + .build(); + negEndcap->setAttachmentStrategy(AttachmentStrategy::First); + + auto posEndcap = + builder.layerHelper() + .endcap() + .setAxes("XZY") + .setPattern("PixelEndcapP\\d") + .setContainer("PixelEndcapP") + .setEnvelope(envelope) + .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { + layer.setNavigationPolicyFactory(endcapPolicyFactory); + }) + .build(); + posEndcap->setAttachmentStrategy(AttachmentStrategy::First); + + pixel.addChild(barrel); + pixel.addChild(negEndcap); + pixel.addChild(posEndcap); + }); + + outer.addCylinderContainer("ShortStrip", AxisZ, [&](auto& sstrip) { + auto envelope = + ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + + auto barrel = + builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("ShortStripLayer\\d") + .setContainer("ShortStripBarrel") + .setEnvelope(envelope) + .customize([&](const dd4hep::DetElement& elem, auto& layer) { + layer.setNavigationPolicyFactory( + NavigationPolicyFactory{} + .add() + .add(SrfArrayNavPol::Config{ + .layerType = SrfArrayNavPol::LayerType::Cylinder, + .bins = makeBinningFromConstants( + elem, std::regex{"ShortStripLayer(\\d)"}, + "ss_b{}_sf_b_phi", "ss_b_sf_b_z")}) + .asUniquePtr()); + }) + .build(); + barrel->setAttachmentStrategy(AttachmentStrategy::First); + + std::shared_ptr endcapPolicyFactory = + NavigationPolicyFactory{} + .add() + .add(SrfArrayNavPol::Config{ + .layerType = SrfArrayNavPol::LayerType::Disc, + .bins = {constant("ss_e_sf_b_r"), constant("ss_e_sf_b_phi")}}) + .asUniquePtr(); + + auto posEndcap = + builder.layerHelper() + .endcap() + .setAxes("XZY") + .setPattern("ShortStripEndcapP\\d") + .setContainer("ShortStripEndcapP") + .setEnvelope(envelope) + .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { + layer.setNavigationPolicyFactory(endcapPolicyFactory); + }) + .build(); + posEndcap->setAttachmentStrategy(AttachmentStrategy::First); + + auto negEndcap = + builder.layerHelper() + .endcap() + .setAxes("XZY") + .setPattern("ShortStripEndcapN\\d") + .setContainer("ShortStripEndcapN") + .setEnvelope(envelope) + .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { + layer.setNavigationPolicyFactory(endcapPolicyFactory); + }) + .build(); + negEndcap->setAttachmentStrategy(AttachmentStrategy::First); + + sstrip.addChild(barrel); + sstrip.addChild(posEndcap); + sstrip.addChild(negEndcap); + }); + + outer.addCylinderContainer("LongStrip", AxisZ, [&](auto& lstrip) { + auto envelope = + ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + + auto barrel = + builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("LongStripLayer\\d") + .setContainer("LongStripBarrel") + .setEnvelope(envelope) + .customize([&](const dd4hep::DetElement& elem, auto& layer) { + layer.setNavigationPolicyFactory( + NavigationPolicyFactory{} + .add() + + .add(SrfArrayNavPol::Config{ + .layerType = SrfArrayNavPol::LayerType::Cylinder, + .bins = makeBinningFromConstants( + elem, std::regex{"LongStripLayer(\\d)"}, + "ls_b{}_sf_b_phi", "ls_b_sf_b_z")}) + .asUniquePtr()); + }) + .build(); + barrel->setAttachmentStrategy(AttachmentStrategy::First); + + std::shared_ptr endcapPolicyFactory = + NavigationPolicyFactory{} + .add() + .add(SrfArrayNavPol::Config{ + .layerType = SrfArrayNavPol::LayerType::Disc, + .bins = {constant("ls_e_sf_b_r"), constant("ls_e_sf_b_phi")}}) + .asUniquePtr(); + + auto posEndcap = + builder.layerHelper() + .endcap() + .setAxes("XZY") + .setPattern("LongStripEndcapP\\d") + .setContainer("LongStripEndcapP") + .setEnvelope(envelope) + .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { + layer.setNavigationPolicyFactory(endcapPolicyFactory); + }) + .build(); + posEndcap->setAttachmentStrategy(AttachmentStrategy::First); + + auto negEndcap = + builder.layerHelper() + .endcap() + .setAxes("XZY") + .setPattern("LongStripEndcapN\\d") + .setContainer("LongStripEndcapN") + .setEnvelope(envelope) + .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { + layer.setNavigationPolicyFactory(endcapPolicyFactory); + }) + .build(); + negEndcap->setAttachmentStrategy(AttachmentStrategy::First); - auto pixelBarrel = builder.addLayers(pixelBarrelElem, "XYZ", AxisR, - std::regex{"PixelLayer\\d"}); + lstrip.addChild(barrel); + lstrip.addChild(posEndcap); + lstrip.addChild(negEndcap); + }); - root.addChild(pixelBarrel); + // @TODO: Add plugin way to take this from xml BlueprintOptions options; diff --git a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp index 1d5d9eed62c..e92c63e2fcb 100644 --- a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp +++ b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp @@ -8,9 +8,10 @@ #pragma once +#include "Acts/Geometry/Extent.hpp" +#include "Acts/Geometry/LayerBlueprintNode.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsPlugins/DD4hep/DD4hepDetectorElement.hpp" -#include "ActsPlugins/DD4hep/LayerBlueprintNode.hpp" #include #include @@ -32,6 +33,7 @@ namespace ActsPlugins { namespace DD4hep { class LayerBlueprintNode; +class LayerHelper; class BlueprintBuilder { public: @@ -63,12 +65,16 @@ class BlueprintBuilder { std::shared_ptr createDetectorElement( const dd4hep::DetElement& detElement, const std::string& axes) const; - std::shared_ptr addLayer( + std::shared_ptr addLayer( const dd4hep::DetElement& detElement, const std::string& axes); + [[deprecated("Consider using .layerHelper() to produce the layers")]] std::shared_ptr addLayers( const dd4hep::DetElement& container, const std::string& axes, - Acts::AxisDirection direction, const std::regex& layerPattern); + Acts::AxisDirection direction, const std::regex& layerPattern, + const Acts::ExtentEnvelope& envelope = Acts::ExtentEnvelope::Zero()); + + LayerHelper layerHelper(); static std::optional findDetElementByName( const dd4hep::DetElement& parent, const std::string& name); @@ -79,6 +85,8 @@ class BlueprintBuilder { static std::vector findDetElementByNamePattern( const dd4hep::DetElement& parent, const std::regex& pattern); + const Acts::Logger& logger() const { return *m_logger; } + private: static std::vector resolveSensitives( const dd4hep::DetElement& detElement); @@ -88,7 +96,71 @@ class BlueprintBuilder { Config m_cfg; std::unique_ptr m_logger; - const Acts::Logger& logger() const { return *m_logger; } +}; + +class LayerHelper { + public: + using LayerType = Acts::Experimental::LayerBlueprintNode::LayerType; + using Customizer = std::function; + + explicit LayerHelper(ActsPlugins::DD4hep::BlueprintBuilder& builder) + : m_builder{&builder} {} + + LayerHelper& setLayerType(LayerType layerType) { + m_layerType = layerType; + return *this; + } + + LayerHelper& endcap() { return setLayerType(LayerType::Disc); } + LayerHelper& barrel() { return setLayerType(LayerType::Cylinder); } + + LayerHelper& setAxes(const std::string& axes) { + m_axes = axes; + return *this; + } + + LayerHelper& setPattern(const std::string& pattern) { + m_pattern = std::regex{pattern}; + return *this; + } + + LayerHelper& setContainer(const dd4hep::DetElement& container) { + m_container = container; + return *this; + } + + LayerHelper& setContainer(const std::string& name) { + m_container = m_builder->findDetElementByName(name); + if (!m_container.has_value()) { + throw std::runtime_error("Could not find DetElement with name " + name + + " in LayerHelper"); + } + return *this; + } + + LayerHelper& setEnvelope(const Acts::ExtentEnvelope& envelope) { + m_envelope = envelope; + return *this; + } + + LayerHelper& customize(Customizer customizer) { + m_customizer = std::move(customizer); + return *this; + } + + std::shared_ptr build() + const; + + private: + ActsPlugins::DD4hep::BlueprintBuilder* m_builder; + std::optional m_layerType; + std::optional m_axes; + std::optional m_pattern; + std::optional m_container; + std::optional m_envelope; + + Customizer m_customizer; }; } // namespace DD4hep diff --git a/Plugins/DD4hep/include/ActsPlugins/DD4hep/LayerBlueprintNode.hpp b/Plugins/DD4hep/include/ActsPlugins/DD4hep/LayerBlueprintNode.hpp deleted file mode 100644 index 9162bb804c5..00000000000 --- a/Plugins/DD4hep/include/ActsPlugins/DD4hep/LayerBlueprintNode.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of the ACTS project. -// -// Copyright (C) 2016 CERN for the benefit of the ACTS project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#pragma once - -#include "Acts/Geometry/LayerBlueprintNode.hpp" - -#include - -#include - -namespace ActsPlugins::DD4hep { - -class LayerBlueprintNode : public Acts::Experimental::LayerBlueprintNode { - public: - // explicit LayerBlueprintNode(const dd4hep::DetElement& detElement, - // std::string_view axes) - // : Acts::Experimental::LayerBlueprintNode(detElement.name()) {} - - using Acts::Experimental::LayerBlueprintNode::LayerBlueprintNode; -}; - -} // namespace ActsPlugins::DD4hep \ No newline at end of file diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index 15f3bcef651..265d31f1333 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -10,9 +10,9 @@ #include "Acts/Definitions/Units.hpp" #include "Acts/Geometry/ContainerBlueprintNode.hpp" +#include "Acts/Geometry/Extent.hpp" #include "Acts/Surfaces/Surface.hpp" #include "ActsPlugins/DD4hep/DD4hepDetectorElement.hpp" -#include "ActsPlugins/DD4hep/LayerBlueprintNode.hpp" #include #include @@ -98,9 +98,11 @@ std::vector BlueprintBuilder::resolveSensitives( return sensitives; } -std::shared_ptr BlueprintBuilder::addLayer( - const dd4hep::DetElement& detElement, const std::string& axes) { - auto node = std::make_shared(detElement.name()); +std::shared_ptr +BlueprintBuilder::addLayer(const dd4hep::DetElement& detElement, + const std::string& axes) { + auto node = std::make_shared( + detElement.name()); auto sensitives = resolveSensitives(detElement); @@ -115,6 +117,8 @@ std::shared_ptr BlueprintBuilder::addLayer( node->setSurfaces(std::move(surfaces)); // @TODO: Try to auto-detect what kind of layer this is + // @TODO: Auto-detect from `detElement` what the layer transform should be + // (lookup rotation and set on layer) return node; } @@ -123,18 +127,24 @@ std::shared_ptr BlueprintBuilder::addLayers(const dd4hep::DetElement& container, const std::string& axes, Acts::AxisDirection direction, - const std::regex& layerPattern) { + const std::regex& layerPattern, + const Acts::ExtentEnvelope& envelope) { + ACTS_DEBUG("Adding layers to container " << container.name()); using enum Acts::AxisDirection; using namespace Acts::UnitLiterals; auto node = std::make_shared( container.name(), direction); - for (const auto& element : - findDetElementByNamePattern(container, layerPattern)) { + const auto& layerElements = + findDetElementByNamePattern(container, layerPattern); + + if (container.children().empty()) { + ACTS_WARNING("Container " << container.name() + << " has no children, no layers added."); + } + for (const auto& element : layerElements) { auto layer = addLayer(element, axes); - layer->setEnvelope(Acts::ExtentEnvelope{} - .set(AxisZ, {-5_mm, 5_mm}) - .set(AxisR, {-5_mm, 5_mm})); + layer->setEnvelope(envelope); node->addChild(layer); } @@ -142,4 +152,59 @@ BlueprintBuilder::addLayers(const dd4hep::DetElement& container, return node; } +LayerHelper BlueprintBuilder::layerHelper() { + return LayerHelper(*this); +} + +std::shared_ptr +LayerHelper::build() const { + const auto& logger = m_builder->logger(); + + if (!m_layerType.has_value()) { + throw std::runtime_error("Layer type not set in LayerHelper"); + } + + if (!m_axes.has_value()) { + throw std::runtime_error("Axes not set in LayerHelper"); + } + + if (!m_pattern.has_value()) { + throw std::runtime_error("Pattern not set in LayerHelper"); + } + + if (!m_container.has_value()) { + throw std::runtime_error("Container not set in LayerHelper"); + } + + Acts::AxisDirection axisDir = m_layerType == LayerType::Cylinder + ? Acts::AxisDirection::AxisR + : Acts::AxisDirection::AxisZ; + + const auto& container = m_container.value(); + auto node = + std::make_shared( + container.name(), axisDir); + + const auto& layerElements = + m_builder->findDetElementByNamePattern(container, m_pattern.value()); + + if (container.children().empty()) { + ACTS_WARNING("Container " << container.name() + << " has no children, no layers added."); + } + for (const auto& element : layerElements) { + auto layer = m_builder->addLayer(element, m_axes.value()); + if (m_envelope.has_value()) { + layer->setEnvelope(m_envelope.value()); + } + node->addChild(layer); + + if (m_customizer) { + m_customizer(element, *layer); + } + } + + return node; +} + } // namespace ActsPlugins::DD4hep From 5d3e0656ede710253f0b072d39920e38c34748f7 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Tue, 2 Dec 2025 17:35:07 +0100 Subject: [PATCH 06/64] (sort of) auto detect layer transform if axes are specified --- .../DD4hepDetector/src/OpenDataDetector.cpp | 1 + .../ActsPlugins/DD4hep/BlueprintBuilder.hpp | 9 ++- Plugins/DD4hep/src/BlueprintBuilder.cpp | 60 +++++++++++++++++-- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp b/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp index 37379c4080e..7146cf4812e 100644 --- a/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp +++ b/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp @@ -193,6 +193,7 @@ void OpenDataDetector::construct(const Acts::GeometryContext& gctx) { auto envelope = ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + // @TODO: Make it configurable if empty result is received auto barrel = builder.layerHelper() .barrel() diff --git a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp index e92c63e2fcb..a5cb4b2f69a 100644 --- a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp +++ b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp @@ -66,7 +66,8 @@ class BlueprintBuilder { const dd4hep::DetElement& detElement, const std::string& axes) const; std::shared_ptr addLayer( - const dd4hep::DetElement& detElement, const std::string& axes); + const dd4hep::DetElement& detElement, const std::string& axes, + std::optional layerAxes = std::nullopt); [[deprecated("Consider using .layerHelper() to produce the layers")]] std::shared_ptr addLayers( @@ -120,6 +121,11 @@ class LayerHelper { return *this; } + LayerHelper& setLayerAxes(const std::string& layerAxes) { + m_layerAxes = layerAxes; + return *this; + } + LayerHelper& setPattern(const std::string& pattern) { m_pattern = std::regex{pattern}; return *this; @@ -156,6 +162,7 @@ class LayerHelper { ActsPlugins::DD4hep::BlueprintBuilder* m_builder; std::optional m_layerType; std::optional m_axes; + std::optional m_layerAxes; std::optional m_pattern; std::optional m_container; std::optional m_envelope; diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index 265d31f1333..8e4beae94a0 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -8,11 +8,13 @@ #include "ActsPlugins/DD4hep/BlueprintBuilder.hpp" +#include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/Geometry/ContainerBlueprintNode.hpp" #include "Acts/Geometry/Extent.hpp" #include "Acts/Surfaces/Surface.hpp" #include "ActsPlugins/DD4hep/DD4hepDetectorElement.hpp" +#include "ActsPlugins/Root/TGeoSurfaceConverter.hpp" #include #include @@ -98,13 +100,53 @@ std::vector BlueprintBuilder::resolveSensitives( return sensitives; } +namespace { +Acts::Transform3 convertTGeoTransform(const TGeoShape& shape, + const TGeoMatrix& transform, + const std::string& axes, + double lengthScale) { + // This is somewhat duplicated from the TGeoDetectorElement constructor + // + const Double_t* translation = transform.GetTranslation(); + const Double_t* rotation = transform.GetRotationMatrix(); + + auto [cBounds, cTransform, cThickness] = + ActsPlugins::TGeoSurfaceConverter::cylinderComponents( + shape, rotation, translation, axes, lengthScale); + if (cBounds != nullptr) { + return cTransform; + } + + auto [dBounds, dTransform, dThickness] = TGeoSurfaceConverter::discComponents( + shape, rotation, translation, axes, lengthScale); + if (dBounds != nullptr) { + return dTransform; + } + + auto [pBounds, pTransform, pThickness] = + TGeoSurfaceConverter::planeComponents(shape, rotation, translation, axes, + lengthScale); + if (pBounds != nullptr) { + return pTransform; + } + + throw std::runtime_error( + "Could not extract transform from TGeoShape of type " + + std::string(shape.ClassName())); +} + +} // namespace + std::shared_ptr BlueprintBuilder::addLayer(const dd4hep::DetElement& detElement, - const std::string& axes) { + const std::string& axes, + std::optional layerAxes) { + ACTS_DEBUG("Adding layer from element: " << detElement.name()); auto node = std::make_shared( detElement.name()); auto sensitives = resolveSensitives(detElement); + ACTS_DEBUG(" Found " << sensitives.size() << " sensitive elements."); std::vector> surfaces; surfaces.reserve(sensitives.size()); @@ -116,9 +158,19 @@ BlueprintBuilder::addLayer(const dd4hep::DetElement& detElement, node->setSurfaces(std::move(surfaces)); + if (layerAxes.has_value()) { + ACTS_DEBUG("Finding layer transform automatically using layer axes: " + << layerAxes.value()); + Acts::Transform3 layerTransform = convertTGeoTransform( + *detElement.placement().ptr()->GetVolume()->GetShape(), + detElement.nominal().worldTransformation(), layerAxes.value(), + m_cfg.lengthScale); + + ACTS_VERBOSE(" -> Layer transform:\n" << layerTransform.matrix()); + node->setTransform(layerTransform); + } + // @TODO: Try to auto-detect what kind of layer this is - // @TODO: Auto-detect from `detElement` what the layer transform should be - // (lookup rotation and set on layer) return node; } @@ -193,7 +245,7 @@ LayerHelper::build() const { << " has no children, no layers added."); } for (const auto& element : layerElements) { - auto layer = m_builder->addLayer(element, m_axes.value()); + auto layer = m_builder->addLayer(element, m_axes.value(), m_layerAxes); if (m_envelope.has_value()) { layer->setEnvelope(m_envelope.value()); } From 06252f9c43b8cad051f5b74d66c9fd66c2b71e98 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Wed, 3 Dec 2025 11:02:23 +0100 Subject: [PATCH 07/64] feat: Beampipe maker for blueprint builder --- .../DD4hepDetector/src/OpenDataDetector.cpp | 5 +- .../ActsPlugins/DD4hep/BlueprintBuilder.hpp | 4 ++ Plugins/DD4hep/src/BlueprintBuilder.cpp | 41 ++++++++++++++++ Plugins/Root/CMakeLists.txt | 1 + .../ActsPlugins/Root/TGeoSurfaceConverter.hpp | 4 ++ .../ActsPlugins/Root/TGeoVolumeConverter.hpp | 29 +++++++++++ Plugins/Root/src/TGeoVolumeConverter.cpp | 48 +++++++++++++++++++ 7 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 Plugins/Root/include/ActsPlugins/Root/TGeoVolumeConverter.hpp create mode 100644 Plugins/Root/src/TGeoVolumeConverter.cpp diff --git a/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp b/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp index 7146cf4812e..5e44e1d3d51 100644 --- a/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp +++ b/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp @@ -95,9 +95,8 @@ void OpenDataDetector::construct(const Acts::GeometryContext& gctx) { Blueprint root{cfg}; auto& outer = root.addCylinderContainer("OpenDataDetector", AxisR); - outer.addStaticVolume( - Transform3::Identity(), - std::make_unique(0_mm, 20_mm, 1000_mm), "Beampipe"); + + outer.addChild(builder.makeBeampipe()); using AttachmentStrategy = Acts::VolumeAttachmentStrategy; using SrfArrayNavPol = Acts::SurfaceArrayNavigationPolicy; diff --git a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp index a5cb4b2f69a..19b3781ba3f 100644 --- a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp +++ b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp @@ -10,6 +10,7 @@ #include "Acts/Geometry/Extent.hpp" #include "Acts/Geometry/LayerBlueprintNode.hpp" +#include "Acts/Geometry/StaticBlueprintNode.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsPlugins/DD4hep/DD4hepDetectorElement.hpp" @@ -17,6 +18,7 @@ #include #include #include +#include namespace dd4hep { class DetElement; @@ -69,6 +71,8 @@ class BlueprintBuilder { const dd4hep::DetElement& detElement, const std::string& axes, std::optional layerAxes = std::nullopt); + std::shared_ptr makeBeampipe() const; + [[deprecated("Consider using .layerHelper() to produce the layers")]] std::shared_ptr addLayers( const dd4hep::DetElement& container, const std::string& axes, diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index 8e4beae94a0..d8a41a43504 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -13,10 +13,13 @@ #include "Acts/Geometry/ContainerBlueprintNode.hpp" #include "Acts/Geometry/Extent.hpp" #include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/Logger.hpp" #include "ActsPlugins/DD4hep/DD4hepDetectorElement.hpp" #include "ActsPlugins/Root/TGeoSurfaceConverter.hpp" +#include "ActsPlugins/Root/TGeoVolumeConverter.hpp" #include +#include #include #include @@ -175,6 +178,44 @@ BlueprintBuilder::addLayer(const dd4hep::DetElement& detElement, return node; } +std::shared_ptr +BlueprintBuilder::makeBeampipe() const { + std::optional beampipeElement = std::nullopt; + + visitSubtree(world(), [&](const dd4hep::DetElement& elem) { + dd4hep::DetType subDetType{elem.typeFlag()}; + if (subDetType.is(dd4hep::DetType::BEAMPIPE)) { + if (beampipeElement.has_value()) { + ACTS_WARNING("Multiple beampipe elements found, using first: " + << beampipeElement->name() + << ", ignoring: " << elem.name()); + return; + } + beampipeElement = elem; + } + }); + + if (!beampipeElement.has_value()) { + ACTS_ERROR("No beampipe element found in DD4hep detector."); + throw std::runtime_error("No beampipe element found in DD4hep detector."); + } + + ACTS_INFO("Beampipe element found: " << beampipeElement->name()); + + std::unique_ptr volume = std::make_unique( + *TGeoVolumeConverter::cylinderVolume( + *beampipeElement->placement().ptr()->GetVolume()->GetShape(), + beampipeElement->nominal().worldTransformation(), m_cfg.lengthScale), + beampipeElement->name()); + + ACTS_INFO("-> Created beampipe volume: " << volume->volumeBounds() + << " transform:\n" + << volume->transform().matrix()); + + return std::make_shared( + std::move(volume)); +} + std::shared_ptr BlueprintBuilder::addLayers(const dd4hep::DetElement& container, const std::string& axes, diff --git a/Plugins/Root/CMakeLists.txt b/Plugins/Root/CMakeLists.txt index e800622ec9f..3bc2e2a200d 100644 --- a/Plugins/Root/CMakeLists.txt +++ b/Plugins/Root/CMakeLists.txt @@ -13,6 +13,7 @@ acts_add_library( src/TGeoPrimitivesHelper.cpp src/TGeoSurfaceConverter.cpp src/HistogramConverter.cpp + src/TGeoVolumeConverter.cpp ACTS_INCLUDE_FOLDER include/ActsPlugins ) diff --git a/Plugins/Root/include/ActsPlugins/Root/TGeoSurfaceConverter.hpp b/Plugins/Root/include/ActsPlugins/Root/TGeoSurfaceConverter.hpp index 7effa832aa0..ab7e81ecfa6 100644 --- a/Plugins/Root/include/ActsPlugins/Root/TGeoSurfaceConverter.hpp +++ b/Plugins/Root/include/ActsPlugins/Root/TGeoSurfaceConverter.hpp @@ -55,6 +55,10 @@ struct TGeoSurfaceConverter { const Double_t* translation, TGeoAxes axes, double scalor = 10.) noexcept(false); + static std::unique_ptr cylinderVolume( + const TGeoShape& tgShape, const TGeoMatrix& tgTransform, + double lengthScale = 10.); + /// Convert a TGeoShape into disk surface components /// /// @param tgShape The TGeoShape diff --git a/Plugins/Root/include/ActsPlugins/Root/TGeoVolumeConverter.hpp b/Plugins/Root/include/ActsPlugins/Root/TGeoVolumeConverter.hpp new file mode 100644 index 00000000000..f84705448dc --- /dev/null +++ b/Plugins/Root/include/ActsPlugins/Root/TGeoVolumeConverter.hpp @@ -0,0 +1,29 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include + +class TGeoShape; +class TGeoMatrix; + +namespace Acts { +class Volume; +} + +namespace ActsPlugins { +struct TGeoVolumeConverter { + TGeoVolumeConverter() = delete; + + static std::unique_ptr cylinderVolume( + const TGeoShape& tgShape, const TGeoMatrix& tgTransform, + double lengthScale = 10.); +}; + +} // namespace ActsPlugins diff --git a/Plugins/Root/src/TGeoVolumeConverter.cpp b/Plugins/Root/src/TGeoVolumeConverter.cpp new file mode 100644 index 00000000000..2032b776960 --- /dev/null +++ b/Plugins/Root/src/TGeoVolumeConverter.cpp @@ -0,0 +1,48 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsPlugins/Root/TGeoVolumeConverter.hpp" + +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "ActsPlugins/Root/TGeoSurfaceConverter.hpp" + +#include "TGeoMatrix.h" +#include "TGeoShape.h" + +using namespace Acts; + +namespace ActsPlugins { + +// @TODO: This currently uses @ref cylinderComponents to create a volume. This is probably not what we want. +std::unique_ptr TGeoVolumeConverter::cylinderVolume( + const TGeoShape& tgShape, const TGeoMatrix& tgTransform, + double lengthScale) { + auto [bounds, transform, thickness] = + ActsPlugins::TGeoSurfaceConverter::cylinderComponents( + tgShape, tgTransform.GetRotationMatrix(), + tgTransform.GetTranslation(), "XY", lengthScale); + + if (bounds == nullptr) { + throw std::invalid_argument( + "TGeoShape -> CylinderVolume: could not convert TGeoShape to " + "CylinderBounds."); + } + + double medR = bounds->get(CylinderBounds::eR); + double minR = medR - 0.5 * thickness; + double maxR = medR + 0.5 * thickness; + double hlZ = bounds->get(CylinderBounds::eHalfLengthZ); + + auto volBounds = + std::make_shared(minR, maxR, hlZ); + + return std::make_unique(transform, volBounds); +} + +} // namespace ActsPlugins From be303d4f4b6335876c89fd6837ace5d1c50970b0 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Wed, 3 Dec 2025 11:02:23 +0100 Subject: [PATCH 08/64] rename addLayer -> makeLayer --- .../include/ActsPlugins/DD4hep/BlueprintBuilder.hpp | 9 ++++++++- Plugins/DD4hep/src/BlueprintBuilder.cpp | 10 +++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp index 19b3781ba3f..3d96aebc5a5 100644 --- a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp +++ b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp @@ -67,9 +67,16 @@ class BlueprintBuilder { std::shared_ptr createDetectorElement( const dd4hep::DetElement& detElement, const std::string& axes) const; + [[deprecated("Renamed to `makeLayer()`")]] std::shared_ptr addLayer( const dd4hep::DetElement& detElement, const std::string& axes, - std::optional layerAxes = std::nullopt); + std::optional layerAxes = std::nullopt) { + return makeLayer(detElement, axes, std::move(layerAxes)); + } + + std::shared_ptr makeLayer( + const dd4hep::DetElement& detElement, const std::string& axes, + std::optional layerAxes = std::nullopt) const; std::shared_ptr makeBeampipe() const; diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index d8a41a43504..bb32e5f178b 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -141,9 +141,9 @@ Acts::Transform3 convertTGeoTransform(const TGeoShape& shape, } // namespace std::shared_ptr -BlueprintBuilder::addLayer(const dd4hep::DetElement& detElement, - const std::string& axes, - std::optional layerAxes) { +BlueprintBuilder::makeLayer(const dd4hep::DetElement& detElement, + const std::string& axes, + std::optional layerAxes) const { ACTS_DEBUG("Adding layer from element: " << detElement.name()); auto node = std::make_shared( detElement.name()); @@ -236,7 +236,7 @@ BlueprintBuilder::addLayers(const dd4hep::DetElement& container, << " has no children, no layers added."); } for (const auto& element : layerElements) { - auto layer = addLayer(element, axes); + auto layer = makeLayer(element, axes); layer->setEnvelope(envelope); node->addChild(layer); @@ -286,7 +286,7 @@ LayerHelper::build() const { << " has no children, no layers added."); } for (const auto& element : layerElements) { - auto layer = m_builder->addLayer(element, m_axes.value(), m_layerAxes); + auto layer = m_builder->makeLayer(element, m_axes.value(), m_layerAxes); if (m_envelope.has_value()) { layer->setEnvelope(m_envelope.value()); } From c14bfe37d0fa460a43e2e2fcc4f5712655caa6dc Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Wed, 3 Dec 2025 11:02:23 +0100 Subject: [PATCH 09/64] add flag to LayerHelper whether empty is ok or not --- .../include/ActsPlugins/DD4hep/BlueprintBuilder.hpp | 6 ++++++ Plugins/DD4hep/src/BlueprintBuilder.cpp | 12 +++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp index 3d96aebc5a5..5c039b04e38 100644 --- a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp +++ b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp @@ -161,6 +161,11 @@ class LayerHelper { return *this; } + LayerHelper& setEmptyOk(bool emptyOk) { + m_emptyOk = emptyOk; + return *this; + } + LayerHelper& customize(Customizer customizer) { m_customizer = std::move(customizer); return *this; @@ -177,6 +182,7 @@ class LayerHelper { std::optional m_pattern; std::optional m_container; std::optional m_envelope; + bool m_emptyOk = false; Customizer m_customizer; }; diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index bb32e5f178b..81db8c966ca 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -281,9 +281,15 @@ LayerHelper::build() const { const auto& layerElements = m_builder->findDetElementByNamePattern(container, m_pattern.value()); - if (container.children().empty()) { - ACTS_WARNING("Container " << container.name() - << " has no children, no layers added."); + if (layerElements.empty()) { + ACTS_LOG(m_emptyOk ? Acts::Logging::INFO : Acts::Logging::ERROR, + "No layers found in container " << container.name() + << " matching pattern"); + if (!m_emptyOk) { + throw std::runtime_error( + std::format("No layers found in container {} matching pattern", + container.name())); + } } for (const auto& element : layerElements) { auto layer = m_builder->makeLayer(element, m_axes.value(), m_layerAxes); From 4d614a774b7449839158e5fa56ca52ec01c865f1 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Wed, 3 Dec 2025 14:07:39 +0100 Subject: [PATCH 10/64] remove artifact --- .../Root/include/ActsPlugins/Root/TGeoSurfaceConverter.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Plugins/Root/include/ActsPlugins/Root/TGeoSurfaceConverter.hpp b/Plugins/Root/include/ActsPlugins/Root/TGeoSurfaceConverter.hpp index ab7e81ecfa6..7effa832aa0 100644 --- a/Plugins/Root/include/ActsPlugins/Root/TGeoSurfaceConverter.hpp +++ b/Plugins/Root/include/ActsPlugins/Root/TGeoSurfaceConverter.hpp @@ -55,10 +55,6 @@ struct TGeoSurfaceConverter { const Double_t* translation, TGeoAxes axes, double scalor = 10.) noexcept(false); - static std::unique_ptr cylinderVolume( - const TGeoShape& tgShape, const TGeoMatrix& tgTransform, - double lengthScale = 10.); - /// Convert a TGeoShape into disk surface components /// /// @param tgShape The TGeoShape From 9495910e81920d47623a3eaa03ddef832cf0e919 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Wed, 3 Dec 2025 15:12:10 +0100 Subject: [PATCH 11/64] fix beampipe builder and make it build to r=0 --- Plugins/DD4hep/src/BlueprintBuilder.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index 81db8c966ca..de76b12de21 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -12,6 +12,7 @@ #include "Acts/Definitions/Units.hpp" #include "Acts/Geometry/ContainerBlueprintNode.hpp" #include "Acts/Geometry/Extent.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsPlugins/DD4hep/DD4hepDetectorElement.hpp" @@ -202,11 +203,19 @@ BlueprintBuilder::makeBeampipe() const { ACTS_INFO("Beampipe element found: " << beampipeElement->name()); - std::unique_ptr volume = std::make_unique( - *TGeoVolumeConverter::cylinderVolume( + const auto& tgTransform = beampipeElement->nominal().worldTransformation(); + auto [bounds, transform, thickness] = + ActsPlugins::TGeoSurfaceConverter::cylinderComponents( *beampipeElement->placement().ptr()->GetVolume()->GetShape(), - beampipeElement->nominal().worldTransformation(), m_cfg.lengthScale), - beampipeElement->name()); + tgTransform.GetRotationMatrix(), tgTransform.GetTranslation(), "XYZ", + m_cfg.lengthScale); + + std::shared_ptr volBounds = std::make_shared( + 0, bounds->get(Acts::CylinderBounds::eR), + bounds->get(Acts::CylinderBounds::eHalfLengthZ)); + + std::unique_ptr volume = std::make_unique( + transform, volBounds, beampipeElement->name()); ACTS_INFO("-> Created beampipe volume: " << volume->volumeBounds() << " transform:\n" From 72eca14ee196777a8cc0b3b96e1c113ae5da5cd1 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Wed, 3 Dec 2025 14:06:54 +0100 Subject: [PATCH 12/64] fix: Check if cylinder conversion failed --- Plugins/DD4hep/src/BlueprintBuilder.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index de76b12de21..dd852bb2444 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -210,6 +210,12 @@ BlueprintBuilder::makeBeampipe() const { tgTransform.GetRotationMatrix(), tgTransform.GetTranslation(), "XYZ", m_cfg.lengthScale); + if (bounds == nullptr) { + ACTS_ERROR("Beampipe element shape could not be converted to cylinder."); + throw std::runtime_error( + "Beampipe element shape could not be converted to cylinder."); + } + std::shared_ptr volBounds = std::make_shared( 0, bounds->get(Acts::CylinderBounds::eR), bounds->get(Acts::CylinderBounds::eHalfLengthZ)); From 088699808d95e1405aa703dbd6c53fc3cd5c696a Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 4 Dec 2025 15:05:20 +0100 Subject: [PATCH 13/64] Make the layer names use the full path name --- .../ActsPlugins/DD4hep/BlueprintBuilder.hpp | 2 ++ Plugins/DD4hep/src/BlueprintBuilder.cpp | 22 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp index 5c039b04e38..f3f1a74ad1b 100644 --- a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp +++ b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp @@ -94,6 +94,8 @@ class BlueprintBuilder { std::optional findDetElementByName( const std::string& name); + std::string getPathToElementName(const dd4hep::DetElement& elem) const; + static std::vector findDetElementByNamePattern( const dd4hep::DetElement& parent, const std::regex& pattern); diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index dd852bb2444..1e937a94b49 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -19,6 +19,9 @@ #include "ActsPlugins/Root/TGeoSurfaceConverter.hpp" #include "ActsPlugins/Root/TGeoVolumeConverter.hpp" +#include +#include + #include #include #include @@ -76,6 +79,23 @@ std::optional BlueprintBuilder::findDetElementByName( return findDetElementByName(world(), name); } +std::string BlueprintBuilder::getPathToElementName( + const dd4hep::DetElement& elem) const { + std::vector names; + names.emplace_back(std::string(elem.name()) + "|"); + auto parent = elem.parent(); + while (parent != world()) { + ACTS_VERBOSE("Adding " << parent.name()); + names.emplace_back(std::string(parent.name()) + "|"); + parent = parent.parent(); + } + + std::string result; + std::ranges::copy(std::ranges::reverse_view{names} | std::views::join, + std::back_inserter(result)); + return result; +} + std::vector BlueprintBuilder::findDetElementByNamePattern( const dd4hep::DetElement& parent, const std::regex& pattern) { std::vector matches; @@ -147,7 +167,7 @@ BlueprintBuilder::makeLayer(const dd4hep::DetElement& detElement, std::optional layerAxes) const { ACTS_DEBUG("Adding layer from element: " << detElement.name()); auto node = std::make_shared( - detElement.name()); + getPathToElementName(detElement)); auto sensitives = resolveSensitives(detElement); ACTS_DEBUG(" Found " << sensitives.size() << " sensitive elements."); From 5099f9441f2208e3fa8d7bc90b402622f83f75b1 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 4 Dec 2025 15:56:42 +0100 Subject: [PATCH 14/64] Use boost join --- Plugins/DD4hep/src/BlueprintBuilder.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index 1e937a94b49..e790c4ca942 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -19,13 +19,11 @@ #include "ActsPlugins/Root/TGeoSurfaceConverter.hpp" #include "ActsPlugins/Root/TGeoVolumeConverter.hpp" -#include -#include - #include #include #include #include +#include namespace ActsPlugins::DD4hep { @@ -82,18 +80,14 @@ std::optional BlueprintBuilder::findDetElementByName( std::string BlueprintBuilder::getPathToElementName( const dd4hep::DetElement& elem) const { std::vector names; - names.emplace_back(std::string(elem.name()) + "|"); + names.emplace_back(elem.name()); auto parent = elem.parent(); while (parent != world()) { - ACTS_VERBOSE("Adding " << parent.name()); - names.emplace_back(std::string(parent.name()) + "|"); + names.emplace_back(parent.name()); parent = parent.parent(); } - - std::string result; - std::ranges::copy(std::ranges::reverse_view{names} | std::views::join, - std::back_inserter(result)); - return result; + std::ranges::reverse(names); + return boost::algorithm::join(names, "|"); } std::vector BlueprintBuilder::findDetElementByNamePattern( From fccf7cc604c959aa4b6f41b6d3ff34a02a4ad7c5 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Wed, 3 Dec 2025 14:06:54 +0100 Subject: [PATCH 15/64] api exploration --- .../DD4hepDetector/src/OpenDataDetector.cpp | 287 +++++------------- .../ActsPlugins/DD4hep/BlueprintBuilder.hpp | 211 ++++++++----- Plugins/DD4hep/src/BlueprintBuilder.cpp | 208 ++++++++++++- 3 files changed, 407 insertions(+), 299 deletions(-) diff --git a/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp b/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp index 5e44e1d3d51..7df4de36ba9 100644 --- a/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp +++ b/Examples/Detectors/DD4hepDetector/src/OpenDataDetector.cpp @@ -59,12 +59,19 @@ OpenDataDetector::defaultDetectorElementFactory( namespace { -std::optional parseLayerNumber(const dd4hep::DetElement& elem, - const std::regex& pattern) { +int parseLayerNumber(const dd4hep::DetElement& elem, + const std::regex& pattern) { std::cmatch match; if (!std::regex_match(elem.name(), match, pattern)) { - return std::nullopt; + throw std::runtime_error(std::format( + "Layer name {} does not match expected pattern", elem.name())); + } + + if (match.size() != 2) { + throw std::runtime_error(std::format( + "Layer name {} matched pattern but did not capture layer number", + elem.name())); } int n = std::stoi(match[1]); @@ -105,220 +112,66 @@ void OpenDataDetector::construct(const Acts::GeometryContext& gctx) { return dd4hepDetector().constant(name); }; - auto makeBinningFromConstants = - [&](const dd4hep::DetElement& elem, const std::regex& pattern, - const std::string& constant0, const std::string& constant1) { - std::cmatch match; - - if (!std::regex_match(elem.name(), match, pattern)) { - throw std::runtime_error(std::format( - "Could not extract layer number from {}", elem.name())); - } - - if (auto n = parseLayerNumber(elem, pattern)) { - return std::pair{ - constant(std::vformat(constant0, std::make_format_args(*n))), - constant(std::vformat(constant1, std::make_format_args(*n)))}; - } else { - throw std::runtime_error(std::format( - "Could not extract layer number from {}", elem.name())); - } - }; - - outer.addCylinderContainer("Pixel", AxisZ, [&](auto& pixel) { - auto envelope = - ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - auto barrel = - builder.layerHelper() - .barrel() - .setAxes("XYZ") - .setPattern("PixelLayer\\d") - .setContainer("PixelBarrel") - .setEnvelope(envelope) - .customize([&](const dd4hep::DetElement& elem, auto& layer) { - layer.setNavigationPolicyFactory( - NavigationPolicyFactory{} - .add() - .add(SrfArrayNavPol::Config{ - .layerType = SrfArrayNavPol::LayerType::Cylinder, - .bins = makeBinningFromConstants( - elem, std::regex{"PixelLayer(\\d+)"}, - "pix_b{}_sf_b_phi", "pix_b_sf_b_z")}) - .asUniquePtr()); - }) - .build(); - barrel->setAttachmentStrategy(AttachmentStrategy::First); - - std::shared_ptr endcapPolicyFactory = - NavigationPolicyFactory{} - .add() - .add(SrfArrayNavPol::Config{ - .layerType = SrfArrayNavPol::LayerType::Disc, - .bins = {constant("pix_e_sf_b_r"), constant("pix_e_sf_b_phi")}}) - .asUniquePtr(); - - auto negEndcap = - builder.layerHelper() - .endcap() - .setAxes("XZY") - .setPattern("PixelEndcapN\\d") - .setContainer("PixelEndcapN") - .setEnvelope(envelope) - .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { - layer.setNavigationPolicyFactory(endcapPolicyFactory); - }) - .build(); - negEndcap->setAttachmentStrategy(AttachmentStrategy::First); - - auto posEndcap = - builder.layerHelper() - .endcap() - .setAxes("XZY") - .setPattern("PixelEndcapP\\d") - .setContainer("PixelEndcapP") - .setEnvelope(envelope) - .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { - layer.setNavigationPolicyFactory(endcapPolicyFactory); - }) - .build(); - posEndcap->setAttachmentStrategy(AttachmentStrategy::First); - - pixel.addChild(barrel); - pixel.addChild(negEndcap); - pixel.addChild(posEndcap); - }); - - outer.addCylinderContainer("ShortStrip", AxisZ, [&](auto& sstrip) { - auto envelope = - ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - - // @TODO: Make it configurable if empty result is received - auto barrel = - builder.layerHelper() - .barrel() - .setAxes("XYZ") - .setPattern("ShortStripLayer\\d") - .setContainer("ShortStripBarrel") - .setEnvelope(envelope) - .customize([&](const dd4hep::DetElement& elem, auto& layer) { - layer.setNavigationPolicyFactory( - NavigationPolicyFactory{} - .add() - .add(SrfArrayNavPol::Config{ - .layerType = SrfArrayNavPol::LayerType::Cylinder, - .bins = makeBinningFromConstants( - elem, std::regex{"ShortStripLayer(\\d)"}, - "ss_b{}_sf_b_phi", "ss_b_sf_b_z")}) - .asUniquePtr()); - }) - .build(); - barrel->setAttachmentStrategy(AttachmentStrategy::First); - - std::shared_ptr endcapPolicyFactory = - NavigationPolicyFactory{} - .add() - .add(SrfArrayNavPol::Config{ - .layerType = SrfArrayNavPol::LayerType::Disc, - .bins = {constant("ss_e_sf_b_r"), constant("ss_e_sf_b_phi")}}) - .asUniquePtr(); - - auto posEndcap = - builder.layerHelper() - .endcap() - .setAxes("XZY") - .setPattern("ShortStripEndcapP\\d") - .setContainer("ShortStripEndcapP") - .setEnvelope(envelope) - .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { - layer.setNavigationPolicyFactory(endcapPolicyFactory); - }) - .build(); - posEndcap->setAttachmentStrategy(AttachmentStrategy::First); - - auto negEndcap = - builder.layerHelper() - .endcap() - .setAxes("XZY") - .setPattern("ShortStripEndcapN\\d") - .setContainer("ShortStripEndcapN") - .setEnvelope(envelope) - .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { - layer.setNavigationPolicyFactory(endcapPolicyFactory); - }) - .build(); - negEndcap->setAttachmentStrategy(AttachmentStrategy::First); - - sstrip.addChild(barrel); - sstrip.addChild(posEndcap); - sstrip.addChild(negEndcap); - }); - - outer.addCylinderContainer("LongStrip", AxisZ, [&](auto& lstrip) { - auto envelope = - ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - - auto barrel = - builder.layerHelper() - .barrel() - .setAxes("XYZ") - .setPattern("LongStripLayer\\d") - .setContainer("LongStripBarrel") - .setEnvelope(envelope) - .customize([&](const dd4hep::DetElement& elem, auto& layer) { - layer.setNavigationPolicyFactory( - NavigationPolicyFactory{} - .add() - - .add(SrfArrayNavPol::Config{ - .layerType = SrfArrayNavPol::LayerType::Cylinder, - .bins = makeBinningFromConstants( - elem, std::regex{"LongStripLayer(\\d)"}, - "ls_b{}_sf_b_phi", "ls_b_sf_b_z")}) - .asUniquePtr()); - }) - .build(); - barrel->setAttachmentStrategy(AttachmentStrategy::First); - - std::shared_ptr endcapPolicyFactory = - NavigationPolicyFactory{} - .add() - .add(SrfArrayNavPol::Config{ - .layerType = SrfArrayNavPol::LayerType::Disc, - .bins = {constant("ls_e_sf_b_r"), constant("ls_e_sf_b_phi")}}) - .asUniquePtr(); - - auto posEndcap = - builder.layerHelper() - .endcap() - .setAxes("XZY") - .setPattern("LongStripEndcapP\\d") - .setContainer("LongStripEndcapP") - .setEnvelope(envelope) - .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { - layer.setNavigationPolicyFactory(endcapPolicyFactory); - }) - .build(); - posEndcap->setAttachmentStrategy(AttachmentStrategy::First); - - auto negEndcap = - builder.layerHelper() - .endcap() - .setAxes("XZY") - .setPattern("LongStripEndcapN\\d") - .setContainer("LongStripEndcapN") - .setEnvelope(envelope) - .customize([&](const dd4hep::DetElement& /*elem*/, auto& layer) { - layer.setNavigationPolicyFactory(endcapPolicyFactory); - }) - .build(); - negEndcap->setAttachmentStrategy(AttachmentStrategy::First); - - lstrip.addChild(barrel); - lstrip.addChild(posEndcap); - lstrip.addChild(negEndcap); - }); - - // @TODO: Add plugin way to take this from xml + auto makeCustomizer = [&](const std::string& det, + const std::regex& layerPattern) + -> ActsPlugins::DD4hep::LayerHelper::Customizer { + return [&, det](const auto& elem, + Acts::Experimental::LayerBlueprintNode& layer) { + int n = parseLayerNumber(elem, layerPattern); + + std::string name = elem.name(); + using enum SrfArrayNavPol::LayerType; + SrfArrayNavPol::Config cfg; + + if (name.find("Endcap") != std::string::npos) { + cfg.bins = {constant(std::format("{}_e_sf_b_r", det)), + constant(std::format("{}_e_sf_b_phi", det))}; + cfg.layerType = Disc; + } else { + cfg.bins = {constant(std::format("{}_b{}_sf_b_phi", det, n)), + constant(std::format("{}_b_sf_b_z", det))}; + cfg.layerType = Cylinder; + } + + layer.setNavigationPolicyFactory(NavigationPolicyFactory{} + .add() + .add(cfg) + .asUniquePtr()); + + // @TODO: This needs to set the material as well + }; + }; + + auto pixelAssembly = builder.findDetElementByName("Pixels").value(); + std::regex pixelLayerPattern{"(?:PixelLayer|PixelEndcap[NP])(\\d)"}; + builder.barrelEndcapAssemblyHelper() + .setAssembly(pixelAssembly) + .setBarrelAxes("XYZ") + .setEndcapAxes("XZY") + .setLayerPattern(pixelLayerPattern) + .setCustomizer(makeCustomizer("pix", pixelLayerPattern)) + .addTo(outer); + + auto sstripAssembly = builder.findDetElementByName("ShortStrips").value(); + std::regex sstripLayerPattern{ + "(?:ShortStripLayer|ShortStripEndcap[NP])(\\d)"}; + builder.barrelEndcapAssemblyHelper() + .setAssembly(sstripAssembly) + .setBarrelAxes("XYZ") + .setEndcapAxes("XZY") + .setLayerPattern(sstripLayerPattern) + .setCustomizer(makeCustomizer("ss", sstripLayerPattern)) + .addTo(outer); + + auto lstripAssembly = builder.findDetElementByName("LongStrips").value(); + std::regex lstripLayerPattern{"(?:LongStripLayer|LongStripEndcap[NP])(\\d)"}; + builder.barrelEndcapAssemblyHelper() + .setAssembly(lstripAssembly) + .setBarrelAxes("XYZ") + .setEndcapAxes("XZY") + .setLayerPattern(lstripLayerPattern) + .setCustomizer(makeCustomizer("ls", lstripLayerPattern)) + .addTo(outer); BlueprintOptions options; diff --git a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp index f3f1a74ad1b..4504c5e8ee6 100644 --- a/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp +++ b/Plugins/DD4hep/include/ActsPlugins/DD4hep/BlueprintBuilder.hpp @@ -8,6 +8,7 @@ #pragma once +#include "Acts/Geometry/BlueprintNode.hpp" #include "Acts/Geometry/Extent.hpp" #include "Acts/Geometry/LayerBlueprintNode.hpp" #include "Acts/Geometry/StaticBlueprintNode.hpp" @@ -35,7 +36,127 @@ namespace ActsPlugins { namespace DD4hep { class LayerBlueprintNode; -class LayerHelper; +class BlueprintBuilder; + +class LayerHelper { + public: + using LayerType = Acts::Experimental::LayerBlueprintNode::LayerType; + using Customizer = std::function; + + explicit LayerHelper(const BlueprintBuilder& builder) : m_builder{&builder} {} + + LayerHelper& setLayerType(LayerType layerType) { + m_layerType = layerType; + return *this; + } + + LayerHelper& endcap() { return setLayerType(LayerType::Disc); } + LayerHelper& barrel() { return setLayerType(LayerType::Cylinder); } + + LayerHelper& setAxes(const std::string& axes) { + m_axes = axes; + return *this; + } + + LayerHelper& setLayerAxes(const std::string& layerAxes) { + m_layerAxes = layerAxes; + return *this; + } + + LayerHelper& setPattern(const std::string& pattern) { + return setPattern(std::regex{pattern}); + } + + LayerHelper& setPattern(const std::regex& pattern) { + m_pattern = pattern; + return *this; + } + + LayerHelper& setContainer(const dd4hep::DetElement& container) { + m_container = container; + return *this; + } + + LayerHelper& setContainer(const std::string& name); + + LayerHelper& setEnvelope(const Acts::ExtentEnvelope& envelope) { + m_envelope = envelope; + return *this; + } + + LayerHelper& setEmptyOk(bool emptyOk) { + m_emptyOk = emptyOk; + return *this; + } + + LayerHelper& customize(Customizer customizer) { + m_customizer = std::move(customizer); + return *this; + } + + std::shared_ptr build() + const; + + void addTo(Acts::Experimental::BlueprintNode& node) const; + + private: + const ActsPlugins::DD4hep::BlueprintBuilder* m_builder; + std::optional m_layerType; + std::optional m_axes; + std::optional m_layerAxes; + std::optional m_pattern; + std::optional m_container; + std::optional m_envelope; + bool m_emptyOk = false; + + Customizer m_customizer; +}; + +class BarrelEndcapAssemblyHelper { + public: + explicit BarrelEndcapAssemblyHelper(const BlueprintBuilder& builder) + : m_builder{&builder} {} + + std::shared_ptr build() + const; + + void addTo(Acts::Experimental::BlueprintNode& node) const; + + auto& setCustomizer(LayerHelper::Customizer customizer) { + m_customizer = std::move(customizer); + return *this; + } + + auto& setAssembly(const dd4hep::DetElement& assembly) { + m_assembly = assembly; + return *this; + } + + auto& setBarrelAxes(const std::string& axes) { + m_barrelAxes = axes; + return *this; + } + + auto& setEndcapAxes(const std::string& axes) { + m_endcapAxes = axes; + return *this; + } + + auto& setLayerPattern(const std::regex& pattern) { + m_layerPattern = pattern; + return *this; + } + + private: + LayerHelper::Customizer m_customizer; + + std::optional m_assembly; + std::optional m_barrelAxes; + std::optional m_endcapAxes; + std::optional m_layerPattern; + const BlueprintBuilder* m_builder; +}; class BlueprintBuilder { public: @@ -86,13 +207,20 @@ class BlueprintBuilder { Acts::AxisDirection direction, const std::regex& layerPattern, const Acts::ExtentEnvelope& envelope = Acts::ExtentEnvelope::Zero()); - LayerHelper layerHelper(); + LayerHelper layerHelper() const; + BarrelEndcapAssemblyHelper barrelEndcapAssemblyHelper() const; + + std::shared_ptr + makeBarrelEndcapAssembly( + const dd4hep::DetElement& assembly, const std::regex& layerPattern, + const std::string& barrelAxes, const std::string& endcapAxes, + const LayerHelper::Customizer& customizer = {}) const; static std::optional findDetElementByName( const dd4hep::DetElement& parent, const std::string& name); std::optional findDetElementByName( - const std::string& name); + const std::string& name) const; std::string getPathToElementName(const dd4hep::DetElement& elem) const; @@ -112,83 +240,6 @@ class BlueprintBuilder { std::unique_ptr m_logger; }; -class LayerHelper { - public: - using LayerType = Acts::Experimental::LayerBlueprintNode::LayerType; - using Customizer = std::function; - - explicit LayerHelper(ActsPlugins::DD4hep::BlueprintBuilder& builder) - : m_builder{&builder} {} - - LayerHelper& setLayerType(LayerType layerType) { - m_layerType = layerType; - return *this; - } - - LayerHelper& endcap() { return setLayerType(LayerType::Disc); } - LayerHelper& barrel() { return setLayerType(LayerType::Cylinder); } - - LayerHelper& setAxes(const std::string& axes) { - m_axes = axes; - return *this; - } - - LayerHelper& setLayerAxes(const std::string& layerAxes) { - m_layerAxes = layerAxes; - return *this; - } - - LayerHelper& setPattern(const std::string& pattern) { - m_pattern = std::regex{pattern}; - return *this; - } - - LayerHelper& setContainer(const dd4hep::DetElement& container) { - m_container = container; - return *this; - } - - LayerHelper& setContainer(const std::string& name) { - m_container = m_builder->findDetElementByName(name); - if (!m_container.has_value()) { - throw std::runtime_error("Could not find DetElement with name " + name + - " in LayerHelper"); - } - return *this; - } - - LayerHelper& setEnvelope(const Acts::ExtentEnvelope& envelope) { - m_envelope = envelope; - return *this; - } - - LayerHelper& setEmptyOk(bool emptyOk) { - m_emptyOk = emptyOk; - return *this; - } - - LayerHelper& customize(Customizer customizer) { - m_customizer = std::move(customizer); - return *this; - } - - std::shared_ptr build() - const; - - private: - ActsPlugins::DD4hep::BlueprintBuilder* m_builder; - std::optional m_layerType; - std::optional m_axes; - std::optional m_layerAxes; - std::optional m_pattern; - std::optional m_container; - std::optional m_envelope; - bool m_emptyOk = false; - - Customizer m_customizer; -}; - } // namespace DD4hep } // namespace ActsPlugins diff --git a/Plugins/DD4hep/src/BlueprintBuilder.cpp b/Plugins/DD4hep/src/BlueprintBuilder.cpp index e790c4ca942..f5359b0fd83 100644 --- a/Plugins/DD4hep/src/BlueprintBuilder.cpp +++ b/Plugins/DD4hep/src/BlueprintBuilder.cpp @@ -12,13 +12,18 @@ #include "Acts/Definitions/Units.hpp" #include "Acts/Geometry/ContainerBlueprintNode.hpp" #include "Acts/Geometry/Extent.hpp" +#include "Acts/Geometry/Layer.hpp" #include "Acts/Surfaces/CylinderBounds.hpp" #include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/AxisDefinitions.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsPlugins/DD4hep/DD4hepDetectorElement.hpp" #include "ActsPlugins/Root/TGeoSurfaceConverter.hpp" #include "ActsPlugins/Root/TGeoVolumeConverter.hpp" +#include +#include + #include #include #include @@ -73,7 +78,7 @@ std::optional BlueprintBuilder::findDetElementByName( } std::optional BlueprintBuilder::findDetElementByName( - const std::string& name) { + const std::string& name) const { return findDetElementByName(world(), name); } @@ -274,10 +279,123 @@ BlueprintBuilder::addLayers(const dd4hep::DetElement& container, return node; } -LayerHelper BlueprintBuilder::layerHelper() { +LayerHelper BlueprintBuilder::layerHelper() const { return LayerHelper(*this); } +BarrelEndcapAssemblyHelper BlueprintBuilder::barrelEndcapAssemblyHelper() + const { + return BarrelEndcapAssemblyHelper(*this); +} + +namespace { +std::vector findDetElementsByType( + const dd4hep::DetElement& parent, + dd4hep::DetType::DetectorTypeEnumeration type) { + std::vector result; + visitSubtree(parent, [&](const auto& elem) { + dd4hep::DetType subDetType{elem.typeFlag()}; + if (subDetType.is(type)) { + result.push_back(elem); + } + }); + return result; +} + +// template