From 7f9db1227a503f9d5fc0a70b1359a5027eb0d3dd Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 14:03:01 +0100 Subject: [PATCH 01/69] Make the IActsGeoSvc return a shared_ptr Most of the Acts downstream assumes a shared_ptr, so we might as well return one from here --- k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h | 4 +++- k4ActsTracking/src/components/ActsGeoSvc.h | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h b/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h index 017464a5..d5a74c98 100644 --- a/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h +++ b/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h @@ -21,6 +21,8 @@ #define IACTSGEOSVC_H #include + +#include #include namespace dd4hep { @@ -41,7 +43,7 @@ class GAUDI_API IActsGeoSvc : virtual public IService { public: DeclareInterfaceID(IActsGeoSvc, 1, 0); - virtual const Acts::TrackingGeometry& trackingGeometry() const = 0; + virtual std::shared_ptr trackingGeometry() const = 0; virtual ~IActsGeoSvc() {} }; diff --git a/k4ActsTracking/src/components/ActsGeoSvc.h b/k4ActsTracking/src/components/ActsGeoSvc.h index 96cda012..faff1e24 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.h +++ b/k4ActsTracking/src/components/ActsGeoSvc.h @@ -56,7 +56,7 @@ class ActsGeoSvc : public extends { Acts::GeometryContext m_trackingGeoCtx; /// ACTS Tracking Geometry - std::unique_ptr m_trackingGeo{nullptr}; + std::shared_ptr m_trackingGeo{nullptr}; /// ACTS Material Decorator std::shared_ptr m_materialDeco{nullptr}; @@ -84,8 +84,8 @@ class ActsGeoSvc : public extends { StatusCode createGeoObj(); - virtual const Acts::TrackingGeometry& trackingGeometry() const; + virtual std::shared_ptr trackingGeometry() const; }; -inline const Acts::TrackingGeometry& ActsGeoSvc::trackingGeometry() const { return *m_trackingGeo; } +inline std::shared_ptr ActsGeoSvc::trackingGeometry() const { return m_trackingGeo; } #endif From fc18585312a3900f8cd022884c96f0a7997a7c83 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 14:15:39 +0100 Subject: [PATCH 02/69] Add retrieving magnetic field to the IActsGeoSvc Make the ActsGeoSvc implementation return a constant magnetic field along for now. --- .../include/k4ActsTracking/IActsGeoSvc.h | 6 ++++-- k4ActsTracking/src/components/ActsGeoSvc.cpp | 15 +++++++++++++++ k4ActsTracking/src/components/ActsGeoSvc.h | 8 ++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h b/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h index d5a74c98..302b4afd 100644 --- a/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h +++ b/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h @@ -34,6 +34,7 @@ namespace dd4hep { namespace Acts { class TrackingGeometry; class Surface; + class MagneticFieldProvider; } // namespace Acts class GAUDI_API IActsGeoSvc : virtual public IService { @@ -43,9 +44,10 @@ class GAUDI_API IActsGeoSvc : virtual public IService { public: DeclareInterfaceID(IActsGeoSvc, 1, 0); - virtual std::shared_ptr trackingGeometry() const = 0; + virtual std::shared_ptr trackingGeometry() const = 0; + virtual std::shared_ptr magneticField() const = 0; - virtual ~IActsGeoSvc() {} + virtual ~IActsGeoSvc() = default; }; #endif // IACTSGEOSVC_H diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index a3489273..241f025e 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -24,6 +24,7 @@ #include "k4Interface/IGeoSvc.h" #include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/MagneticField/ConstantBField.hpp" #include "Acts/Visualization/GeometryView3D.hpp" #include "Acts/Visualization/ObjVisualization3D.hpp" #if __has_include("ActsPlugins/DD4hep/ConvertDD4hepDetector.hpp") @@ -37,6 +38,11 @@ namespace ActsPlugins { } // namespace ActsPlugins #endif +#include +#include + +#include + using namespace Gaudi; DECLARE_COMPONENT(ActsGeoSvc) @@ -61,6 +67,15 @@ StatusCode ActsGeoSvc::initialize() { m_dd4hepGeo->world(), *logger, bTypePhi, bTypeR, bTypeZ, layerEnvelopeR, layerEnvelopeZ, defaultLayerThickness, ActsPlugins::sortDetElementsByID, m_trackingGeoCtx, m_materialDeco); + std::array magneticFieldVector = {0, 0, 0}; + std::array position = {0, 0, 0}; + m_dd4hepGeo->field().magneticField(position.data(), magneticFieldVector.data()); + debug() << fmt::format("Retrieved magnetic field at position {}: {}", position, magneticFieldVector) << endmsg; + m_magneticField = std::make_shared( + Acts::Vector3(magneticFieldVector[0] / dd4hep::tesla * Acts::UnitConstants::T, + magneticFieldVector[1] / dd4hep::tesla * Acts::UnitConstants::T, + magneticFieldVector[2] / dd4hep::tesla * Acts::UnitConstants::T)); + /// Setting geometry debug option if (m_debugGeometry == true) { info() << "Geometry debugging is ON." << endmsg; diff --git a/k4ActsTracking/src/components/ActsGeoSvc.h b/k4ActsTracking/src/components/ActsGeoSvc.h index faff1e24..2b733f68 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.h +++ b/k4ActsTracking/src/components/ActsGeoSvc.h @@ -27,6 +27,7 @@ #include "Acts/Definitions/Units.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/MagneticField/MagneticFieldProvider.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Logger.hpp" @@ -58,6 +59,9 @@ class ActsGeoSvc : public extends { /// ACTS Tracking Geometry std::shared_ptr m_trackingGeo{nullptr}; + /// ACTS Magnetic field + std::shared_ptr m_magneticField{nullptr}; + /// ACTS Material Decorator std::shared_ptr m_materialDeco{nullptr}; @@ -85,7 +89,11 @@ class ActsGeoSvc : public extends { StatusCode createGeoObj(); virtual std::shared_ptr trackingGeometry() const; + + virtual std::shared_ptr magneticField() const; }; inline std::shared_ptr ActsGeoSvc::trackingGeometry() const { return m_trackingGeo; } + +inline std::shared_ptr ActsGeoSvc::magneticField() const { return m_magneticField; } #endif From 0832ea0a6567eaa56a5f61c77ea6f3a604be8daa Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 2 Dec 2025 15:19:34 +0100 Subject: [PATCH 03/69] First version of service for building Gen3 geometries --- .../src/components/ActsGeoGen3Svc.cpp | 57 +++++++++++++++++++ .../src/components/ActsGeoGen3Svc.h | 39 +++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 k4ActsTracking/src/components/ActsGeoGen3Svc.cpp create mode 100644 k4ActsTracking/src/components/ActsGeoGen3Svc.h diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp new file mode 100644 index 00000000..3250c8ac --- /dev/null +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -0,0 +1,57 @@ +#include "ActsGeoGen3Svc.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +DECLARE_COMPONENT(ActsGeoGen3Svc) + +ActsGeoGen3Svc::ActsGeoGen3Svc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) {} + +StatusCode ActsGeoGen3Svc::initialize() { + m_geoSvc = Gaudi::svcLocator()->service("GeoSvc"); + K4_GAUDI_CHECK(m_geoSvc); + + ActsPlugins::DD4hep::BlueprintBuilder builder{{ + .dd4hepDetector = m_geoSvc->getDetector(), + .lengthScale = Acts::UnitConstants::cm, + }}; + + using Acts::Experimental::Blueprint; + using Acts::Experimental::BlueprintOptions; + using namespace Acts::UnitLiterals; + using enum Acts::AxisDirection; + + Blueprint::Config cfg; + // Padding around subvolumes of the world volume + cfg.envelope[AxisZ] = {20_mm, 20_mm}; + cfg.envelope[AxisR] = {0_mm, 20_mm}; + Blueprint root{cfg}; + + auto innerTrackerBarrelElem = builder.findDetElementByName("InnerTrackerBarrel").value(); + auto innerTrackerBarrel = builder.addLayers(innerTrackerBarrelElem, "XYZ", AxisR, std::regex{"layer\\d_\\d"}); + + BlueprintOptions options; + Acts::GeometryContext gctxt{}; + + m_trackingGeo = root.construct(options, gctxt); + + Acts::ObjVisualization3D vis{}; + m_trackingGeo->visualize(vis, gctxt); + vis.write("dumped_acts_geo.obj"); + + return StatusCode::SUCCESS; +} diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.h b/k4ActsTracking/src/components/ActsGeoGen3Svc.h new file mode 100644 index 00000000..fe96ed49 --- /dev/null +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.h @@ -0,0 +1,39 @@ +#ifndef K4ACTSTRACKING_ACTSGEOGEN3SVC_H +#define K4ACTSTRACKING_ACTSGEOGEN3SVC_H + +#include "k4ActsTracking/IActsGeoSvc.h" + +#include + +#include "GaudiKernel/Service.h" + +#include +#include + +namespace Acts { + class TrackingGeometry; +} + +namespace dd4hep { + class Detector; +} + +class ActsGeoGen3Svc : public extends { +public: + const Acts::TrackingGeometry& trackingGeometry() const override; + + ActsGeoGen3Svc(const std::string& name, ISvcLocator* svcLoc); + + ~ActsGeoGen3Svc() = default; + + StatusCode initialize() override; + +private: + dd4hep::Detector* m_dd4hepGeo{nullptr}; + SmartIF m_geoSvc; + std::unique_ptr m_trackingGeo{nullptr}; +}; + +inline const Acts::TrackingGeometry& ActsGeoGen3Svc::trackingGeometry() const { return *m_trackingGeo; } + +#endif // K4ACTSTRACKING_ACTSGEOGEN3SVC_H From ab05f389860e6aa1af094628d72890e7d3093c1d Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 11:23:17 +0100 Subject: [PATCH 04/69] Make selection configurable and add example --- k4ActsTracking/examples/visActsGEo.py | 22 ++++++++++++++++ .../src/components/ActsGeoGen3Svc.cpp | 26 +++++++++++++++---- .../src/components/ActsGeoGen3Svc.h | 4 +++ 3 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 k4ActsTracking/examples/visActsGEo.py diff --git a/k4ActsTracking/examples/visActsGEo.py b/k4ActsTracking/examples/visActsGEo.py new file mode 100644 index 00000000..29112341 --- /dev/null +++ b/k4ActsTracking/examples/visActsGEo.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +from Gaudi.Configuration import VERBOSE + +from Configurables import ActsGeoGen3Svc, GeoSvc +from k4FWCore import ApplicationMgr +from k4FWCore.parseArgs import parser + +parser.add_argument("--compactFile", help="Compact file") + +args = parser.parse_known_args()[0] + +geoSvc = GeoSvc() +geoSvc.detectors = [args.compactFile] + +actsGeoSvc = ActsGeoGen3Svc("ActsGeoSvc") +actsGeoSvc.DetElementName = "InnerTrackerBarrel" +actsGeoSvc.LayerPatternExpr = r"layer\\d" +actsGeoSvc.OutputLevel = VERBOSE + + +ApplicationMgr(TopAlg=[], ExtSvc=[geoSvc, actsGeoSvc]) diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp index 3250c8ac..88cc8db9 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -1,10 +1,14 @@ #include "ActsGeoGen3Svc.h" +#include "k4ActsTracking/ActsGaudiLogger.h" + #include #include #include +#include #include +#include #include #include #include @@ -25,10 +29,13 @@ StatusCode ActsGeoGen3Svc::initialize() { m_geoSvc = Gaudi::svcLocator()->service("GeoSvc"); K4_GAUDI_CHECK(m_geoSvc); + auto gaudiLogger = makeActsGaudiLogger(this); + ActsPlugins::DD4hep::BlueprintBuilder builder{{ - .dd4hepDetector = m_geoSvc->getDetector(), - .lengthScale = Acts::UnitConstants::cm, - }}; + .dd4hepDetector = m_geoSvc->getDetector(), + .lengthScale = Acts::UnitConstants::cm, + }, + gaudiLogger->cloneWithSuffix("BlpBld")}; using Acts::Experimental::Blueprint; using Acts::Experimental::BlueprintOptions; @@ -41,14 +48,23 @@ StatusCode ActsGeoGen3Svc::initialize() { cfg.envelope[AxisR] = {0_mm, 20_mm}; Blueprint root{cfg}; - auto innerTrackerBarrelElem = builder.findDetElementByName("InnerTrackerBarrel").value(); - auto innerTrackerBarrel = builder.addLayers(innerTrackerBarrelElem, "XYZ", AxisR, std::regex{"layer\\d_\\d"}); + debug() << "Finding " << m_detElementName.value() << " detector element" << endmsg; + auto innerTrackerBarrelElem = builder.findDetElementByName(m_detElementName.value()).value(); + debug() << "Adding layers from " << m_detElementName.value() << " matching " << m_layerPattern.value() << endmsg; + auto innerTrackerBarrel = builder.addLayers(innerTrackerBarrelElem, "XYZ", AxisR, std::regex{m_layerPattern.value()}); + + debug() << "Found " << innerTrackerBarrel->children().size() << " children in innerTrackerBarrel" << endmsg; + + debug() << "Adding inner tracker barrel to the root blueprint" << endmsg; + root.addChild(innerTrackerBarrel); BlueprintOptions options; Acts::GeometryContext gctxt{}; + debug() << "Constructing tracking geometry" << endmsg; m_trackingGeo = root.construct(options, gctxt); + debug() << "Creating visualiztion" << endmsg; Acts::ObjVisualization3D vis{}; m_trackingGeo->visualize(vis, gctxt); vis.write("dumped_acts_geo.obj"); diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.h b/k4ActsTracking/src/components/ActsGeoGen3Svc.h index fe96ed49..2ed4e260 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.h +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.h @@ -3,6 +3,7 @@ #include "k4ActsTracking/IActsGeoSvc.h" +#include #include #include "GaudiKernel/Service.h" @@ -28,6 +29,9 @@ class ActsGeoGen3Svc : public extends { StatusCode initialize() override; + Gaudi::Property m_detElementName{this, "DetElementName", "Name of the DetElement", "InnerTrackerBarrel"}; + Gaudi::Property m_layerPattern{this, "LayerPatternExpr", "Layer pattern match expression", "layer\\d"}; + private: dd4hep::Detector* m_dd4hepGeo{nullptr}; SmartIF m_geoSvc; From f9932ed2520ea9b8c5dd9ab2a6fd0db68788b3aa Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 11:45:53 +0100 Subject: [PATCH 05/69] Move to builder pattern for constructing the detector --- .../src/components/ActsGeoGen3Svc.cpp | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp index 88cc8db9..49c0f179 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -4,14 +4,18 @@ #include +#include #include #include #include #include #include +#include +#include #include #include #include +#include #include #include #include @@ -35,7 +39,7 @@ StatusCode ActsGeoGen3Svc::initialize() { .dd4hepDetector = m_geoSvc->getDetector(), .lengthScale = Acts::UnitConstants::cm, }, - gaudiLogger->cloneWithSuffix("BlpBld")}; + gaudiLogger->cloneWithSuffix("|BlpBld")}; using Acts::Experimental::Blueprint; using Acts::Experimental::BlueprintOptions; @@ -48,15 +52,25 @@ StatusCode ActsGeoGen3Svc::initialize() { cfg.envelope[AxisR] = {0_mm, 20_mm}; Blueprint root{cfg}; - debug() << "Finding " << m_detElementName.value() << " detector element" << endmsg; - auto innerTrackerBarrelElem = builder.findDetElementByName(m_detElementName.value()).value(); - debug() << "Adding layers from " << m_detElementName.value() << " matching " << m_layerPattern.value() << endmsg; - auto innerTrackerBarrel = builder.addLayers(innerTrackerBarrelElem, "XYZ", AxisR, std::regex{m_layerPattern.value()}); + auto& outer = root.addCylinderContainer("MAIA_v0", AxisR); + outer.addStaticVolume(Acts::Transform3::Identity(), + std::make_unique(0_mm, 10_mm, 1000_mm), "Beampipe"); - debug() << "Found " << innerTrackerBarrel->children().size() << " children in innerTrackerBarrel" << endmsg; + outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - debug() << "Adding inner tracker barrel to the root blueprint" << endmsg; - root.addChild(innerTrackerBarrel); + auto barrel = builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern(m_layerPattern.value()) + .setContainer(m_detElementName.value()) + .setEnvelope(envelope) + .build(); + + barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + innerTracker.addChild(barrel); + }); BlueprintOptions options; Acts::GeometryContext gctxt{}; From 9a987bd4705e93f8e92ff98964f429a752e2e576 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 13:29:38 +0100 Subject: [PATCH 06/69] Add algorithm to test propagation through geometry --- .../src/components/ActsGeoGen3Svc.h | 2 +- .../src/components/ActsTestPropagator.cpp | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 k4ActsTracking/src/components/ActsTestPropagator.cpp diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.h b/k4ActsTracking/src/components/ActsGeoGen3Svc.h index 2ed4e260..57da5145 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.h +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.h @@ -30,7 +30,7 @@ class ActsGeoGen3Svc : public extends { StatusCode initialize() override; Gaudi::Property m_detElementName{this, "DetElementName", "Name of the DetElement", "InnerTrackerBarrel"}; - Gaudi::Property m_layerPattern{this, "LayerPatternExpr", "Layer pattern match expression", "layer\\d"}; + Gaudi::Property m_layerPattern{this, "LayerPatternExpr", "Layer pattern match expression", "layer"}; private: dd4hep::Detector* m_dd4hepGeo{nullptr}; diff --git a/k4ActsTracking/src/components/ActsTestPropagator.cpp b/k4ActsTracking/src/components/ActsTestPropagator.cpp new file mode 100644 index 00000000..e8a1a8d5 --- /dev/null +++ b/k4ActsTracking/src/components/ActsTestPropagator.cpp @@ -0,0 +1,93 @@ +#include "k4ActsTracking/ActsGaudiLogger.h" +#include "k4ActsTracking/IActsGeoSvc.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +struct ActsTestPropagator : public Gaudi::Algorithm { + explicit ActsTestPropagator(const std::string& name, ISvcLocator* svcLoc); + + StatusCode initialize() override; + + StatusCode execute(const EventContext&) const override; + +private: + SmartIF m_actsGeoSvc; + SmartIF m_geoSvc; + + std::shared_ptr m_magneticField{nullptr}; + + std::unique_ptr m_actsLogger{nullptr}; +}; + +StatusCode ActsTestPropagator::initialize() { + m_geoSvc = svcLoc()->service("GeoSvc"); + K4_GAUDI_CHECK(m_geoSvc); + + m_actsGeoSvc = svcLoc()->service("ActsGeoSvc"); + K4_GAUDI_CHECK(m_actsGeoSvc); + + std::array magneticFieldVector = {0, 0, 0}; + std::array position = {0, 0, 0}; + m_geoSvc->getDetector()->field().magneticField(position.data(), magneticFieldVector.data()); + debug() << fmt::format("Retrieved magnetic field at position {}: {}", position, magneticFieldVector) << endmsg; + m_magneticField = std::make_shared( + Acts::Vector3(magneticFieldVector[0] / dd4hep::tesla * Acts::UnitConstants::T, + magneticFieldVector[1] / dd4hep::tesla * Acts::UnitConstants::T, + magneticFieldVector[2] / dd4hep::tesla * Acts::UnitConstants::T)); + + m_actsLogger = makeActsGaudiLogger(this); + + return StatusCode::SUCCESS; +} + +StatusCode ActsTestPropagator::execute(const EventContext&) const { + // Largely taken from ActsExamples/Propagation/PropagatorInterface + + // The step length logger for testing & end of world aborter + using MaterialInteractor = Acts::MaterialInteractor; + using SteppingLogger = Acts::detail::SteppingLogger; + using EndOfWorld = Acts::EndOfWorldReached; + + using Stepper = Acts::EigenStepper<>; + using Navigator = Acts::Navigator; + using Propagator = Acts::Propagator; + + using ActorList = Acts::ActorList; + using PropagatorOptions = Propagator::template Options; + + // Configurations + // Navigator::Config navigatorCfg{trackingGeometry()}; + // navigatorCfg.resolvePassive = false; + // navigatorCfg.resolveMaterial = true; + // navigatorCfg.resolveSensitive = true; + + // Stepper stepper(m_magneticField); + // Navigator navigator(navigatorCfg); + // Propagator propagator(std::move(stepper), std::move(navigator)); + + auto options = PropagatorOptions{Acts::GeometryContext{}, Acts::MagneticFieldContext{}}; + + return StatusCode::SUCCESS; +} From 9dc1b29b4359623e6e363d416ec756d60f6fdb92 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 14:07:00 +0100 Subject: [PATCH 07/69] Switch to new interface for IActsGeoSvc --- .../src/components/ActsGeoGen3Svc.cpp | 15 ++++++++++++++ .../src/components/ActsGeoGen3Svc.h | 20 +++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp index 49c0f179..cf5768dd 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,11 @@ #include +#include +#include + +#include + DECLARE_COMPONENT(ActsGeoGen3Svc) ActsGeoGen3Svc::ActsGeoGen3Svc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) {} @@ -33,6 +39,15 @@ StatusCode ActsGeoGen3Svc::initialize() { m_geoSvc = Gaudi::svcLocator()->service("GeoSvc"); K4_GAUDI_CHECK(m_geoSvc); + std::array magneticFieldVector = {0, 0, 0}; + std::array position = {0, 0, 0}; + m_geoSvc->getDetector()->field().magneticField(position.data(), magneticFieldVector.data()); + debug() << fmt::format("Retrieved magnetic field at position {}: {}", position, magneticFieldVector) << endmsg; + m_magneticField = std::make_shared( + Acts::Vector3(magneticFieldVector[0] / dd4hep::tesla * Acts::UnitConstants::T, + magneticFieldVector[1] / dd4hep::tesla * Acts::UnitConstants::T, + magneticFieldVector[2] / dd4hep::tesla * Acts::UnitConstants::T)); + auto gaudiLogger = makeActsGaudiLogger(this); ActsPlugins::DD4hep::BlueprintBuilder builder{{ diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.h b/k4ActsTracking/src/components/ActsGeoGen3Svc.h index 57da5145..df7f8f38 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.h +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.h @@ -13,7 +13,8 @@ namespace Acts { class TrackingGeometry; -} + class MagneticFieldProvider; +} // namespace Acts namespace dd4hep { class Detector; @@ -21,7 +22,9 @@ namespace dd4hep { class ActsGeoGen3Svc : public extends { public: - const Acts::TrackingGeometry& trackingGeometry() const override; + std::shared_ptr trackingGeometry() const override; + + std::shared_ptr magneticField() const override; ActsGeoGen3Svc(const std::string& name, ISvcLocator* svcLoc); @@ -33,11 +36,16 @@ class ActsGeoGen3Svc : public extends { Gaudi::Property m_layerPattern{this, "LayerPatternExpr", "Layer pattern match expression", "layer"}; private: - dd4hep::Detector* m_dd4hepGeo{nullptr}; - SmartIF m_geoSvc; - std::unique_ptr m_trackingGeo{nullptr}; + dd4hep::Detector* m_dd4hepGeo{nullptr}; + SmartIF m_geoSvc; + std::shared_ptr m_trackingGeo{nullptr}; + std::shared_ptr m_magneticField{nullptr}; }; -inline const Acts::TrackingGeometry& ActsGeoGen3Svc::trackingGeometry() const { return *m_trackingGeo; } +inline std::shared_ptr ActsGeoGen3Svc::trackingGeometry() const { return m_trackingGeo; } + +inline std::shared_ptr ActsGeoGen3Svc::magneticField() const { + return m_magneticField; +} #endif // K4ACTSTRACKING_ACTSGEOGEN3SVC_H From 7bfa676cf256c3de3aee3c23f903f740e84642cd Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 15:58:42 +0100 Subject: [PATCH 08/69] Make propagation through geometry work --- k4ActsTracking/examples/visActsGEo.py | 16 +++-- .../src/components/ActsGeoGen3Svc.cpp | 4 ++ .../src/components/ActsTestPropagator.cpp | 64 +++++++++++++------ 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/k4ActsTracking/examples/visActsGEo.py b/k4ActsTracking/examples/visActsGEo.py index 29112341..1a5093ad 100644 --- a/k4ActsTracking/examples/visActsGEo.py +++ b/k4ActsTracking/examples/visActsGEo.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 -from Gaudi.Configuration import VERBOSE +from Gaudi.Configuration import VERBOSE, DEBUG -from Configurables import ActsGeoGen3Svc, GeoSvc +from Configurables import ActsGeoGen3Svc, GeoSvc, ActsTestPropagator, EventDataSvc from k4FWCore import ApplicationMgr from k4FWCore.parseArgs import parser @@ -16,7 +16,15 @@ actsGeoSvc = ActsGeoGen3Svc("ActsGeoSvc") actsGeoSvc.DetElementName = "InnerTrackerBarrel" actsGeoSvc.LayerPatternExpr = r"layer\\d" -actsGeoSvc.OutputLevel = VERBOSE +actsGeoSvc.OutputLevel = DEBUG +propTest = ActsTestPropagator("TestPropagator") +propTest.OutputLevel = VERBOSE -ApplicationMgr(TopAlg=[], ExtSvc=[geoSvc, actsGeoSvc]) + +ApplicationMgr( + TopAlg=[propTest], + ExtSvc=[geoSvc, actsGeoSvc, EventDataSvc()], + EvtMax=1, + EvtSel="NONE", +) diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp index cf5768dd..40989043 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -70,6 +70,10 @@ StatusCode ActsGeoGen3Svc::initialize() { auto& outer = root.addCylinderContainer("MAIA_v0", AxisR); outer.addStaticVolume(Acts::Transform3::Identity(), std::make_unique(0_mm, 10_mm, 1000_mm), "Beampipe"); + // We want to pull the next volume in towards the beampipe to map material to + // the correct places in the end. We need to ensure that the enclosing + // cylinder contains the beampipe entirely. + outer.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); diff --git a/k4ActsTracking/src/components/ActsTestPropagator.cpp b/k4ActsTracking/src/components/ActsTestPropagator.cpp index e8a1a8d5..82e2be07 100644 --- a/k4ActsTracking/src/components/ActsTestPropagator.cpp +++ b/k4ActsTracking/src/components/ActsTestPropagator.cpp @@ -1,10 +1,12 @@ #include "k4ActsTracking/ActsGaudiLogger.h" #include "k4ActsTracking/IActsGeoSvc.h" -#include #include #include +#include +#include +#include #include #include #include @@ -22,11 +24,13 @@ #include #include -#include +#include #include struct ActsTestPropagator : public Gaudi::Algorithm { - explicit ActsTestPropagator(const std::string& name, ISvcLocator* svcLoc); + explicit ActsTestPropagator(const std::string& name, ISvcLocator* svcLoc) : Gaudi::Algorithm(name, svcLoc) {} + + ~ActsTestPropagator() = default; StatusCode initialize() override; @@ -39,6 +43,8 @@ struct ActsTestPropagator : public Gaudi::Algorithm { std::shared_ptr m_magneticField{nullptr}; std::unique_ptr m_actsLogger{nullptr}; + + std::ofstream m_outputFile; }; StatusCode ActsTestPropagator::initialize() { @@ -48,15 +54,6 @@ StatusCode ActsTestPropagator::initialize() { m_actsGeoSvc = svcLoc()->service("ActsGeoSvc"); K4_GAUDI_CHECK(m_actsGeoSvc); - std::array magneticFieldVector = {0, 0, 0}; - std::array position = {0, 0, 0}; - m_geoSvc->getDetector()->field().magneticField(position.data(), magneticFieldVector.data()); - debug() << fmt::format("Retrieved magnetic field at position {}: {}", position, magneticFieldVector) << endmsg; - m_magneticField = std::make_shared( - Acts::Vector3(magneticFieldVector[0] / dd4hep::tesla * Acts::UnitConstants::T, - magneticFieldVector[1] / dd4hep::tesla * Acts::UnitConstants::T, - magneticFieldVector[2] / dd4hep::tesla * Acts::UnitConstants::T)); - m_actsLogger = makeActsGaudiLogger(this); return StatusCode::SUCCESS; @@ -78,16 +75,45 @@ StatusCode ActsTestPropagator::execute(const EventContext&) const { using PropagatorOptions = Propagator::template Options; // Configurations - // Navigator::Config navigatorCfg{trackingGeometry()}; - // navigatorCfg.resolvePassive = false; - // navigatorCfg.resolveMaterial = true; - // navigatorCfg.resolveSensitive = true; + Navigator::Config navigatorCfg{m_actsGeoSvc->trackingGeometry()}; + navigatorCfg.resolvePassive = false; + navigatorCfg.resolveMaterial = true; + navigatorCfg.resolveSensitive = true; - // Stepper stepper(m_magneticField); - // Navigator navigator(navigatorCfg); - // Propagator propagator(std::move(stepper), std::move(navigator)); + Stepper stepper(m_actsGeoSvc->magneticField()); + Navigator navigator(navigatorCfg, m_actsLogger->cloneWithSuffix(":Nav")); + Propagator propagator(std::move(stepper), std::move(navigator), m_actsLogger->cloneWithSuffix(":Prop")); auto options = PropagatorOptions{Acts::GeometryContext{}, Acts::MagneticFieldContext{}}; + auto state = propagator.makeState(options); + + const auto startParameters = Acts::BoundTrackParameters::createCurvilinear( + Acts::Vector4{0, 0, 0, 0}, Acts::Vector3{0, 0.5, 0.5}, 0.5, std::nullopt, Acts::ParticleHypothesis::pion()); + + auto initResult = propagator.initialize(state, startParameters); + if (!initResult.ok()) { + error() << initResult.error() << endmsg; + return StatusCode::FAILURE; + } + debug() << "Initialized propagator" << endmsg; + + // Propagate using the propagator + debug() << "Starting propagation" << endmsg; + auto resultTmp = propagator.propagate(state); + if (!resultTmp.ok()) { + error() << resultTmp.error() << endmsg; + return StatusCode::FAILURE; + } + debug() << "Done with propagation" << endmsg; + + auto result = propagator.makeResult(std::move(state), resultTmp, options, true); + if (!result.ok()) { + error() << result.error() << endmsg; + return StatusCode::FAILURE + } + return StatusCode::SUCCESS; } + +DECLARE_COMPONENT(ActsTestPropagator); From 7625324250623312f71486bcc900f3d52e060d11 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 16:07:57 +0100 Subject: [PATCH 09/69] Fix order of Property --- k4ActsTracking/src/components/ActsGeoGen3Svc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.h b/k4ActsTracking/src/components/ActsGeoGen3Svc.h index df7f8f38..e88c23d3 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.h +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.h @@ -32,8 +32,8 @@ class ActsGeoGen3Svc : public extends { StatusCode initialize() override; - Gaudi::Property m_detElementName{this, "DetElementName", "Name of the DetElement", "InnerTrackerBarrel"}; - Gaudi::Property m_layerPattern{this, "LayerPatternExpr", "Layer pattern match expression", "layer"}; + Gaudi::Property m_detElementName{this, "DetElementName", "InnerTrackerBarrel", "Name of the DetElement"}; + Gaudi::Property m_layerPattern{this, "LayerPatternExpr", "layer", "Layer pattern match expression"}; private: dd4hep::Detector* m_dd4hepGeo{nullptr}; From 7dc48bca45031f13cc85b7849c516b950ea6ed39 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 16:25:32 +0100 Subject: [PATCH 10/69] Store step outptus --- .../src/components/ActsTestPropagator.cpp | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/k4ActsTracking/src/components/ActsTestPropagator.cpp b/k4ActsTracking/src/components/ActsTestPropagator.cpp index 82e2be07..2e2b6f8a 100644 --- a/k4ActsTracking/src/components/ActsTestPropagator.cpp +++ b/k4ActsTracking/src/components/ActsTestPropagator.cpp @@ -36,6 +36,11 @@ struct ActsTestPropagator : public Gaudi::Algorithm { StatusCode execute(const EventContext&) const override; + StatusCode finalize() override; + + Gaudi::Property m_outFileName{this, "StepsOutputFile", "acts_steps.csv", + "Output file for writing step positions and geometry id"}; + private: SmartIF m_actsGeoSvc; SmartIF m_geoSvc; @@ -44,7 +49,7 @@ struct ActsTestPropagator : public Gaudi::Algorithm { std::unique_ptr m_actsLogger{nullptr}; - std::ofstream m_outputFile; + mutable std::ofstream m_outputFile; }; StatusCode ActsTestPropagator::initialize() { @@ -56,6 +61,12 @@ StatusCode ActsTestPropagator::initialize() { m_actsLogger = makeActsGaudiLogger(this); + m_outputFile.open(m_outFileName.value()); + if (!m_outputFile.is_open()) { + error() << "Failed to open output file: " << m_outFileName.value() << endmsg; + return StatusCode::FAILURE; + } + return StatusCode::SUCCESS; } @@ -110,10 +121,22 @@ StatusCode ActsTestPropagator::execute(const EventContext&) const { auto result = propagator.makeResult(std::move(state), resultTmp, options, true); if (!result.ok()) { error() << result.error() << endmsg; - return StatusCode::FAILURE + return StatusCode::FAILURE; + } + const auto& steppingResults = result.value().get(); + for (const auto& step : steppingResults.steps) { + m_outputFile << fmt::format("{}, {}, {}, {}, {}\n", step.position.x(), step.position.y(), step.position.z(), + step.stepSize.value(), step.geoID.value()); } return StatusCode::SUCCESS; } +StatusCode ActsTestPropagator::finalize() { + if (m_outputFile.is_open()) { + m_outputFile.close(); + } + return StatusCode::SUCCESS; +} + DECLARE_COMPONENT(ActsTestPropagator); From 18fcd723249527c3e7d603f7d3cccad53e00d7ef Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 17:01:09 +0100 Subject: [PATCH 11/69] Make the output as root file --- k4ActsTracking/examples/visActsGEo.py | 5 +- .../src/components/ActsTestPropagator.cpp | 58 +++++++++---------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/k4ActsTracking/examples/visActsGEo.py b/k4ActsTracking/examples/visActsGEo.py index 1a5093ad..c8a66776 100644 --- a/k4ActsTracking/examples/visActsGEo.py +++ b/k4ActsTracking/examples/visActsGEo.py @@ -3,13 +3,16 @@ from Gaudi.Configuration import VERBOSE, DEBUG from Configurables import ActsGeoGen3Svc, GeoSvc, ActsTestPropagator, EventDataSvc -from k4FWCore import ApplicationMgr +from k4FWCore import ApplicationMgr, IOSvc from k4FWCore.parseArgs import parser parser.add_argument("--compactFile", help="Compact file") args = parser.parse_known_args()[0] +iosvc = IOSvc() +iosvc.Output = "steps.root" + geoSvc = GeoSvc() geoSvc.detectors = [args.compactFile] diff --git a/k4ActsTracking/src/components/ActsTestPropagator.cpp b/k4ActsTracking/src/components/ActsTestPropagator.cpp index 2e2b6f8a..fd6a0e9d 100644 --- a/k4ActsTracking/src/components/ActsTestPropagator.cpp +++ b/k4ActsTracking/src/components/ActsTestPropagator.cpp @@ -2,8 +2,11 @@ #include "k4ActsTracking/IActsGeoSvc.h" #include +#include #include +#include + #include #include #include @@ -18,25 +21,21 @@ #include -#include -#include - #include #include -#include #include -struct ActsTestPropagator : public Gaudi::Algorithm { - explicit ActsTestPropagator(const std::string& name, ISvcLocator* svcLoc) : Gaudi::Algorithm(name, svcLoc) {} +struct ActsTestPropagator final : public k4FWCore::Producer>()> { + explicit ActsTestPropagator(const std::string& name, ISvcLocator* svcLoc) + : Producer(name, svcLoc, {}, + {KeyValues("OutputCollections", {"step_x", "step_y", "step_z", "step_geoID", "step_size"})}) {} ~ActsTestPropagator() = default; StatusCode initialize() override; - StatusCode execute(const EventContext&) const override; - - StatusCode finalize() override; + std::vector> operator()() const override; Gaudi::Property m_outFileName{this, "StepsOutputFile", "acts_steps.csv", "Output file for writing step positions and geometry id"}; @@ -48,8 +47,6 @@ struct ActsTestPropagator : public Gaudi::Algorithm { std::shared_ptr m_magneticField{nullptr}; std::unique_ptr m_actsLogger{nullptr}; - - mutable std::ofstream m_outputFile; }; StatusCode ActsTestPropagator::initialize() { @@ -61,16 +58,10 @@ StatusCode ActsTestPropagator::initialize() { m_actsLogger = makeActsGaudiLogger(this); - m_outputFile.open(m_outFileName.value()); - if (!m_outputFile.is_open()) { - error() << "Failed to open output file: " << m_outFileName.value() << endmsg; - return StatusCode::FAILURE; - } - return StatusCode::SUCCESS; } -StatusCode ActsTestPropagator::execute(const EventContext&) const { +std::vector> ActsTestPropagator::operator()() const { // Largely taken from ActsExamples/Propagation/PropagatorInterface // The step length logger for testing & end of world aborter @@ -102,10 +93,12 @@ StatusCode ActsTestPropagator::execute(const EventContext&) const { const auto startParameters = Acts::BoundTrackParameters::createCurvilinear( Acts::Vector4{0, 0, 0, 0}, Acts::Vector3{0, 0.5, 0.5}, 0.5, std::nullopt, Acts::ParticleHypothesis::pion()); + std::vector> stepOutputs(5); + auto initResult = propagator.initialize(state, startParameters); if (!initResult.ok()) { error() << initResult.error() << endmsg; - return StatusCode::FAILURE; + return stepOutputs; } debug() << "Initialized propagator" << endmsg; @@ -114,29 +107,32 @@ StatusCode ActsTestPropagator::execute(const EventContext&) const { auto resultTmp = propagator.propagate(state); if (!resultTmp.ok()) { error() << resultTmp.error() << endmsg; - return StatusCode::FAILURE; + return stepOutputs; } debug() << "Done with propagation" << endmsg; auto result = propagator.makeResult(std::move(state), resultTmp, options, true); if (!result.ok()) { error() << result.error() << endmsg; - return StatusCode::FAILURE; + return stepOutputs; } const auto& steppingResults = result.value().get(); - for (const auto& step : steppingResults.steps) { - m_outputFile << fmt::format("{}, {}, {}, {}, {}\n", step.position.x(), step.position.y(), step.position.z(), - step.stepSize.value(), step.geoID.value()); - } - return StatusCode::SUCCESS; -} + auto& stepsX = stepOutputs[0].vec(); + auto& stepsY = stepOutputs[1].vec(); + auto& stepsZ = stepOutputs[2].vec(); + auto& stepsGeoID = stepOutputs[3].vec(); + auto& stepsLength = stepOutputs[4].vec(); -StatusCode ActsTestPropagator::finalize() { - if (m_outputFile.is_open()) { - m_outputFile.close(); + for (const auto& step : steppingResults.steps) { + stepsX.push_back(step.position.x()); + stepsY.push_back(step.position.y()); + stepsZ.push_back(step.position.z()); + stepsGeoID.push_back(step.geoID.value()); + stepsLength.push_back(step.stepSize.value()); } - return StatusCode::SUCCESS; + + return stepOutputs; } DECLARE_COMPONENT(ActsTestPropagator); From bd9ab99182c7e6b01ab6a514d15ed754131761ce Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 17:58:57 +0100 Subject: [PATCH 12/69] Add plotting script and make gun configurable --- k4ActsTracking/examples/plot_steps.py | 49 +++++++ k4ActsTracking/examples/visActsGEo.py | 3 +- .../src/components/ActsTestPropagator.cpp | 130 ++++++++++++------ 3 files changed, 141 insertions(+), 41 deletions(-) create mode 100644 k4ActsTracking/examples/plot_steps.py diff --git a/k4ActsTracking/examples/plot_steps.py b/k4ActsTracking/examples/plot_steps.py new file mode 100644 index 00000000..90297bd6 --- /dev/null +++ b/k4ActsTracking/examples/plot_steps.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import ROOT +import argparse +import sys + + +def main(): + parser = argparse.ArgumentParser(description="Plot step_r vs step_z from ROOT file") + parser.add_argument("input_file", help="Input ROOT file") + parser.add_argument( + "-o", "--output", default="step_plot.png", help="Output plot file" + ) + args = parser.parse_args() + + # Enable multi-threading for RDataFrame + ROOT.EnableImplicitMT() + + # Open the ROOT file and create RDataFrame + try: + df = ROOT.RDataFrame("events", args.input_file) + except Exception as e: + print(f"Error opening file {args.input_file}: {e}") + sys.exit(1) + + # Define step_r as sqrt(step_x^2 + step_y^2) + df = df.Define("step_r", "sqrt(step_x*step_x + step_y*step_y)") + + # Create the scatter plot + hist = df.Graph("step_z", "step_r") + + # Create canvas and draw + canvas = ROOT.TCanvas("canvas", "Step R vs Z", 800, 600) + # hist.SetMarkerStyle(20) + # hist.SetMarkerSize(0.5) + hist.SetTitle("Step R vs Z;step_z;step_r") + hist.Draw("AP") + + # Set axis limits + hist.GetXaxis().SetRangeUser(-2000, 2000) + hist.GetYaxis().SetRangeUser(0, 1000) + + # Save the plot + canvas.SaveAs(args.output) + print(f"Plot saved as {args.output}") + + +if __name__ == "__main__": + main() diff --git a/k4ActsTracking/examples/visActsGEo.py b/k4ActsTracking/examples/visActsGEo.py index c8a66776..36a2d6f3 100644 --- a/k4ActsTracking/examples/visActsGEo.py +++ b/k4ActsTracking/examples/visActsGEo.py @@ -22,7 +22,8 @@ actsGeoSvc.OutputLevel = DEBUG propTest = ActsTestPropagator("TestPropagator") -propTest.OutputLevel = VERBOSE +propTest.OutputLevel = DEBUG +propTest.NumTracks = 10000 ApplicationMgr( diff --git a/k4ActsTracking/src/components/ActsTestPropagator.cpp b/k4ActsTracking/src/components/ActsTestPropagator.cpp index fd6a0e9d..2effea37 100644 --- a/k4ActsTracking/src/components/ActsTestPropagator.cpp +++ b/k4ActsTracking/src/components/ActsTestPropagator.cpp @@ -24,7 +24,9 @@ #include #include +#include #include +#include struct ActsTestPropagator final : public k4FWCore::Producer>()> { explicit ActsTestPropagator(const std::string& name, ISvcLocator* svcLoc) @@ -39,6 +41,11 @@ struct ActsTestPropagator final : public k4FWCore::Producer m_outFileName{this, "StepsOutputFile", "acts_steps.csv", "Output file for writing step positions and geometry id"}; + Gaudi::Property m_numTracks{this, "NumTracks", 100, "Number of random tracks to propagate"}; + Gaudi::Property m_minMomentum{this, "MinMomentum", 10.0, "Minimum particle momentum in GeV"}; + Gaudi::Property m_maxMomentum{this, "MaxMomentum", 1000.0, "Maximum particle momentum in GeV"}; + Gaudi::Property m_minEta{this, "MinEta", -2.5, "Minimum pseudorapidity"}; + Gaudi::Property m_maxEta{this, "MaxEta", 2.5, "Maximum pseudorapidity"}; private: SmartIF m_actsGeoSvc; @@ -47,6 +54,12 @@ struct ActsTestPropagator final : public k4FWCore::Producer m_magneticField{nullptr}; std::unique_ptr m_actsLogger{nullptr}; + + // Random number generator for generating different start parameters + mutable std::mt19937 m_gen; + mutable std::uniform_real_distribution m_posDist; + mutable std::uniform_real_distribution m_dirDist; + mutable std::uniform_real_distribution m_qOverPDist; }; StatusCode ActsTestPropagator::initialize() { @@ -58,6 +71,23 @@ StatusCode ActsTestPropagator::initialize() { m_actsLogger = makeActsGaudiLogger(this); + // Initialize random number generator + std::random_device rd; + m_gen.seed(rd()); + m_posDist = std::uniform_real_distribution(-100.0, 100.0); // Position range in mm + + // Compute distributions from particle momenta and eta direction range + // Convert eta range to theta range for direction generation + double minTheta = 2.0 * std::atan(std::exp(-m_maxEta)); // theta from max eta + double maxTheta = 2.0 * std::atan(std::exp(-m_minEta)); // theta from min eta + + m_dirDist = std::uniform_real_distribution(minTheta, maxTheta); // theta range + + // q/p distribution based on momentum range (assuming charge ±1) + double maxQOverP = 1.0 / m_minMomentum; // 1/GeV + double minQOverP = -1.0 / m_maxMomentum; // 1/GeV (negative charge) + m_qOverPDist = std::uniform_real_distribution(minQOverP, maxQOverP); + return StatusCode::SUCCESS; } @@ -88,49 +118,69 @@ std::vector> ActsTestPropagator::operator()() auto options = PropagatorOptions{Acts::GeometryContext{}, Acts::MagneticFieldContext{}}; - auto state = propagator.makeState(options); - - const auto startParameters = Acts::BoundTrackParameters::createCurvilinear( - Acts::Vector4{0, 0, 0, 0}, Acts::Vector3{0, 0.5, 0.5}, 0.5, std::nullopt, Acts::ParticleHypothesis::pion()); - std::vector> stepOutputs(5); - - auto initResult = propagator.initialize(state, startParameters); - if (!initResult.ok()) { - error() << initResult.error() << endmsg; - return stepOutputs; - } - debug() << "Initialized propagator" << endmsg; - - // Propagate using the propagator - debug() << "Starting propagation" << endmsg; - auto resultTmp = propagator.propagate(state); - if (!resultTmp.ok()) { - error() << resultTmp.error() << endmsg; - return stepOutputs; + auto& stepsX = stepOutputs[0].vec(); + auto& stepsY = stepOutputs[1].vec(); + auto& stepsZ = stepOutputs[2].vec(); + auto& stepsGeoID = stepOutputs[3].vec(); + auto& stepsLength = stepOutputs[4].vec(); + + for (int i = 0; i < m_numTracks; ++i) { + // Generate random start position + Acts::Vector4 startPos{m_posDist(m_gen), m_posDist(m_gen), m_posDist(m_gen), 0}; + + // Generate random direction using theta from eta range and uniform phi + double theta = m_dirDist(m_gen); + double phi = std::uniform_real_distribution(0.0, 2.0 * M_PI)(m_gen); + + Acts::Vector3 direction{std::sin(theta) * std::cos(phi), std::sin(theta) * std::sin(phi), std::cos(theta)}; + + // Generate random charge over momentum + double qOverP = m_qOverPDist(m_gen); + + verbose() << fmt::format( + "Track {}: Initial state - Position: ({:.2f}, {:.2f}, {:.2f}) mm, " + "Direction: ({:.3f}, {:.3f}, {:.3f}), q/p: {:.4f} 1/GeV", + i, startPos.x(), startPos.y(), startPos.z(), direction.x(), direction.y(), direction.z(), qOverP) + << endmsg; + + const auto startParameters = Acts::BoundTrackParameters::createCurvilinear( + startPos, direction, qOverP, std::nullopt, Acts::ParticleHypothesis::pion()); + + auto state = propagator.makeState(options); + + auto initResult = propagator.initialize(state, startParameters); + if (!initResult.ok()) { + warning() << "Failed to initialize propagator for track " << i << ": " << initResult.error() << endmsg; + continue; + } + + // Propagate using the propagator + auto resultTmp = propagator.propagate(state); + if (!resultTmp.ok()) { + warning() << "Propagation failed for track " << i << ": " << resultTmp.error() << endmsg; + continue; + } + + auto result = propagator.makeResult(std::move(state), resultTmp, options, true); + if (!result.ok()) { + warning() << "Failed to make result for track " << i << ": " << result.error() << endmsg; + continue; + } + + const auto& steppingResults = result.value().get(); + + // Store step results from this track + for (const auto& step : steppingResults.steps) { + stepsX.push_back(step.position.x()); + stepsY.push_back(step.position.y()); + stepsZ.push_back(step.position.z()); + stepsGeoID.push_back(step.geoID.value()); + stepsLength.push_back(step.stepSize.value()); + } } - debug() << "Done with propagation" << endmsg; - auto result = propagator.makeResult(std::move(state), resultTmp, options, true); - if (!result.ok()) { - error() << result.error() << endmsg; - return stepOutputs; - } - const auto& steppingResults = result.value().get(); - - auto& stepsX = stepOutputs[0].vec(); - auto& stepsY = stepOutputs[1].vec(); - auto& stepsZ = stepOutputs[2].vec(); - auto& stepsGeoID = stepOutputs[3].vec(); - auto& stepsLength = stepOutputs[4].vec(); - - for (const auto& step : steppingResults.steps) { - stepsX.push_back(step.position.x()); - stepsY.push_back(step.position.y()); - stepsZ.push_back(step.position.z()); - stepsGeoID.push_back(step.geoID.value()); - stepsLength.push_back(step.stepSize.value()); - } + debug() << "Completed propagation of " << m_numTracks << " tracks with total " << stepsX.size() << " steps" << endmsg; return stepOutputs; } From b06a14a86ce91fcb654b00f2a0e480a431f14b9e Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 3 Dec 2025 17:59:33 +0100 Subject: [PATCH 13/69] Make sure to set a proper length scale for conversion --- k4ActsTracking/src/components/ActsGeoGen3Svc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp index 40989043..75d39fba 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -50,9 +50,11 @@ StatusCode ActsGeoGen3Svc::initialize() { auto gaudiLogger = makeActsGaudiLogger(this); + info() << fmt::format("Acts::cm: {}, dd4hep::cm: {}", Acts::UnitConstants::cm, dd4hep::cm) << endmsg; + ActsPlugins::DD4hep::BlueprintBuilder builder{{ .dd4hepDetector = m_geoSvc->getDetector(), - .lengthScale = Acts::UnitConstants::cm, + .lengthScale = Acts::UnitConstants::cm / dd4hep::cm, }, gaudiLogger->cloneWithSuffix("|BlpBld")}; From 4be557ecc6306c1462f43156f87b6eca0e55e236 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 4 Dec 2025 11:01:49 +0100 Subject: [PATCH 14/69] (Almost) add an endcap --- .../src/components/ActsGeoGen3Svc.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp index 75d39fba..9d908941 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -90,14 +90,31 @@ StatusCode ActsGeoGen3Svc::initialize() { barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + // TODO: This currently doesn't work because the cylinder we get from the + // barrel overlaps in z with the cylinder we get from here, because the + // first (innermost) endcap layer "sticks" into the envelope of the barrel. + // This will require dedicated stacking of the cylinders in r and z in the + // order that doesn't produce overlaps. + // + // auto negEndcap = builder.layerHelper() + // .endcap() + // .setAxes("XZY") + // .setContainer("InnerTrackerEndcap") + // .setPattern("layer_pos\\d") + // .setEnvelope(envelope) + // .build(); + + // negEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + innerTracker.addChild(barrel); + // innerTracker.addChild(negEndcap); }); BlueprintOptions options; Acts::GeometryContext gctxt{}; debug() << "Constructing tracking geometry" << endmsg; - m_trackingGeo = root.construct(options, gctxt); + m_trackingGeo = root.construct(options, gctxt, *gaudiLogger->cloneWithSuffix("|Construct")); debug() << "Creating visualiztion" << endmsg; Acts::ObjVisualization3D vis{}; From 6b47546b0e333251bb3e1638c5126c0b7ddde8ef Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 4 Dec 2025 15:15:02 +0100 Subject: [PATCH 15/69] Add the outer tracker barrel to do some tracking --- k4ActsTracking/examples/plot_steps.py | 4 +- k4ActsTracking/examples/visActsGEo.py | 5 +- .../src/components/ActsGeoGen3Svc.cpp | 48 +++++++++++++++++-- .../src/components/ActsTestPropagator.cpp | 1 + 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/k4ActsTracking/examples/plot_steps.py b/k4ActsTracking/examples/plot_steps.py index 90297bd6..3d4d564c 100644 --- a/k4ActsTracking/examples/plot_steps.py +++ b/k4ActsTracking/examples/plot_steps.py @@ -30,7 +30,7 @@ def main(): hist = df.Graph("step_z", "step_r") # Create canvas and draw - canvas = ROOT.TCanvas("canvas", "Step R vs Z", 800, 600) + canvas = ROOT.TCanvas("canvas", "Step R vs Z", 1200, 800) # hist.SetMarkerStyle(20) # hist.SetMarkerSize(0.5) hist.SetTitle("Step R vs Z;step_z;step_r") @@ -38,7 +38,7 @@ def main(): # Set axis limits hist.GetXaxis().SetRangeUser(-2000, 2000) - hist.GetYaxis().SetRangeUser(0, 1000) + hist.GetYaxis().SetRangeUser(0, 1500) # Save the plot canvas.SaveAs(args.output) diff --git a/k4ActsTracking/examples/visActsGEo.py b/k4ActsTracking/examples/visActsGEo.py index 36a2d6f3..43767c28 100644 --- a/k4ActsTracking/examples/visActsGEo.py +++ b/k4ActsTracking/examples/visActsGEo.py @@ -19,15 +19,16 @@ actsGeoSvc = ActsGeoGen3Svc("ActsGeoSvc") actsGeoSvc.DetElementName = "InnerTrackerBarrel" actsGeoSvc.LayerPatternExpr = r"layer\\d" -actsGeoSvc.OutputLevel = DEBUG +actsGeoSvc.OutputLevel = VERBOSE propTest = ActsTestPropagator("TestPropagator") propTest.OutputLevel = DEBUG -propTest.NumTracks = 10000 +propTest.NumTracks = 20000 ApplicationMgr( TopAlg=[propTest], + # TopAlg=[], ExtSvc=[geoSvc, actsGeoSvc, EventDataSvc()], EvtMax=1, EvtSel="NONE", diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp index 9d908941..d63fb19d 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -77,6 +77,33 @@ StatusCode ActsGeoGen3Svc::initialize() { // cylinder contains the beampipe entirely. outer.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + // TODO: Acts detects some overlaps when stacking this up. Need to figure out + // what they are about and how to properly configure the building below. Maybe + // there is better matching pattern. + // + // outer.addCylinderContainer("Vertex", AxisZ, [&](auto& vertex) { + // auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + + // auto barrel = builder.layerHelper() + // .barrel() + // .setAxes("XYZ") + // .setPattern("layer_\\d") + // .setContainer("VertexBarrel") + // .setEnvelope(envelope) + // .customize([&](const dd4hep::DetElement&, auto& layer) { + // // Force the Barrel onto the z-axis by not using the + // // center of gravity for auto-sizing. We do this because + // // the VertexBarrel has an odd number of modules, which + // // shifts them off-axis when using CoG + // layer.setUseCenterOfGravity(false, false, true); + // }) + // .build(); + + // barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + // vertex.addChild(barrel); + // }); + outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); @@ -96,7 +123,7 @@ StatusCode ActsGeoGen3Svc::initialize() { // This will require dedicated stacking of the cylinders in r and z in the // order that doesn't produce overlaps. // - // auto negEndcap = builder.layerHelper() + // auto posEndcap = builder.layerHelper() // .endcap() // .setAxes("XZY") // .setContainer("InnerTrackerEndcap") @@ -104,10 +131,25 @@ StatusCode ActsGeoGen3Svc::initialize() { // .setEnvelope(envelope) // .build(); - // negEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + // posEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); innerTracker.addChild(barrel); - // innerTracker.addChild(negEndcap); + // innerTracker.addChild(posEndcap); + }); + + outer.addCylinderContainer("OuterTracker", AxisZ, [&](auto& outerTracker) { + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + + auto barrel = builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("layer\\d") + .setContainer("OuterTrackerBarrel") + .setEnvelope(envelope) + .build(); + + barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + outerTracker.addChild(barrel); }); BlueprintOptions options; diff --git a/k4ActsTracking/src/components/ActsTestPropagator.cpp b/k4ActsTracking/src/components/ActsTestPropagator.cpp index 2effea37..db83e748 100644 --- a/k4ActsTracking/src/components/ActsTestPropagator.cpp +++ b/k4ActsTracking/src/components/ActsTestPropagator.cpp @@ -125,6 +125,7 @@ std::vector> ActsTestPropagator::operator()() auto& stepsGeoID = stepOutputs[3].vec(); auto& stepsLength = stepOutputs[4].vec(); + debug() << fmt::format("Creating {} random tracks", m_numTracks.value()) << endmsg; for (int i = 0; i < m_numTracks; ++i) { // Generate random start position Acts::Vector4 startPos{m_posDist(m_gen), m_posDist(m_gen), m_posDist(m_gen), 0}; From eadef364235b0edfa443de68003594a5efc4690f Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 11 Dec 2025 20:03:35 +0100 Subject: [PATCH 16/69] Make geometry conversion work for full barrel --- k4ActsTracking/examples/visActsGEo.py | 4 +- .../src/components/ActsGeoGen3Svc.cpp | 104 +++++++++++------- .../src/components/ActsGeoGen3Svc.h | 6 +- 3 files changed, 72 insertions(+), 42 deletions(-) diff --git a/k4ActsTracking/examples/visActsGEo.py b/k4ActsTracking/examples/visActsGEo.py index 43767c28..52ce5dc2 100644 --- a/k4ActsTracking/examples/visActsGEo.py +++ b/k4ActsTracking/examples/visActsGEo.py @@ -17,8 +17,8 @@ geoSvc.detectors = [args.compactFile] actsGeoSvc = ActsGeoGen3Svc("ActsGeoSvc") -actsGeoSvc.DetElementName = "InnerTrackerBarrel" -actsGeoSvc.LayerPatternExpr = r"layer\\d" +actsGeoSvc.DumpVisualization = True +actsGeoSvc.ObjVisFileName = "full_barrel.obj" actsGeoSvc.OutputLevel = VERBOSE propTest = ActsTestPropagator("TestPropagator") diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp index d63fb19d..3731d0e4 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -27,12 +27,15 @@ #include #include +#include #include #include DECLARE_COMPONENT(ActsGeoGen3Svc) +template <> struct fmt::formatter : fmt::ostream_formatter {}; + ActsGeoGen3Svc::ActsGeoGen3Svc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) {} StatusCode ActsGeoGen3Svc::initialize() { @@ -77,32 +80,33 @@ StatusCode ActsGeoGen3Svc::initialize() { // cylinder contains the beampipe entirely. outer.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - // TODO: Acts detects some overlaps when stacking this up. Need to figure out - // what they are about and how to properly configure the building below. Maybe - // there is better matching pattern. - // - // outer.addCylinderContainer("Vertex", AxisZ, [&](auto& vertex) { - // auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - - // auto barrel = builder.layerHelper() - // .barrel() - // .setAxes("XYZ") - // .setPattern("layer_\\d") - // .setContainer("VertexBarrel") - // .setEnvelope(envelope) - // .customize([&](const dd4hep::DetElement&, auto& layer) { - // // Force the Barrel onto the z-axis by not using the - // // center of gravity for auto-sizing. We do this because - // // the VertexBarrel has an odd number of modules, which - // // shifts them off-axis when using CoG - // layer.setUseCenterOfGravity(false, false, true); - // }) - // .build(); - - // barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - // vertex.addChild(barrel); - // }); + outer.addCylinderContainer("Vertex", AxisZ, [&](auto& vertex) { + // NOTE: Need to set rather small padding here for the R-direction, because + // the innermost two layers are a double layer for which the cylindrical + // volumes are overlapping otherwise + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); + + auto barrel = + builder.layerHelper() + .barrel() + .setAxes("ZYX") + .setPattern("layer_\\d") + .setContainer("VertexBarrel") + .setEnvelope(envelope) + .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { + // Force the Barrel onto the z-axis by not using the + // center of gravity for auto-sizing. We do this because + // the VertexBarrel has an odd number of modules, which + // shifts them off-axis when using CoG + layer->setUseCenterOfGravity(false, false, true); + return layer; + }) + .build(); + + barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + vertex.addChild(barrel); + }); outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); @@ -117,15 +121,15 @@ StatusCode ActsGeoGen3Svc::initialize() { barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - // TODO: This currently doesn't work because the cylinder we get from the - // barrel overlaps in z with the cylinder we get from here, because the - // first (innermost) endcap layer "sticks" into the envelope of the barrel. - // This will require dedicated stacking of the cylinders in r and z in the - // order that doesn't produce overlaps. - // + // // TODO: This currently doesn't work because the cylinder we get from the + // // barrel overlaps in z with the cylinder we get from here, because the + // // first (innermost) endcap layer "sticks" into the envelope of the barrel. + // // This will require dedicated stacking of the cylinders in r and z in the + // // order that doesn't produce overlaps. + // // // auto posEndcap = builder.layerHelper() // .endcap() - // .setAxes("XZY") + // .setAxes("XzY") // .setContainer("InnerTrackerEndcap") // .setPattern("layer_pos\\d") // .setEnvelope(envelope) @@ -156,12 +160,36 @@ StatusCode ActsGeoGen3Svc::initialize() { Acts::GeometryContext gctxt{}; debug() << "Constructing tracking geometry" << endmsg; - m_trackingGeo = root.construct(options, gctxt, *gaudiLogger->cloneWithSuffix("|Construct")); + m_trackingGeo = root.construct(options, gctxt, *gaudiLogger->cloneWithSuffix("|Construct")); + std::size_t nSurfaces = 0; + m_trackingGeo->visitSurfaces([&](const Acts::Surface* surface) { + nSurfaces++; + const auto& actsDetElem = + dynamic_cast(*surface->associatedDetectorElement()); + const auto& detElem = actsDetElem.sourceElement(); + verbose() << fmt::format("Adding Acts surface {} pointing to dd4hep DetElement {}", surface->geometryId(), + detElem.volumeID()) + << endmsg; + const auto& [existing, inserted] = m_cellIDToSurface.emplace(detElem.volumeID(), surface); + if (!inserted) { + error() << fmt::format( + "The Acts surface {} pointing to dd4hep DetElement with cellID {} is already registered in the " + "map for Acts surface {}", + surface->geometryId(), detElem.volumeID(), existing->second->geometryId()) + << endmsg; + } + }); - debug() << "Creating visualiztion" << endmsg; - Acts::ObjVisualization3D vis{}; - m_trackingGeo->visualize(vis, gctxt); - vis.write("dumped_acts_geo.obj"); + info() << fmt::format("Visited {} Surfaces and inserted {} pairs of CellID -> Acts::Surface* into the map.", + nSurfaces, m_cellIDToSurface.size()) + << endmsg; + if (m_dumpVisualization.value()) { + info() << "Creating visualiztion" << endmsg; + // Adjust the scale here to make it easier to import in blender + Acts::ObjVisualization3D vis{4, 0.001}; + m_trackingGeo->visualize(vis, gctxt); + vis.write(m_objDumpFileName.value()); + } return StatusCode::SUCCESS; } diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.h b/k4ActsTracking/src/components/ActsGeoGen3Svc.h index e88c23d3..b4c00587 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.h +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.h @@ -32,8 +32,10 @@ class ActsGeoGen3Svc : public extends { StatusCode initialize() override; - Gaudi::Property m_detElementName{this, "DetElementName", "InnerTrackerBarrel", "Name of the DetElement"}; - Gaudi::Property m_layerPattern{this, "LayerPatternExpr", "layer", "Layer pattern match expression"}; + Gaudi::Property m_objDumpFileName{this, "ObjVisFileName", "dump_acts_geo.obj", + "Name of the 3D visualization file"}; + Gaudi::Property m_dumpVisualization{this, "DumpVisualization", false, + "Whether or not to create a 3D visualization dump"}; private: dd4hep::Detector* m_dd4hepGeo{nullptr}; From e86ef671fc6a3ed84b57a3f89f9dee4ff54ec6af Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 11 Dec 2025 21:43:29 +0100 Subject: [PATCH 17/69] Add first attempts of getting full InnerTracker converted --- .../src/components/ActsGeoGen3Svc.cpp | 164 +++++++++++------- 1 file changed, 104 insertions(+), 60 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp index 3731d0e4..f8b8bc86 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -80,70 +80,115 @@ StatusCode ActsGeoGen3Svc::initialize() { // cylinder contains the beampipe entirely. outer.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - outer.addCylinderContainer("Vertex", AxisZ, [&](auto& vertex) { - // NOTE: Need to set rather small padding here for the R-direction, because - // the innermost two layers are a double layer for which the cylindrical - // volumes are overlapping otherwise - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); - - auto barrel = - builder.layerHelper() - .barrel() - .setAxes("ZYX") - .setPattern("layer_\\d") - .setContainer("VertexBarrel") - .setEnvelope(envelope) - .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { - // Force the Barrel onto the z-axis by not using the - // center of gravity for auto-sizing. We do this because - // the VertexBarrel has an odd number of modules, which - // shifts them off-axis when using CoG - layer->setUseCenterOfGravity(false, false, true); - return layer; - }) - .build(); - - barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - vertex.addChild(barrel); - }); + // outer.addCylinderContainer("Vertex", AxisZ, [&](auto& vertex) { + // // NOTE: Need to set rather small padding here for the R-direction, because + // // the innermost two layers are a double layer for which the cylindrical + // // volumes are overlapping otherwise + // auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); + + // auto barrel = + // builder.layerHelper() + // .barrel() + // .setAxes("ZYX") + // .setPattern("layer_\\d") + // .setContainer("VertexBarrel") + // .setEnvelope(barrelEnvelope) + // .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { + // // Force the Barrel onto the z-axis by not using the + // // center of gravity for auto-sizing. We do this because + // // the VertexBarrel has an odd number of modules, which + // // shifts them off-axis when using CoG + // layer->setUseCenterOfGravity(false, false, true); + // return layer; + // }) + // .build(); + // barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + // vertex.addChild(barrel); + + // auto endcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + + // // TODO: Endcap. Almost certainly will have to touch the DD4hep constructor + // // for that because it looks like there are no layer DetElements again (similar to what happens in the InnerTrackerEndcap) + // }); + + // We have to create the inner tracker in several steps, because the inner + // most endcap layer protrudes into the envelope that is created by the + // outermost barrel layer. That creates an overlap in z while stacking. Hence, + // we build it in steps grouping the innermost two layers of the barrel and + // the innermost layer of the endcap into an "inner" inner tracker (stacking + // them along z), we then stack the last barrel layer along r, before stacking + // the remaining endcap layers along z + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + auto innerInnerBarrel = builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("layer[01]") + .setContainer("InnerTrackerBarrel") + .setEnvelope(envelope) + .build(); + innerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + auto outerInnerBarrel = builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("layer2") + .setContainer("InnerTrackerBarrel") + .setEnvelope(envelope) + .build(); + outerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto innerPosEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_pos0") + .setEnvelope(envelope) + .build(); + innerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + auto outerPosEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_pos[1-6]") + .setEnvelope(envelope) + .build(); + outerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto innerNegEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_neg0") + .setEnvelope(envelope) + .build(); + innerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + auto outerNegEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_neg[1-6]") + .setEnvelope(envelope) + .build(); + outerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto innerInnerTracker = + std::make_shared("InnerInnerTracker", AxisZ); + innerInnerTracker->addChild(innerPosEndcapInner); + innerInnerTracker->addChild(innerNegEndcapInner); + innerInnerTracker->addChild(innerInnerBarrel); + + auto innerTrackerBarrel = + std::make_shared("InnerTrackerBarrel", AxisR); + innerTrackerBarrel->addChild(innerInnerTracker); + innerTrackerBarrel->addChild(outerInnerBarrel); outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - - auto barrel = builder.layerHelper() - .barrel() - .setAxes("XYZ") - .setPattern(m_layerPattern.value()) - .setContainer(m_detElementName.value()) - .setEnvelope(envelope) - .build(); - - barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - // // TODO: This currently doesn't work because the cylinder we get from the - // // barrel overlaps in z with the cylinder we get from here, because the - // // first (innermost) endcap layer "sticks" into the envelope of the barrel. - // // This will require dedicated stacking of the cylinders in r and z in the - // // order that doesn't produce overlaps. - // // - // auto posEndcap = builder.layerHelper() - // .endcap() - // .setAxes("XzY") - // .setContainer("InnerTrackerEndcap") - // .setPattern("layer_pos\\d") - // .setEnvelope(envelope) - // .build(); - - // posEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - innerTracker.addChild(barrel); - // innerTracker.addChild(posEndcap); + innerTracker.addChild(innerTrackerBarrel); + innerTracker.addChild(outerNegEndcapInner); + innerTracker.addChild(outerPosEndcapInner); }); outer.addCylinderContainer("OuterTracker", AxisZ, [&](auto& outerTracker) { - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - auto barrel = builder.layerHelper() .barrel() .setAxes("XYZ") @@ -151,7 +196,6 @@ StatusCode ActsGeoGen3Svc::initialize() { .setContainer("OuterTrackerBarrel") .setEnvelope(envelope) .build(); - barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); outerTracker.addChild(barrel); }); From bb35f2774eaf57f3de1aa6c12fe37190f0e851c6 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 12 Dec 2025 10:47:17 +0100 Subject: [PATCH 18/69] Add the OuterTracker endcaps --- .../src/components/ActsGeoGen3Svc.cpp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp index f8b8bc86..0d59d34b 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -188,6 +188,8 @@ StatusCode ActsGeoGen3Svc::initialize() { innerTracker.addChild(outerPosEndcapInner); }); + // The OuterTracker is a bit more simple because it has the barrel and endcap + // more clearly separated outer.addCylinderContainer("OuterTracker", AxisZ, [&](auto& outerTracker) { auto barrel = builder.layerHelper() .barrel() @@ -197,7 +199,28 @@ StatusCode ActsGeoGen3Svc::initialize() { .setEnvelope(envelope) .build(); barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto negEndcap = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("OuterTrackerEndcap") + .setPattern("layer_neg\\d") + .setEnvelope(envelope) + .build(); + negEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto posEndcap = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("OuterTrackerEndcap") + .setPattern("layer_pos\\d") + .setEnvelope(envelope) + .build(); + posEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + outerTracker.addChild(barrel); + outerTracker.addChild(negEndcap); + outerTracker.addChild(posEndcap); }); BlueprintOptions options; From 8c117add9770e55873aa7631a63e138b8015e356 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 12 Dec 2025 16:52:52 +0100 Subject: [PATCH 19/69] Add Vertex detector to geometry conversion --- .../src/components/ActsGeoGen3Svc.cpp | 93 ++++++++++++------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp index 0d59d34b..fcf3b770 100644 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp @@ -80,45 +80,70 @@ StatusCode ActsGeoGen3Svc::initialize() { // cylinder contains the beampipe entirely. outer.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - // outer.addCylinderContainer("Vertex", AxisZ, [&](auto& vertex) { - // // NOTE: Need to set rather small padding here for the R-direction, because - // // the innermost two layers are a double layer for which the cylindrical - // // volumes are overlapping otherwise - // auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); - - // auto barrel = - // builder.layerHelper() - // .barrel() - // .setAxes("ZYX") - // .setPattern("layer_\\d") - // .setContainer("VertexBarrel") - // .setEnvelope(barrelEnvelope) - // .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { - // // Force the Barrel onto the z-axis by not using the - // // center of gravity for auto-sizing. We do this because - // // the VertexBarrel has an odd number of modules, which - // // shifts them off-axis when using CoG - // layer->setUseCenterOfGravity(false, false, true); - // return layer; - // }) - // .build(); - // barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - // vertex.addChild(barrel); - - // auto endcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - - // // TODO: Endcap. Almost certainly will have to touch the DD4hep constructor - // // for that because it looks like there are no layer DetElements again (similar to what happens in the InnerTrackerEndcap) - // }); - // We have to create the inner tracker in several steps, because the inner // most endcap layer protrudes into the envelope that is created by the // outermost barrel layer. That creates an overlap in z while stacking. Hence, // we build it in steps grouping the innermost two layers of the barrel and // the innermost layer of the endcap into an "inner" inner tracker (stacking // them along z), we then stack the last barrel layer along r, before stacking - // the remaining endcap layers along z + // the remaining endcap layers along z. Additionally, we have to first put the + // whole vertex detector inside the two innermost InnerTrackerBarrel layers + // because the outermost vertex layer extends further in r, than the innermost + // border of the InnerTracker endcaps. Hence, we also need to stack them in + // the correct order. + + // NOTE: Need to set rather small padding here for the R-direction, because + // the innermost two layers are a double layer for which the cylindrical + // volumes are overlapping otherwise + auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); + auto vertexBarrel = + builder.layerHelper() + .barrel() + .setAxes("ZYX") + .setPattern("layer_\\d") + .setContainer("VertexBarrel") + .setEnvelope(barrelEnvelope) + .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { + // Force the Barrel onto the z-axis by not using the + // center of gravity for auto-sizing. We do this because + // the VertexBarrel has an odd number of modules, which + // shifts them off-axis when using CoG + layer->setUseCenterOfGravity(false, false, true); + return layer; + }) + .build(); + vertexBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + // The VertexEncap again suffers from not having a *layer* DetElement + auto endcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); + auto vtxEndcapDetElem = builder.findDetElementByName("VertexEndcap"); + auto negVtxEndcapContainer = std::make_shared( + "VertexEndcapNeg", Acts::AxisDirection::AxisZ); + negVtxEndcapContainer->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + auto posVtxEndcapContainer = std::make_shared( + "VertexEndcapPos", Acts::AxisDirection::AxisZ); + posVtxEndcapContainer->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + for (int i = 0; i < 8; ++i) { + auto layerName = "layer" + std::to_string(i); + auto layerPattern = std::regex{layerName + "_module0_sensor\\d+_neg"}; + auto sensorElements = builder.findDetElementByNamePattern(vtxEndcapDetElem.value(), layerPattern); + + auto layer = builder.makeLayer(vtxEndcapDetElem.value(), sensorElements, "XZY", layerName + "_neg"); + layer->setEnvelope(endcapEnvelope); + negVtxEndcapContainer->addChild(layer); + + layerPattern = layerName + "_module0_sensor\\d+_pos"; + sensorElements = builder.findDetElementByNamePattern(vtxEndcapDetElem.value(), layerPattern); + layer = builder.makeLayer(vtxEndcapDetElem.value(), sensorElements, "XZY", layerName + "_pos"); + posVtxEndcapContainer->addChild(layer); + } + + auto vertex = std::make_shared("Vertex", AxisZ); + vertex->addChild(vertexBarrel); + vertex->addChild(negVtxEndcapContainer); + vertex->addChild(posVtxEndcapContainer); + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); auto innerInnerBarrel = builder.layerHelper() .barrel() @@ -128,6 +153,8 @@ StatusCode ActsGeoGen3Svc::initialize() { .setEnvelope(envelope) .build(); innerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + innerInnerBarrel->addChild(vertex); + auto outerInnerBarrel = builder.layerHelper() .barrel() .setAxes("XYZ") From 808b2895fdb5f4cfb6a33698f6342efb5b66de97 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 19 Dec 2025 16:02:08 +0100 Subject: [PATCH 20/69] Replace old Geo svc with new implementation --- k4ActsTracking/examples/visActsGEo.py | 4 +- .../include/k4ActsTracking/IActsGeoSvc.h | 8 +- .../src/components/ActsGeoGen3Svc.cpp | 289 ---------------- .../src/components/ActsGeoGen3Svc.h | 53 --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 315 ++++++++++++++---- k4ActsTracking/src/components/ActsGeoSvc.h | 113 ++----- 6 files changed, 297 insertions(+), 485 deletions(-) delete mode 100644 k4ActsTracking/src/components/ActsGeoGen3Svc.cpp delete mode 100644 k4ActsTracking/src/components/ActsGeoGen3Svc.h diff --git a/k4ActsTracking/examples/visActsGEo.py b/k4ActsTracking/examples/visActsGEo.py index 52ce5dc2..167b3e81 100644 --- a/k4ActsTracking/examples/visActsGEo.py +++ b/k4ActsTracking/examples/visActsGEo.py @@ -2,7 +2,7 @@ from Gaudi.Configuration import VERBOSE, DEBUG -from Configurables import ActsGeoGen3Svc, GeoSvc, ActsTestPropagator, EventDataSvc +from Configurables import ActsGeoSvc, GeoSvc, ActsTestPropagator, EventDataSvc from k4FWCore import ApplicationMgr, IOSvc from k4FWCore.parseArgs import parser @@ -16,7 +16,7 @@ geoSvc = GeoSvc() geoSvc.detectors = [args.compactFile] -actsGeoSvc = ActsGeoGen3Svc("ActsGeoSvc") +actsGeoSvc = ActsGeoSvc("ActsGeoSvc") actsGeoSvc.DumpVisualization = True actsGeoSvc.ObjVisFileName = "full_barrel.obj" actsGeoSvc.OutputLevel = VERBOSE diff --git a/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h b/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h index 302b4afd..250ed777 100644 --- a/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h +++ b/k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h @@ -22,6 +22,7 @@ #include +#include #include #include @@ -39,13 +40,14 @@ namespace Acts { class GAUDI_API IActsGeoSvc : virtual public IService { public: - using VolumeSurfaceMap = std::unordered_map; + using CellIDSurfaceMap = std::unordered_map; public: DeclareInterfaceID(IActsGeoSvc, 1, 0); - virtual std::shared_ptr trackingGeometry() const = 0; - virtual std::shared_ptr magneticField() const = 0; + virtual std::shared_ptr trackingGeometry() const = 0; + virtual std::shared_ptr magneticField() const = 0; + virtual const CellIDSurfaceMap& cellIdToSurfaceMap() const = 0; virtual ~IActsGeoSvc() = default; }; diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp b/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp deleted file mode 100644 index fcf3b770..00000000 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.cpp +++ /dev/null @@ -1,289 +0,0 @@ -#include "ActsGeoGen3Svc.h" - -#include "k4ActsTracking/ActsGaudiLogger.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include - -#include - -DECLARE_COMPONENT(ActsGeoGen3Svc) - -template <> struct fmt::formatter : fmt::ostream_formatter {}; - -ActsGeoGen3Svc::ActsGeoGen3Svc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) {} - -StatusCode ActsGeoGen3Svc::initialize() { - m_geoSvc = Gaudi::svcLocator()->service("GeoSvc"); - K4_GAUDI_CHECK(m_geoSvc); - - std::array magneticFieldVector = {0, 0, 0}; - std::array position = {0, 0, 0}; - m_geoSvc->getDetector()->field().magneticField(position.data(), magneticFieldVector.data()); - debug() << fmt::format("Retrieved magnetic field at position {}: {}", position, magneticFieldVector) << endmsg; - m_magneticField = std::make_shared( - Acts::Vector3(magneticFieldVector[0] / dd4hep::tesla * Acts::UnitConstants::T, - magneticFieldVector[1] / dd4hep::tesla * Acts::UnitConstants::T, - magneticFieldVector[2] / dd4hep::tesla * Acts::UnitConstants::T)); - - auto gaudiLogger = makeActsGaudiLogger(this); - - info() << fmt::format("Acts::cm: {}, dd4hep::cm: {}", Acts::UnitConstants::cm, dd4hep::cm) << endmsg; - - ActsPlugins::DD4hep::BlueprintBuilder builder{{ - .dd4hepDetector = m_geoSvc->getDetector(), - .lengthScale = Acts::UnitConstants::cm / dd4hep::cm, - }, - gaudiLogger->cloneWithSuffix("|BlpBld")}; - - using Acts::Experimental::Blueprint; - using Acts::Experimental::BlueprintOptions; - using namespace Acts::UnitLiterals; - using enum Acts::AxisDirection; - - Blueprint::Config cfg; - // Padding around subvolumes of the world volume - cfg.envelope[AxisZ] = {20_mm, 20_mm}; - cfg.envelope[AxisR] = {0_mm, 20_mm}; - Blueprint root{cfg}; - - auto& outer = root.addCylinderContainer("MAIA_v0", AxisR); - outer.addStaticVolume(Acts::Transform3::Identity(), - std::make_unique(0_mm, 10_mm, 1000_mm), "Beampipe"); - // We want to pull the next volume in towards the beampipe to map material to - // the correct places in the end. We need to ensure that the enclosing - // cylinder contains the beampipe entirely. - outer.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - // We have to create the inner tracker in several steps, because the inner - // most endcap layer protrudes into the envelope that is created by the - // outermost barrel layer. That creates an overlap in z while stacking. Hence, - // we build it in steps grouping the innermost two layers of the barrel and - // the innermost layer of the endcap into an "inner" inner tracker (stacking - // them along z), we then stack the last barrel layer along r, before stacking - // the remaining endcap layers along z. Additionally, we have to first put the - // whole vertex detector inside the two innermost InnerTrackerBarrel layers - // because the outermost vertex layer extends further in r, than the innermost - // border of the InnerTracker endcaps. Hence, we also need to stack them in - // the correct order. - - // NOTE: Need to set rather small padding here for the R-direction, because - // the innermost two layers are a double layer for which the cylindrical - // volumes are overlapping otherwise - auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); - auto vertexBarrel = - builder.layerHelper() - .barrel() - .setAxes("ZYX") - .setPattern("layer_\\d") - .setContainer("VertexBarrel") - .setEnvelope(barrelEnvelope) - .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { - // Force the Barrel onto the z-axis by not using the - // center of gravity for auto-sizing. We do this because - // the VertexBarrel has an odd number of modules, which - // shifts them off-axis when using CoG - layer->setUseCenterOfGravity(false, false, true); - return layer; - }) - .build(); - vertexBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - // The VertexEncap again suffers from not having a *layer* DetElement - auto endcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); - auto vtxEndcapDetElem = builder.findDetElementByName("VertexEndcap"); - auto negVtxEndcapContainer = std::make_shared( - "VertexEndcapNeg", Acts::AxisDirection::AxisZ); - negVtxEndcapContainer->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto posVtxEndcapContainer = std::make_shared( - "VertexEndcapPos", Acts::AxisDirection::AxisZ); - posVtxEndcapContainer->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - for (int i = 0; i < 8; ++i) { - auto layerName = "layer" + std::to_string(i); - auto layerPattern = std::regex{layerName + "_module0_sensor\\d+_neg"}; - auto sensorElements = builder.findDetElementByNamePattern(vtxEndcapDetElem.value(), layerPattern); - - auto layer = builder.makeLayer(vtxEndcapDetElem.value(), sensorElements, "XZY", layerName + "_neg"); - layer->setEnvelope(endcapEnvelope); - negVtxEndcapContainer->addChild(layer); - - layerPattern = layerName + "_module0_sensor\\d+_pos"; - sensorElements = builder.findDetElementByNamePattern(vtxEndcapDetElem.value(), layerPattern); - layer = builder.makeLayer(vtxEndcapDetElem.value(), sensorElements, "XZY", layerName + "_pos"); - posVtxEndcapContainer->addChild(layer); - } - - auto vertex = std::make_shared("Vertex", AxisZ); - vertex->addChild(vertexBarrel); - vertex->addChild(negVtxEndcapContainer); - vertex->addChild(posVtxEndcapContainer); - - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - auto innerInnerBarrel = builder.layerHelper() - .barrel() - .setAxes("XYZ") - .setPattern("layer[01]") - .setContainer("InnerTrackerBarrel") - .setEnvelope(envelope) - .build(); - innerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - innerInnerBarrel->addChild(vertex); - - auto outerInnerBarrel = builder.layerHelper() - .barrel() - .setAxes("XYZ") - .setPattern("layer2") - .setContainer("InnerTrackerBarrel") - .setEnvelope(envelope) - .build(); - outerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - auto innerPosEndcapInner = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setPattern("layer_pos0") - .setEnvelope(envelope) - .build(); - innerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto outerPosEndcapInner = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setPattern("layer_pos[1-6]") - .setEnvelope(envelope) - .build(); - outerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - auto innerNegEndcapInner = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setPattern("layer_neg0") - .setEnvelope(envelope) - .build(); - innerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto outerNegEndcapInner = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setPattern("layer_neg[1-6]") - .setEnvelope(envelope) - .build(); - outerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - auto innerInnerTracker = - std::make_shared("InnerInnerTracker", AxisZ); - innerInnerTracker->addChild(innerPosEndcapInner); - innerInnerTracker->addChild(innerNegEndcapInner); - innerInnerTracker->addChild(innerInnerBarrel); - - auto innerTrackerBarrel = - std::make_shared("InnerTrackerBarrel", AxisR); - innerTrackerBarrel->addChild(innerInnerTracker); - innerTrackerBarrel->addChild(outerInnerBarrel); - - outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { - innerTracker.addChild(innerTrackerBarrel); - innerTracker.addChild(outerNegEndcapInner); - innerTracker.addChild(outerPosEndcapInner); - }); - - // The OuterTracker is a bit more simple because it has the barrel and endcap - // more clearly separated - outer.addCylinderContainer("OuterTracker", AxisZ, [&](auto& outerTracker) { - auto barrel = builder.layerHelper() - .barrel() - .setAxes("XYZ") - .setPattern("layer\\d") - .setContainer("OuterTrackerBarrel") - .setEnvelope(envelope) - .build(); - barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - auto negEndcap = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("OuterTrackerEndcap") - .setPattern("layer_neg\\d") - .setEnvelope(envelope) - .build(); - negEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - auto posEndcap = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("OuterTrackerEndcap") - .setPattern("layer_pos\\d") - .setEnvelope(envelope) - .build(); - posEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - outerTracker.addChild(barrel); - outerTracker.addChild(negEndcap); - outerTracker.addChild(posEndcap); - }); - - BlueprintOptions options; - Acts::GeometryContext gctxt{}; - - debug() << "Constructing tracking geometry" << endmsg; - m_trackingGeo = root.construct(options, gctxt, *gaudiLogger->cloneWithSuffix("|Construct")); - std::size_t nSurfaces = 0; - m_trackingGeo->visitSurfaces([&](const Acts::Surface* surface) { - nSurfaces++; - const auto& actsDetElem = - dynamic_cast(*surface->associatedDetectorElement()); - const auto& detElem = actsDetElem.sourceElement(); - verbose() << fmt::format("Adding Acts surface {} pointing to dd4hep DetElement {}", surface->geometryId(), - detElem.volumeID()) - << endmsg; - const auto& [existing, inserted] = m_cellIDToSurface.emplace(detElem.volumeID(), surface); - if (!inserted) { - error() << fmt::format( - "The Acts surface {} pointing to dd4hep DetElement with cellID {} is already registered in the " - "map for Acts surface {}", - surface->geometryId(), detElem.volumeID(), existing->second->geometryId()) - << endmsg; - } - }); - - info() << fmt::format("Visited {} Surfaces and inserted {} pairs of CellID -> Acts::Surface* into the map.", - nSurfaces, m_cellIDToSurface.size()) - << endmsg; - if (m_dumpVisualization.value()) { - info() << "Creating visualiztion" << endmsg; - // Adjust the scale here to make it easier to import in blender - Acts::ObjVisualization3D vis{4, 0.001}; - m_trackingGeo->visualize(vis, gctxt); - vis.write(m_objDumpFileName.value()); - } - - return StatusCode::SUCCESS; -} diff --git a/k4ActsTracking/src/components/ActsGeoGen3Svc.h b/k4ActsTracking/src/components/ActsGeoGen3Svc.h deleted file mode 100644 index b4c00587..00000000 --- a/k4ActsTracking/src/components/ActsGeoGen3Svc.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef K4ACTSTRACKING_ACTSGEOGEN3SVC_H -#define K4ACTSTRACKING_ACTSGEOGEN3SVC_H - -#include "k4ActsTracking/IActsGeoSvc.h" - -#include -#include - -#include "GaudiKernel/Service.h" - -#include -#include - -namespace Acts { - class TrackingGeometry; - class MagneticFieldProvider; -} // namespace Acts - -namespace dd4hep { - class Detector; -} - -class ActsGeoGen3Svc : public extends { -public: - std::shared_ptr trackingGeometry() const override; - - std::shared_ptr magneticField() const override; - - ActsGeoGen3Svc(const std::string& name, ISvcLocator* svcLoc); - - ~ActsGeoGen3Svc() = default; - - StatusCode initialize() override; - - Gaudi::Property m_objDumpFileName{this, "ObjVisFileName", "dump_acts_geo.obj", - "Name of the 3D visualization file"}; - Gaudi::Property m_dumpVisualization{this, "DumpVisualization", false, - "Whether or not to create a 3D visualization dump"}; - -private: - dd4hep::Detector* m_dd4hepGeo{nullptr}; - SmartIF m_geoSvc; - std::shared_ptr m_trackingGeo{nullptr}; - std::shared_ptr m_magneticField{nullptr}; -}; - -inline std::shared_ptr ActsGeoGen3Svc::trackingGeometry() const { return m_trackingGeo; } - -inline std::shared_ptr ActsGeoGen3Svc::magneticField() const { - return m_magneticField; -} - -#endif // K4ACTSTRACKING_ACTSGEOGEN3SVC_H diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index 241f025e..22d00ad8 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -16,105 +16,298 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "ActsGeoSvc.h" #include #include "k4ActsTracking/ActsGaudiLogger.h" -#include "k4Interface/IGeoSvc.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "Acts/Geometry/TrackingGeometry.hpp" -#include "Acts/MagneticField/ConstantBField.hpp" -#include "Acts/Visualization/GeometryView3D.hpp" -#include "Acts/Visualization/ObjVisualization3D.hpp" -#if __has_include("ActsPlugins/DD4hep/ConvertDD4hepDetector.hpp") -#include "ActsPlugins/DD4hep/ConvertDD4hepDetector.hpp" -#else -#include "Acts/Plugins/DD4hep/ConvertDD4hepDetector.hpp" -namespace ActsPlugins { - using Acts::convertDD4hepDetector; - using Acts::sortDetElementsByID; +#include +#include +#include -} // namespace ActsPlugins -#endif +#include #include +#include #include #include -using namespace Gaudi; +template <> struct fmt::formatter : fmt::ostream_formatter {}; DECLARE_COMPONENT(ActsGeoSvc) -ActsGeoSvc::ActsGeoSvc(const std::string& name, ISvcLocator* svc) - : base_class(name, svc), m_trackingGeoCtx(Acts::GeometryContext::dangerouslyDefaultConstruct()) {} +ActsGeoSvc::ActsGeoSvc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) {} StatusCode ActsGeoSvc::initialize() { - m_dd4hepGeo = svcLocator()->service(m_geoSvcName)->getDetector(); - // necessary? - // m_dd4hepGeo->addExtension(this); - - Acts::BinningType bTypePhi = Acts::equidistant; - Acts::BinningType bTypeR = Acts::equidistant; - Acts::BinningType bTypeZ = Acts::equidistant; - double layerEnvelopeR = Acts::UnitConstants::mm; - double layerEnvelopeZ = Acts::UnitConstants::mm; - double defaultLayerThickness = Acts::UnitConstants::fm; - - auto logger = makeActsGaudiLogger(this); - m_trackingGeo = ActsPlugins::convertDD4hepDetector( - m_dd4hepGeo->world(), *logger, bTypePhi, bTypeR, bTypeZ, layerEnvelopeR, layerEnvelopeZ, defaultLayerThickness, - ActsPlugins::sortDetElementsByID, m_trackingGeoCtx, m_materialDeco); + m_geoSvc = Gaudi::svcLocator()->service("GeoSvc"); + K4_GAUDI_CHECK(m_geoSvc); std::array magneticFieldVector = {0, 0, 0}; std::array position = {0, 0, 0}; - m_dd4hepGeo->field().magneticField(position.data(), magneticFieldVector.data()); + m_geoSvc->getDetector()->field().magneticField(position.data(), magneticFieldVector.data()); debug() << fmt::format("Retrieved magnetic field at position {}: {}", position, magneticFieldVector) << endmsg; m_magneticField = std::make_shared( Acts::Vector3(magneticFieldVector[0] / dd4hep::tesla * Acts::UnitConstants::T, magneticFieldVector[1] / dd4hep::tesla * Acts::UnitConstants::T, magneticFieldVector[2] / dd4hep::tesla * Acts::UnitConstants::T)); - /// Setting geometry debug option - if (m_debugGeometry == true) { - info() << "Geometry debugging is ON." << endmsg; + auto gaudiLogger = makeActsGaudiLogger(this); - if (createGeoObj().isFailure()) { - error() << "Could not create geometry OBJ" << endmsg; - return StatusCode::FAILURE; - } else { - info() << "Geometry OBJ SUCCESSFULLY created" << endmsg; - } - } else { - info() << "Geometry converted without checking if GeoObj can be created" << endmsg; - } + info() << fmt::format("Acts::cm: {}, dd4hep::cm: {}", Acts::UnitConstants::cm, dd4hep::cm) << endmsg; - return StatusCode::SUCCESS; -} + ActsPlugins::DD4hep::BlueprintBuilder builder{{ + .dd4hepDetector = m_geoSvc->getDetector(), + .lengthScale = Acts::UnitConstants::cm / dd4hep::cm, + }, + gaudiLogger->cloneWithSuffix("|BlpBld")}; + + using Acts::Experimental::Blueprint; + using Acts::Experimental::BlueprintOptions; + using namespace Acts::UnitLiterals; + using enum Acts::AxisDirection; -StatusCode ActsGeoSvc::execute() { return StatusCode::SUCCESS; } + Blueprint::Config cfg; + // Padding around subvolumes of the world volume + cfg.envelope[AxisZ] = {20_mm, 20_mm}; + cfg.envelope[AxisR] = {0_mm, 20_mm}; + Blueprint root{cfg}; -StatusCode ActsGeoSvc::finalize() { return StatusCode::SUCCESS; } + auto& outer = root.addCylinderContainer("MAIA_v0", AxisR); + outer.addStaticVolume(Acts::Transform3::Identity(), + std::make_unique(0_mm, 10_mm, 1000_mm), "Beampipe"); + // We want to pull the next volume in towards the beampipe to map material to + // the correct places in the end. We need to ensure that the enclosing + // cylinder contains the beampipe entirely. + outer.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); -/// Create a geometry OBJ file -StatusCode ActsGeoSvc::createGeoObj() { - // Convert DD4Hep geometry to acts + // We have to create the inner tracker in several steps, because the inner + // most endcap layer protrudes into the envelope that is created by the + // outermost barrel layer. That creates an overlap in z while stacking. Hence, + // we build it in steps grouping the innermost two layers of the barrel and + // the innermost layer of the endcap into an "inner" inner tracker (stacking + // them along z), we then stack the last barrel layer along r, before stacking + // the remaining endcap layers along z. Additionally, we have to first put the + // whole vertex detector inside the two innermost InnerTrackerBarrel layers + // because the outermost vertex layer extends further in r, than the innermost + // border of the InnerTracker endcaps. Hence, we also need to stack them in + // the correct order. - Acts::ObjVisualization3D m_obj; + // NOTE: Need to set rather small padding here for the R-direction, because + // the innermost two layers are a double layer for which the cylindrical + // volumes are overlapping otherwise + auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); + auto vertexBarrel = + builder.layerHelper() + .barrel() + .setAxes("ZYX") + .setPattern("layer_\\d") + .setContainer("VertexBarrel") + .setEnvelope(barrelEnvelope) + .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { + // Force the Barrel onto the z-axis by not using the + // center of gravity for auto-sizing. We do this because + // the VertexBarrel has an odd number of modules, which + // shifts them off-axis when using CoG + layer->setUseCenterOfGravity(false, false, true); + return layer; + }) + .build(); + vertexBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - if (!m_trackingGeo) { - return StatusCode::FAILURE; + // The VertexEncap again suffers from not having a *layer* DetElement + auto endcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); + auto vtxEndcapDetElem = builder.findDetElementByName("VertexEndcap"); + auto negVtxEndcapContainer = std::make_shared( + "VertexEndcapNeg", Acts::AxisDirection::AxisZ); + negVtxEndcapContainer->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + auto posVtxEndcapContainer = std::make_shared( + "VertexEndcapPos", Acts::AxisDirection::AxisZ); + posVtxEndcapContainer->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + for (int i = 0; i < 8; ++i) { + auto layerName = "layer" + std::to_string(i); + auto layerPattern = std::regex{layerName + "_module0_sensor\\d+_neg"}; + auto sensorElements = builder.findDetElementByNamePattern(vtxEndcapDetElem.value(), layerPattern); + + auto layer = builder.makeLayer(vtxEndcapDetElem.value(), sensorElements, "XZY", layerName + "_neg"); + layer->setEnvelope(endcapEnvelope); + negVtxEndcapContainer->addChild(layer); + + layerPattern = layerName + "_module0_sensor\\d+_pos"; + sensorElements = builder.findDetElementByNamePattern(vtxEndcapDetElem.value(), layerPattern); + layer = builder.makeLayer(vtxEndcapDetElem.value(), sensorElements, "XZY", layerName + "_pos"); + posVtxEndcapContainer->addChild(layer); } + + auto vertex = std::make_shared("Vertex", AxisZ); + vertex->addChild(vertexBarrel); + vertex->addChild(negVtxEndcapContainer); + vertex->addChild(posVtxEndcapContainer); + + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + auto innerInnerBarrel = builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("layer[01]") + .setContainer("InnerTrackerBarrel") + .setEnvelope(envelope) + .build(); + innerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + innerInnerBarrel->addChild(vertex); + + auto outerInnerBarrel = builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("layer2") + .setContainer("InnerTrackerBarrel") + .setEnvelope(envelope) + .build(); + outerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto innerPosEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_pos0") + .setEnvelope(envelope) + .build(); + innerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + auto outerPosEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_pos[1-6]") + .setEnvelope(envelope) + .build(); + outerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto innerNegEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_neg0") + .setEnvelope(envelope) + .build(); + innerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + auto outerNegEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_neg[1-6]") + .setEnvelope(envelope) + .build(); + outerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto innerInnerTracker = + std::make_shared("InnerInnerTracker", AxisZ); + innerInnerTracker->addChild(innerPosEndcapInner); + innerInnerTracker->addChild(innerNegEndcapInner); + innerInnerTracker->addChild(innerInnerBarrel); + + auto innerTrackerBarrel = + std::make_shared("InnerTrackerBarrel", AxisR); + innerTrackerBarrel->addChild(innerInnerTracker); + innerTrackerBarrel->addChild(outerInnerBarrel); + + outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { + innerTracker.addChild(innerTrackerBarrel); + innerTracker.addChild(outerNegEndcapInner); + innerTracker.addChild(outerPosEndcapInner); + }); + + // The OuterTracker is a bit more simple because it has the barrel and endcap + // more clearly separated + outer.addCylinderContainer("OuterTracker", AxisZ, [&](auto& outerTracker) { + auto barrel = builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("layer\\d") + .setContainer("OuterTrackerBarrel") + .setEnvelope(envelope) + .build(); + barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto negEndcap = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("OuterTrackerEndcap") + .setPattern("layer_neg\\d") + .setEnvelope(envelope) + .build(); + negEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto posEndcap = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("OuterTrackerEndcap") + .setPattern("layer_pos\\d") + .setEnvelope(envelope) + .build(); + posEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + outerTracker.addChild(barrel); + outerTracker.addChild(negEndcap); + outerTracker.addChild(posEndcap); + }); + + BlueprintOptions options; + Acts::GeometryContext gctxt{}; + + debug() << "Constructing tracking geometry" << endmsg; + m_trackingGeo = root.construct(options, gctxt, *gaudiLogger->cloneWithSuffix("|Construct")); + + std::size_t nSurfaces = 0; m_trackingGeo->visitSurfaces([&](const Acts::Surface* surface) { - if (surface == nullptr) { - info() << "no surface??? " << endmsg; - return; + nSurfaces++; + const auto& actsDetElem = + dynamic_cast(*surface->associatedDetectorElement()); + const auto& detElem = actsDetElem.sourceElement(); + verbose() << fmt::format("Adding Acts surface {} pointing to dd4hep DetElement {}", surface->geometryId(), + detElem.volumeID()) + << endmsg; + const auto& [existing, inserted] = m_cellIDToSurface.emplace(detElem.volumeID(), surface); + if (!inserted) { + error() << fmt::format( + "The Acts surface {} pointing to dd4hep DetElement with cellID {} is already registered in the " + "map for Acts surface {}", + surface->geometryId(), detElem.volumeID(), existing->second->geometryId()) + << endmsg; } - Acts::GeometryView3D::drawSurface(m_obj, *surface, m_trackingGeoCtx); }); - m_obj.write(m_outputFileName.value()); - info() << m_outputFileName << " SUCCESSFULLY written." << endmsg; + + info() << fmt::format("Visited {} Surfaces and inserted {} pairs of CellID -> Acts::Surface* into the map.", + nSurfaces, m_cellIDToSurface.size()) + << endmsg; + if (m_dumpVisualization.value()) { + info() << "Creating visualiztion" << endmsg; + // Adjust the scale here to make it easier to import in blender + Acts::ObjVisualization3D vis{4, 0.001}; + m_trackingGeo->visualize(vis, gctxt); + vis.write(m_objDumpFileName.value()); + } return StatusCode::SUCCESS; } diff --git a/k4ActsTracking/src/components/ActsGeoSvc.h b/k4ActsTracking/src/components/ActsGeoSvc.h index 2b733f68..c35ced4d 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.h +++ b/k4ActsTracking/src/components/ActsGeoSvc.h @@ -1,99 +1,58 @@ -/* - * Copyright (c) 2014-2024 Key4hep-Project. - * - * This file is part of Key4hep. - * See https://key4hep.github.io/key4hep-doc/ for further info. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ACTSGEOSVC_H -#define ACTSGEOSVC_H +#ifndef K4ACTSTRACKING_ACTSGEOSVC_H +#define K4ACTSTRACKING_ACTSGEOSVC_H #include "k4ActsTracking/IActsGeoSvc.h" -#include -#include "Acts/Definitions/Common.hpp" -#include "Acts/Definitions/Units.hpp" -#include "Acts/Geometry/GeometryContext.hpp" -#include "Acts/Geometry/TrackingGeometry.hpp" -#include "Acts/MagneticField/MagneticFieldProvider.hpp" -#include "Acts/Surfaces/Surface.hpp" -#include "Acts/Utilities/Logger.hpp" - -#include "DD4hep/DD4hepUnits.h" -#include "DD4hep/Detector.h" -#include "DDRec/Surface.h" -#include "DDRec/SurfaceManager.h" - -#include "GaudiKernel/MsgStream.h" -#include "GaudiKernel/Service.h" -#include "GaudiKernel/ServiceHandle.h" - -class ActsGeoSvc : public extends { -public: - using VolumeSurfaceMap = std::unordered_map; - -private: - dd4hep::Detector* m_dd4hepGeo = nullptr; - - /// DD4hep surface map - std::map m_surfaceMap; +#include +#include - /// ACTS Logging Level - Acts::Logging::Level m_actsLoggingLevel = Acts::Logging::INFO; +#include - /// ACTS Tracking Geometry Context - Acts::GeometryContext m_trackingGeoCtx; - - /// ACTS Tracking Geometry - std::shared_ptr m_trackingGeo{nullptr}; - - /// ACTS Magnetic field - std::shared_ptr m_magneticField{nullptr}; - - /// ACTS Material Decorator - std::shared_ptr m_materialDeco{nullptr}; +#include "GaudiKernel/Service.h" - /// ACTS surface lookup container for hit surfaces that generate smeared hits - VolumeSurfaceMap m_surfaces; +#include +#include +#include - Gaudi::Property m_geoSvcName{this, "GeoSvcName", "GeoSvc", "The name of the GeoSvc instance"}; +namespace Acts { + class TrackingGeometry; + class MagneticFieldProvider; + class Surface; +} // namespace Acts - /// Option for the Debug Geometry - Gaudi::Property m_debugGeometry{this, "debugGeometry", false, "Option for geometry debugging"}; - /// Output file name - Gaudi::Property m_outputFileName{this, "outputFileName", "", "Output file name"}; +namespace dd4hep { + class Detector; +} +class ActsGeoSvc : public extends { public: - ActsGeoSvc(const std::string& name, ISvcLocator* svc); + std::shared_ptr trackingGeometry() const override; - virtual ~ActsGeoSvc() = default; + std::shared_ptr magneticField() const override; - virtual StatusCode initialize() final; + ActsGeoSvc(const std::string& name, ISvcLocator* svcLoc); - virtual StatusCode execute() final; + ~ActsGeoSvc() = default; - virtual StatusCode finalize() final; + StatusCode initialize() override; - StatusCode createGeoObj(); + Gaudi::Property m_objDumpFileName{this, "ObjVisFileName", "dump_acts_geo.obj", + "Name of the 3D visualization file"}; + Gaudi::Property m_dumpVisualization{this, "DumpVisualization", false, + "Whether or not to create a 3D visualization dump"}; - virtual std::shared_ptr trackingGeometry() const; + const CellIDSurfaceMap& cellIdToSurfaceMap() const override { return m_cellIDToSurface; } - virtual std::shared_ptr magneticField() const; +private: + dd4hep::Detector* m_dd4hepGeo{nullptr}; + SmartIF m_geoSvc; + std::shared_ptr m_trackingGeo{nullptr}; + std::shared_ptr m_magneticField{nullptr}; + std::unordered_map m_cellIDToSurface{}; }; inline std::shared_ptr ActsGeoSvc::trackingGeometry() const { return m_trackingGeo; } inline std::shared_ptr ActsGeoSvc::magneticField() const { return m_magneticField; } -#endif + +#endif // K4ACTSTRACKING_ACTSGEOSVC_H From 993a618708dee3e35480239beeb090475e6ea3d2 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 27 Feb 2026 14:25:12 +0100 Subject: [PATCH 21/69] Make things compile again after upstream changes --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 21 ++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index 22d00ad8..1194ee8d 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -78,11 +78,14 @@ StatusCode ActsGeoSvc::initialize() { info() << fmt::format("Acts::cm: {}, dd4hep::cm: {}", Acts::UnitConstants::cm, dd4hep::cm) << endmsg; - ActsPlugins::DD4hep::BlueprintBuilder builder{{ - .dd4hepDetector = m_geoSvc->getDetector(), - .lengthScale = Acts::UnitConstants::cm / dd4hep::cm, - }, - gaudiLogger->cloneWithSuffix("|BlpBld")}; + auto gctxt = Acts::GeometryContext::dangerouslyDefaultConstruct(); + + ActsPlugins::DD4hep::BlueprintBuilder builder{ + {.elementFactory = ActsPlugins::DD4hep::BlueprintBuilder::defaultElementFactory, + .dd4hepDetector = m_geoSvc->getDetector(), + .lengthScale = Acts::UnitConstants::cm / dd4hep::cm, + .gctx = gctxt}, + gaudiLogger->cloneWithSuffix("|BlpBld")}; using Acts::Experimental::Blueprint; using Acts::Experimental::BlueprintOptions; @@ -273,8 +276,7 @@ StatusCode ActsGeoSvc::initialize() { outerTracker.addChild(posEndcap); }); - BlueprintOptions options; - Acts::GeometryContext gctxt{}; + BlueprintOptions options; debug() << "Constructing tracking geometry" << endmsg; m_trackingGeo = root.construct(options, gctxt, *gaudiLogger->cloneWithSuffix("|Construct")); @@ -282,9 +284,8 @@ StatusCode ActsGeoSvc::initialize() { std::size_t nSurfaces = 0; m_trackingGeo->visitSurfaces([&](const Acts::Surface* surface) { nSurfaces++; - const auto& actsDetElem = - dynamic_cast(*surface->associatedDetectorElement()); - const auto& detElem = actsDetElem.sourceElement(); + const auto& actsDetElem = dynamic_cast(*surface->surfacePlacement()); + const auto& detElem = actsDetElem.sourceElement(); verbose() << fmt::format("Adding Acts surface {} pointing to dd4hep DetElement {}", surface->geometryId(), detElem.volumeID()) << endmsg; From 1efd71cecb66e2b78b1d5f8d3afa09d29b918460 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 27 Feb 2026 15:31:01 +0100 Subject: [PATCH 22/69] Make sure to build TestPropagator --- k4ActsTracking/CMakeLists.txt | 1 + k4ActsTracking/src/components/ActsTestPropagator.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/k4ActsTracking/CMakeLists.txt b/k4ActsTracking/CMakeLists.txt index 73cd8002..87472096 100644 --- a/k4ActsTracking/CMakeLists.txt +++ b/k4ActsTracking/CMakeLists.txt @@ -41,6 +41,7 @@ set(_plugin_sources src/components/GeometryIdSelector.cxx src/components/Helpers.cxx src/components/TrackTruthAlg.cxx + src/components/ActsTestPropagator.cpp ) gaudi_add_module(k4ActsTrackingPlugins diff --git a/k4ActsTracking/src/components/ActsTestPropagator.cpp b/k4ActsTracking/src/components/ActsTestPropagator.cpp index db83e748..30f2a0f2 100644 --- a/k4ActsTracking/src/components/ActsTestPropagator.cpp +++ b/k4ActsTracking/src/components/ActsTestPropagator.cpp @@ -116,7 +116,7 @@ std::vector> ActsTestPropagator::operator()() Navigator navigator(navigatorCfg, m_actsLogger->cloneWithSuffix(":Nav")); Propagator propagator(std::move(stepper), std::move(navigator), m_actsLogger->cloneWithSuffix(":Prop")); - auto options = PropagatorOptions{Acts::GeometryContext{}, Acts::MagneticFieldContext{}}; + auto options = PropagatorOptions{Acts::GeometryContext::dangerouslyDefaultConstruct(), Acts::MagneticFieldContext{}}; std::vector> stepOutputs(5); auto& stepsX = stepOutputs[0].vec(); From 4c4514ff4b2686605cb282759b82d49d5c567029 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 27 Feb 2026 15:51:36 +0100 Subject: [PATCH 23/69] Use newer version of VertexEncap geometry with layers --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 43 ++++++++------------ 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index 1194ee8d..5088bc94 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -98,7 +98,7 @@ StatusCode ActsGeoSvc::initialize() { cfg.envelope[AxisR] = {0_mm, 20_mm}; Blueprint root{cfg}; - auto& outer = root.addCylinderContainer("MAIA_v0", AxisR); + auto& outer = root.addCylinderContainer(detName, AxisR); outer.addStaticVolume(Acts::Transform3::Identity(), std::make_unique(0_mm, 10_mm, 1000_mm), "Beampipe"); // We want to pull the next volume in towards the beampipe to map material to @@ -140,30 +140,23 @@ StatusCode ActsGeoSvc::initialize() { .build(); vertexBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - // The VertexEncap again suffers from not having a *layer* DetElement - auto endcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); - auto vtxEndcapDetElem = builder.findDetElementByName("VertexEndcap"); - auto negVtxEndcapContainer = std::make_shared( - "VertexEndcapNeg", Acts::AxisDirection::AxisZ); - negVtxEndcapContainer->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto posVtxEndcapContainer = std::make_shared( - "VertexEndcapPos", Acts::AxisDirection::AxisZ); - posVtxEndcapContainer->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - for (int i = 0; i < 8; ++i) { - auto layerName = "layer" + std::to_string(i); - auto layerPattern = std::regex{layerName + "_module0_sensor\\d+_neg"}; - auto sensorElements = builder.findDetElementByNamePattern(vtxEndcapDetElem.value(), layerPattern); - - auto layer = builder.makeLayer(vtxEndcapDetElem.value(), sensorElements, "XZY", layerName + "_neg"); - layer->setEnvelope(endcapEnvelope); - negVtxEndcapContainer->addChild(layer); - - layerPattern = layerName + "_module0_sensor\\d+_pos"; - sensorElements = builder.findDetElementByNamePattern(vtxEndcapDetElem.value(), layerPattern); - layer = builder.makeLayer(vtxEndcapDetElem.value(), sensorElements, "XZY", layerName + "_pos"); - posVtxEndcapContainer->addChild(layer); - } + // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure + auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); + auto posVtxEndcapContainer = builder.layerHelper() + .endcap() + .setAxes("XZY") + .setContainer("VertexEndcap") + .setPattern("layer_pos\\d+") + .setEnvelope(vtxEndcapEnvelope) + .build(); + + auto negVtxEndcapContainer = builder.layerHelper() + .endcap() + .setAxes("XZY") + .setContainer("VertexEndcap") + .setPattern("layer_neg\\d+") + .setEnvelope(vtxEndcapEnvelope) + .build(); auto vertex = std::make_shared("Vertex", AxisZ); vertex->addChild(vertexBarrel); From e5d9bb7483f2fd0c28d3478c6c6f0e800d3cfd45 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 27 Feb 2026 15:51:53 +0100 Subject: [PATCH 24/69] Add printout of detector name --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index 5088bc94..dd999c7b 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -80,9 +80,13 @@ StatusCode ActsGeoSvc::initialize() { auto gctxt = Acts::GeometryContext::dangerouslyDefaultConstruct(); + const auto* dd4hepDet = m_geoSvc->getDetector(); + const auto detName = dd4hepDet->header().name(); + info() << fmt::format("Constructing detector with name: {}", dd4hepDet->header().name()) << endmsg; + ActsPlugins::DD4hep::BlueprintBuilder builder{ {.elementFactory = ActsPlugins::DD4hep::BlueprintBuilder::defaultElementFactory, - .dd4hepDetector = m_geoSvc->getDetector(), + .dd4hepDetector = dd4hepDet, .lengthScale = Acts::UnitConstants::cm / dd4hep::cm, .gctx = gctxt}, gaudiLogger->cloneWithSuffix("|BlpBld")}; From 12aae12f7e75035b693bcff7e69876b69506ab91 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 2 Mar 2026 11:18:34 +0100 Subject: [PATCH 25/69] Move blueprint population into separate function --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 84 +++++++++++--------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index dd999c7b..2995f2e4 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -61,47 +61,11 @@ DECLARE_COMPONENT(ActsGeoSvc) ActsGeoSvc::ActsGeoSvc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) {} -StatusCode ActsGeoSvc::initialize() { - m_geoSvc = Gaudi::svcLocator()->service("GeoSvc"); - K4_GAUDI_CHECK(m_geoSvc); - - std::array magneticFieldVector = {0, 0, 0}; - std::array position = {0, 0, 0}; - m_geoSvc->getDetector()->field().magneticField(position.data(), magneticFieldVector.data()); - debug() << fmt::format("Retrieved magnetic field at position {}: {}", position, magneticFieldVector) << endmsg; - m_magneticField = std::make_shared( - Acts::Vector3(magneticFieldVector[0] / dd4hep::tesla * Acts::UnitConstants::T, - magneticFieldVector[1] / dd4hep::tesla * Acts::UnitConstants::T, - magneticFieldVector[2] / dd4hep::tesla * Acts::UnitConstants::T)); - - auto gaudiLogger = makeActsGaudiLogger(this); - - info() << fmt::format("Acts::cm: {}, dd4hep::cm: {}", Acts::UnitConstants::cm, dd4hep::cm) << endmsg; - - auto gctxt = Acts::GeometryContext::dangerouslyDefaultConstruct(); - - const auto* dd4hepDet = m_geoSvc->getDetector(); - const auto detName = dd4hepDet->header().name(); - info() << fmt::format("Constructing detector with name: {}", dd4hepDet->header().name()) << endmsg; - - ActsPlugins::DD4hep::BlueprintBuilder builder{ - {.elementFactory = ActsPlugins::DD4hep::BlueprintBuilder::defaultElementFactory, - .dd4hepDetector = dd4hepDet, - .lengthScale = Acts::UnitConstants::cm / dd4hep::cm, - .gctx = gctxt}, - gaudiLogger->cloneWithSuffix("|BlpBld")}; - - using Acts::Experimental::Blueprint; - using Acts::Experimental::BlueprintOptions; +void populateBluePrint(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder) { using namespace Acts::UnitLiterals; using enum Acts::AxisDirection; - Blueprint::Config cfg; - // Padding around subvolumes of the world volume - cfg.envelope[AxisZ] = {20_mm, 20_mm}; - cfg.envelope[AxisR] = {0_mm, 20_mm}; - Blueprint root{cfg}; - auto& outer = root.addCylinderContainer(detName, AxisR); outer.addStaticVolume(Acts::Transform3::Identity(), std::make_unique(0_mm, 10_mm, 1000_mm), "Beampipe"); @@ -272,6 +236,50 @@ StatusCode ActsGeoSvc::initialize() { outerTracker.addChild(negEndcap); outerTracker.addChild(posEndcap); }); +} + +StatusCode ActsGeoSvc::initialize() { + m_geoSvc = Gaudi::svcLocator()->service("GeoSvc"); + K4_GAUDI_CHECK(m_geoSvc); + + std::array magneticFieldVector = {0, 0, 0}; + std::array position = {0, 0, 0}; + m_geoSvc->getDetector()->field().magneticField(position.data(), magneticFieldVector.data()); + debug() << fmt::format("Retrieved magnetic field at position {}: {}", position, magneticFieldVector) << endmsg; + m_magneticField = std::make_shared( + Acts::Vector3(magneticFieldVector[0] / dd4hep::tesla * Acts::UnitConstants::T, + magneticFieldVector[1] / dd4hep::tesla * Acts::UnitConstants::T, + magneticFieldVector[2] / dd4hep::tesla * Acts::UnitConstants::T)); + + auto gaudiLogger = makeActsGaudiLogger(this); + + info() << fmt::format("Acts::cm: {}, dd4hep::cm: {}", Acts::UnitConstants::cm, dd4hep::cm) << endmsg; + + auto gctxt = Acts::GeometryContext::dangerouslyDefaultConstruct(); + + const auto* dd4hepDet = m_geoSvc->getDetector(); + const auto detName = dd4hepDet->header().name(); + info() << fmt::format("Constructing detector with name: {}", dd4hepDet->header().name()) << endmsg; + + ActsPlugins::DD4hep::BlueprintBuilder builder{ + {.elementFactory = ActsPlugins::DD4hep::BlueprintBuilder::defaultElementFactory, + .dd4hepDetector = dd4hepDet, + .lengthScale = Acts::UnitConstants::cm / dd4hep::cm, + .gctx = gctxt}, + gaudiLogger->cloneWithSuffix("|BlpBld")}; + + using Acts::Experimental::Blueprint; + using Acts::Experimental::BlueprintOptions; + using namespace Acts::UnitLiterals; + using enum Acts::AxisDirection; + + Blueprint::Config cfg; + // Padding around subvolumes of the world volume + cfg.envelope[AxisZ] = {20_mm, 20_mm}; + cfg.envelope[AxisR] = {0_mm, 20_mm}; + Blueprint root{cfg}; + + populateBluePrint(detName, root, builder); BlueprintOptions options; From 56d1a10e0fb463e63a06a7b1aa237983ac925509 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 2 Mar 2026 11:29:37 +0100 Subject: [PATCH 26/69] Introduce a runtime map for population functions --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 18 ++++++++++++----- k4ActsTracking/src/components/ActsGeoSvc.h | 21 +++++++++++++++----- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index 2995f2e4..6edde73a 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -59,10 +59,8 @@ template <> struct fmt::formatter : fmt::ostream_forma DECLARE_COMPONENT(ActsGeoSvc) -ActsGeoSvc::ActsGeoSvc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) {} - -void populateBluePrint(const std::string& detName, Acts::Experimental::Blueprint& root, - ActsPlugins::DD4hep::BlueprintBuilder& builder) { +void populateBluePrintMAIA_v0(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder) { using namespace Acts::UnitLiterals; using enum Acts::AxisDirection; @@ -238,6 +236,10 @@ void populateBluePrint(const std::string& detName, Acts::Experimental::Blueprint }); } +ActsGeoSvc::ActsGeoSvc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) { + m_bluePrintPopulationFuncs = {{"MAIA_v0", populateBluePrintMAIA_v0}}; +} + StatusCode ActsGeoSvc::initialize() { m_geoSvc = Gaudi::svcLocator()->service("GeoSvc"); K4_GAUDI_CHECK(m_geoSvc); @@ -279,7 +281,13 @@ StatusCode ActsGeoSvc::initialize() { cfg.envelope[AxisR] = {0_mm, 20_mm}; Blueprint root{cfg}; - populateBluePrint(detName, root, builder); + if (const auto it = m_bluePrintPopulationFuncs.find(detName); it != m_bluePrintPopulationFuncs.end()) { + auto bluePrintFunc = it->second; + bluePrintFunc(detName, root, builder); + } else { + error() << fmt::format("Cannot find a Blueprint construction function for detector: {}", detName) << endmsg; + return StatusCode::FAILURE; + } BlueprintOptions options; diff --git a/k4ActsTracking/src/components/ActsGeoSvc.h b/k4ActsTracking/src/components/ActsGeoSvc.h index c35ced4d..1f0ec695 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.h +++ b/k4ActsTracking/src/components/ActsGeoSvc.h @@ -18,8 +18,15 @@ namespace Acts { class TrackingGeometry; class MagneticFieldProvider; class Surface; + namespace Experimental { + class Blueprint; + } } // namespace Acts +namespace ActsPlugins::DD4hep { + class BlueprintBuilder; +} + namespace dd4hep { class Detector; } @@ -44,11 +51,15 @@ class ActsGeoSvc : public extends { const CellIDSurfaceMap& cellIdToSurfaceMap() const override { return m_cellIDToSurface; } private: - dd4hep::Detector* m_dd4hepGeo{nullptr}; - SmartIF m_geoSvc; - std::shared_ptr m_trackingGeo{nullptr}; - std::shared_ptr m_magneticField{nullptr}; - std::unordered_map m_cellIDToSurface{}; + using BlueprintPopulationFunc = void(const std::string&, Acts::Experimental::Blueprint&, + ActsPlugins::DD4hep::BlueprintBuilder&); + + dd4hep::Detector* m_dd4hepGeo{nullptr}; + SmartIF m_geoSvc; + std::shared_ptr m_trackingGeo{nullptr}; + std::shared_ptr m_magneticField{nullptr}; + std::unordered_map m_cellIDToSurface{}; + std::unordered_map m_bluePrintPopulationFuncs{}; }; inline std::shared_ptr ActsGeoSvc::trackingGeometry() const { return m_trackingGeo; } From cc967a6da7a21976051c435d0c2576712570d607 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 4 Mar 2026 10:03:54 +0100 Subject: [PATCH 27/69] Move blueprint population into library-like construct --- k4ActsTracking/CMakeLists.txt | 1 + k4ActsTracking/examples/visActsGEo.py | 6 +- k4ActsTracking/src/components/ActsGeoSvc.cpp | 186 +---------- .../DD4hepBlueprintConstruction.cpp | 291 ++++++++++++++++++ .../components/DD4hepBlueprintConstruction.h | 38 +++ 5 files changed, 337 insertions(+), 185 deletions(-) create mode 100644 k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp create mode 100644 k4ActsTracking/src/components/DD4hepBlueprintConstruction.h diff --git a/k4ActsTracking/CMakeLists.txt b/k4ActsTracking/CMakeLists.txt index 87472096..9bb0f90a 100644 --- a/k4ActsTracking/CMakeLists.txt +++ b/k4ActsTracking/CMakeLists.txt @@ -42,6 +42,7 @@ set(_plugin_sources src/components/Helpers.cxx src/components/TrackTruthAlg.cxx src/components/ActsTestPropagator.cpp + src/components/DD4hepBlueprintConstruction.cpp ) gaudi_add_module(k4ActsTrackingPlugins diff --git a/k4ActsTracking/examples/visActsGEo.py b/k4ActsTracking/examples/visActsGEo.py index 167b3e81..79fac28a 100644 --- a/k4ActsTracking/examples/visActsGEo.py +++ b/k4ActsTracking/examples/visActsGEo.py @@ -18,8 +18,8 @@ actsGeoSvc = ActsGeoSvc("ActsGeoSvc") actsGeoSvc.DumpVisualization = True -actsGeoSvc.ObjVisFileName = "full_barrel.obj" -actsGeoSvc.OutputLevel = VERBOSE +actsGeoSvc.ObjVisFileName = "full_mucoll_old_vertex_endcap.obj" +actsGeoSvc.OutputLevel = DEBUG propTest = ActsTestPropagator("TestPropagator") propTest.OutputLevel = DEBUG @@ -27,7 +27,7 @@ ApplicationMgr( - TopAlg=[propTest], + TopAlg=[], # TopAlg=[], ExtSvc=[geoSvc, actsGeoSvc, EventDataSvc()], EvtMax=1, diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index 6edde73a..8c8f0e4a 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -18,7 +18,7 @@ */ #include "ActsGeoSvc.h" -#include +#include "DD4hepBlueprintConstruction.h" #include "k4ActsTracking/ActsGaudiLogger.h" @@ -27,15 +27,12 @@ #include #include #include -#include #include -#include #include #include #include #include #include -#include #include #include #include @@ -59,185 +56,9 @@ template <> struct fmt::formatter : fmt::ostream_forma DECLARE_COMPONENT(ActsGeoSvc) -void populateBluePrintMAIA_v0(const std::string& detName, Acts::Experimental::Blueprint& root, - ActsPlugins::DD4hep::BlueprintBuilder& builder) { - using namespace Acts::UnitLiterals; - using enum Acts::AxisDirection; - - auto& outer = root.addCylinderContainer(detName, AxisR); - outer.addStaticVolume(Acts::Transform3::Identity(), - std::make_unique(0_mm, 10_mm, 1000_mm), "Beampipe"); - // We want to pull the next volume in towards the beampipe to map material to - // the correct places in the end. We need to ensure that the enclosing - // cylinder contains the beampipe entirely. - outer.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - // We have to create the inner tracker in several steps, because the inner - // most endcap layer protrudes into the envelope that is created by the - // outermost barrel layer. That creates an overlap in z while stacking. Hence, - // we build it in steps grouping the innermost two layers of the barrel and - // the innermost layer of the endcap into an "inner" inner tracker (stacking - // them along z), we then stack the last barrel layer along r, before stacking - // the remaining endcap layers along z. Additionally, we have to first put the - // whole vertex detector inside the two innermost InnerTrackerBarrel layers - // because the outermost vertex layer extends further in r, than the innermost - // border of the InnerTracker endcaps. Hence, we also need to stack them in - // the correct order. - - // NOTE: Need to set rather small padding here for the R-direction, because - // the innermost two layers are a double layer for which the cylindrical - // volumes are overlapping otherwise - auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); - auto vertexBarrel = - builder.layerHelper() - .barrel() - .setAxes("ZYX") - .setPattern("layer_\\d") - .setContainer("VertexBarrel") - .setEnvelope(barrelEnvelope) - .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { - // Force the Barrel onto the z-axis by not using the - // center of gravity for auto-sizing. We do this because - // the VertexBarrel has an odd number of modules, which - // shifts them off-axis when using CoG - layer->setUseCenterOfGravity(false, false, true); - return layer; - }) - .build(); - vertexBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure - auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); - auto posVtxEndcapContainer = builder.layerHelper() - .endcap() - .setAxes("XZY") - .setContainer("VertexEndcap") - .setPattern("layer_pos\\d+") - .setEnvelope(vtxEndcapEnvelope) - .build(); - - auto negVtxEndcapContainer = builder.layerHelper() - .endcap() - .setAxes("XZY") - .setContainer("VertexEndcap") - .setPattern("layer_neg\\d+") - .setEnvelope(vtxEndcapEnvelope) - .build(); - - auto vertex = std::make_shared("Vertex", AxisZ); - vertex->addChild(vertexBarrel); - vertex->addChild(negVtxEndcapContainer); - vertex->addChild(posVtxEndcapContainer); - - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - auto innerInnerBarrel = builder.layerHelper() - .barrel() - .setAxes("XYZ") - .setPattern("layer[01]") - .setContainer("InnerTrackerBarrel") - .setEnvelope(envelope) - .build(); - innerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - innerInnerBarrel->addChild(vertex); - - auto outerInnerBarrel = builder.layerHelper() - .barrel() - .setAxes("XYZ") - .setPattern("layer2") - .setContainer("InnerTrackerBarrel") - .setEnvelope(envelope) - .build(); - outerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - auto innerPosEndcapInner = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setPattern("layer_pos0") - .setEnvelope(envelope) - .build(); - innerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto outerPosEndcapInner = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setPattern("layer_pos[1-6]") - .setEnvelope(envelope) - .build(); - outerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - auto innerNegEndcapInner = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setPattern("layer_neg0") - .setEnvelope(envelope) - .build(); - innerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto outerNegEndcapInner = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setPattern("layer_neg[1-6]") - .setEnvelope(envelope) - .build(); - outerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - auto innerInnerTracker = - std::make_shared("InnerInnerTracker", AxisZ); - innerInnerTracker->addChild(innerPosEndcapInner); - innerInnerTracker->addChild(innerNegEndcapInner); - innerInnerTracker->addChild(innerInnerBarrel); - - auto innerTrackerBarrel = - std::make_shared("InnerTrackerBarrel", AxisR); - innerTrackerBarrel->addChild(innerInnerTracker); - innerTrackerBarrel->addChild(outerInnerBarrel); - - outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { - innerTracker.addChild(innerTrackerBarrel); - innerTracker.addChild(outerNegEndcapInner); - innerTracker.addChild(outerPosEndcapInner); - }); - - // The OuterTracker is a bit more simple because it has the barrel and endcap - // more clearly separated - outer.addCylinderContainer("OuterTracker", AxisZ, [&](auto& outerTracker) { - auto barrel = builder.layerHelper() - .barrel() - .setAxes("XYZ") - .setPattern("layer\\d") - .setContainer("OuterTrackerBarrel") - .setEnvelope(envelope) - .build(); - barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - auto negEndcap = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("OuterTrackerEndcap") - .setPattern("layer_neg\\d") - .setEnvelope(envelope) - .build(); - negEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - auto posEndcap = builder.layerHelper() - .endcap() - .setAxes("YXZ") - .setContainer("OuterTrackerEndcap") - .setPattern("layer_pos\\d") - .setEnvelope(envelope) - .build(); - posEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - outerTracker.addChild(barrel); - outerTracker.addChild(negEndcap); - outerTracker.addChild(posEndcap); - }); -} - ActsGeoSvc::ActsGeoSvc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) { - m_bluePrintPopulationFuncs = {{"MAIA_v0", populateBluePrintMAIA_v0}}; + m_bluePrintPopulationFuncs = {{"MAIA_v0", MuColl::MAIA_v0::populateBlueprint}, + {"ILD_FCCee_v01", FCCee::ILD_FCCee::populateBlueprint}}; } StatusCode ActsGeoSvc::initialize() { @@ -281,6 +102,7 @@ StatusCode ActsGeoSvc::initialize() { cfg.envelope[AxisR] = {0_mm, 20_mm}; Blueprint root{cfg}; + debug() << fmt::format("Getting Blueprint construction function for detector: {}", detName) << endmsg; if (const auto it = m_bluePrintPopulationFuncs.find(detName); it != m_bluePrintPopulationFuncs.end()) { auto bluePrintFunc = it->second; bluePrintFunc(detName, root, builder); diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp new file mode 100644 index 00000000..ee54cf8d --- /dev/null +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -0,0 +1,291 @@ +#include "DD4hepBlueprintConstruction.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace polyfill { +#if defined(__cpp_lib_ranges_chunk) + inline constexpr chunk = std::views::chunk; +#else + namespace detail { + struct chunk_fn { + std::size_t n; + + template auto operator()(R&& r) const { + const std::size_t size = std::ranges::size(r); + auto* data = std::ranges::data(r); + + return std::views::iota(std::size_t{0}, size / n) | + std::views::transform([data, n = n](std::size_t i) { return std::span(data + i * n, n); }); + } + + // Support pipeline syntax: v | compat::views::chunk(2) + template friend auto operator|(R&& r, const chunk_fn& fn) { + return fn(std::forward(r)); + } + }; + + struct chunk_adaptor { + auto operator()(std::size_t n) const { return chunk_fn{n}; } + }; + } // namespace detail + inline constexpr detail::chunk_adaptor chunk; +#endif +} // namespace polyfill + +namespace Blueprints { + void addCylindricalBeampipe(Acts::Experimental::ContainerBlueprintNode& node, double rMax, double halfZ) { + node.addStaticVolume(Acts::Transform3::Identity(), std::make_unique(0_mm, rMax, halfZ), + "Beampipe"); + // We want to pull the next volume in towards the beampipe to map material to + // the correct places in the end. We need to ensure that the enclosing + // cylinder contains the beampipe entirely. + node.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + } +} // namespace Blueprints + +namespace MuColl { + namespace MAIA_v0 { + void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder) { + using namespace Acts::UnitLiterals; + using enum Acts::AxisDirection; + auto& outer = root.addCylinderContainer(detName, AxisR); + + Blueprints::addCylindricalBeampipe(outer); + + // We have to create the inner tracker in several steps, because the inner + // most endcap layer protrudes into the envelope that is created by the + // outermost barrel layer. That creates an overlap in z while stacking. Hence, + // we build it in steps grouping the innermost two layers of the barrel and + // the innermost layer of the endcap into an "inner" inner tracker (stacking + // them along z), we then stack the last barrel layer along r, before stacking + // the remaining endcap layers along z. Additionally, we have to first put the + // whole vertex detector inside the two innermost InnerTrackerBarrel layers + // because the outermost vertex layer extends further in r, than the innermost + // border of the InnerTracker endcaps. Hence, we also need to stack them in + // the correct order. + + // NOTE: Need to set rather small padding here for the R-direction, because + // the innermost two layers are a double layer for which the cylindrical + // volumes are overlapping otherwise + auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); + auto vertexBarrel = + builder.layerHelper() + .barrel() + .setAxes("ZYX") + .setPattern("layer_\\d") + .setContainer("VertexBarrel") + .setEnvelope(barrelEnvelope) + .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { + // Force the Barrel onto the z-axis by not using the + // center of gravity for auto-sizing. We do this because + // the VertexBarrel has an odd number of modules, which + // shifts them off-axis when using CoG + layer->setUseCenterOfGravity(false, false, true); + return layer; + }) + .build(); + vertexBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure + auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); + auto posVtxEndcapContainer = builder.layerHelper() + .endcap() + .setAxes("XZY") + .setContainer("VertexEndcap") + .setPattern("layer_pos\\d+") + .setEnvelope(vtxEndcapEnvelope) + .build(); + + auto negVtxEndcapContainer = builder.layerHelper() + .endcap() + .setAxes("XZY") + .setContainer("VertexEndcap") + .setPattern("layer_neg\\d+") + .setEnvelope(vtxEndcapEnvelope) + .build(); + + auto vertex = std::make_shared("Vertex", AxisZ); + vertex->addChild(vertexBarrel); + vertex->addChild(negVtxEndcapContainer); + vertex->addChild(posVtxEndcapContainer); + + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + auto innerInnerBarrel = builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("layer[01]") + .setContainer("InnerTrackerBarrel") + .setEnvelope(envelope) + .build(); + innerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + innerInnerBarrel->addChild(vertex); + + auto outerInnerBarrel = builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("layer2") + .setContainer("InnerTrackerBarrel") + .setEnvelope(envelope) + .build(); + outerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto innerPosEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_pos0") + .setEnvelope(envelope) + .build(); + innerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + auto outerPosEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_pos[1-6]") + .setEnvelope(envelope) + .build(); + outerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto innerNegEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_neg0") + .setEnvelope(envelope) + .build(); + innerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + auto outerNegEndcapInner = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setPattern("layer_neg[1-6]") + .setEnvelope(envelope) + .build(); + outerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto innerInnerTracker = + std::make_shared("InnerInnerTracker", AxisZ); + innerInnerTracker->addChild(innerPosEndcapInner); + innerInnerTracker->addChild(innerNegEndcapInner); + innerInnerTracker->addChild(innerInnerBarrel); + + auto innerTrackerBarrel = + std::make_shared("InnerTrackerBarrel", AxisR); + innerTrackerBarrel->addChild(innerInnerTracker); + innerTrackerBarrel->addChild(outerInnerBarrel); + + outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { + innerTracker.addChild(innerTrackerBarrel); + innerTracker.addChild(outerNegEndcapInner); + innerTracker.addChild(outerPosEndcapInner); + }); + + // The OuterTracker is a bit more simple because it has the barrel and endcap + // more clearly separated + outer.addCylinderContainer("OuterTracker", AxisZ, [&](auto& outerTracker) { + auto barrel = builder.layerHelper() + .barrel() + .setAxes("XYZ") + .setPattern("layer\\d") + .setContainer("OuterTrackerBarrel") + .setEnvelope(envelope) + .build(); + barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto negEndcap = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("OuterTrackerEndcap") + .setPattern("layer_neg\\d") + .setEnvelope(envelope) + .build(); + negEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + auto posEndcap = builder.layerHelper() + .endcap() + .setAxes("YXZ") + .setContainer("OuterTrackerEndcap") + .setPattern("layer_pos\\d") + .setEnvelope(envelope) + .build(); + posEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + outerTracker.addChild(barrel); + outerTracker.addChild(negEndcap); + outerTracker.addChild(posEndcap); + }); + } + + } // namespace MAIA_v0 +} // namespace MuColl + +namespace FCCee { + namespace ILD_FCCee { + void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder) { + using namespace Acts::UnitLiterals; + using enum Acts::AxisDirection; + + auto& outer = root.addCylinderContainer(detName, AxisR); + + Blueprints::addCylindricalBeampipe(outer); + + auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {1_mm, 1_mm}); + + // Vertex Barrel has a double layer gap of only 1 mm. This makes it + // (almost) impossible to fit them into mutually exclusive cylinder shell + // volumes. Hence, we make each double layer an Acts layer / volume. + const auto vtxBarrelDetElem = builder.findDetElementByName("VertexBarrel"); + const auto vtxBarrelLayers = + builder.findDetElementByNamePattern(vtxBarrelDetElem.value(), std::regex{"layer_\\d"}); + + auto vtxBarrel = std::make_shared("VertexBarrel", + Acts::AxisDirection::AxisR); + + int layerNum = 0; + for (const auto layerElems : vtxBarrelLayers | polyfill::chunk(2)) { + auto layerName = "doubleLayer_" + std::to_string(layerNum++); + // TODO: Extract all the sensitive elements from the layers here (or do + // that a step further up) + auto layer = builder.makeLayer(vtxBarrelDetElem.value(), layerElems, "XYZ", layerName); + layer->setEnvelope(barrelEnvelope); + // Force the Barrel onto the z-axis by not using the + // center of gravity for auto-sizing. We do this because + // the VertexBarrel has an odd number of modules, which + // shifts them off-axis when using CoG + layer->setUseCenterOfGravity(false, false, true); + vtxBarrel->addChild(layer); + } + + // auto vertexBarrel = + // builder.layerHelper() + // .barrel() + // .setAxes("ZYX") + // .setPattern("layer_\\d") + // .setContainer("VertexBarrel") + // .setEnvelope(barrelEnvelope) + // .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + // .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { + // // Force the Barrel onto the z-axis by not using the + // // center of gravity for auto-sizing. We do this because + // // the VertexBarrel has an odd number of modules, which + // // shifts them off-axis when using CoG + // layer->setUseCenterOfGravity(false, false, true); + // return layer; + // }) + // .build(); + + outer.addChild(vtxBarrel); + } + } // namespace ILD_FCCee +} // namespace FCCee diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h new file mode 100644 index 00000000..04e24127 --- /dev/null +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h @@ -0,0 +1,38 @@ +#ifndef K4ACTSTRACKING_DD4HEPBLUEPRINTCONSTRUCTION_H +#define K4ACTSTRACKING_DD4HEPBLUEPRINTCONSTRUCTION_H + +#include + +#include + +namespace Acts::Experimental { + class ContainerBlueprintNode; + class Blueprint; +} // namespace Acts::Experimental + +namespace ActsPlugins::DD4hep { + class BlueprintBuilder; +} + +namespace Blueprints { + using namespace Acts::UnitLiterals; + void addCylindricalBeampipe(Acts::Experimental::ContainerBlueprintNode& root, double rMax = 10_mm, + double halfZ = 1000_mm); +} // namespace Blueprints + +namespace MuColl { + namespace MAIA_v0 { + void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder); + } +} // namespace MuColl + +namespace FCCee { + namespace ILD_FCCee { + void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder); + } + // namespace FCCee +} // namespace FCCee + +#endif // K4ACTSTRACKING_DD4HEPBLUEPRINTCONSTRUCTION_H From e31e2324889947b3e7987651efad8cda04a749f4 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 4 Mar 2026 10:51:20 +0100 Subject: [PATCH 28/69] Fix issues after uptream API update --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 7 +-- k4ActsTracking/src/components/ActsGeoSvc.h | 11 ++-- .../DD4hepBlueprintConstruction.cpp | 63 ++++++++++--------- .../components/DD4hepBlueprintConstruction.h | 5 +- 4 files changed, 42 insertions(+), 44 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index 8c8f0e4a..b5104ecf 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -84,11 +84,8 @@ StatusCode ActsGeoSvc::initialize() { const auto detName = dd4hepDet->header().name(); info() << fmt::format("Constructing detector with name: {}", dd4hepDet->header().name()) << endmsg; - ActsPlugins::DD4hep::BlueprintBuilder builder{ - {.elementFactory = ActsPlugins::DD4hep::BlueprintBuilder::defaultElementFactory, - .dd4hepDetector = dd4hepDet, - .lengthScale = Acts::UnitConstants::cm / dd4hep::cm, - .gctx = gctxt}, + BlueprintBuilder builder{ + {.dd4hepDetector = dd4hepDet, .lengthScale = Acts::UnitConstants::cm / dd4hep::cm, .gctx = gctxt}, gaudiLogger->cloneWithSuffix("|BlpBld")}; using Acts::Experimental::Blueprint; diff --git a/k4ActsTracking/src/components/ActsGeoSvc.h b/k4ActsTracking/src/components/ActsGeoSvc.h index 1f0ec695..2434411f 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.h +++ b/k4ActsTracking/src/components/ActsGeoSvc.h @@ -8,6 +8,8 @@ #include +#include + #include "GaudiKernel/Service.h" #include @@ -23,10 +25,6 @@ namespace Acts { } } // namespace Acts -namespace ActsPlugins::DD4hep { - class BlueprintBuilder; -} - namespace dd4hep { class Detector; } @@ -51,8 +49,9 @@ class ActsGeoSvc : public extends { const CellIDSurfaceMap& cellIdToSurfaceMap() const override { return m_cellIDToSurface; } private: - using BlueprintPopulationFunc = void(const std::string&, Acts::Experimental::Blueprint&, - ActsPlugins::DD4hep::BlueprintBuilder&); + using BlueprintBuilder = ActsPlugins::DD4hep::BlueprintBuilder; + + using BlueprintPopulationFunc = void(const std::string&, Acts::Experimental::Blueprint&, BlueprintBuilder&); dd4hep::Detector* m_dd4hepGeo{nullptr}; SmartIF m_geoSvc; diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index ee54cf8d..ffe2d8bb 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -79,13 +80,13 @@ namespace MuColl { // volumes are overlapping otherwise auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); auto vertexBarrel = - builder.layerHelper() + builder.layers() .barrel() .setAxes("ZYX") - .setPattern("layer_\\d") + .setFilter("layer_\\d") .setContainer("VertexBarrel") .setEnvelope(barrelEnvelope) - .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { + .onLayer([&](const dd4hep::DetElement&, std::shared_ptr layer) { // Force the Barrel onto the z-axis by not using the // center of gravity for auto-sizing. We do this because // the VertexBarrel has an odd number of modules, which @@ -98,19 +99,19 @@ namespace MuColl { // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); - auto posVtxEndcapContainer = builder.layerHelper() + auto posVtxEndcapContainer = builder.layers() .endcap() .setAxes("XZY") .setContainer("VertexEndcap") - .setPattern("layer_pos\\d+") + .setFilter("layer_pos\\d+") .setEnvelope(vtxEndcapEnvelope) .build(); - auto negVtxEndcapContainer = builder.layerHelper() + auto negVtxEndcapContainer = builder.layers() .endcap() .setAxes("XZY") .setContainer("VertexEndcap") - .setPattern("layer_neg\\d+") + .setFilter("layer_neg\\d+") .setEnvelope(vtxEndcapEnvelope) .build(); @@ -120,55 +121,55 @@ namespace MuColl { vertex->addChild(posVtxEndcapContainer); auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - auto innerInnerBarrel = builder.layerHelper() + auto innerInnerBarrel = builder.layers() .barrel() .setAxes("XYZ") - .setPattern("layer[01]") + .setFilter("layer[01]") .setContainer("InnerTrackerBarrel") .setEnvelope(envelope) .build(); innerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); innerInnerBarrel->addChild(vertex); - auto outerInnerBarrel = builder.layerHelper() + auto outerInnerBarrel = builder.layers() .barrel() .setAxes("XYZ") - .setPattern("layer2") + .setFilter("layer2") .setContainer("InnerTrackerBarrel") .setEnvelope(envelope) .build(); outerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto innerPosEndcapInner = builder.layerHelper() + auto innerPosEndcapInner = builder.layers() .endcap() .setAxes("YXZ") .setContainer("InnerTrackerEndcap") - .setPattern("layer_pos0") + .setFilter("layer_pos0") .setEnvelope(envelope) .build(); innerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto outerPosEndcapInner = builder.layerHelper() + auto outerPosEndcapInner = builder.layers() .endcap() .setAxes("YXZ") .setContainer("InnerTrackerEndcap") - .setPattern("layer_pos[1-6]") + .setFilter("layer_pos[1-6]") .setEnvelope(envelope) .build(); outerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto innerNegEndcapInner = builder.layerHelper() + auto innerNegEndcapInner = builder.layers() .endcap() .setAxes("YXZ") .setContainer("InnerTrackerEndcap") - .setPattern("layer_neg0") + .setFilter("layer_neg0") .setEnvelope(envelope) .build(); innerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto outerNegEndcapInner = builder.layerHelper() + auto outerNegEndcapInner = builder.layers() .endcap() .setAxes("YXZ") .setContainer("InnerTrackerEndcap") - .setPattern("layer_neg[1-6]") + .setFilter("layer_neg[1-6]") .setEnvelope(envelope) .build(); outerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); @@ -193,29 +194,29 @@ namespace MuColl { // The OuterTracker is a bit more simple because it has the barrel and endcap // more clearly separated outer.addCylinderContainer("OuterTracker", AxisZ, [&](auto& outerTracker) { - auto barrel = builder.layerHelper() + auto barrel = builder.layers() .barrel() .setAxes("XYZ") - .setPattern("layer\\d") + .setFilter("layer\\d") .setContainer("OuterTrackerBarrel") .setEnvelope(envelope) .build(); barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto negEndcap = builder.layerHelper() + auto negEndcap = builder.layers() .endcap() .setAxes("YXZ") .setContainer("OuterTrackerEndcap") - .setPattern("layer_neg\\d") + .setFilter("layer_neg\\d") .setEnvelope(envelope) .build(); negEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - auto posEndcap = builder.layerHelper() + auto posEndcap = builder.layers() .endcap() .setAxes("YXZ") .setContainer("OuterTrackerEndcap") - .setPattern("layer_pos\\d") + .setFilter("layer_pos\\d") .setEnvelope(envelope) .build(); posEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); @@ -254,10 +255,14 @@ namespace FCCee { int layerNum = 0; for (const auto layerElems : vtxBarrelLayers | polyfill::chunk(2)) { - auto layerName = "doubleLayer_" + std::to_string(layerNum++); + const auto layerSpec = + ActsPlugins::DD4hep::DD4hepBackend::LayerSpec{.axes = ActsPlugins::TGeoAxes("XYZ"), + .layerAxes = std::nullopt, + .layerName = "doubleLayer_" + std::to_string(layerNum++)}; + // TODO: Extract all the sensitive elements from the layers here (or do // that a step further up) - auto layer = builder.makeLayer(vtxBarrelDetElem.value(), layerElems, "XYZ", layerName); + auto layer = builder.makeLayer(vtxBarrelDetElem.value(), layerElems, layerSpec); layer->setEnvelope(barrelEnvelope); // Force the Barrel onto the z-axis by not using the // center of gravity for auto-sizing. We do this because @@ -268,10 +273,10 @@ namespace FCCee { } // auto vertexBarrel = - // builder.layerHelper() + // builder.layers() // .barrel() // .setAxes("ZYX") - // .setPattern("layer_\\d") + // .setFilter("layer_\\d") // .setContainer("VertexBarrel") // .setEnvelope(barrelEnvelope) // .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h index 04e24127..e9328980 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h @@ -2,6 +2,7 @@ #define K4ACTSTRACKING_DD4HEPBLUEPRINTCONSTRUCTION_H #include +#include #include @@ -10,10 +11,6 @@ namespace Acts::Experimental { class Blueprint; } // namespace Acts::Experimental -namespace ActsPlugins::DD4hep { - class BlueprintBuilder; -} - namespace Blueprints { using namespace Acts::UnitLiterals; void addCylindricalBeampipe(Acts::Experimental::ContainerBlueprintNode& root, double rMax = 10_mm, From f925e90a4f3cfa86ba97901c4e271b42d8a7273a Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 4 Mar 2026 15:00:05 +0100 Subject: [PATCH 29/69] Make double layers for the ILD Vertex Barrel --- .../DD4hepBlueprintConstruction.cpp | 82 ++++++------------- 1 file changed, 26 insertions(+), 56 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index ffe2d8bb..7bf3ffe5 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -10,37 +10,9 @@ #include #include +#include #include - -namespace polyfill { -#if defined(__cpp_lib_ranges_chunk) - inline constexpr chunk = std::views::chunk; -#else - namespace detail { - struct chunk_fn { - std::size_t n; - - template auto operator()(R&& r) const { - const std::size_t size = std::ranges::size(r); - auto* data = std::ranges::data(r); - - return std::views::iota(std::size_t{0}, size / n) | - std::views::transform([data, n = n](std::size_t i) { return std::span(data + i * n, n); }); - } - - // Support pipeline syntax: v | compat::views::chunk(2) - template friend auto operator|(R&& r, const chunk_fn& fn) { - return fn(std::forward(r)); - } - }; - - struct chunk_adaptor { - auto operator()(std::size_t n) const { return chunk_fn{n}; } - }; - } // namespace detail - inline constexpr detail::chunk_adaptor chunk; -#endif -} // namespace polyfill +#include namespace Blueprints { void addCylindricalBeampipe(Acts::Experimental::ContainerBlueprintNode& node, double rMax, double halfZ) { @@ -246,24 +218,40 @@ namespace FCCee { // Vertex Barrel has a double layer gap of only 1 mm. This makes it // (almost) impossible to fit them into mutually exclusive cylinder shell // volumes. Hence, we make each double layer an Acts layer / volume. - const auto vtxBarrelDetElem = builder.findDetElementByName("VertexBarrel"); - const auto vtxBarrelLayers = - builder.findDetElementByNamePattern(vtxBarrelDetElem.value(), std::regex{"layer_\\d"}); + const auto vtxBarrelDetElem = builder.findDetElementByName("VertexBarrel"); + const auto vtxBarrelLayerRgx = std::regex{"VertexBarrel_layer(\\d)_ladder\\d+"}; + const auto vtxBarrelLayerElems = builder.findDetElementByNamePattern(vtxBarrelDetElem.value(), vtxBarrelLayerRgx); + + const auto doubleLayerGrouper = [&](const std::span elements) { + const auto layerElements = elements | std::views::transform([&](const auto e) { + std::smatch match; + const std::string elemName = e.name(); + std::regex_match(elemName, match, vtxBarrelLayerRgx); + const auto layer = std::stoi(match[1].str()); + return std::make_pair(layer, e); + }); + const auto nLayers = std::ranges::max(layerElements, std::less{}, [](const auto p) { return p.first; }).first; + std::vector> layers((nLayers + 1) / 2); + + for (const auto& [layer, elem] : layerElements) { + layers[layer / 2].emplace_back(elem); + } + + return layers; + }; auto vtxBarrel = std::make_shared("VertexBarrel", Acts::AxisDirection::AxisR); int layerNum = 0; - for (const auto layerElems : vtxBarrelLayers | polyfill::chunk(2)) { + for (const auto& layerElems : doubleLayerGrouper(vtxBarrelLayerElems)) { const auto layerSpec = - ActsPlugins::DD4hep::DD4hepBackend::LayerSpec{.axes = ActsPlugins::TGeoAxes("XYZ"), + ActsPlugins::DD4hep::DD4hepBackend::LayerSpec{.axes = ActsPlugins::TGeoAxes("ZYX"), .layerAxes = std::nullopt, .layerName = "doubleLayer_" + std::to_string(layerNum++)}; - - // TODO: Extract all the sensitive elements from the layers here (or do - // that a step further up) auto layer = builder.makeLayer(vtxBarrelDetElem.value(), layerElems, layerSpec); layer->setEnvelope(barrelEnvelope); + // Force the Barrel onto the z-axis by not using the // center of gravity for auto-sizing. We do this because // the VertexBarrel has an odd number of modules, which @@ -272,24 +260,6 @@ namespace FCCee { vtxBarrel->addChild(layer); } - // auto vertexBarrel = - // builder.layers() - // .barrel() - // .setAxes("ZYX") - // .setFilter("layer_\\d") - // .setContainer("VertexBarrel") - // .setEnvelope(barrelEnvelope) - // .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - // .customize([&](const dd4hep::DetElement&, std::shared_ptr layer) { - // // Force the Barrel onto the z-axis by not using the - // // center of gravity for auto-sizing. We do this because - // // the VertexBarrel has an odd number of modules, which - // // shifts them off-axis when using CoG - // layer->setUseCenterOfGravity(false, false, true); - // return layer; - // }) - // .build(); - outer.addChild(vtxBarrel); } } // namespace ILD_FCCee From 7540db67c8643d55ca2eea4273c5aefeb53d90ee Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 4 Mar 2026 16:37:09 +0100 Subject: [PATCH 30/69] Add rest of inner tracker for ILD at FCCee --- .../DD4hepBlueprintConstruction.cpp | 64 ++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 7bf3ffe5..abd81a25 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -214,7 +214,6 @@ namespace FCCee { Blueprints::addCylindricalBeampipe(outer); auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {1_mm, 1_mm}); - // Vertex Barrel has a double layer gap of only 1 mm. This makes it // (almost) impossible to fit them into mutually exclusive cylinder shell // volumes. Hence, we make each double layer an Acts layer / volume. @@ -259,8 +258,69 @@ namespace FCCee { layer->setUseCenterOfGravity(false, false, true); vtxBarrel->addChild(layer); } + vtxBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure + auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); + auto posVtxEndcapContainer = builder.layers() + .endcap() + .setAxes("XZY") + .setContainer("VertexEndcap") + .setFilter("layer_pos\\d+") + .setEnvelope(vtxEndcapEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .build(); - outer.addChild(vtxBarrel); + auto negVtxEndcapContainer = builder.layers() + .endcap() + .setAxes("XZY") + .setContainer("VertexEndcap") + .setFilter("layer_neg\\d+") + .setEnvelope(vtxEndcapEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .build(); + + auto vertex = std::make_shared("Vertex", AxisZ); + vertex->addChild(vtxBarrel); + vertex->addChild(negVtxEndcapContainer); + vertex->addChild(posVtxEndcapContainer); + + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + + auto innerTrackerBarrel = builder.layers() + .barrel() + .setAxes("XYZ") + .setContainer("InnerTrackerBarrel") + .setFilter("layer\\d") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .build(); + auto innerTrackerPosEndcap = builder.layers() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setFilter("layer_pos\\d") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .build(); + auto innerTrackerNegEndcap = builder.layers() + .endcap() + .setAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setFilter("layer_neg\\d") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .build(); + + auto trackerBarrel = std::make_shared("TrackerBarrel", AxisR); + trackerBarrel->addChild(vertex); + trackerBarrel->addChild(innerTrackerBarrel); + + outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { + innerTracker.addChild(trackerBarrel); + innerTracker.addChild(innerTrackerNegEndcap); + innerTracker.addChild(innerTrackerPosEndcap); + }); } } // namespace ILD_FCCee } // namespace FCCee From 1541f4eef2fabe3a6b56c36cd52d22854c7762ef Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 4 Mar 2026 16:43:40 +0100 Subject: [PATCH 31/69] Set attachment strategy via builder --- .../DD4hepBlueprintConstruction.cpp | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index abd81a25..911c0400 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -58,6 +58,7 @@ namespace MuColl { .setFilter("layer_\\d") .setContainer("VertexBarrel") .setEnvelope(barrelEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .onLayer([&](const dd4hep::DetElement&, std::shared_ptr layer) { // Force the Barrel onto the z-axis by not using the // center of gravity for auto-sizing. We do this because @@ -67,7 +68,6 @@ namespace MuColl { return layer; }) .build(); - vertexBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); @@ -99,8 +99,8 @@ namespace MuColl { .setFilter("layer[01]") .setContainer("InnerTrackerBarrel") .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); - innerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); innerInnerBarrel->addChild(vertex); auto outerInnerBarrel = builder.layers() @@ -109,8 +109,8 @@ namespace MuColl { .setFilter("layer2") .setContainer("InnerTrackerBarrel") .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); - outerInnerBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); auto innerPosEndcapInner = builder.layers() .endcap() @@ -118,16 +118,17 @@ namespace MuColl { .setContainer("InnerTrackerEndcap") .setFilter("layer_pos0") .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); - innerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + auto outerPosEndcapInner = builder.layers() .endcap() .setAxes("YXZ") .setContainer("InnerTrackerEndcap") .setFilter("layer_pos[1-6]") .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); - outerPosEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); auto innerNegEndcapInner = builder.layers() .endcap() @@ -135,16 +136,16 @@ namespace MuColl { .setContainer("InnerTrackerEndcap") .setFilter("layer_neg0") .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); - innerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); auto outerNegEndcapInner = builder.layers() .endcap() .setAxes("YXZ") .setContainer("InnerTrackerEndcap") .setFilter("layer_neg[1-6]") .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); - outerNegEndcapInner->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); auto innerInnerTracker = std::make_shared("InnerInnerTracker", AxisZ); @@ -172,8 +173,8 @@ namespace MuColl { .setFilter("layer\\d") .setContainer("OuterTrackerBarrel") .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); - barrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); auto negEndcap = builder.layers() .endcap() @@ -181,8 +182,8 @@ namespace MuColl { .setContainer("OuterTrackerEndcap") .setFilter("layer_neg\\d") .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); - negEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); auto posEndcap = builder.layers() .endcap() @@ -190,8 +191,8 @@ namespace MuColl { .setContainer("OuterTrackerEndcap") .setFilter("layer_pos\\d") .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); - posEndcap->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); outerTracker.addChild(barrel); outerTracker.addChild(negEndcap); From f758e0bcb03de969f258ef6b87caa1d51e8f91c7 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 5 Mar 2026 16:53:41 +0100 Subject: [PATCH 32/69] Start adding FCCee_v02 model for ILD and create some utilities --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 3 +- .../DD4hepBlueprintConstruction.cpp | 262 +++++++++++------- .../components/DD4hepBlueprintConstruction.h | 14 +- 3 files changed, 167 insertions(+), 112 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index b5104ecf..a16fdcf7 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -58,7 +58,8 @@ DECLARE_COMPONENT(ActsGeoSvc) ActsGeoSvc::ActsGeoSvc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) { m_bluePrintPopulationFuncs = {{"MAIA_v0", MuColl::MAIA_v0::populateBlueprint}, - {"ILD_FCCee_v01", FCCee::ILD_FCCee::populateBlueprint}}; + {"ILD_FCCee_v01", FCCee::ILD_FCCee_v01::populateBlueprint}, + {"ILD_FCCee_v02", FCCee::ILD_FCCee_v02::populateBlueprint}}; } StatusCode ActsGeoSvc::initialize() { diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 911c0400..206886fd 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -14,8 +14,23 @@ #include #include +using Acts::Experimental::ContainerBlueprintNode; +using Acts::Experimental::CylinderContainerBlueprintNode; + +using namespace Acts::UnitLiterals; +using enum Acts::AxisDirection; + namespace Blueprints { - void addCylindricalBeampipe(Acts::Experimental::ContainerBlueprintNode& node, double rMax, double halfZ) { + /// Add a cylindrical beampipe to the passed node using the measures passed as arguments. + /// + /// We use this to enclose our actual beampipe because that is not a sipmle + /// cylinder. However, we mainly need a surface / volume to attach the + /// material of the beampipe, for which we use this cylindrical volume here. + /// + /// @param node The Blueprint container to which the beampipe should be added + /// @param rMax The (initial) max radius of the cylinder + /// @param halfZ the half-length in z of this cylinder + void addCylindricalBeampipe(ContainerBlueprintNode& node, double rMax = 10_mm, double halfZ = 1000_mm) { node.addStaticVolume(Acts::Transform3::Identity(), std::make_unique(0_mm, rMax, halfZ), "Beampipe"); // We want to pull the next volume in towards the beampipe to map material to @@ -23,14 +38,133 @@ namespace Blueprints { // cylinder contains the beampipe entirely. node.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); } + + /// Make the Acts volumes for a VertexBarrel detector where the layers are + /// grouped into double layers such that these double layers end up in one + /// volume in the Acts geometry. + /// + /// This might be necessary in case the spacing between double layers is too + /// small to have cylinder shells that do not overlap + /// + /// @param builder The Blueprint builder that drives the construction + /// @param containerName The detector name in which all the sensitive elements + /// are placed + /// @param layerRgx The match expression to filter out the sensitive + /// elements. @note that these should not be the + /// "top-level" layer DetElements, but rather the + /// ladders. @note This needs to contain exactly one + /// matching group which has to be convertible to int as + /// that is what will be used for grouping them into + /// double layers + /// + /// @returns The vertex barrel blueprint node + std::shared_ptr makeDoubleLayerVertexBarrel( + ActsPlugins::DD4hep::BlueprintBuilder& builder, const std::string& containerName = "VertexBarrel", + const std::regex& layerRgx = std::regex{"VertexBarrel_layer(\\d)_ladder\\d+"}) { + // Vertex Barrel has a double layer gap of only 1 mm. This makes it + // (almost) impossible to fit them into mutually exclusive cylinder shell + // volumes. Hence, we make each double layer an Acts layer / volume. + const auto vtxBarrelDetElem = builder.findDetElementByName(containerName); + const auto vtxBarrelLayerElems = builder.findDetElementByNamePattern(vtxBarrelDetElem.value(), layerRgx); + + const auto doubleLayerGrouper = [&](const std::span elements) { + const auto layerElements = elements | std::views::transform([&](const auto e) { + std::smatch match; + const std::string elemName = e.name(); + std::regex_match(elemName, match, layerRgx); + const auto layer = std::stoi(match[1].str()); + return std::make_pair(layer, e); + }); + const auto nLayers = std::ranges::max(layerElements, std::less{}, [](const auto p) { return p.first; }).first; + std::vector> layers((nLayers + 1) / 2); + + for (const auto& [layer, elem] : layerElements) { + layers[layer / 2].emplace_back(elem); + } + + return layers; + }; + + auto vtxBarrel = std::make_shared("VertexBarrel", + Acts::AxisDirection::AxisR); + auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {1_mm, 1_mm}); + + int layerNum = 0; + for (const auto& layerElems : doubleLayerGrouper(vtxBarrelLayerElems)) { + const auto layerSpec = + ActsPlugins::DD4hep::DD4hepBackend::LayerSpec{.axes = ActsPlugins::TGeoAxes("ZYX"), + .layerAxes = std::nullopt, + .layerName = "doubleLayer_" + std::to_string(layerNum++)}; + auto layer = builder.makeLayer(vtxBarrelDetElem.value(), layerElems, layerSpec); + layer->setEnvelope(barrelEnvelope); + + // Force the Barrel onto the z-axis by not using the + // center of gravity for auto-sizing. We do this because + // the VertexBarrel has an odd number of modules, which + // shifts them off-axis when using CoG + layer->setUseCenterOfGravity(false, false, true); + vtxBarrel->addChild(layer); + } + vtxBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); + + return vtxBarrel; + } + + /// Attach the Endcaps to the VertexBarrel after constructing them to create + /// the full Vertex detector node. + /// + /// This accepts an existing VertexBarrel blueprint node and stacks the + /// endcaps onto it along the z-axis. + /// + /// @param builder The Blueprint builder that drives the construction + /// @param vtxBarrel The vertex barrel bluprint node + /// @param containerName The detector name in which all the sensitive elements + /// are placed + /// + /// @param posLayerPattern The expression for selecting layers from the + /// DetElement with the @containerName name for the + /// positive endcap + /// @param negLayerPattern The expression for selecting layers from the + /// DetElement with the @containerName name for the + /// negative endcap + std::shared_ptr completeVertexWithEndcaps( + ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& vtxBarrel, + const std::string& containerName = "VertexEndcap", + const std::regex& posLayerPattern = std::regex{"layer_pos\\d+"}, + const std::regex& negLayerPattern = std::regex{"layer_neg\\d+"}) { + // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure + auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); + auto posVtxEndcapContainer = builder.layers() + .endcap() + .setAxes("XZY") + .setContainer(containerName) + .setFilter(posLayerPattern) + .setEnvelope(vtxEndcapEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .build(); + + auto negVtxEndcapContainer = builder.layers() + .endcap() + .setAxes("XZY") + .setContainer(containerName) + .setFilter(negLayerPattern) + .setEnvelope(vtxEndcapEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .build(); + + auto vertex = std::make_shared("Vertex", AxisZ); + vertex->addChild(vtxBarrel); + vertex->addChild(negVtxEndcapContainer); + vertex->addChild(posVtxEndcapContainer); + return vertex; + } + } // namespace Blueprints namespace MuColl { namespace MAIA_v0 { void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, ActsPlugins::DD4hep::BlueprintBuilder& builder) { - using namespace Acts::UnitLiterals; - using enum Acts::AxisDirection; auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); @@ -69,28 +203,7 @@ namespace MuColl { }) .build(); - // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure - auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); - auto posVtxEndcapContainer = builder.layers() - .endcap() - .setAxes("XZY") - .setContainer("VertexEndcap") - .setFilter("layer_pos\\d+") - .setEnvelope(vtxEndcapEnvelope) - .build(); - - auto negVtxEndcapContainer = builder.layers() - .endcap() - .setAxes("XZY") - .setContainer("VertexEndcap") - .setFilter("layer_neg\\d+") - .setEnvelope(vtxEndcapEnvelope) - .build(); - - auto vertex = std::make_shared("Vertex", AxisZ); - vertex->addChild(vertexBarrel); - vertex->addChild(negVtxEndcapContainer); - vertex->addChild(posVtxEndcapContainer); + auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vertexBarrel)); auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); auto innerInnerBarrel = builder.layers() @@ -204,90 +317,17 @@ namespace MuColl { } // namespace MuColl namespace FCCee { - namespace ILD_FCCee { + namespace ILD_FCCee_v01 { void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, ActsPlugins::DD4hep::BlueprintBuilder& builder) { - using namespace Acts::UnitLiterals; - using enum Acts::AxisDirection; - auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); - auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {1_mm, 1_mm}); - // Vertex Barrel has a double layer gap of only 1 mm. This makes it - // (almost) impossible to fit them into mutually exclusive cylinder shell - // volumes. Hence, we make each double layer an Acts layer / volume. - const auto vtxBarrelDetElem = builder.findDetElementByName("VertexBarrel"); - const auto vtxBarrelLayerRgx = std::regex{"VertexBarrel_layer(\\d)_ladder\\d+"}; - const auto vtxBarrelLayerElems = builder.findDetElementByNamePattern(vtxBarrelDetElem.value(), vtxBarrelLayerRgx); - - const auto doubleLayerGrouper = [&](const std::span elements) { - const auto layerElements = elements | std::views::transform([&](const auto e) { - std::smatch match; - const std::string elemName = e.name(); - std::regex_match(elemName, match, vtxBarrelLayerRgx); - const auto layer = std::stoi(match[1].str()); - return std::make_pair(layer, e); - }); - const auto nLayers = std::ranges::max(layerElements, std::less{}, [](const auto p) { return p.first; }).first; - std::vector> layers((nLayers + 1) / 2); - - for (const auto& [layer, elem] : layerElements) { - layers[layer / 2].emplace_back(elem); - } - - return layers; - }; - - auto vtxBarrel = std::make_shared("VertexBarrel", - Acts::AxisDirection::AxisR); - - int layerNum = 0; - for (const auto& layerElems : doubleLayerGrouper(vtxBarrelLayerElems)) { - const auto layerSpec = - ActsPlugins::DD4hep::DD4hepBackend::LayerSpec{.axes = ActsPlugins::TGeoAxes("ZYX"), - .layerAxes = std::nullopt, - .layerName = "doubleLayer_" + std::to_string(layerNum++)}; - auto layer = builder.makeLayer(vtxBarrelDetElem.value(), layerElems, layerSpec); - layer->setEnvelope(barrelEnvelope); - - // Force the Barrel onto the z-axis by not using the - // center of gravity for auto-sizing. We do this because - // the VertexBarrel has an odd number of modules, which - // shifts them off-axis when using CoG - layer->setUseCenterOfGravity(false, false, true); - vtxBarrel->addChild(layer); - } - vtxBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure - auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); - auto posVtxEndcapContainer = builder.layers() - .endcap() - .setAxes("XZY") - .setContainer("VertexEndcap") - .setFilter("layer_pos\\d+") - .setEnvelope(vtxEndcapEnvelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - - auto negVtxEndcapContainer = builder.layers() - .endcap() - .setAxes("XZY") - .setContainer("VertexEndcap") - .setFilter("layer_neg\\d+") - .setEnvelope(vtxEndcapEnvelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - - auto vertex = std::make_shared("Vertex", AxisZ); - vertex->addChild(vtxBarrel); - vertex->addChild(negVtxEndcapContainer); - vertex->addChild(posVtxEndcapContainer); - - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); + auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vtxBarrel)); + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); auto innerTrackerBarrel = builder.layers() .barrel() .setAxes("XYZ") @@ -323,5 +363,21 @@ namespace FCCee { innerTracker.addChild(innerTrackerPosEndcap); }); } - } // namespace ILD_FCCee + } // namespace ILD_FCCee_v01 + + namespace ILD_FCCee_v02 { + void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder) { + using namespace Acts::UnitLiterals; + using enum Acts::AxisDirection; + + auto& outer = root.addCylinderContainer(detName, AxisR); + + Blueprints::addCylindricalBeampipe(outer); + auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); + auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vtxBarrel)); + + outer.addCylinderContainer("Vertex", AxisR, [&](auto& innerTracker) { innerTracker.addChild(vertex); }); + } + } // namespace ILD_FCCee_v02 } // namespace FCCee diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h index e9328980..974cc191 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h @@ -11,12 +11,6 @@ namespace Acts::Experimental { class Blueprint; } // namespace Acts::Experimental -namespace Blueprints { - using namespace Acts::UnitLiterals; - void addCylindricalBeampipe(Acts::Experimental::ContainerBlueprintNode& root, double rMax = 10_mm, - double halfZ = 1000_mm); -} // namespace Blueprints - namespace MuColl { namespace MAIA_v0 { void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, @@ -25,11 +19,15 @@ namespace MuColl { } // namespace MuColl namespace FCCee { - namespace ILD_FCCee { + namespace ILD_FCCee_v01 { + void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder); + } + + namespace ILD_FCCee_v02 { void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, ActsPlugins::DD4hep::BlueprintBuilder& builder); } - // namespace FCCee } // namespace FCCee #endif // K4ACTSTRACKING_DD4HEPBLUEPRINTCONSTRUCTION_H From 96d0271042a56238d3698f725ba4990326a4befe Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 5 Mar 2026 19:58:56 +0100 Subject: [PATCH 33/69] Upstream API change fixes --- .../DD4hepBlueprintConstruction.cpp | 89 +++++++++---------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 206886fd..2ebc8660 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -136,18 +136,18 @@ namespace Blueprints { auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); auto posVtxEndcapContainer = builder.layers() .endcap() - .setAxes("XZY") + .setSensorAxes("XZY") .setContainer(containerName) - .setFilter(posLayerPattern) + .setLayerFilter(posLayerPattern) .setEnvelope(vtxEndcapEnvelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); auto negVtxEndcapContainer = builder.layers() .endcap() - .setAxes("XZY") + .setSensorAxes("XZY") .setContainer(containerName) - .setFilter(negLayerPattern) + .setLayerFilter(negLayerPattern) .setEnvelope(vtxEndcapEnvelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); @@ -185,31 +185,30 @@ namespace MuColl { // the innermost two layers are a double layer for which the cylindrical // volumes are overlapping otherwise auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); - auto vertexBarrel = - builder.layers() - .barrel() - .setAxes("ZYX") - .setFilter("layer_\\d") - .setContainer("VertexBarrel") - .setEnvelope(barrelEnvelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .onLayer([&](const dd4hep::DetElement&, std::shared_ptr layer) { - // Force the Barrel onto the z-axis by not using the - // center of gravity for auto-sizing. We do this because - // the VertexBarrel has an odd number of modules, which - // shifts them off-axis when using CoG - layer->setUseCenterOfGravity(false, false, true); - return layer; - }) - .build(); + auto vertexBarrel = builder.layers() + .barrel() + .setSensorAxes("ZYX") + .setLayerFilter("layer_\\d") + .setContainer("VertexBarrel") + .setEnvelope(barrelEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .onLayer([&](const auto&, std::shared_ptr layer) { + // Force the Barrel onto the z-axis by not using the + // center of gravity for auto-sizing. We do this because + // the VertexBarrel has an odd number of modules, which + // shifts them off-axis when using CoG + layer->setUseCenterOfGravity(false, false, true); + return layer; + }) + .build(); auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vertexBarrel)); auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); auto innerInnerBarrel = builder.layers() .barrel() - .setAxes("XYZ") - .setFilter("layer[01]") + .setSensorAxes("XYZ") + .setLayerFilter("layer[01]") .setContainer("InnerTrackerBarrel") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) @@ -218,8 +217,8 @@ namespace MuColl { auto outerInnerBarrel = builder.layers() .barrel() - .setAxes("XYZ") - .setFilter("layer2") + .setSensorAxes("XYZ") + .setLayerFilter("layer2") .setContainer("InnerTrackerBarrel") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) @@ -227,35 +226,35 @@ namespace MuColl { auto innerPosEndcapInner = builder.layers() .endcap() - .setAxes("YXZ") + .setSensorAxes("YXZ") .setContainer("InnerTrackerEndcap") - .setFilter("layer_pos0") + .setLayerFilter("layer_pos0") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); auto outerPosEndcapInner = builder.layers() .endcap() - .setAxes("YXZ") + .setSensorAxes("YXZ") .setContainer("InnerTrackerEndcap") - .setFilter("layer_pos[1-6]") + .setLayerFilter("layer_pos[1-6]") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); auto innerNegEndcapInner = builder.layers() .endcap() - .setAxes("YXZ") + .setSensorAxes("YXZ") .setContainer("InnerTrackerEndcap") - .setFilter("layer_neg0") + .setLayerFilter("layer_neg0") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); auto outerNegEndcapInner = builder.layers() .endcap() - .setAxes("YXZ") + .setSensorAxes("YXZ") .setContainer("InnerTrackerEndcap") - .setFilter("layer_neg[1-6]") + .setLayerFilter("layer_neg[1-6]") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); @@ -282,8 +281,8 @@ namespace MuColl { outer.addCylinderContainer("OuterTracker", AxisZ, [&](auto& outerTracker) { auto barrel = builder.layers() .barrel() - .setAxes("XYZ") - .setFilter("layer\\d") + .setSensorAxes("XYZ") + .setLayerFilter("layer\\d") .setContainer("OuterTrackerBarrel") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) @@ -291,18 +290,18 @@ namespace MuColl { auto negEndcap = builder.layers() .endcap() - .setAxes("YXZ") + .setSensorAxes("YXZ") .setContainer("OuterTrackerEndcap") - .setFilter("layer_neg\\d") + .setLayerFilter("layer_neg\\d") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); auto posEndcap = builder.layers() .endcap() - .setAxes("YXZ") + .setSensorAxes("YXZ") .setContainer("OuterTrackerEndcap") - .setFilter("layer_pos\\d") + .setLayerFilter("layer_pos\\d") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); @@ -330,25 +329,25 @@ namespace FCCee { auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); auto innerTrackerBarrel = builder.layers() .barrel() - .setAxes("XYZ") + .setSensorAxes("XYZ") .setContainer("InnerTrackerBarrel") - .setFilter("layer\\d") + .setLayerFilter("layer\\d") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); auto innerTrackerPosEndcap = builder.layers() .endcap() - .setAxes("YXZ") + .setSensorAxes("YXZ") .setContainer("InnerTrackerEndcap") - .setFilter("layer_pos\\d") + .setLayerFilter("layer_pos\\d") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); auto innerTrackerNegEndcap = builder.layers() .endcap() - .setAxes("YXZ") + .setSensorAxes("YXZ") .setContainer("InnerTrackerEndcap") - .setFilter("layer_neg\\d") + .setLayerFilter("layer_neg\\d") .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .build(); From e169de83cea21f7920e8f4fcbc800919c07ef8bb Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 5 Mar 2026 20:12:53 +0100 Subject: [PATCH 34/69] Use new possibilities to group Layers internally --- .../DD4hepBlueprintConstruction.cpp | 61 +++++++------------ 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 2ebc8660..aee30cbe 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #include #include @@ -67,47 +69,30 @@ namespace Blueprints { const auto vtxBarrelDetElem = builder.findDetElementByName(containerName); const auto vtxBarrelLayerElems = builder.findDetElementByNamePattern(vtxBarrelDetElem.value(), layerRgx); - const auto doubleLayerGrouper = [&](const std::span elements) { - const auto layerElements = elements | std::views::transform([&](const auto e) { - std::smatch match; - const std::string elemName = e.name(); - std::regex_match(elemName, match, layerRgx); - const auto layer = std::stoi(match[1].str()); - return std::make_pair(layer, e); - }); - const auto nLayers = std::ranges::max(layerElements, std::less{}, [](const auto p) { return p.first; }).first; - std::vector> layers((nLayers + 1) / 2); - - for (const auto& [layer, elem] : layerElements) { - layers[layer / 2].emplace_back(elem); - } - - return layers; + const auto doubleLayerName = [&](const auto& e) { + std::smatch match; + const std::string elemName = e.name(); + std::regex_match(elemName, match, layerRgx); + const auto layer = std::stoi(match[1].str()); + // We divide the layer number by 2 and let integer division automatially + // sort that into the correct double layer + return fmt::format("doubleLayer_{}", layer / 2); }; - auto vtxBarrel = std::make_shared("VertexBarrel", - Acts::AxisDirection::AxisR); auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {1_mm, 1_mm}); - - int layerNum = 0; - for (const auto& layerElems : doubleLayerGrouper(vtxBarrelLayerElems)) { - const auto layerSpec = - ActsPlugins::DD4hep::DD4hepBackend::LayerSpec{.axes = ActsPlugins::TGeoAxes("ZYX"), - .layerAxes = std::nullopt, - .layerName = "doubleLayer_" + std::to_string(layerNum++)}; - auto layer = builder.makeLayer(vtxBarrelDetElem.value(), layerElems, layerSpec); - layer->setEnvelope(barrelEnvelope); - - // Force the Barrel onto the z-axis by not using the - // center of gravity for auto-sizing. We do this because - // the VertexBarrel has an odd number of modules, which - // shifts them off-axis when using CoG - layer->setUseCenterOfGravity(false, false, true); - vtxBarrel->addChild(layer); - } - vtxBarrel->setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); - - return vtxBarrel; + return builder.layersFromSensors() + .barrel() + .setEnvelope(barrelEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .setSensorAxes("ZYX") + .setSensors(std::move(vtxBarrelLayerElems)) + .groupBy(doubleLayerName) + .setContainerName(containerName) + .onLayer([&](const auto&, std::shared_ptr layer) { + layer->setUseCenterOfGravity(false, false, true); + return layer; + }) + .build(); } /// Attach the Endcaps to the VertexBarrel after constructing them to create From 59a78c555caf17a10dcbcdf412db76a13177bd47 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 5 Mar 2026 20:20:13 +0100 Subject: [PATCH 35/69] Refactor vertex building --- .../DD4hepBlueprintConstruction.cpp | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index aee30cbe..3563a8d6 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -117,30 +117,29 @@ namespace Blueprints { const std::string& containerName = "VertexEndcap", const std::regex& posLayerPattern = std::regex{"layer_pos\\d+"}, const std::regex& negLayerPattern = std::regex{"layer_neg\\d+"}) { - // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure - auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); - auto posVtxEndcapContainer = builder.layers() - .endcap() - .setSensorAxes("XZY") - .setContainer(containerName) - .setLayerFilter(posLayerPattern) - .setEnvelope(vtxEndcapEnvelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - - auto negVtxEndcapContainer = builder.layers() - .endcap() - .setSensorAxes("XZY") - .setContainer(containerName) - .setLayerFilter(negLayerPattern) - .setEnvelope(vtxEndcapEnvelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - auto vertex = std::make_shared("Vertex", AxisZ); vertex->addChild(vtxBarrel); - vertex->addChild(negVtxEndcapContainer); - vertex->addChild(posVtxEndcapContainer); + + // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure + auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); + builder.layers() + .endcap() + .setSensorAxes("XZY") + .setContainer(containerName) + .setLayerFilter(posLayerPattern) + .setEnvelope(vtxEndcapEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*vertex); + + builder.layers() + .endcap() + .setSensorAxes("XZY") + .setContainer(containerName) + .setLayerFilter(negLayerPattern) + .setEnvelope(vtxEndcapEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*vertex); + return vertex; } From 2dda5d8dc831fac1fb2b66d8759c30f8a1e46160 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 5 Mar 2026 20:26:22 +0100 Subject: [PATCH 36/69] Refactor inner tracker construction for ILD_FCCee_v01 --- .../DD4hepBlueprintConstruction.cpp | 63 +++++++++---------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 3563a8d6..b680bde8 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -310,40 +310,37 @@ namespace FCCee { auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vtxBarrel)); - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - auto innerTrackerBarrel = builder.layers() - .barrel() - .setSensorAxes("XYZ") - .setContainer("InnerTrackerBarrel") - .setLayerFilter("layer\\d") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - auto innerTrackerPosEndcap = builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_pos\\d") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - auto innerTrackerNegEndcap = builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_neg\\d") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - - auto trackerBarrel = std::make_shared("TrackerBarrel", AxisR); - trackerBarrel->addChild(vertex); - trackerBarrel->addChild(innerTrackerBarrel); - + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { - innerTracker.addChild(trackerBarrel); - innerTracker.addChild(innerTrackerNegEndcap); - innerTracker.addChild(innerTrackerPosEndcap); + // First stack the full vertex and the IT Barrel in R + innerTracker.addCylinderContainer("InnerTrackerBarrel", AxisR, [&](auto& innerBarrel) { + innerBarrel.addChild(vertex); + builder.layers() + .barrel() + .setSensorAxes("XYZ") + .setContainer("InnerTrackerBarrel") + .setLayerFilter("layer\\d") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(innerBarrel); + }); + // Then stack the two endcaps in Z + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setLayerFilter("layer_pos\\d") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(innerTracker); + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setLayerFilter("layer_neg\\d") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(innerTracker); }); } } // namespace ILD_FCCee_v01 From d3355e52ad4f047953ecd6b7e6fe8305defcf0c4 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 5 Mar 2026 20:28:23 +0100 Subject: [PATCH 37/69] Refactor outer tracker construction for MAIA_v0 --- .../DD4hepBlueprintConstruction.cpp | 57 +++++++++---------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index b680bde8..6fd2f1cf 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -263,39 +263,34 @@ namespace MuColl { // The OuterTracker is a bit more simple because it has the barrel and endcap // more clearly separated outer.addCylinderContainer("OuterTracker", AxisZ, [&](auto& outerTracker) { - auto barrel = builder.layers() - .barrel() - .setSensorAxes("XYZ") - .setLayerFilter("layer\\d") - .setContainer("OuterTrackerBarrel") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - - auto negEndcap = builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("OuterTrackerEndcap") - .setLayerFilter("layer_neg\\d") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - - auto posEndcap = builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("OuterTrackerEndcap") - .setLayerFilter("layer_pos\\d") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - - outerTracker.addChild(barrel); - outerTracker.addChild(negEndcap); - outerTracker.addChild(posEndcap); + builder.layers() + .barrel() + .setSensorAxes("XYZ") + .setLayerFilter("layer\\d") + .setContainer("OuterTrackerBarrel") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(outerTracker); + + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("OuterTrackerEndcap") + .setLayerFilter("layer_neg\\d") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(outerTracker); + + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("OuterTrackerEndcap") + .setLayerFilter("layer_pos\\d") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(outerTracker); }); } - } // namespace MAIA_v0 } // namespace MuColl From 6c94bc013a04242b2877dc251d51f5355fd5035d Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 5 Mar 2026 20:38:25 +0100 Subject: [PATCH 38/69] Refactor inner tracker construction for MAIA_v0 --- .../DD4hepBlueprintConstruction.cpp | 126 ++++++++---------- 1 file changed, 56 insertions(+), 70 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 6fd2f1cf..fee7b34f 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -150,21 +150,8 @@ namespace MuColl { void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, ActsPlugins::DD4hep::BlueprintBuilder& builder) { auto& outer = root.addCylinderContainer(detName, AxisR); - Blueprints::addCylindricalBeampipe(outer); - // We have to create the inner tracker in several steps, because the inner - // most endcap layer protrudes into the envelope that is created by the - // outermost barrel layer. That creates an overlap in z while stacking. Hence, - // we build it in steps grouping the innermost two layers of the barrel and - // the innermost layer of the endcap into an "inner" inner tracker (stacking - // them along z), we then stack the last barrel layer along r, before stacking - // the remaining endcap layers along z. Additionally, we have to first put the - // whole vertex detector inside the two innermost InnerTrackerBarrel layers - // because the outermost vertex layer extends further in r, than the innermost - // border of the InnerTracker endcaps. Hence, we also need to stack them in - // the correct order. - // NOTE: Need to set rather small padding here for the R-direction, because // the innermost two layers are a double layer for which the cylindrical // volumes are overlapping otherwise @@ -185,9 +172,19 @@ namespace MuColl { return layer; }) .build(); - auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vertexBarrel)); + // We have to create the inner tracker in several steps, because the inner + // most endcap layer protrudes into the envelope that is created by the + // outermost barrel layer. That creates an overlap in z while stacking. Hence, + // we build it in steps grouping the innermost two layers of the barrel and + // the innermost layer of the endcap into an "inner" inner tracker (stacking + // them along z), we then stack the last barrel layer along r, before stacking + // the remaining endcap layers along z. Additionally, we have to first put the + // whole vertex detector inside the two innermost InnerTrackerBarrel layers + // because the outermost vertex layer extends further in r, than the innermost + // border of the InnerTracker endcaps. Hence, we also need to stack them in + // the correct order. auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); auto innerInnerBarrel = builder.layers() .barrel() @@ -199,65 +196,56 @@ namespace MuColl { .build(); innerInnerBarrel->addChild(vertex); - auto outerInnerBarrel = builder.layers() - .barrel() - .setSensorAxes("XYZ") - .setLayerFilter("layer2") - .setContainer("InnerTrackerBarrel") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - - auto innerPosEndcapInner = builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_pos0") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - - auto outerPosEndcapInner = builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_pos[1-6]") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - - auto innerNegEndcapInner = builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_neg0") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - auto outerNegEndcapInner = builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_neg[1-6]") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - auto innerInnerTracker = std::make_shared("InnerInnerTracker", AxisZ); - innerInnerTracker->addChild(innerPosEndcapInner); - innerInnerTracker->addChild(innerNegEndcapInner); innerInnerTracker->addChild(innerInnerBarrel); - - auto innerTrackerBarrel = - std::make_shared("InnerTrackerBarrel", AxisR); - innerTrackerBarrel->addChild(innerInnerTracker); - innerTrackerBarrel->addChild(outerInnerBarrel); + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setLayerFilter("layer_pos0") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerInnerTracker); + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setLayerFilter("layer_neg0") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerInnerTracker); outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { - innerTracker.addChild(innerTrackerBarrel); - innerTracker.addChild(outerNegEndcapInner); - innerTracker.addChild(outerPosEndcapInner); + // First build up everything so far and the rest of the IT barrel + innerTracker.addCylinderContainer("InnerTrackerBarrel", AxisR, [&](auto& innerBarrel) { + innerBarrel.addChild(innerInnerTracker); + builder.layers() + .barrel() + .setSensorAxes("XYZ") + .setLayerFilter("layer2") + .setContainer("InnerTrackerBarrel") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(innerBarrel); + }); + // Then add the (rest of the) two endcaps + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setLayerFilter("layer_pos[1-6]") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(innerTracker); + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setLayerFilter("layer_neg[1-6]") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(innerTracker); }); // The OuterTracker is a bit more simple because it has the barrel and endcap @@ -271,7 +259,6 @@ namespace MuColl { .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .addTo(outerTracker); - builder.layers() .endcap() .setSensorAxes("YXZ") @@ -280,7 +267,6 @@ namespace MuColl { .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .addTo(outerTracker); - builder.layers() .endcap() .setSensorAxes("YXZ") From c9d2b7ccee432516474c85540114e11b3b76cf02 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 6 Mar 2026 15:56:32 +0100 Subject: [PATCH 39/69] Finalize conversion of ILD_FCCee_v02 --- .../DD4hepBlueprintConstruction.cpp | 93 ++++++++++++++++--- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index fee7b34f..ac8196f2 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -12,12 +13,12 @@ #include -#include -#include +#include #include using Acts::Experimental::ContainerBlueprintNode; using Acts::Experimental::CylinderContainerBlueprintNode; +using Acts::Experimental::LayerBlueprintNode; using namespace Acts::UnitLiterals; using enum Acts::AxisDirection; @@ -41,6 +42,16 @@ namespace Blueprints { node.setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First); } + /// Layer customizer function to force the Barrel onto the z-axis by not using + /// the center of gravity for auto-sizing. This is useful for cases where the + /// detectors have has an odd number of modules, which shifts them off the + /// z-axis with the default sizing + std::shared_ptr unsetXYCoG(const std::optional&, + std::shared_ptr layer) { + layer->setUseCenterOfGravity(false, false, true); + return layer; + } + /// Make the Acts volumes for a VertexBarrel detector where the layers are /// grouped into double layers such that these double layers end up in one /// volume in the Acts geometry. @@ -88,10 +99,7 @@ namespace Blueprints { .setSensors(std::move(vtxBarrelLayerElems)) .groupBy(doubleLayerName) .setContainerName(containerName) - .onLayer([&](const auto&, std::shared_ptr layer) { - layer->setUseCenterOfGravity(false, false, true); - return layer; - }) + .onLayer(unsetXYCoG) .build(); } @@ -163,14 +171,7 @@ namespace MuColl { .setContainer("VertexBarrel") .setEnvelope(barrelEnvelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .onLayer([&](const auto&, std::shared_ptr layer) { - // Force the Barrel onto the z-axis by not using the - // center of gravity for auto-sizing. We do this because - // the VertexBarrel has an odd number of modules, which - // shifts them off-axis when using CoG - layer->setUseCenterOfGravity(false, false, true); - return layer; - }) + .onLayer(Blueprints::unsetXYCoG) .build(); auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vertexBarrel)); @@ -338,7 +339,69 @@ namespace FCCee { auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vtxBarrel)); - outer.addCylinderContainer("Vertex", AxisR, [&](auto& innerTracker) { innerTracker.addChild(vertex); }); + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + auto innerInnerBarrel = builder.layers() + .barrel() + .setSensorAxes("XYZ") + .setLayerFilter("layer[01]") + .setContainer("InnerTrackerBarrel") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .onLayer(Blueprints::unsetXYCoG) + .build(); + innerInnerBarrel->addChild(vertex); + + auto innerInnerTracker = std::make_shared("InnerInnerTracker", AxisZ); + innerInnerTracker->addChild(innerInnerBarrel); + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setLayerFilter("layer_pos0") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerInnerTracker); + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setLayerFilter("layer_neg0") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerInnerTracker); + + outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { + innerTracker.addCylinderContainer("InnerTrackerBarrel", AxisR, [&](auto& innerBarrel) { + innerBarrel.addChild(innerInnerTracker); + builder.layers() + .barrel() + .setSensorAxes("XYZ") + .setContainer("InnerTrackerBarrel") + .setLayerFilter("layer2") + .setEnvelope(envelope) + .onLayer(Blueprints::unsetXYCoG) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(innerBarrel); + }); + // Then add the (rest of the) two endcaps + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setLayerFilter("layer_pos[1-5]") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(innerTracker); + builder.layers() + .endcap() + .setSensorAxes("YXZ") + .setContainer("InnerTrackerEndcap") + .setLayerFilter("layer_neg[1-5]") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(innerTracker); + }); } + } // namespace ILD_FCCee_v02 } // namespace FCCee From f3f72bdadf587fc664ff9c83bfc422ae922acb27 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 6 Mar 2026 18:47:13 +0100 Subject: [PATCH 40/69] Lift out more common functionality into blueprint functions --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 5 +- .../DD4hepBlueprintConstruction.cpp | 448 ++++++++++-------- .../components/DD4hepBlueprintConstruction.h | 6 + 3 files changed, 258 insertions(+), 201 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index a16fdcf7..5786f44c 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -58,8 +58,11 @@ DECLARE_COMPONENT(ActsGeoSvc) ActsGeoSvc::ActsGeoSvc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) { m_bluePrintPopulationFuncs = {{"MAIA_v0", MuColl::MAIA_v0::populateBlueprint}, + {"MuSIC_v2", MuColl::MAIA_v0::populateBlueprint}, {"ILD_FCCee_v01", FCCee::ILD_FCCee_v01::populateBlueprint}, - {"ILD_FCCee_v02", FCCee::ILD_FCCee_v02::populateBlueprint}}; + {"ILD_FCCee_v02", FCCee::ILD_FCCee_v02::populateBlueprint}, + {"CLD_o2_v07", FCCee::CLD_o2_v07::populateBlueprint}, + {"CLD_o2_v08", FCCee::CLD_o2_v07::populateBlueprint}}; } StatusCode ActsGeoSvc::initialize() { diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index ac8196f2..c51273bd 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -20,6 +20,8 @@ using Acts::Experimental::ContainerBlueprintNode; using Acts::Experimental::CylinderContainerBlueprintNode; using Acts::Experimental::LayerBlueprintNode; +using AxisDefinition = ActsPlugins::DD4hep::BlueprintBuilder::AxisDefinition; + using namespace Acts::UnitLiterals; using enum Acts::AxisDirection; @@ -151,6 +153,226 @@ namespace Blueprints { return vertex; } + /// A simple struct to contain the configuration for building a regular + /// detector where the barrel and the endcaps can be cleanly stacked along the + /// z-axis + struct TrackerSpec { + std::string barrelContainer; ///< Name of the DetElement containing the barrel + AxisDefinition barrelAxes; ///< The axes directions for the barrel sensors + std::regex barrelFilter; ///< The layer pattern to filter out barrel layers + std::string endcapContainer; ///< Name of the DetElement containing the endcaps + AxisDefinition endcapAxes; ///< The axes directions for the endcap sensors + std::regex endcapPosFilter; ///< The layer pattern to filter out positive endcap layers + std::regex endcapNegFilter; ///< The layer pattern to filter out negative endcap layers + }; + + const auto OuterTrackerSpec = TrackerSpec{ + .barrelContainer = "OuterTrackerBarrel", + .barrelAxes = "XYZ", + .barrelFilter = std::regex{"layer\\d"}, + .endcapContainer = "OuterTrackerEndcap", + .endcapAxes = "YXZ", + .endcapPosFilter = std::regex{"layer_pos\\d"}, + .endcapNegFilter = std::regex{"layer_neg\\d"}, + }; + + const auto InnerTrackerSpec = TrackerSpec{ + .barrelContainer = "InnerTrackerBarrel", + .barrelAxes = "XYZ", + .barrelFilter = std::regex{"layer\\d"}, + .endcapContainer = "InnerTrackerEndcap", + .endcapAxes = "YXZ", + .endcapPosFilter = std::regex{"layer_pos\\d"}, + .endcapNegFilter = std::regex{"layer_neg\\d"}, + }; + + /// Make the Acts volumes for a regular tracker consisting of a barrel and two + /// endcaps that can be cleanly stacked along the z-axis without nesting. + /// + /// This is the simple case where all endcap layers fit within the z-extent of + /// the barrel, i.e. no endcap layer protrudes into the radial envelope of the + /// barrel layers. The barrel and both endcaps are stacked along z inside a + /// single container node. + /// + /// @param builder The Blueprint builder that drives the construction + /// @param spec The configuration spec defining the barrel and endcap + /// container names, sensor axes, and layer filters + /// @param trackerName The name of the resulting top-level tracker node + /// + /// @returns The tracker blueprint node + std::shared_ptr makeRegularTracker(ActsPlugins::DD4hep::BlueprintBuilder& builder, + const TrackerSpec& spec, + const std::string& trackerName) { + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + auto tracker = std::make_shared(trackerName, AxisZ); + + builder.layers() + .barrel() + .setSensorAxes(spec.barrelAxes) + .setLayerFilter(spec.barrelFilter) + .setContainer(spec.barrelContainer) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*tracker); + builder.layers() + .endcap() + .setSensorAxes(spec.endcapAxes) + .setContainer(spec.endcapContainer) + .setLayerFilter(spec.endcapNegFilter) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*tracker); + builder.layers() + .endcap() + .setSensorAxes(spec.endcapAxes) + .setContainer(spec.endcapContainer) + .setLayerFilter(spec.endcapPosFilter) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*tracker); + + return tracker; + } + + /// A simple struct to hold configuration to build a tracker that is nested + /// such that a simple stacking in z does not work. + struct NestedInnerTrackerSpec { + std::string barrelContainer{"InnerTrackerBarrel"}; ///< Name of the DetElement containing the barrel + AxisDefinition barrelAxes{"XYZ"}; ///< The axes directions for the barrel sensors + std::regex barrelInnerFilter = std::regex{"layer[01]"}; ///< The layer pattern to filter the inner barrel layers + ///< that enclose the vertex detector + std::regex barrelOuterFilter = std::regex{"layer2"}; ///< The layer pattern to filter the outer barrel + ///< layer(s) stacked around the inner barrel + std::string endcapContainer{"InnerTrackerEndcap"}; ///< Name of the DetElement containing the endcaps + AxisDefinition endcapAxes{"YXZ"}; ///< The axes directions for the endcap sensors + std::regex endcapPosInnerFilter = std::regex{"layer_pos0"}; ///< The layer pattern to filter the innermost + ///< positive endcap layers that protrude into + ///< the barrel radial envelope + std::regex endcapPosOuterFilter = std::regex{"layer_pos[1-6]"}; ///< The layer pattern to filter the outer + ///< positive endcap layers + std::regex endcapNegInnerFilter = std::regex{"layer_neg0"}; ///< The layer pattern to filter the innermost + ///< negative endcap layers that protrude into + ///< the barrel radial envelope + std::regex endcapNegOuterFilter = std::regex{"layer_neg[1-6]"}; ///< The layer pattern to filter the outer + ///< negative endcap layers + }; + + /// Make a nested inner tracker that encloses the vertex. + /// + /// Nesting in this case means that at least one of the endcap layers + /// protrudes into the cylinder described by the barrel layers. This makes it + /// necessary to stack the volumes surrounding the layers in the correct order + /// in r and z to avoid overlapping volumes. + /// + /// For this specific case the tracker can only be nested "once" this means + /// that it looks something like the following. + /// + /// b b + /// a a + /// r endcap(Pos|Neg)OuterFilter r + /// r ⌄ ⌄ ⌄ ⌄ ⌄ ⌄ r + /// r | | | ───────────────────── | | | < e + /// e | | | ───────────────────── | | | < l + /// l > | | | | | ───────── | | | | | O + /// I > | | | | | ───────── | | | | | u + /// n | | | | | | | | | | t + /// n | | | | | VTX | | | | | e + /// e | | | | | | | | | | r + /// r > | | | | | ───────── | | | | | F + /// F > | | | | | ───────── | | | | | i + /// i | | | ───────────────────── | | | < l + /// l | | | ───────────────────── | | | < t + /// t e + /// e ^ ^ ^ ^ r + /// r endcap(Pos|Neg)InnerFilter + /// + /// The labels correspond to the members of the NestedInnerTrackerSpec. + /// + /// @param builder The Blueprint builder that drives the construction + /// @param vertex The vertex detector blueprint node + /// @param spec The spec for defining how the nesting is done specifically + /// for this detector + /// + /// @returns The inner tracker blueprint node + std::shared_ptr makeNestedInnerTracker( + ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& vertex, + const NestedInnerTrackerSpec& spec = NestedInnerTrackerSpec{}) { + // We have to create the inner tracker in several steps, because the inner + // most endcap layer protrudes into the envelope that is created by the + // outermost barrel layer. That creates an overlap in z while stacking. + // Hence, we build it in steps grouping the innermost two layers of the + // barrel and the innermost layer of the endcap into an "inner" inner + // tracker (stacking them along z), we then stack the last barrel layer + // along r, before stacking the remaining endcap layers along z. + // Additionally, we have to first put the whole vertex detector inside the + // two innermost InnerTrackerBarrel layers because the outermost vertex + // layer extends further in r, than the innermost border of the InnerTracker + // endcaps. Hence, we also need to stack them in the correct order. + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + auto innerInnerBarrel = builder.layers() + .barrel() + .setSensorAxes(spec.barrelAxes) + .setLayerFilter(spec.barrelInnerFilter) + .setContainer(spec.barrelContainer) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .onLayer(Blueprints::unsetXYCoG) + .build(); + innerInnerBarrel->addChild(vertex); + + auto innerInnerTracker = std::make_shared("InnerInnerTracker", AxisZ); + innerInnerTracker->addChild(innerInnerBarrel); + builder.layers() + .endcap() + .setSensorAxes(spec.endcapAxes) + .setContainer(spec.endcapContainer) + .setLayerFilter(spec.endcapPosInnerFilter) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerInnerTracker); + builder.layers() + .endcap() + .setSensorAxes(spec.endcapAxes) + .setContainer(spec.endcapContainer) + .setLayerFilter(spec.endcapNegInnerFilter) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerInnerTracker); + + auto innerTracker = std::make_shared("InnerTracker", AxisZ); + innerTracker->addCylinderContainer("InnerTrackerBarrel", AxisR, [&](auto& innerBarrel) { + innerBarrel.addChild(innerInnerTracker); + builder.layers() + .barrel() + .setSensorAxes(spec.barrelAxes) + .setContainer(spec.barrelContainer) + .setLayerFilter(spec.barrelOuterFilter) + .setEnvelope(envelope) + .onLayer(Blueprints::unsetXYCoG) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(innerBarrel); + }); + // Then add the (rest of the) two endcaps + builder.layers() + .endcap() + .setSensorAxes(spec.endcapAxes) + .setContainer(spec.endcapContainer) + .setLayerFilter(spec.endcapPosOuterFilter) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerTracker); + builder.layers() + .endcap() + .setSensorAxes(spec.endcapAxes) + .setContainer(spec.endcapContainer) + .setLayerFilter(spec.endcapNegOuterFilter) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerTracker); + + return innerTracker; + } + } // namespace Blueprints namespace MuColl { @@ -175,108 +397,11 @@ namespace MuColl { .build(); auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vertexBarrel)); - // We have to create the inner tracker in several steps, because the inner - // most endcap layer protrudes into the envelope that is created by the - // outermost barrel layer. That creates an overlap in z while stacking. Hence, - // we build it in steps grouping the innermost two layers of the barrel and - // the innermost layer of the endcap into an "inner" inner tracker (stacking - // them along z), we then stack the last barrel layer along r, before stacking - // the remaining endcap layers along z. Additionally, we have to first put the - // whole vertex detector inside the two innermost InnerTrackerBarrel layers - // because the outermost vertex layer extends further in r, than the innermost - // border of the InnerTracker endcaps. Hence, we also need to stack them in - // the correct order. - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - auto innerInnerBarrel = builder.layers() - .barrel() - .setSensorAxes("XYZ") - .setLayerFilter("layer[01]") - .setContainer("InnerTrackerBarrel") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .build(); - innerInnerBarrel->addChild(vertex); - - auto innerInnerTracker = - std::make_shared("InnerInnerTracker", AxisZ); - innerInnerTracker->addChild(innerInnerBarrel); - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_pos0") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerInnerTracker); - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_neg0") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerInnerTracker); - - outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { - // First build up everything so far and the rest of the IT barrel - innerTracker.addCylinderContainer("InnerTrackerBarrel", AxisR, [&](auto& innerBarrel) { - innerBarrel.addChild(innerInnerTracker); - builder.layers() - .barrel() - .setSensorAxes("XYZ") - .setLayerFilter("layer2") - .setContainer("InnerTrackerBarrel") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(innerBarrel); - }); - // Then add the (rest of the) two endcaps - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_pos[1-6]") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(innerTracker); - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_neg[1-6]") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(innerTracker); - }); - - // The OuterTracker is a bit more simple because it has the barrel and endcap - // more clearly separated - outer.addCylinderContainer("OuterTracker", AxisZ, [&](auto& outerTracker) { - builder.layers() - .barrel() - .setSensorAxes("XYZ") - .setLayerFilter("layer\\d") - .setContainer("OuterTrackerBarrel") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(outerTracker); - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("OuterTrackerEndcap") - .setLayerFilter("layer_neg\\d") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(outerTracker); - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("OuterTrackerEndcap") - .setLayerFilter("layer_pos\\d") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(outerTracker); - }); + auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex)); + outer.addChild(innerTracker); + + auto outerTracker = Blueprints::makeRegularTracker(builder, Blueprints::OuterTrackerSpec, "OuterTracker"); + outer.addChild(outerTracker); } } // namespace MAIA_v0 } // namespace MuColl @@ -291,117 +416,40 @@ namespace FCCee { auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vtxBarrel)); + outer.addChild(vertex); - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { - // First stack the full vertex and the IT Barrel in R - innerTracker.addCylinderContainer("InnerTrackerBarrel", AxisR, [&](auto& innerBarrel) { - innerBarrel.addChild(vertex); - builder.layers() - .barrel() - .setSensorAxes("XYZ") - .setContainer("InnerTrackerBarrel") - .setLayerFilter("layer\\d") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(innerBarrel); - }); - // Then stack the two endcaps in Z - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_pos\\d") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(innerTracker); - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_neg\\d") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(innerTracker); - }); + auto innerTracker = Blueprints::makeRegularTracker(builder, Blueprints::InnerTrackerSpec, "InnerTracker"); + outer.addChild(innerTracker); } } // namespace ILD_FCCee_v01 namespace ILD_FCCee_v02 { void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, ActsPlugins::DD4hep::BlueprintBuilder& builder) { - using namespace Acts::UnitLiterals; - using enum Acts::AxisDirection; - auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vtxBarrel)); - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - auto innerInnerBarrel = builder.layers() - .barrel() - .setSensorAxes("XYZ") - .setLayerFilter("layer[01]") - .setContainer("InnerTrackerBarrel") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .onLayer(Blueprints::unsetXYCoG) - .build(); - innerInnerBarrel->addChild(vertex); - - auto innerInnerTracker = std::make_shared("InnerInnerTracker", AxisZ); - innerInnerTracker->addChild(innerInnerBarrel); - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_pos0") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerInnerTracker); - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_neg0") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerInnerTracker); - - outer.addCylinderContainer("InnerTracker", AxisZ, [&](auto& innerTracker) { - innerTracker.addCylinderContainer("InnerTrackerBarrel", AxisR, [&](auto& innerBarrel) { - innerBarrel.addChild(innerInnerTracker); - builder.layers() - .barrel() - .setSensorAxes("XYZ") - .setContainer("InnerTrackerBarrel") - .setLayerFilter("layer2") - .setEnvelope(envelope) - .onLayer(Blueprints::unsetXYCoG) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(innerBarrel); - }); - // Then add the (rest of the) two endcaps - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_pos[1-5]") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(innerTracker); - builder.layers() - .endcap() - .setSensorAxes("YXZ") - .setContainer("InnerTrackerEndcap") - .setLayerFilter("layer_neg[1-5]") - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(innerTracker); - }); + auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex)); + outer.addChild(innerTracker); } - } // namespace ILD_FCCee_v02 + + namespace CLD_o2_v07 { + void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder) { + auto& outer = root.addCylinderContainer(detName, AxisR); + Blueprints::addCylindricalBeampipe(outer); + auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); + auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vtxBarrel)); + + auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex)); + outer.addChild(innerTracker); + + auto outerTracker = Blueprints::makeRegularTracker(builder, Blueprints::OuterTrackerSpec, "OuterTracker"); + outer.addChild(outerTracker); + } + } // namespace CLD_o2_v07 } // namespace FCCee diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h index 974cc191..4cd283c3 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h @@ -28,6 +28,12 @@ namespace FCCee { void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, ActsPlugins::DD4hep::BlueprintBuilder& builder); } + + namespace CLD_o2_v07 { + void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder); + } + } // namespace FCCee #endif // K4ACTSTRACKING_DD4HEPBLUEPRINTCONSTRUCTION_H From 76f31231ccb12f099500f60f4999bb521f720204 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 6 Mar 2026 20:04:48 +0100 Subject: [PATCH 41/69] Add license headers to new files --- k4ActsTracking/examples/plot_steps.py | 18 ++++++++++++++++++ k4ActsTracking/examples/visActsGEo.py | 18 ++++++++++++++++++ k4ActsTracking/src/components/ActsGeoSvc.h | 18 ++++++++++++++++++ .../src/components/ActsTestPropagator.cpp | 18 ++++++++++++++++++ .../components/DD4hepBlueprintConstruction.cpp | 18 ++++++++++++++++++ .../components/DD4hepBlueprintConstruction.h | 18 ++++++++++++++++++ 6 files changed, 108 insertions(+) diff --git a/k4ActsTracking/examples/plot_steps.py b/k4ActsTracking/examples/plot_steps.py index 3d4d564c..4d6c7d85 100644 --- a/k4ActsTracking/examples/plot_steps.py +++ b/k4ActsTracking/examples/plot_steps.py @@ -1,4 +1,22 @@ #!/usr/bin/env python3 +# +# Copyright (c) 2014-2024 Key4hep-Project. +# +# This file is part of Key4hep. +# See https://key4hep.github.io/key4hep-doc/ for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# import ROOT import argparse diff --git a/k4ActsTracking/examples/visActsGEo.py b/k4ActsTracking/examples/visActsGEo.py index 79fac28a..2fde615f 100644 --- a/k4ActsTracking/examples/visActsGEo.py +++ b/k4ActsTracking/examples/visActsGEo.py @@ -1,4 +1,22 @@ #!/usr/bin/env python3 +# +# Copyright (c) 2014-2024 Key4hep-Project. +# +# This file is part of Key4hep. +# See https://key4hep.github.io/key4hep-doc/ for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# from Gaudi.Configuration import VERBOSE, DEBUG diff --git a/k4ActsTracking/src/components/ActsGeoSvc.h b/k4ActsTracking/src/components/ActsGeoSvc.h index 2434411f..f0d112e5 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.h +++ b/k4ActsTracking/src/components/ActsGeoSvc.h @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2014-2024 Key4hep-Project. + * + * This file is part of Key4hep. + * See https://key4hep.github.io/key4hep-doc/ for further info. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #ifndef K4ACTSTRACKING_ACTSGEOSVC_H #define K4ACTSTRACKING_ACTSGEOSVC_H diff --git a/k4ActsTracking/src/components/ActsTestPropagator.cpp b/k4ActsTracking/src/components/ActsTestPropagator.cpp index 30f2a0f2..c00e0244 100644 --- a/k4ActsTracking/src/components/ActsTestPropagator.cpp +++ b/k4ActsTracking/src/components/ActsTestPropagator.cpp @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2014-2024 Key4hep-Project. + * + * This file is part of Key4hep. + * See https://key4hep.github.io/key4hep-doc/ for further info. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "k4ActsTracking/ActsGaudiLogger.h" #include "k4ActsTracking/IActsGeoSvc.h" diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index c51273bd..d0be24a0 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2014-2024 Key4hep-Project. + * + * This file is part of Key4hep. + * See https://key4hep.github.io/key4hep-doc/ for further info. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "DD4hepBlueprintConstruction.h" #include diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h index 4cd283c3..c7224cb8 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2014-2024 Key4hep-Project. + * + * This file is part of Key4hep. + * See https://key4hep.github.io/key4hep-doc/ for further info. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #ifndef K4ACTSTRACKING_DD4HEPBLUEPRINTCONSTRUCTION_H #define K4ACTSTRACKING_DD4HEPBLUEPRINTCONSTRUCTION_H From d49d980d91049097e04a0d5d60e664ac75f247ac Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 9 Mar 2026 21:52:32 +0100 Subject: [PATCH 42/69] Interface fixes for upstream planar layer changes --- env.sh | 10 + k4ActsTracking/CMakeLists.txt | 4 + .../k4ActsTracking/ITrackerMappingSvc.h | 23 ++ .../src/components/ActsGeoGen3PlaneSvc.cpp | 145 +++++++++++ .../src/components/ActsGeoGen3PlaneSvc.h | 51 ++++ .../DD4hepBlueprintConstruction.cpp | 9 +- .../components/DumpSimTrackerHitCellIDAlg.cpp | 156 ++++++++++++ .../components/DumpSimTrackerHitCellIDAlg.h | 40 +++ .../components/PropagateToHitSurfaceAlg.cpp | 239 ++++++++++++++++++ .../src/components/PropagateToHitSurfaceAlg.h | 40 +++ .../src/components/TrackerMappingSvc.cpp | 181 +++++++++++++ .../src/components/TrackerMappingSvc.h | 71 ++++++ test/options/checkMapping.py | 59 +++++ test/options/visActsGEo.py | 36 +++ test/options/visActsPlaneGeo.py | 31 +++ 15 files changed, 1091 insertions(+), 4 deletions(-) create mode 100644 env.sh create mode 100644 k4ActsTracking/include/k4ActsTracking/ITrackerMappingSvc.h create mode 100644 k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.cpp create mode 100644 k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.h create mode 100644 k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.cpp create mode 100644 k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.h create mode 100644 k4ActsTracking/src/components/PropagateToHitSurfaceAlg.cpp create mode 100644 k4ActsTracking/src/components/PropagateToHitSurfaceAlg.h create mode 100644 k4ActsTracking/src/components/TrackerMappingSvc.cpp create mode 100644 k4ActsTracking/src/components/TrackerMappingSvc.h create mode 100644 test/options/checkMapping.py create mode 100644 test/options/visActsGEo.py create mode 100644 test/options/visActsPlaneGeo.py diff --git a/env.sh b/env.sh new file mode 100644 index 00000000..8ad2c664 --- /dev/null +++ b/env.sh @@ -0,0 +1,10 @@ +#source /cvmfs/sw-nightlies.hsf.org/key4hep/setup.sh -r 2025-12-04 #Outdated. Boost 1.88 +source /cvmfs/sw-nightlies.hsf.org/key4hep/setup.sh -r 2026-02-08 +# LUXE compact +source /data/dust/user/wangyufe/luxegeo/install/bin/thisluxegeo.sh +export LD_LIBRARY_PATH=/data/dust/user/wangyufe/luxegeo/install/lib:$LD_LIBRARY_PATH +export DD4hep_LIBRARY_PATH=/data/dust/user/wangyufe/luxegeo/install/lib64:$DD4hep_LIBRARY_PATH + +export LD_LIBRARY_PATH=/data/dust/user/wangyufe/playground/k4ActsTracking/install/lib:$LD_LIBRARY_PATH +export GAUDI_PLUGIN_PATH=/data/dust/user/wangyufe/playground/k4ActsTracking/install/lib:$GAUDI_PLUGIN_PATH +export PYTHONPATH=/data/dust/user/wangyufe/playground/k4ActsTracking/install/python:$PYTHONPATH diff --git a/k4ActsTracking/CMakeLists.txt b/k4ActsTracking/CMakeLists.txt index 9bb0f90a..09d8195b 100644 --- a/k4ActsTracking/CMakeLists.txt +++ b/k4ActsTracking/CMakeLists.txt @@ -43,6 +43,10 @@ set(_plugin_sources src/components/TrackTruthAlg.cxx src/components/ActsTestPropagator.cpp src/components/DD4hepBlueprintConstruction.cpp + src/components/ActsGeoGen3PlaneSvc.cpp + src/components/TrackerMappingSvc.cpp + src/components/DumpSimTrackerHitCellIDAlg.cpp + src/components/PropagateToHitSurfaceAlg.cpp ) gaudi_add_module(k4ActsTrackingPlugins diff --git a/k4ActsTracking/include/k4ActsTracking/ITrackerMappingSvc.h b/k4ActsTracking/include/k4ActsTracking/ITrackerMappingSvc.h new file mode 100644 index 00000000..5d70c4e5 --- /dev/null +++ b/k4ActsTracking/include/k4ActsTracking/ITrackerMappingSvc.h @@ -0,0 +1,23 @@ +// Service interface: CellID (DD4hep VolumeID) -> Acts::Surface mapping + +#pragma once + +#include + +#include + +namespace Acts { +class Surface; +} + +class GAUDI_API ITrackerMappingSvc : virtual public IInterface { +public: + DeclareInterfaceID(ITrackerMappingSvc, 1, 0); + + virtual const Acts::Surface* surface(std::uint64_t cellID) const = 0; + virtual bool hasSurface(std::uint64_t cellID) const = 0; + virtual std::size_t size() const = 0; + +protected: + ~ITrackerMappingSvc() override = default; +}; diff --git a/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.cpp b/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.cpp new file mode 100644 index 00000000..8e93b5c5 --- /dev/null +++ b/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.cpp @@ -0,0 +1,145 @@ +#include "ActsGeoGen3PlaneSvc.h" + +#include "k4ActsTracking/ActsGaudiLogger.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +DECLARE_COMPONENT(ActsGeoGen3PlaneSvc) + +ActsGeoGen3PlaneSvc::ActsGeoGen3PlaneSvc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) {} + +StatusCode ActsGeoGen3PlaneSvc::initialize() { + m_geoSvc = Gaudi::svcLocator()->service("GeoSvc"); + K4_GAUDI_CHECK(m_geoSvc); + + + std::array magneticFieldVector = {0, 0, 0}; + std::array position = {0, 0, 0}; + m_geoSvc->getDetector()->field().magneticField(position.data(), magneticFieldVector.data()); + debug() << fmt::format("Retrieved magnetic field at position {}: {}", position, magneticFieldVector) << endmsg; + m_magneticField = std::make_shared( + Acts::Vector3(magneticFieldVector[0] / dd4hep::tesla * Acts::UnitConstants::T, + magneticFieldVector[1] / dd4hep::tesla * Acts::UnitConstants::T, + magneticFieldVector[2] / dd4hep::tesla * Acts::UnitConstants::T)); + + +// --- DD4hep sanity check: print layer world transforms before building planes --- +auto det = m_geoSvc->getDetector(); +auto trackerDE = det->detector("Tracker"); // name in compact + +info() << "DD4hep sanity: Tracker DetElement path/name=" << trackerDE.path() + << " / " << trackerDE.name() << endmsg; + +// helper lambda to print translation +auto dumpDE = [&](const dd4hep::DetElement& de) { + // Copy the matrix (avoid dangling reference to temporary) + auto w = de.nominal().worldTransformation(); // returns TGeoHMatrix in this setup + + const double* tr = w.GetTranslation(); // ROOT public API: returns double[3] + + // Convert dd4hep internal length units to mm explicitly + double x_mm = tr[0] / dd4hep::mm; + double y_mm = tr[1] / dd4hep::mm; + double z_mm = tr[2] / dd4hep::mm; + + info() << fmt::format( + " DE {:<20} path={:<40} world T [mm] = ({:9.3f}, {:9.3f}, {:9.3f})", + de.name(), de.path(), x_mm, y_mm, z_mm) + << endmsg; +}; + +// In your logs, layers are named layer0..layer3 (not id=1..4). We'll dump those. +for (int i = 0; i < 4; ++i) { + std::string lname = fmt::format("layer{}", i); + auto layerDE = trackerDE.child(lname); + if (!layerDE.isValid()) { + warning() << "DD4hep sanity: cannot find child DetElement '" << lname + << "' under Tracker. Available path=" << trackerDE.path() << endmsg; + continue; + } + dumpDE(layerDE); +} +//---------sanity check over-------------- + + auto gaudiLogger = makeActsGaudiLogger(this); + + //info() << fmt::format("Acts::cm: {}, dd4hep::mm: {}", Acts::UnitConstants::cm, dd4hep::mm) << endmsg; + + ActsPlugins::DD4hep::BlueprintBuilder builder{{ + .dd4hepDetector = m_geoSvc->getDetector(), + .lengthScale = Acts::UnitConstants::mm / dd4hep::mm, + }, + gaudiLogger->cloneWithSuffix("|BlpBld")}; + + using Acts::Experimental::Blueprint; + using Acts::Experimental::BlueprintOptions; + using namespace Acts::UnitLiterals; + using enum Acts::AxisDirection; + + Blueprint::Config cfg; + // Padding around subvolumes of the world volume + cfg.envelope[AxisX] = {10_mm, 10_mm}; + cfg.envelope[AxisY] = {10_mm, 10_mm}; + cfg.envelope[AxisZ] = {10_mm, 10_mm}; + Blueprint root{cfg}; + +// -------------------------------------- +root.addCuboidContainer("LUXE", AxisZ, [&](auto& worldBox) { + auto& tracker = worldBox.addCuboidContainer("OuterBox", AxisZ); + auto envelope = Acts::ExtentEnvelope{} + .set(AxisZ, {0.4_mm, 0.4_mm}) + .set(AxisX, {0.4_mm, 0.4_mm}) + .set(AxisY, {0.4_mm, 0.4_mm}); + + auto planes = builder.planeHelper() + .setAxes("XYZ") + .setLayerAxes("XYZ") + .setPattern(m_layerPattern.value()) // e.g. r"layer\d" + .setContainer(m_detElementName.value()) // e.g. "Tracker" + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::Gap) // Gap, Midpoint, First + .build(); + tracker.addChild(planes); + }); +// -------------------------------------- + + BlueprintOptions options; + Acts::GeometryContext gctxt{}; + + debug() << "Constructing tracking geometry" << endmsg; + m_trackingGeo = root.construct(options, gctxt, *gaudiLogger->cloneWithSuffix("|Construct")); + + debug() << "Creating visualiztion" << endmsg; + Acts::ObjVisualization3D vis{}; + m_trackingGeo->visualize(vis, gctxt); + vis.write("dumped_acts_geo.obj"); + + return StatusCode::SUCCESS; +} diff --git a/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.h b/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.h new file mode 100644 index 00000000..b074f0e1 --- /dev/null +++ b/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.h @@ -0,0 +1,51 @@ +#ifndef K4ACTSTRACKING_ACTSGEOGEN3SVC_H +#define K4ACTSTRACKING_ACTSGEOGEN3SVC_H + +#include "k4ActsTracking/IActsGeoSvc.h" + +#include +#include + +#include "GaudiKernel/Service.h" + +#include +#include + +namespace Acts { + class TrackingGeometry; + class MagneticFieldProvider; +} // namespace Acts + +namespace dd4hep { + class Detector; +} + +class ActsGeoGen3PlaneSvc : public extends { +public: + std::shared_ptr trackingGeometry() const override; + + std::shared_ptr magneticField() const override; + + ActsGeoGen3PlaneSvc(const std::string& name, ISvcLocator* svcLoc); + + ~ActsGeoGen3PlaneSvc() = default; + + StatusCode initialize() override; + + Gaudi::Property m_detElementName{this, "DetElementName", "Tracker", "Name of the DetElement"}; + Gaudi::Property m_layerPattern{this, "LayerPatternExpr", "layer", "Layer pattern match expression"}; + +private: + dd4hep::Detector* m_dd4hepGeo{nullptr}; + SmartIF m_geoSvc; + std::shared_ptr m_trackingGeo{nullptr}; + std::shared_ptr m_magneticField{nullptr}; +}; + +inline std::shared_ptr ActsGeoGen3PlaneSvc::trackingGeometry() const { return m_trackingGeo; } + +inline std::shared_ptr ActsGeoGen3PlaneSvc::magneticField() const { + return m_magneticField; +} + +#endif // K4ACTSTRACKING_ACTSGEOGEN3SVC_H diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index d0be24a0..5eb01070 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -91,9 +91,10 @@ namespace Blueprints { /// double layers /// /// @returns The vertex barrel blueprint node - std::shared_ptr makeDoubleLayerVertexBarrel( - ActsPlugins::DD4hep::BlueprintBuilder& builder, const std::string& containerName = "VertexBarrel", - const std::regex& layerRgx = std::regex{"VertexBarrel_layer(\\d)_ladder\\d+"}) { + std::shared_ptr makeDoubleLayerVertexBarrel(ActsPlugins::DD4hep::BlueprintBuilder& builder, + const std::string& containerName = "VertexBarrel", + const std::regex& layerRgx = std::regex{ + "VertexBarrel_layer(\\d)_ladder\\d+"}) { // Vertex Barrel has a double layer gap of only 1 mm. This makes it // (almost) impossible to fit them into mutually exclusive cylinder shell // volumes. Hence, we make each double layer an Acts layer / volume. @@ -141,7 +142,7 @@ namespace Blueprints { /// DetElement with the @containerName name for the /// negative endcap std::shared_ptr completeVertexWithEndcaps( - ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& vtxBarrel, + ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& vtxBarrel, const std::string& containerName = "VertexEndcap", const std::regex& posLayerPattern = std::regex{"layer_pos\\d+"}, const std::regex& negLayerPattern = std::regex{"layer_neg\\d+"}) { diff --git a/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.cpp b/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.cpp new file mode 100644 index 00000000..a72d8cb4 --- /dev/null +++ b/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.cpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include + +#include +#include "DumpSimTrackerHitCellIDAlg.h" +#include +#include +#include + +#include + +DECLARE_COMPONENT(DumpSimTrackerHitCellIDAlg) + +DumpSimTrackerHitCellIDAlg::DumpSimTrackerHitCellIDAlg(const std::string& name, ISvcLocator* svcLoc) + : Algorithm(name, svcLoc) {} + +StatusCode DumpSimTrackerHitCellIDAlg::initialize() { + K4_GAUDI_CHECK(Algorithm::initialize()); + + // Rebind the DataHandle key to the property (so option file can override) + m_inHits = DataHandle(m_inputColName.value(), + Gaudi::DataHandle::Reader, this); + + // Configure ServiceHandle name (so option file can override) + m_mappingSvc = ServiceHandle(m_mappingSvcName.value(), name()); + + K4_GAUDI_CHECK(m_mappingSvc.retrieve()); + + info() << fmt::format("Initialized. InputCollection='{}', MappingSvc='{}'", + m_inputColName.value(), m_mappingSvcName.value()) + << endmsg; + + return StatusCode::SUCCESS; +} + +StatusCode DumpSimTrackerHitCellIDAlg::execute() { + const auto* hits = m_inHits.get(); + if (hits == nullptr) { + warning() << "Input collection is missing in TES." << endmsg; + return StatusCode::SUCCESS; + } + + info() << fmt::format("Event: SimTrackerHits size={}", hits->size()) << endmsg; + + int nPrint = 0; + for (const auto& h : *hits) { + if (nPrint >= m_maxHits.value()) { + break; + } + + const std::uint64_t cellID = static_cast(h.getCellID()); + const auto* surf = m_mappingSvc->surface(cellID); + + // Print some hit info for sanity + const auto p = h.getPosition(); // edm4hep::Vector3f + const auto m = h.getMomentum(); // edm4hep::Vector3f (at entry) + const double t = h.getTime(); + + if (surf == nullptr) { + warning() << fmt::format(" hit[{}] cellID={} pos=({:.3f},{:.3f},{:.3f}) " + "mom=({:.3f},{:.3f},{:.3f}) t={:.3f} ==> NO SURFACE", + nPrint, cellID, p.x, p.y, p.z, m.x, m.y, m.z, t) + << endmsg; + } else { + // Acts context (stateless for now) + Acts::GeometryContext gctx{}; + + // Convert hit pos/mom to Acts vectors (assume EDM4hep uses mm / GeV in your chain; + // geometry was built with mm scale already, so treat numbers as mm here) + const Acts::Vector3 gpos(p.x, p.y, p.z); + + Acts::Vector3 gdir(m.x, m.y, m.z); + const double dirNorm = gdir.norm(); + if (dirNorm > 0.) { + gdir /= dirNorm; + } else { + // fallback direction + gdir = Acts::Vector3(0., 0., 1.); + } + + // Basic IDs + const auto gid = surf->geometryId(); + + // Surface transform info + const Acts::Transform3& T = surf->transform(gctx); + const Acts::Vector3 center = T.translation(); + + // Surface normal in global frame + const Acts::Vector3 n = surf->normal(gctx, gpos, gdir); + + // Signed distance to the plane along normal + const double signedDist = n.dot(gpos - center); + + auto lres = surf->globalToLocal(gctx, gpos, gdir); //default tolerance + bool gotLocal = lres.ok(); + Acts::Vector2 lpos(0., 0.); + if (gotLocal) { + lpos = *lres; + } + + bool inside = gotLocal ? surf->bounds().inside(lpos) : false; + + // DD4hep DetElement semantic check + std::string dePath = ""; + std::string deName = ""; + std::uint64_t deVid = 0; + + const auto* ade = surf->associatedDetectorElement(); + if (ade != nullptr) { + if (const auto* dd4hepDE = + dynamic_cast(ade)) { + const auto& src = dd4hepDE->sourceElement(); + dePath = src.path(); + deName = src.name(); + deVid = static_cast(src.volumeID()); + } else { + dePath = ""; + deName = ""; + } + } + + // Print summary + info() << fmt::format( + " hit[{}] cellID={} pos=({:.3f},{:.3f},{:.3f}) ==> " + "surface={} geoId=0x{:x} | " + "center=({:.3f},{:.3f},{:.3f}) n=({:.3f},{:.3f},{:.3f}) " + "dist={:.3f}mm | local={} ({:.3f},{:.3f}) inside={} | " + "DE name='{}' path='{}' volID={}", + nPrint, cellID, p.x, p.y, p.z, + (const void*)surf, + static_cast(gid.value()), + center.x(), center.y(), center.z(), + n.x(), n.y(), n.z(), + signedDist, + gotLocal ? "OK" : "FAIL", + lpos.x(), lpos.y(), + inside ? "YES" : "NO", + deName, dePath, deVid) + << endmsg; + } +// } else { +// const auto gid = surf->geometryId(); +// info() << fmt::format(" hit[{}] cellID={} pos=({:.3f},{:.3f},{:.3f}) " +// "==> surface={} geoId=0x{:x}", +// nPrint, cellID, p.x, p.y, p.z, (const void*)surf, +// static_cast(gid.value())) +// << endmsg; +// }// else + + ++nPrint; + } + + return StatusCode::SUCCESS; +} diff --git a/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.h b/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.h new file mode 100644 index 00000000..b61a88e7 --- /dev/null +++ b/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.h @@ -0,0 +1,40 @@ +// a tiny alg for cellID checking +// + +#pragma once + +#include "k4ActsTracking/ITrackerMappingSvc.h" + +#include +#include +#include + +#include + +#include + +#include + +class DumpSimTrackerHitCellIDAlg final : public Algorithm { +public: + DumpSimTrackerHitCellIDAlg(const std::string& name, ISvcLocator* svcLoc); + + StatusCode initialize() override; + StatusCode execute() override; + +private: + /// Input collection name (edm4hep::SimTrackerHitCollection) + Gaudi::Property m_inputColName{this, "InputCollection", "SiHits", + "Input edm4hep::SimTrackerHit collection name"}; + + /// Tracker mapping service name + Gaudi::Property m_mappingSvcName{this, "MappingSvc", "TrackerMappingSvc", + "Name of ITrackerMappingSvc implementation"}; + + /// Max number of hits to print per event + Gaudi::Property m_maxHits{this, "MaxHits", 50, "Maximum hits printed per event"}; + + k4FWCore::DataHandle m_inHits{"SiHits", Gaudi::DataHandle::Reader, this}; + + ServiceHandle m_mappingSvc{this, "TrackerMappingSvc", "TrackerMappingSvc"}; +}; diff --git a/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.cpp b/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.cpp new file mode 100644 index 00000000..07e39590 --- /dev/null +++ b/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.cpp @@ -0,0 +1,239 @@ +#include "PropagateToHitSurfaceAlg.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +DECLARE_COMPONENT(PropagateToHitSurfaceAlg) + +PropagateToHitSurfaceAlg::PropagateToHitSurfaceAlg(const std::string& name, ISvcLocator* svcLoc) + : Gaudi::Algorithm(name, svcLoc) {} + +StatusCode PropagateToHitSurfaceAlg::initialize() { + K4_GAUDI_CHECK(Gaudi::Algorithm::initialize()); + K4_GAUDI_CHECK(m_mappingSvc.retrieve()); + K4_GAUDI_CHECK(m_actsGeoSvc.retrieve()); + +m_inHits = k4FWCore::DataHandle( + m_inputColName.value(), Gaudi::DataHandle::Reader, this); + + + // Acts logger, same pattern as ActsTestPropagator + m_actsLogger = makeActsGaudiLogger(this); + + info() << fmt::format( + "Initialized. Input='{}', MappingSvc='{}', ActsGeoSvc='{}', MaxHits={}, Backstep={} mm, AssumeCharge={}", + m_inputColName.value(), + m_mappingSvc.name(), + m_actsGeoSvc.name(), + m_maxHits.value(), + m_backstepMm.value(), + m_assumeCharge.value()) + << endmsg; + + // Create/overwrite CSV +{ + std::ofstream ofs(m_csvFile.value(), std::ios::out | std::ios::trunc); + ofs << "event,ihit,cellID," + "hit_x,hit_y,hit_z," + "start_x,start_y,start_z," + "end_x,end_y,end_z," + "dist_mm,local_u,local_v,inside,local_ok\n"; +} +info() << fmt::format("CSV output: '{}'", m_csvFile.value()) << endmsg; + // Create/overwrite obj +{ + std::ofstream obj(m_objFile.value(), std::ios::out | std::ios::trunc); + obj << "# PropagateToHitSurfaceAlg segments\n"; + obj << "# v x y z\n"; + obj << "# l i j\n"; +} +info() << fmt::format("OBJ output: '{}'", m_objFile.value()) << endmsg; + + return StatusCode::SUCCESS; +}// init + +StatusCode PropagateToHitSurfaceAlg::execute(const EventContext& ctx) const { + (void)ctx; + + const auto* hits = m_inHits.get(); + + if (hits == nullptr) { + warning() << "Input collection missing." << endmsg; + return StatusCode::SUCCESS; + } + + const auto tg = m_actsGeoSvc->trackingGeometry(); + if (!tg) { + error() << "ActsGeoSvc returned null TrackingGeometry." << endmsg; + return StatusCode::FAILURE; + } + + // Contexts (you can later thread real contexts through here if needed) + Acts::GeometryContext gctx{}; + Acts::MagneticFieldContext bctx{}; + + // --- Build propagator like ActsTestPropagator (EigenStepper + Navigator + Logger) + using Stepper = Acts::EigenStepper<>; + using Navigator = Acts::Navigator; + using Propagator = Acts::Propagator; + + Navigator::Config navCfg{tg}; + navCfg.resolvePassive = false; + navCfg.resolveMaterial = false; + navCfg.resolveSensitive = true; + + Stepper stepper(m_actsGeoSvc->magneticField()); + Navigator navigator(navCfg, m_actsLogger->cloneWithSuffix(":Nav")); + Propagator propagator(std::move(stepper), std::move(navigator), + m_actsLogger->cloneWithSuffix(":Prop")); + + // Minimal actor list is fine; keep EndOfWorldReached as in ActsTestPropagator + using EndOfWorld = Acts::EndOfWorldReached; + using ActorList = Acts::ActorList; + using PropagatorOptions = Propagator::template Options; + + PropagatorOptions options{gctx, bctx}; + + info() << fmt::format("Event: {} size={}", m_inputColName.value(), hits->size()) << endmsg; + std::ofstream csv(m_csvFile.value(), std::ios::out | std::ios::app); + if (!csv) { + warning() << fmt::format("Cannot open CSV file '{}'", m_csvFile.value()) << endmsg; + } + std::ofstream obj(m_objFile.value(), std::ios::out | std::ios::app); + if (!obj) { + warning() << fmt::format("Cannot open OBJ file '{}'", m_objFile.value()) << endmsg; + } + static std::atomic globalVtx{1}; + + int nPrint = 0; + for (const auto& h : *hits) { + if (nPrint >= m_maxHits.value()) break; + + const std::uint64_t cellID = static_cast(h.getCellID()); + const Acts::Surface* surf = m_mappingSvc->surface(cellID); + + const auto p = h.getPosition(); + const auto m = h.getMomentum(); + + Acts::Vector3 hitPos(p.x, p.y, p.z); + + Acts::Vector3 mom(m.x, m.y, m.z); + const double pAbs = mom.norm(); + if (pAbs <= 0.) { + warning() << fmt::format(" hit[{}] cellID={} has zero momentum vector.", nPrint, cellID) << endmsg; + ++nPrint; + continue; + } + Acts::Vector3 dir = mom / pAbs; + + if (surf == nullptr) { + warning() << fmt::format(" hit[{}] cellID={} ==> NO SURFACE", nPrint, cellID) << endmsg; + ++nPrint; + continue; + } + + // Backstep upstream to avoid starting exactly on the surface + const Acts::Vector3 startPos = + hitPos - (m_backstepMm.value() * Acts::UnitConstants::mm) * dir; + + // --- Build start parameters in the same style as ActsTestPropagator + // Momentum is assumed in GeV from edm4hep SimTrackerHit; we just need qOverP numerically. + // If you want "pure straight-line validation", set AssumeCharge=0 -> qOverP=0. + const double qOverP = (m_assumeCharge.value() == 0.0) ? 0.0 : (m_assumeCharge.value() / pAbs); + + Acts::Vector4 startPos4{startPos.x(), startPos.y(), startPos.z(), 0.0}; + + const auto startParams = Acts::BoundTrackParameters::createCurvilinear( + startPos4, dir, qOverP, std::nullopt, Acts::ParticleHypothesis::pion()); + + // --- Propagate to target surface + auto result = propagator.propagate(startParams, *surf, options); + if (!result.ok()) { + warning() << fmt::format(" hit[{}] cellID={} propagate FAILED: {}", nPrint, cellID, + result.error().message()) + << endmsg; + ++nPrint; + continue; + } + + const auto& propRes = result.value(); + + if (!propRes.endParameters) { + warning() << fmt::format(" hit[{}] cellID={} propagate OK but endParameters is empty", nPrint, cellID) + << endmsg; + ++nPrint; + continue; + } + + const auto& endParams = *(propRes.endParameters); + Acts::Vector3 endPos = endParams.position(gctx); + + const double dist = (endPos - hitPos).norm(); + + // Local check + auto glRes = surf->globalToLocal(gctx, endPos, dir); + bool localOk = glRes.ok(); + Acts::Vector2 lpos = localOk ? glRes.value() : Acts::Vector2(0, 0); + + bool inside = false; + if (localOk) { + inside = surf->bounds().inside(lpos); + } + + info() << fmt::format( + " hit[{}] cellID={} hit=({:.3f},{:.3f},{:.3f}) start=({:.3f},{:.3f},{:.3f}) " + "-> end=({:.3f},{:.3f},{:.3f}) | |end-hit|={:.6f} mm | local={} ({:.3f},{:.3f}) inside={}", + nPrint, cellID, + hitPos.x(), hitPos.y(), hitPos.z(), + startPos.x(), startPos.y(), startPos.z(), + endPos.x(), endPos.y(), endPos.z(), + dist / Acts::UnitConstants::mm, + (localOk ? "OK" : "FAIL"), + lpos.x(), lpos.y(), + (inside ? "YES" : "NO")) + << endmsg; + + // csv + obj + if (csv) { + csv << 0 << "," + << nPrint << "," + << cellID << "," + << hitPos.x() << "," << hitPos.y() << "," << hitPos.z() << "," + << startPos.x() << "," << startPos.y() << "," << startPos.z() << "," + << endPos.x() << "," << endPos.y() << "," << endPos.z() << "," + << (dist / Acts::UnitConstants::mm) << "," + << (localOk ? lpos.x() : 0.0) << "," + << (localOk ? lpos.y() : 0.0) << "," + << (inside ? 1 : 0) << "," + << (localOk ? 1 : 0) + << "\n"; + } + if (obj) { + long long v0 = globalVtx.fetch_add(2); + long long v1 = v0 + 1; + + obj << "v " << startPos.x() << " " << startPos.y() << " " << startPos.z() << "\n"; + obj << "v " << endPos.x() << " " << endPos.y() << " " << endPos.z() << "\n"; + obj << "l " << v0 << " " << v1 << "\n"; + } + + + ++nPrint; + } + + return StatusCode::SUCCESS; +} + diff --git a/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.h b/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.h new file mode 100644 index 00000000..9b9e8de5 --- /dev/null +++ b/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +#include + +#include "k4ActsTracking/ActsGaudiLogger.h" +#include "k4ActsTracking/IActsGeoSvc.h" +#include "k4ActsTracking/ITrackerMappingSvc.h" + +#include +#include + +class PropagateToHitSurfaceAlg : public Gaudi::Algorithm { +public: + PropagateToHitSurfaceAlg(const std::string& name, ISvcLocator* svcLoc); + + StatusCode initialize() override; + StatusCode execute(const EventContext& ctx) const override; + +private: + Gaudi::Property m_inputColName{this, "InputCollection", "SiHits"}; + Gaudi::Property m_maxHits{this, "MaxHits", 10}; + Gaudi::Property m_backstepMm{this, "BackstepMm", 1.0}; // in mm + Gaudi::Property m_csvFile{this, "CsvFile", "propagate_hits.csv"}; + Gaudi::Property m_objFile{this, "ObjFile", "propagate_segments.obj"}; + // if no charge in hit + Gaudi::Property m_assumeCharge{this, "AssumeCharge", 0.0}; + + mutable k4FWCore::DataHandle m_inHits{ + "", Gaudi::DataHandle::Reader, this}; + + ServiceHandle m_mappingSvc{this, "MappingSvc", "TrackerMappingSvc"}; + ServiceHandle m_actsGeoSvc{this, "ActsGeoSvc", "ActsGeoPlaneSvc"}; + + std::unique_ptr m_actsLogger{nullptr}; +}; + diff --git a/k4ActsTracking/src/components/TrackerMappingSvc.cpp b/k4ActsTracking/src/components/TrackerMappingSvc.cpp new file mode 100644 index 00000000..37e8d883 --- /dev/null +++ b/k4ActsTracking/src/components/TrackerMappingSvc.cpp @@ -0,0 +1,181 @@ +#include "TrackerMappingSvc.h" + +#include "k4ActsTracking/ActsGaudiLogger.h" + +#include + +#include +#include + +#include + +#include + +DECLARE_COMPONENT(TrackerMappingSvc) + +TrackerMappingSvc::TrackerMappingSvc(const std::string& name, ISvcLocator* svcLoc) + : base_class(name, svcLoc) {} + +StatusCode TrackerMappingSvc::initialize() { + m_actsGeoSvc = service(m_actsGeoSvcName.value()); + K4_GAUDI_CHECK(m_actsGeoSvc); + + // build BitFieldCoder from layout string + try { + m_decoder = std::make_unique(m_idLayout.value()); + // Detect whether x/y exist + try { + (void)m_decoder->get(0ULL, "x"); + m_hasX = true; + } catch (...) { + m_hasX = false; + } + try { + (void)m_decoder->get(0ULL, "y"); + m_hasY = true; + } catch (...) { + m_hasY = false; + } + + info() << fmt::format("TrackerMappingSvc: BitFieldCoder initialized. MaskXY={}, hasX={}, hasY={}, layout='{}'", + m_maskXY.value(), m_hasX, m_hasY, m_idLayout.value()) + << endmsg; + + } catch (const std::exception& e) { + error() << fmt::format("TrackerMappingSvc: failed to construct BitFieldCoder from layout '{}': {}", + m_idLayout.value(), e.what()) + << endmsg; + return StatusCode::FAILURE; + } + + return buildMapping(); +} + +StatusCode TrackerMappingSvc::finalize() { + std::scoped_lock lock{m_mutex}; + info() << fmt::format("Final mapping size={}, visited={}, withADE={}, dd4hepADE={}, inserted={}", + m_cellIDToSurface.size(), m_nSurfacesVisited, m_nWithAssociatedDetElem, + m_nDD4hepDetElem, m_nInserted) + << endmsg; + m_cellIDToSurface.clear(); + return StatusCode::SUCCESS; +} + +std::uint64_t TrackerMappingSvc::normalizeCellID(std::uint64_t cellID) const { + if (!m_maskXY.value() || !m_decoder) { + return cellID; + } + std::uint64_t key = cellID; + + // mask x/y only if present + // BitFieldCoder::set modifies the VolumeID in-place + if (m_hasX) { + m_decoder->set(key, "x", 0); + } + if (m_hasY) { + m_decoder->set(key, "y", 0); + } + + return key; +} + +const Acts::Surface* TrackerMappingSvc::surface(std::uint64_t cellID) const { + std::scoped_lock lock{m_mutex}; + + const auto key = normalizeCellID(cellID); + auto it = m_cellIDToSurface.find(key); + return (it == m_cellIDToSurface.end()) ? nullptr : it->second; +} + +bool TrackerMappingSvc::hasSurface(std::uint64_t cellID) const { + std::scoped_lock lock{m_mutex}; + + const auto key = normalizeCellID(cellID); + return m_cellIDToSurface.find(key) != m_cellIDToSurface.end(); +} + +std::size_t TrackerMappingSvc::size() const { + std::scoped_lock lock{m_mutex}; + return m_cellIDToSurface.size(); +} + +StatusCode TrackerMappingSvc::buildMapping() { + std::scoped_lock lock{m_mutex}; + + m_cellIDToSurface.clear(); + m_nSurfacesVisited = 0; + m_nWithAssociatedDetElem = 0; + m_nDD4hepDetElem = 0; + m_nInserted = 0; + + auto tg = m_actsGeoSvc->trackingGeometry(); + if (!tg) { + error() << "Acts tracking geometry is null. Cannot build CellID->Surface mapping." << endmsg; + return StatusCode::FAILURE; + } + + info() << fmt::format("Building CellID->Surface mapping from TrackingGeometry via visitSurfaces()") + << endmsg; + + tg->visitSurfaces([&](const Acts::Surface* s) { + ++m_nSurfacesVisited; + if (s == nullptr) { + return; + } + + const auto* ade = s->associatedDetectorElement(); + if (ade == nullptr) { + return; + } + ++m_nWithAssociatedDetElem; + + const auto* dd4hepDE = dynamic_cast(ade); + if (dd4hepDE == nullptr) { + return; + } + ++m_nDD4hepDetElem; + + const auto cellID = static_cast(dd4hepDE->sourceElement().volumeID()); + if (cellID == 0) { + return; + } + + auto [it, inserted] = m_cellIDToSurface.emplace(cellID, s); + if (!inserted) { + warning() << fmt::format("Duplicate cellID={} for surface (existing ptr={}, new ptr={})", + cellID, (const void*)it->second, (const void*)s) + << endmsg; + return; + } + ++m_nInserted; + }); + + info() << fmt::format( + "Built CellID->Surface mapping: size={}, visited={}, withADE={}, dd4hepADE={}, inserted={}", + m_cellIDToSurface.size(), m_nSurfacesVisited, m_nWithAssociatedDetElem, + m_nDD4hepDetElem, m_nInserted) + << endmsg; + + if (m_cellIDToSurface.empty()) { + error() << "Mapping table is empty. This usually means surfaces have no DD4hepDetectorElement " + "associated (wrong geometry builder output or visiting wrong surfaces)." + << endmsg; + return StatusCode::FAILURE; + } + + return StatusCode::SUCCESS; +} + +std::uint64_t TrackerMappingSvc::cellIDFromSurface(const Acts::Surface& surface) { + const auto* ade = surface.associatedDetectorElement(); + if (ade == nullptr) { + return 0; + } + + const auto* dd4hepDE = dynamic_cast(ade); + if (dd4hepDE == nullptr) { + return 0; + } + + return static_cast(dd4hepDE->sourceElement().volumeID()); +} diff --git a/k4ActsTracking/src/components/TrackerMappingSvc.h b/k4ActsTracking/src/components/TrackerMappingSvc.h new file mode 100644 index 00000000..f1340a4c --- /dev/null +++ b/k4ActsTracking/src/components/TrackerMappingSvc.h @@ -0,0 +1,71 @@ +// CellID (DD4hep VolumeID) -> Acts::Surface mapping + +#pragma once + +#include "k4ActsTracking/ITrackerMappingSvc.h" +#include "k4ActsTracking/IActsGeoSvc.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "DDSegmentation/BitFieldCoder.h" + +namespace Acts { +class Surface; +} + +class TrackerMappingSvc final : public extends { +public: + TrackerMappingSvc(const std::string& name, ISvcLocator* svcLoc); + + StatusCode initialize() override; + StatusCode finalize() override; + + const Acts::Surface* surface(std::uint64_t cellID) const override; + bool hasSurface(std::uint64_t cellID) const override; + std::size_t size() const override; + +private: + StatusCode buildMapping(); + + static std::uint64_t cellIDFromSurface(const Acts::Surface& surface); + // normalize hit cellID (mask x/y) -> sensor-level key + std::uint64_t normalizeCellID(std::uint64_t cellID) const; + +private: + Gaudi::Property m_actsGeoSvcName{this, "ActsGeoSvc", "ActsGeoPlaneSvc", + "Name of the IActsGeoSvc provider."}; + + // bitfield layout string (same as LUXE XML constant GlobalTrackerReadoutID) + Gaudi::Property m_idLayout{ + this, + "IDLayout", + "system:1,side:1,layer:2,module:1,sensor:5,x:32:-16,y:-16", + "DD4hep BitFieldCoder layout string for masking x/y"}; + // whether to mask x/y before lookup + Gaudi::Property m_maskXY{ + this, "MaskXY", true, "Mask x/y fields before CellID->Surface lookup"}; + + SmartIF m_actsGeoSvc{}; + + std::unordered_map m_cellIDToSurface{}; + + mutable std::mutex m_mutex{}; + + std::size_t m_nSurfacesVisited{0}; + std::size_t m_nWithAssociatedDetElem{0}; + std::size_t m_nDD4hepDetElem{0}; + std::size_t m_nInserted{0}; + + // decoder instance + field availability flags + std::unique_ptr m_decoder{}; + bool m_hasX{false}; + bool m_hasY{false}; +}; diff --git a/test/options/checkMapping.py b/test/options/checkMapping.py new file mode 100644 index 00000000..c0f6b742 --- /dev/null +++ b/test/options/checkMapping.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +from Gaudi.Configuration import VERBOSE, DEBUG + +from Configurables import GeoSvc, EventDataSvc, ActsGeoGen3PlaneSvc, TrackerMappingSvc +from Configurables import DumpSimTrackerHitCellIDAlg, PropagateToHitSurfaceAlg +from k4FWCore import ApplicationMgr, IOSvc +from k4FWCore.parseArgs import parser + +parser.add_argument("--compactFile", help="Compact file") +args = parser.parse_known_args()[0] + +# DD4hep geometry service +geoSvc = GeoSvc() +geoSvc.detectors = [args.compactFile] + +# Plane geometry service +actsGeoPlaneSvc = ActsGeoGen3PlaneSvc("ActsGeoPlaneSvc") +actsGeoPlaneSvc.DetElementName = "Tracker" +actsGeoPlaneSvc.LayerPatternExpr = r"layer\d" +actsGeoPlaneSvc.OutputLevel = DEBUG + +# Mapping service +mappingSvc = TrackerMappingSvc("TrackerMappingSvc") +mappingSvc.ActsGeoSvc = "ActsGeoPlaneSvc" +mappingSvc.OutputLevel = DEBUG + +# I/O service +iosvc = IOSvc() +iosvc.Input = "positrons_1_edm4hep.root" +iosvc.OutputLevel = DEBUG + +#-------------- +# simple dump alg +dump = DumpSimTrackerHitCellIDAlg("DumpSimTrackerHitCellIDAlg") +dump.InputCollection = "SiHits" +dump.MappingSvc = "TrackerMappingSvc" +dump.MaxHits = 50 +dump.OutputLevel = DEBUG + +# simple prop check +prop = PropagateToHitSurfaceAlg("PropagateToHitSurfaceAlg") +prop.InputCollection = "SiHits" +prop.MappingSvc = "TrackerMappingSvc" +prop.ActsGeoSvc = "ActsGeoPlaneSvc" +prop.MaxHits = 50 +prop.BackstepMm = 1.0 +prop.AssumeCharge = 0.0 +prop.OutputLevel = DEBUG +prop.CsvFile = "propagate_hits.csv" +prop.ObjFile = "propagate_segments.obj" + +ApplicationMgr( + TopAlg=[dump, prop], + ExtSvc=[geoSvc, actsGeoPlaneSvc, mappingSvc, EventDataSvc(), iosvc], + EvtMax=1, + EvtSel="NONE", +) + diff --git a/test/options/visActsGEo.py b/test/options/visActsGEo.py new file mode 100644 index 00000000..9d787f08 --- /dev/null +++ b/test/options/visActsGEo.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +from Gaudi.Configuration import VERBOSE, DEBUG + +from Configurables import ActsGeoGen3Svc, GeoSvc, ActsTestPropagator, EventDataSvc +from k4FWCore import ApplicationMgr, IOSvc +from k4FWCore.parseArgs import parser + +parser.add_argument("--compactFile", help="Compact file") + +args = parser.parse_known_args()[0] + +iosvc = IOSvc() +iosvc.Output = "steps.root" + +geoSvc = GeoSvc() +geoSvc.detectors = [args.compactFile] + +actsGeoSvc = ActsGeoGen3Svc("ActsGeoSvc") +actsGeoSvc.DetElementName = "ring" +#actsGeoSvc.DetElementName = "InnerTrackerBarrel" +actsGeoSvc.LayerPatternExpr = r"layer\\d" +actsGeoSvc.OutputLevel = VERBOSE + +#propTest = ActsTestPropagator("TestPropagator") +#propTest.OutputLevel = DEBUG +#propTest.NumTracks = 20000 + + +ApplicationMgr( + #TopAlg=[propTest], + TopAlg=[], + ExtSvc=[geoSvc, actsGeoSvc, EventDataSvc()], + EvtMax=1, + EvtSel="NONE", +) diff --git a/test/options/visActsPlaneGeo.py b/test/options/visActsPlaneGeo.py new file mode 100644 index 00000000..91b31ada --- /dev/null +++ b/test/options/visActsPlaneGeo.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +from Gaudi.Configuration import VERBOSE, DEBUG + +from Configurables import GeoSvc, EventDataSvc, ActsGeoGen3PlaneSvc +from k4FWCore import ApplicationMgr, IOSvc +from k4FWCore.parseArgs import parser + +parser.add_argument("--compactFile", help="Compact file") +args = parser.parse_known_args()[0] + +#iosvc = IOSvc() +#iosvc.Output = "steps.root" + +# DD4hep geometry service +geoSvc = GeoSvc() +geoSvc.detectors = [args.compactFile] + +# Plane geometry service +actsGeoPlaneSvc = ActsGeoGen3PlaneSvc("ActsGeoPlaneSvc") +actsGeoPlaneSvc.DetElementName = "Tracker" +actsGeoPlaneSvc.LayerPatternExpr = r"layer\d" +actsGeoPlaneSvc.OutputLevel = VERBOSE + +ApplicationMgr( + TopAlg=[], + ExtSvc=[geoSvc, actsGeoPlaneSvc, EventDataSvc()], + EvtMax=1, + EvtSel="NONE", +) + From 16f85394ba0fd257b27452a28a3aca4c282f3d9d Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 10 Mar 2026 13:56:44 +0100 Subject: [PATCH 43/69] Move LUXE construction into general GeoSvc --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 3 ++- .../DD4hepBlueprintConstruction.cpp | 20 +++++++++++++++++++ .../components/DD4hepBlueprintConstruction.h | 7 +++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index 5786f44c..2fabdba7 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -62,7 +62,8 @@ ActsGeoSvc::ActsGeoSvc(const std::string& name, ISvcLocator* svcLoc) : base_clas {"ILD_FCCee_v01", FCCee::ILD_FCCee_v01::populateBlueprint}, {"ILD_FCCee_v02", FCCee::ILD_FCCee_v02::populateBlueprint}, {"CLD_o2_v07", FCCee::CLD_o2_v07::populateBlueprint}, - {"CLD_o2_v08", FCCee::CLD_o2_v07::populateBlueprint}}; + {"CLD_o2_v08", FCCee::CLD_o2_v07::populateBlueprint}, + {"LUXE_v0", LUXE::LUXE_v0::populateBlueprint}}; } StatusCode ActsGeoSvc::initialize() { diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 5eb01070..6f73dbe0 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -472,3 +472,23 @@ namespace FCCee { } } // namespace CLD_o2_v07 } // namespace FCCee + +namespace LUXE { + namespace LUXE_v0 { + void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder) { + auto& tracker = root.addCuboidContainer(detName, AxisZ); + auto envelope = + Acts::ExtentEnvelope{}.set(AxisZ, {0.4_mm, 0.4_mm}).set(AxisX, {0.4_mm, 0.4_mm}).set(AxisY, {0.4_mm, 0.4_mm}); + + builder.layers() + .planar() + .setSensorAxes("XYZ") + .setLayerFilter("layer\\d") + .setContainer("Tracker") + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::Gap) + .addTo(tracker); + } + } // namespace LUXE_v0 +} // namespace LUXE diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h index c7224cb8..e180a68a 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.h @@ -54,4 +54,11 @@ namespace FCCee { } // namespace FCCee +namespace LUXE { + namespace LUXE_v0 { + void populateBlueprint(const std::string& detName, Acts::Experimental::Blueprint& root, + ActsPlugins::DD4hep::BlueprintBuilder& builder); + } +} // namespace LUXE + #endif // K4ACTSTRACKING_DD4HEPBLUEPRINTCONSTRUCTION_H From c086e5444f8d3e3cb8195bff90133e282b6311db Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 10 Mar 2026 14:20:18 +0100 Subject: [PATCH 44/69] Keep things compiling --- k4ActsTracking/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/k4ActsTracking/CMakeLists.txt b/k4ActsTracking/CMakeLists.txt index 09d8195b..61fda48a 100644 --- a/k4ActsTracking/CMakeLists.txt +++ b/k4ActsTracking/CMakeLists.txt @@ -43,10 +43,10 @@ set(_plugin_sources src/components/TrackTruthAlg.cxx src/components/ActsTestPropagator.cpp src/components/DD4hepBlueprintConstruction.cpp - src/components/ActsGeoGen3PlaneSvc.cpp - src/components/TrackerMappingSvc.cpp - src/components/DumpSimTrackerHitCellIDAlg.cpp - src/components/PropagateToHitSurfaceAlg.cpp + # src/components/ActsGeoGen3PlaneSvc.cpp + # src/components/TrackerMappingSvc.cpp + # src/components/DumpSimTrackerHitCellIDAlg.cpp + # src/components/PropagateToHitSurfaceAlg.cpp ) gaudi_add_module(k4ActsTrackingPlugins From 80908bcf5abb4d4c024bf3227f410185e4899408 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 10 Mar 2026 15:06:51 +0100 Subject: [PATCH 45/69] Remove algorithms and services that are out of scope Either absorbed by other services or not part of the context of this PR --- k4ActsTracking/CMakeLists.txt | 4 - .../k4ActsTracking/ITrackerMappingSvc.h | 23 -- .../src/components/ActsGeoGen3PlaneSvc.cpp | 145 ----------- .../src/components/ActsGeoGen3PlaneSvc.h | 51 ---- .../components/DumpSimTrackerHitCellIDAlg.cpp | 156 ------------ .../components/DumpSimTrackerHitCellIDAlg.h | 40 --- .../components/PropagateToHitSurfaceAlg.cpp | 239 ------------------ .../src/components/PropagateToHitSurfaceAlg.h | 40 --- .../src/components/TrackerMappingSvc.cpp | 181 ------------- .../src/components/TrackerMappingSvc.h | 71 ------ 10 files changed, 950 deletions(-) delete mode 100644 k4ActsTracking/include/k4ActsTracking/ITrackerMappingSvc.h delete mode 100644 k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.cpp delete mode 100644 k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.h delete mode 100644 k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.cpp delete mode 100644 k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.h delete mode 100644 k4ActsTracking/src/components/PropagateToHitSurfaceAlg.cpp delete mode 100644 k4ActsTracking/src/components/PropagateToHitSurfaceAlg.h delete mode 100644 k4ActsTracking/src/components/TrackerMappingSvc.cpp delete mode 100644 k4ActsTracking/src/components/TrackerMappingSvc.h diff --git a/k4ActsTracking/CMakeLists.txt b/k4ActsTracking/CMakeLists.txt index 61fda48a..9bb0f90a 100644 --- a/k4ActsTracking/CMakeLists.txt +++ b/k4ActsTracking/CMakeLists.txt @@ -43,10 +43,6 @@ set(_plugin_sources src/components/TrackTruthAlg.cxx src/components/ActsTestPropagator.cpp src/components/DD4hepBlueprintConstruction.cpp - # src/components/ActsGeoGen3PlaneSvc.cpp - # src/components/TrackerMappingSvc.cpp - # src/components/DumpSimTrackerHitCellIDAlg.cpp - # src/components/PropagateToHitSurfaceAlg.cpp ) gaudi_add_module(k4ActsTrackingPlugins diff --git a/k4ActsTracking/include/k4ActsTracking/ITrackerMappingSvc.h b/k4ActsTracking/include/k4ActsTracking/ITrackerMappingSvc.h deleted file mode 100644 index 5d70c4e5..00000000 --- a/k4ActsTracking/include/k4ActsTracking/ITrackerMappingSvc.h +++ /dev/null @@ -1,23 +0,0 @@ -// Service interface: CellID (DD4hep VolumeID) -> Acts::Surface mapping - -#pragma once - -#include - -#include - -namespace Acts { -class Surface; -} - -class GAUDI_API ITrackerMappingSvc : virtual public IInterface { -public: - DeclareInterfaceID(ITrackerMappingSvc, 1, 0); - - virtual const Acts::Surface* surface(std::uint64_t cellID) const = 0; - virtual bool hasSurface(std::uint64_t cellID) const = 0; - virtual std::size_t size() const = 0; - -protected: - ~ITrackerMappingSvc() override = default; -}; diff --git a/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.cpp b/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.cpp deleted file mode 100644 index 8e93b5c5..00000000 --- a/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "ActsGeoGen3PlaneSvc.h" - -#include "k4ActsTracking/ActsGaudiLogger.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include - -#include - -DECLARE_COMPONENT(ActsGeoGen3PlaneSvc) - -ActsGeoGen3PlaneSvc::ActsGeoGen3PlaneSvc(const std::string& name, ISvcLocator* svcLoc) : base_class(name, svcLoc) {} - -StatusCode ActsGeoGen3PlaneSvc::initialize() { - m_geoSvc = Gaudi::svcLocator()->service("GeoSvc"); - K4_GAUDI_CHECK(m_geoSvc); - - - std::array magneticFieldVector = {0, 0, 0}; - std::array position = {0, 0, 0}; - m_geoSvc->getDetector()->field().magneticField(position.data(), magneticFieldVector.data()); - debug() << fmt::format("Retrieved magnetic field at position {}: {}", position, magneticFieldVector) << endmsg; - m_magneticField = std::make_shared( - Acts::Vector3(magneticFieldVector[0] / dd4hep::tesla * Acts::UnitConstants::T, - magneticFieldVector[1] / dd4hep::tesla * Acts::UnitConstants::T, - magneticFieldVector[2] / dd4hep::tesla * Acts::UnitConstants::T)); - - -// --- DD4hep sanity check: print layer world transforms before building planes --- -auto det = m_geoSvc->getDetector(); -auto trackerDE = det->detector("Tracker"); // name in compact - -info() << "DD4hep sanity: Tracker DetElement path/name=" << trackerDE.path() - << " / " << trackerDE.name() << endmsg; - -// helper lambda to print translation -auto dumpDE = [&](const dd4hep::DetElement& de) { - // Copy the matrix (avoid dangling reference to temporary) - auto w = de.nominal().worldTransformation(); // returns TGeoHMatrix in this setup - - const double* tr = w.GetTranslation(); // ROOT public API: returns double[3] - - // Convert dd4hep internal length units to mm explicitly - double x_mm = tr[0] / dd4hep::mm; - double y_mm = tr[1] / dd4hep::mm; - double z_mm = tr[2] / dd4hep::mm; - - info() << fmt::format( - " DE {:<20} path={:<40} world T [mm] = ({:9.3f}, {:9.3f}, {:9.3f})", - de.name(), de.path(), x_mm, y_mm, z_mm) - << endmsg; -}; - -// In your logs, layers are named layer0..layer3 (not id=1..4). We'll dump those. -for (int i = 0; i < 4; ++i) { - std::string lname = fmt::format("layer{}", i); - auto layerDE = trackerDE.child(lname); - if (!layerDE.isValid()) { - warning() << "DD4hep sanity: cannot find child DetElement '" << lname - << "' under Tracker. Available path=" << trackerDE.path() << endmsg; - continue; - } - dumpDE(layerDE); -} -//---------sanity check over-------------- - - auto gaudiLogger = makeActsGaudiLogger(this); - - //info() << fmt::format("Acts::cm: {}, dd4hep::mm: {}", Acts::UnitConstants::cm, dd4hep::mm) << endmsg; - - ActsPlugins::DD4hep::BlueprintBuilder builder{{ - .dd4hepDetector = m_geoSvc->getDetector(), - .lengthScale = Acts::UnitConstants::mm / dd4hep::mm, - }, - gaudiLogger->cloneWithSuffix("|BlpBld")}; - - using Acts::Experimental::Blueprint; - using Acts::Experimental::BlueprintOptions; - using namespace Acts::UnitLiterals; - using enum Acts::AxisDirection; - - Blueprint::Config cfg; - // Padding around subvolumes of the world volume - cfg.envelope[AxisX] = {10_mm, 10_mm}; - cfg.envelope[AxisY] = {10_mm, 10_mm}; - cfg.envelope[AxisZ] = {10_mm, 10_mm}; - Blueprint root{cfg}; - -// -------------------------------------- -root.addCuboidContainer("LUXE", AxisZ, [&](auto& worldBox) { - auto& tracker = worldBox.addCuboidContainer("OuterBox", AxisZ); - auto envelope = Acts::ExtentEnvelope{} - .set(AxisZ, {0.4_mm, 0.4_mm}) - .set(AxisX, {0.4_mm, 0.4_mm}) - .set(AxisY, {0.4_mm, 0.4_mm}); - - auto planes = builder.planeHelper() - .setAxes("XYZ") - .setLayerAxes("XYZ") - .setPattern(m_layerPattern.value()) // e.g. r"layer\d" - .setContainer(m_detElementName.value()) // e.g. "Tracker" - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::Gap) // Gap, Midpoint, First - .build(); - tracker.addChild(planes); - }); -// -------------------------------------- - - BlueprintOptions options; - Acts::GeometryContext gctxt{}; - - debug() << "Constructing tracking geometry" << endmsg; - m_trackingGeo = root.construct(options, gctxt, *gaudiLogger->cloneWithSuffix("|Construct")); - - debug() << "Creating visualiztion" << endmsg; - Acts::ObjVisualization3D vis{}; - m_trackingGeo->visualize(vis, gctxt); - vis.write("dumped_acts_geo.obj"); - - return StatusCode::SUCCESS; -} diff --git a/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.h b/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.h deleted file mode 100644 index b074f0e1..00000000 --- a/k4ActsTracking/src/components/ActsGeoGen3PlaneSvc.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef K4ACTSTRACKING_ACTSGEOGEN3SVC_H -#define K4ACTSTRACKING_ACTSGEOGEN3SVC_H - -#include "k4ActsTracking/IActsGeoSvc.h" - -#include -#include - -#include "GaudiKernel/Service.h" - -#include -#include - -namespace Acts { - class TrackingGeometry; - class MagneticFieldProvider; -} // namespace Acts - -namespace dd4hep { - class Detector; -} - -class ActsGeoGen3PlaneSvc : public extends { -public: - std::shared_ptr trackingGeometry() const override; - - std::shared_ptr magneticField() const override; - - ActsGeoGen3PlaneSvc(const std::string& name, ISvcLocator* svcLoc); - - ~ActsGeoGen3PlaneSvc() = default; - - StatusCode initialize() override; - - Gaudi::Property m_detElementName{this, "DetElementName", "Tracker", "Name of the DetElement"}; - Gaudi::Property m_layerPattern{this, "LayerPatternExpr", "layer", "Layer pattern match expression"}; - -private: - dd4hep::Detector* m_dd4hepGeo{nullptr}; - SmartIF m_geoSvc; - std::shared_ptr m_trackingGeo{nullptr}; - std::shared_ptr m_magneticField{nullptr}; -}; - -inline std::shared_ptr ActsGeoGen3PlaneSvc::trackingGeometry() const { return m_trackingGeo; } - -inline std::shared_ptr ActsGeoGen3PlaneSvc::magneticField() const { - return m_magneticField; -} - -#endif // K4ACTSTRACKING_ACTSGEOGEN3SVC_H diff --git a/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.cpp b/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.cpp deleted file mode 100644 index a72d8cb4..00000000 --- a/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include -#include -#include -#include - -#include -#include "DumpSimTrackerHitCellIDAlg.h" -#include -#include -#include - -#include - -DECLARE_COMPONENT(DumpSimTrackerHitCellIDAlg) - -DumpSimTrackerHitCellIDAlg::DumpSimTrackerHitCellIDAlg(const std::string& name, ISvcLocator* svcLoc) - : Algorithm(name, svcLoc) {} - -StatusCode DumpSimTrackerHitCellIDAlg::initialize() { - K4_GAUDI_CHECK(Algorithm::initialize()); - - // Rebind the DataHandle key to the property (so option file can override) - m_inHits = DataHandle(m_inputColName.value(), - Gaudi::DataHandle::Reader, this); - - // Configure ServiceHandle name (so option file can override) - m_mappingSvc = ServiceHandle(m_mappingSvcName.value(), name()); - - K4_GAUDI_CHECK(m_mappingSvc.retrieve()); - - info() << fmt::format("Initialized. InputCollection='{}', MappingSvc='{}'", - m_inputColName.value(), m_mappingSvcName.value()) - << endmsg; - - return StatusCode::SUCCESS; -} - -StatusCode DumpSimTrackerHitCellIDAlg::execute() { - const auto* hits = m_inHits.get(); - if (hits == nullptr) { - warning() << "Input collection is missing in TES." << endmsg; - return StatusCode::SUCCESS; - } - - info() << fmt::format("Event: SimTrackerHits size={}", hits->size()) << endmsg; - - int nPrint = 0; - for (const auto& h : *hits) { - if (nPrint >= m_maxHits.value()) { - break; - } - - const std::uint64_t cellID = static_cast(h.getCellID()); - const auto* surf = m_mappingSvc->surface(cellID); - - // Print some hit info for sanity - const auto p = h.getPosition(); // edm4hep::Vector3f - const auto m = h.getMomentum(); // edm4hep::Vector3f (at entry) - const double t = h.getTime(); - - if (surf == nullptr) { - warning() << fmt::format(" hit[{}] cellID={} pos=({:.3f},{:.3f},{:.3f}) " - "mom=({:.3f},{:.3f},{:.3f}) t={:.3f} ==> NO SURFACE", - nPrint, cellID, p.x, p.y, p.z, m.x, m.y, m.z, t) - << endmsg; - } else { - // Acts context (stateless for now) - Acts::GeometryContext gctx{}; - - // Convert hit pos/mom to Acts vectors (assume EDM4hep uses mm / GeV in your chain; - // geometry was built with mm scale already, so treat numbers as mm here) - const Acts::Vector3 gpos(p.x, p.y, p.z); - - Acts::Vector3 gdir(m.x, m.y, m.z); - const double dirNorm = gdir.norm(); - if (dirNorm > 0.) { - gdir /= dirNorm; - } else { - // fallback direction - gdir = Acts::Vector3(0., 0., 1.); - } - - // Basic IDs - const auto gid = surf->geometryId(); - - // Surface transform info - const Acts::Transform3& T = surf->transform(gctx); - const Acts::Vector3 center = T.translation(); - - // Surface normal in global frame - const Acts::Vector3 n = surf->normal(gctx, gpos, gdir); - - // Signed distance to the plane along normal - const double signedDist = n.dot(gpos - center); - - auto lres = surf->globalToLocal(gctx, gpos, gdir); //default tolerance - bool gotLocal = lres.ok(); - Acts::Vector2 lpos(0., 0.); - if (gotLocal) { - lpos = *lres; - } - - bool inside = gotLocal ? surf->bounds().inside(lpos) : false; - - // DD4hep DetElement semantic check - std::string dePath = ""; - std::string deName = ""; - std::uint64_t deVid = 0; - - const auto* ade = surf->associatedDetectorElement(); - if (ade != nullptr) { - if (const auto* dd4hepDE = - dynamic_cast(ade)) { - const auto& src = dd4hepDE->sourceElement(); - dePath = src.path(); - deName = src.name(); - deVid = static_cast(src.volumeID()); - } else { - dePath = ""; - deName = ""; - } - } - - // Print summary - info() << fmt::format( - " hit[{}] cellID={} pos=({:.3f},{:.3f},{:.3f}) ==> " - "surface={} geoId=0x{:x} | " - "center=({:.3f},{:.3f},{:.3f}) n=({:.3f},{:.3f},{:.3f}) " - "dist={:.3f}mm | local={} ({:.3f},{:.3f}) inside={} | " - "DE name='{}' path='{}' volID={}", - nPrint, cellID, p.x, p.y, p.z, - (const void*)surf, - static_cast(gid.value()), - center.x(), center.y(), center.z(), - n.x(), n.y(), n.z(), - signedDist, - gotLocal ? "OK" : "FAIL", - lpos.x(), lpos.y(), - inside ? "YES" : "NO", - deName, dePath, deVid) - << endmsg; - } -// } else { -// const auto gid = surf->geometryId(); -// info() << fmt::format(" hit[{}] cellID={} pos=({:.3f},{:.3f},{:.3f}) " -// "==> surface={} geoId=0x{:x}", -// nPrint, cellID, p.x, p.y, p.z, (const void*)surf, -// static_cast(gid.value())) -// << endmsg; -// }// else - - ++nPrint; - } - - return StatusCode::SUCCESS; -} diff --git a/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.h b/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.h deleted file mode 100644 index b61a88e7..00000000 --- a/k4ActsTracking/src/components/DumpSimTrackerHitCellIDAlg.h +++ /dev/null @@ -1,40 +0,0 @@ -// a tiny alg for cellID checking -// - -#pragma once - -#include "k4ActsTracking/ITrackerMappingSvc.h" - -#include -#include -#include - -#include - -#include - -#include - -class DumpSimTrackerHitCellIDAlg final : public Algorithm { -public: - DumpSimTrackerHitCellIDAlg(const std::string& name, ISvcLocator* svcLoc); - - StatusCode initialize() override; - StatusCode execute() override; - -private: - /// Input collection name (edm4hep::SimTrackerHitCollection) - Gaudi::Property m_inputColName{this, "InputCollection", "SiHits", - "Input edm4hep::SimTrackerHit collection name"}; - - /// Tracker mapping service name - Gaudi::Property m_mappingSvcName{this, "MappingSvc", "TrackerMappingSvc", - "Name of ITrackerMappingSvc implementation"}; - - /// Max number of hits to print per event - Gaudi::Property m_maxHits{this, "MaxHits", 50, "Maximum hits printed per event"}; - - k4FWCore::DataHandle m_inHits{"SiHits", Gaudi::DataHandle::Reader, this}; - - ServiceHandle m_mappingSvc{this, "TrackerMappingSvc", "TrackerMappingSvc"}; -}; diff --git a/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.cpp b/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.cpp deleted file mode 100644 index 07e39590..00000000 --- a/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.cpp +++ /dev/null @@ -1,239 +0,0 @@ -#include "PropagateToHitSurfaceAlg.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -DECLARE_COMPONENT(PropagateToHitSurfaceAlg) - -PropagateToHitSurfaceAlg::PropagateToHitSurfaceAlg(const std::string& name, ISvcLocator* svcLoc) - : Gaudi::Algorithm(name, svcLoc) {} - -StatusCode PropagateToHitSurfaceAlg::initialize() { - K4_GAUDI_CHECK(Gaudi::Algorithm::initialize()); - K4_GAUDI_CHECK(m_mappingSvc.retrieve()); - K4_GAUDI_CHECK(m_actsGeoSvc.retrieve()); - -m_inHits = k4FWCore::DataHandle( - m_inputColName.value(), Gaudi::DataHandle::Reader, this); - - - // Acts logger, same pattern as ActsTestPropagator - m_actsLogger = makeActsGaudiLogger(this); - - info() << fmt::format( - "Initialized. Input='{}', MappingSvc='{}', ActsGeoSvc='{}', MaxHits={}, Backstep={} mm, AssumeCharge={}", - m_inputColName.value(), - m_mappingSvc.name(), - m_actsGeoSvc.name(), - m_maxHits.value(), - m_backstepMm.value(), - m_assumeCharge.value()) - << endmsg; - - // Create/overwrite CSV -{ - std::ofstream ofs(m_csvFile.value(), std::ios::out | std::ios::trunc); - ofs << "event,ihit,cellID," - "hit_x,hit_y,hit_z," - "start_x,start_y,start_z," - "end_x,end_y,end_z," - "dist_mm,local_u,local_v,inside,local_ok\n"; -} -info() << fmt::format("CSV output: '{}'", m_csvFile.value()) << endmsg; - // Create/overwrite obj -{ - std::ofstream obj(m_objFile.value(), std::ios::out | std::ios::trunc); - obj << "# PropagateToHitSurfaceAlg segments\n"; - obj << "# v x y z\n"; - obj << "# l i j\n"; -} -info() << fmt::format("OBJ output: '{}'", m_objFile.value()) << endmsg; - - return StatusCode::SUCCESS; -}// init - -StatusCode PropagateToHitSurfaceAlg::execute(const EventContext& ctx) const { - (void)ctx; - - const auto* hits = m_inHits.get(); - - if (hits == nullptr) { - warning() << "Input collection missing." << endmsg; - return StatusCode::SUCCESS; - } - - const auto tg = m_actsGeoSvc->trackingGeometry(); - if (!tg) { - error() << "ActsGeoSvc returned null TrackingGeometry." << endmsg; - return StatusCode::FAILURE; - } - - // Contexts (you can later thread real contexts through here if needed) - Acts::GeometryContext gctx{}; - Acts::MagneticFieldContext bctx{}; - - // --- Build propagator like ActsTestPropagator (EigenStepper + Navigator + Logger) - using Stepper = Acts::EigenStepper<>; - using Navigator = Acts::Navigator; - using Propagator = Acts::Propagator; - - Navigator::Config navCfg{tg}; - navCfg.resolvePassive = false; - navCfg.resolveMaterial = false; - navCfg.resolveSensitive = true; - - Stepper stepper(m_actsGeoSvc->magneticField()); - Navigator navigator(navCfg, m_actsLogger->cloneWithSuffix(":Nav")); - Propagator propagator(std::move(stepper), std::move(navigator), - m_actsLogger->cloneWithSuffix(":Prop")); - - // Minimal actor list is fine; keep EndOfWorldReached as in ActsTestPropagator - using EndOfWorld = Acts::EndOfWorldReached; - using ActorList = Acts::ActorList; - using PropagatorOptions = Propagator::template Options; - - PropagatorOptions options{gctx, bctx}; - - info() << fmt::format("Event: {} size={}", m_inputColName.value(), hits->size()) << endmsg; - std::ofstream csv(m_csvFile.value(), std::ios::out | std::ios::app); - if (!csv) { - warning() << fmt::format("Cannot open CSV file '{}'", m_csvFile.value()) << endmsg; - } - std::ofstream obj(m_objFile.value(), std::ios::out | std::ios::app); - if (!obj) { - warning() << fmt::format("Cannot open OBJ file '{}'", m_objFile.value()) << endmsg; - } - static std::atomic globalVtx{1}; - - int nPrint = 0; - for (const auto& h : *hits) { - if (nPrint >= m_maxHits.value()) break; - - const std::uint64_t cellID = static_cast(h.getCellID()); - const Acts::Surface* surf = m_mappingSvc->surface(cellID); - - const auto p = h.getPosition(); - const auto m = h.getMomentum(); - - Acts::Vector3 hitPos(p.x, p.y, p.z); - - Acts::Vector3 mom(m.x, m.y, m.z); - const double pAbs = mom.norm(); - if (pAbs <= 0.) { - warning() << fmt::format(" hit[{}] cellID={} has zero momentum vector.", nPrint, cellID) << endmsg; - ++nPrint; - continue; - } - Acts::Vector3 dir = mom / pAbs; - - if (surf == nullptr) { - warning() << fmt::format(" hit[{}] cellID={} ==> NO SURFACE", nPrint, cellID) << endmsg; - ++nPrint; - continue; - } - - // Backstep upstream to avoid starting exactly on the surface - const Acts::Vector3 startPos = - hitPos - (m_backstepMm.value() * Acts::UnitConstants::mm) * dir; - - // --- Build start parameters in the same style as ActsTestPropagator - // Momentum is assumed in GeV from edm4hep SimTrackerHit; we just need qOverP numerically. - // If you want "pure straight-line validation", set AssumeCharge=0 -> qOverP=0. - const double qOverP = (m_assumeCharge.value() == 0.0) ? 0.0 : (m_assumeCharge.value() / pAbs); - - Acts::Vector4 startPos4{startPos.x(), startPos.y(), startPos.z(), 0.0}; - - const auto startParams = Acts::BoundTrackParameters::createCurvilinear( - startPos4, dir, qOverP, std::nullopt, Acts::ParticleHypothesis::pion()); - - // --- Propagate to target surface - auto result = propagator.propagate(startParams, *surf, options); - if (!result.ok()) { - warning() << fmt::format(" hit[{}] cellID={} propagate FAILED: {}", nPrint, cellID, - result.error().message()) - << endmsg; - ++nPrint; - continue; - } - - const auto& propRes = result.value(); - - if (!propRes.endParameters) { - warning() << fmt::format(" hit[{}] cellID={} propagate OK but endParameters is empty", nPrint, cellID) - << endmsg; - ++nPrint; - continue; - } - - const auto& endParams = *(propRes.endParameters); - Acts::Vector3 endPos = endParams.position(gctx); - - const double dist = (endPos - hitPos).norm(); - - // Local check - auto glRes = surf->globalToLocal(gctx, endPos, dir); - bool localOk = glRes.ok(); - Acts::Vector2 lpos = localOk ? glRes.value() : Acts::Vector2(0, 0); - - bool inside = false; - if (localOk) { - inside = surf->bounds().inside(lpos); - } - - info() << fmt::format( - " hit[{}] cellID={} hit=({:.3f},{:.3f},{:.3f}) start=({:.3f},{:.3f},{:.3f}) " - "-> end=({:.3f},{:.3f},{:.3f}) | |end-hit|={:.6f} mm | local={} ({:.3f},{:.3f}) inside={}", - nPrint, cellID, - hitPos.x(), hitPos.y(), hitPos.z(), - startPos.x(), startPos.y(), startPos.z(), - endPos.x(), endPos.y(), endPos.z(), - dist / Acts::UnitConstants::mm, - (localOk ? "OK" : "FAIL"), - lpos.x(), lpos.y(), - (inside ? "YES" : "NO")) - << endmsg; - - // csv + obj - if (csv) { - csv << 0 << "," - << nPrint << "," - << cellID << "," - << hitPos.x() << "," << hitPos.y() << "," << hitPos.z() << "," - << startPos.x() << "," << startPos.y() << "," << startPos.z() << "," - << endPos.x() << "," << endPos.y() << "," << endPos.z() << "," - << (dist / Acts::UnitConstants::mm) << "," - << (localOk ? lpos.x() : 0.0) << "," - << (localOk ? lpos.y() : 0.0) << "," - << (inside ? 1 : 0) << "," - << (localOk ? 1 : 0) - << "\n"; - } - if (obj) { - long long v0 = globalVtx.fetch_add(2); - long long v1 = v0 + 1; - - obj << "v " << startPos.x() << " " << startPos.y() << " " << startPos.z() << "\n"; - obj << "v " << endPos.x() << " " << endPos.y() << " " << endPos.z() << "\n"; - obj << "l " << v0 << " " << v1 << "\n"; - } - - - ++nPrint; - } - - return StatusCode::SUCCESS; -} - diff --git a/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.h b/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.h deleted file mode 100644 index 9b9e8de5..00000000 --- a/k4ActsTracking/src/components/PropagateToHitSurfaceAlg.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include "k4ActsTracking/ActsGaudiLogger.h" -#include "k4ActsTracking/IActsGeoSvc.h" -#include "k4ActsTracking/ITrackerMappingSvc.h" - -#include -#include - -class PropagateToHitSurfaceAlg : public Gaudi::Algorithm { -public: - PropagateToHitSurfaceAlg(const std::string& name, ISvcLocator* svcLoc); - - StatusCode initialize() override; - StatusCode execute(const EventContext& ctx) const override; - -private: - Gaudi::Property m_inputColName{this, "InputCollection", "SiHits"}; - Gaudi::Property m_maxHits{this, "MaxHits", 10}; - Gaudi::Property m_backstepMm{this, "BackstepMm", 1.0}; // in mm - Gaudi::Property m_csvFile{this, "CsvFile", "propagate_hits.csv"}; - Gaudi::Property m_objFile{this, "ObjFile", "propagate_segments.obj"}; - // if no charge in hit - Gaudi::Property m_assumeCharge{this, "AssumeCharge", 0.0}; - - mutable k4FWCore::DataHandle m_inHits{ - "", Gaudi::DataHandle::Reader, this}; - - ServiceHandle m_mappingSvc{this, "MappingSvc", "TrackerMappingSvc"}; - ServiceHandle m_actsGeoSvc{this, "ActsGeoSvc", "ActsGeoPlaneSvc"}; - - std::unique_ptr m_actsLogger{nullptr}; -}; - diff --git a/k4ActsTracking/src/components/TrackerMappingSvc.cpp b/k4ActsTracking/src/components/TrackerMappingSvc.cpp deleted file mode 100644 index 37e8d883..00000000 --- a/k4ActsTracking/src/components/TrackerMappingSvc.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include "TrackerMappingSvc.h" - -#include "k4ActsTracking/ActsGaudiLogger.h" - -#include - -#include -#include - -#include - -#include - -DECLARE_COMPONENT(TrackerMappingSvc) - -TrackerMappingSvc::TrackerMappingSvc(const std::string& name, ISvcLocator* svcLoc) - : base_class(name, svcLoc) {} - -StatusCode TrackerMappingSvc::initialize() { - m_actsGeoSvc = service(m_actsGeoSvcName.value()); - K4_GAUDI_CHECK(m_actsGeoSvc); - - // build BitFieldCoder from layout string - try { - m_decoder = std::make_unique(m_idLayout.value()); - // Detect whether x/y exist - try { - (void)m_decoder->get(0ULL, "x"); - m_hasX = true; - } catch (...) { - m_hasX = false; - } - try { - (void)m_decoder->get(0ULL, "y"); - m_hasY = true; - } catch (...) { - m_hasY = false; - } - - info() << fmt::format("TrackerMappingSvc: BitFieldCoder initialized. MaskXY={}, hasX={}, hasY={}, layout='{}'", - m_maskXY.value(), m_hasX, m_hasY, m_idLayout.value()) - << endmsg; - - } catch (const std::exception& e) { - error() << fmt::format("TrackerMappingSvc: failed to construct BitFieldCoder from layout '{}': {}", - m_idLayout.value(), e.what()) - << endmsg; - return StatusCode::FAILURE; - } - - return buildMapping(); -} - -StatusCode TrackerMappingSvc::finalize() { - std::scoped_lock lock{m_mutex}; - info() << fmt::format("Final mapping size={}, visited={}, withADE={}, dd4hepADE={}, inserted={}", - m_cellIDToSurface.size(), m_nSurfacesVisited, m_nWithAssociatedDetElem, - m_nDD4hepDetElem, m_nInserted) - << endmsg; - m_cellIDToSurface.clear(); - return StatusCode::SUCCESS; -} - -std::uint64_t TrackerMappingSvc::normalizeCellID(std::uint64_t cellID) const { - if (!m_maskXY.value() || !m_decoder) { - return cellID; - } - std::uint64_t key = cellID; - - // mask x/y only if present - // BitFieldCoder::set modifies the VolumeID in-place - if (m_hasX) { - m_decoder->set(key, "x", 0); - } - if (m_hasY) { - m_decoder->set(key, "y", 0); - } - - return key; -} - -const Acts::Surface* TrackerMappingSvc::surface(std::uint64_t cellID) const { - std::scoped_lock lock{m_mutex}; - - const auto key = normalizeCellID(cellID); - auto it = m_cellIDToSurface.find(key); - return (it == m_cellIDToSurface.end()) ? nullptr : it->second; -} - -bool TrackerMappingSvc::hasSurface(std::uint64_t cellID) const { - std::scoped_lock lock{m_mutex}; - - const auto key = normalizeCellID(cellID); - return m_cellIDToSurface.find(key) != m_cellIDToSurface.end(); -} - -std::size_t TrackerMappingSvc::size() const { - std::scoped_lock lock{m_mutex}; - return m_cellIDToSurface.size(); -} - -StatusCode TrackerMappingSvc::buildMapping() { - std::scoped_lock lock{m_mutex}; - - m_cellIDToSurface.clear(); - m_nSurfacesVisited = 0; - m_nWithAssociatedDetElem = 0; - m_nDD4hepDetElem = 0; - m_nInserted = 0; - - auto tg = m_actsGeoSvc->trackingGeometry(); - if (!tg) { - error() << "Acts tracking geometry is null. Cannot build CellID->Surface mapping." << endmsg; - return StatusCode::FAILURE; - } - - info() << fmt::format("Building CellID->Surface mapping from TrackingGeometry via visitSurfaces()") - << endmsg; - - tg->visitSurfaces([&](const Acts::Surface* s) { - ++m_nSurfacesVisited; - if (s == nullptr) { - return; - } - - const auto* ade = s->associatedDetectorElement(); - if (ade == nullptr) { - return; - } - ++m_nWithAssociatedDetElem; - - const auto* dd4hepDE = dynamic_cast(ade); - if (dd4hepDE == nullptr) { - return; - } - ++m_nDD4hepDetElem; - - const auto cellID = static_cast(dd4hepDE->sourceElement().volumeID()); - if (cellID == 0) { - return; - } - - auto [it, inserted] = m_cellIDToSurface.emplace(cellID, s); - if (!inserted) { - warning() << fmt::format("Duplicate cellID={} for surface (existing ptr={}, new ptr={})", - cellID, (const void*)it->second, (const void*)s) - << endmsg; - return; - } - ++m_nInserted; - }); - - info() << fmt::format( - "Built CellID->Surface mapping: size={}, visited={}, withADE={}, dd4hepADE={}, inserted={}", - m_cellIDToSurface.size(), m_nSurfacesVisited, m_nWithAssociatedDetElem, - m_nDD4hepDetElem, m_nInserted) - << endmsg; - - if (m_cellIDToSurface.empty()) { - error() << "Mapping table is empty. This usually means surfaces have no DD4hepDetectorElement " - "associated (wrong geometry builder output or visiting wrong surfaces)." - << endmsg; - return StatusCode::FAILURE; - } - - return StatusCode::SUCCESS; -} - -std::uint64_t TrackerMappingSvc::cellIDFromSurface(const Acts::Surface& surface) { - const auto* ade = surface.associatedDetectorElement(); - if (ade == nullptr) { - return 0; - } - - const auto* dd4hepDE = dynamic_cast(ade); - if (dd4hepDE == nullptr) { - return 0; - } - - return static_cast(dd4hepDE->sourceElement().volumeID()); -} diff --git a/k4ActsTracking/src/components/TrackerMappingSvc.h b/k4ActsTracking/src/components/TrackerMappingSvc.h deleted file mode 100644 index f1340a4c..00000000 --- a/k4ActsTracking/src/components/TrackerMappingSvc.h +++ /dev/null @@ -1,71 +0,0 @@ -// CellID (DD4hep VolumeID) -> Acts::Surface mapping - -#pragma once - -#include "k4ActsTracking/ITrackerMappingSvc.h" -#include "k4ActsTracking/IActsGeoSvc.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "DDSegmentation/BitFieldCoder.h" - -namespace Acts { -class Surface; -} - -class TrackerMappingSvc final : public extends { -public: - TrackerMappingSvc(const std::string& name, ISvcLocator* svcLoc); - - StatusCode initialize() override; - StatusCode finalize() override; - - const Acts::Surface* surface(std::uint64_t cellID) const override; - bool hasSurface(std::uint64_t cellID) const override; - std::size_t size() const override; - -private: - StatusCode buildMapping(); - - static std::uint64_t cellIDFromSurface(const Acts::Surface& surface); - // normalize hit cellID (mask x/y) -> sensor-level key - std::uint64_t normalizeCellID(std::uint64_t cellID) const; - -private: - Gaudi::Property m_actsGeoSvcName{this, "ActsGeoSvc", "ActsGeoPlaneSvc", - "Name of the IActsGeoSvc provider."}; - - // bitfield layout string (same as LUXE XML constant GlobalTrackerReadoutID) - Gaudi::Property m_idLayout{ - this, - "IDLayout", - "system:1,side:1,layer:2,module:1,sensor:5,x:32:-16,y:-16", - "DD4hep BitFieldCoder layout string for masking x/y"}; - // whether to mask x/y before lookup - Gaudi::Property m_maskXY{ - this, "MaskXY", true, "Mask x/y fields before CellID->Surface lookup"}; - - SmartIF m_actsGeoSvc{}; - - std::unordered_map m_cellIDToSurface{}; - - mutable std::mutex m_mutex{}; - - std::size_t m_nSurfacesVisited{0}; - std::size_t m_nWithAssociatedDetElem{0}; - std::size_t m_nDD4hepDetElem{0}; - std::size_t m_nInserted{0}; - - // decoder instance + field availability flags - std::unique_ptr m_decoder{}; - bool m_hasX{false}; - bool m_hasY{false}; -}; From 0c31a3deae320b2b0c0754a8a401b46a22458a6c Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 17 Mar 2026 20:16:25 +0100 Subject: [PATCH 46/69] Remove duplicated files from merging LUXE --- test/options/checkMapping.py | 59 --------------------------------- test/options/visActsGEo.py | 36 -------------------- test/options/visActsPlaneGeo.py | 31 ----------------- 3 files changed, 126 deletions(-) delete mode 100644 test/options/checkMapping.py delete mode 100644 test/options/visActsGEo.py delete mode 100644 test/options/visActsPlaneGeo.py diff --git a/test/options/checkMapping.py b/test/options/checkMapping.py deleted file mode 100644 index c0f6b742..00000000 --- a/test/options/checkMapping.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python3 - -from Gaudi.Configuration import VERBOSE, DEBUG - -from Configurables import GeoSvc, EventDataSvc, ActsGeoGen3PlaneSvc, TrackerMappingSvc -from Configurables import DumpSimTrackerHitCellIDAlg, PropagateToHitSurfaceAlg -from k4FWCore import ApplicationMgr, IOSvc -from k4FWCore.parseArgs import parser - -parser.add_argument("--compactFile", help="Compact file") -args = parser.parse_known_args()[0] - -# DD4hep geometry service -geoSvc = GeoSvc() -geoSvc.detectors = [args.compactFile] - -# Plane geometry service -actsGeoPlaneSvc = ActsGeoGen3PlaneSvc("ActsGeoPlaneSvc") -actsGeoPlaneSvc.DetElementName = "Tracker" -actsGeoPlaneSvc.LayerPatternExpr = r"layer\d" -actsGeoPlaneSvc.OutputLevel = DEBUG - -# Mapping service -mappingSvc = TrackerMappingSvc("TrackerMappingSvc") -mappingSvc.ActsGeoSvc = "ActsGeoPlaneSvc" -mappingSvc.OutputLevel = DEBUG - -# I/O service -iosvc = IOSvc() -iosvc.Input = "positrons_1_edm4hep.root" -iosvc.OutputLevel = DEBUG - -#-------------- -# simple dump alg -dump = DumpSimTrackerHitCellIDAlg("DumpSimTrackerHitCellIDAlg") -dump.InputCollection = "SiHits" -dump.MappingSvc = "TrackerMappingSvc" -dump.MaxHits = 50 -dump.OutputLevel = DEBUG - -# simple prop check -prop = PropagateToHitSurfaceAlg("PropagateToHitSurfaceAlg") -prop.InputCollection = "SiHits" -prop.MappingSvc = "TrackerMappingSvc" -prop.ActsGeoSvc = "ActsGeoPlaneSvc" -prop.MaxHits = 50 -prop.BackstepMm = 1.0 -prop.AssumeCharge = 0.0 -prop.OutputLevel = DEBUG -prop.CsvFile = "propagate_hits.csv" -prop.ObjFile = "propagate_segments.obj" - -ApplicationMgr( - TopAlg=[dump, prop], - ExtSvc=[geoSvc, actsGeoPlaneSvc, mappingSvc, EventDataSvc(), iosvc], - EvtMax=1, - EvtSel="NONE", -) - diff --git a/test/options/visActsGEo.py b/test/options/visActsGEo.py deleted file mode 100644 index 9d787f08..00000000 --- a/test/options/visActsGEo.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python3 - -from Gaudi.Configuration import VERBOSE, DEBUG - -from Configurables import ActsGeoGen3Svc, GeoSvc, ActsTestPropagator, EventDataSvc -from k4FWCore import ApplicationMgr, IOSvc -from k4FWCore.parseArgs import parser - -parser.add_argument("--compactFile", help="Compact file") - -args = parser.parse_known_args()[0] - -iosvc = IOSvc() -iosvc.Output = "steps.root" - -geoSvc = GeoSvc() -geoSvc.detectors = [args.compactFile] - -actsGeoSvc = ActsGeoGen3Svc("ActsGeoSvc") -actsGeoSvc.DetElementName = "ring" -#actsGeoSvc.DetElementName = "InnerTrackerBarrel" -actsGeoSvc.LayerPatternExpr = r"layer\\d" -actsGeoSvc.OutputLevel = VERBOSE - -#propTest = ActsTestPropagator("TestPropagator") -#propTest.OutputLevel = DEBUG -#propTest.NumTracks = 20000 - - -ApplicationMgr( - #TopAlg=[propTest], - TopAlg=[], - ExtSvc=[geoSvc, actsGeoSvc, EventDataSvc()], - EvtMax=1, - EvtSel="NONE", -) diff --git a/test/options/visActsPlaneGeo.py b/test/options/visActsPlaneGeo.py deleted file mode 100644 index 91b31ada..00000000 --- a/test/options/visActsPlaneGeo.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python3 - -from Gaudi.Configuration import VERBOSE, DEBUG - -from Configurables import GeoSvc, EventDataSvc, ActsGeoGen3PlaneSvc -from k4FWCore import ApplicationMgr, IOSvc -from k4FWCore.parseArgs import parser - -parser.add_argument("--compactFile", help="Compact file") -args = parser.parse_known_args()[0] - -#iosvc = IOSvc() -#iosvc.Output = "steps.root" - -# DD4hep geometry service -geoSvc = GeoSvc() -geoSvc.detectors = [args.compactFile] - -# Plane geometry service -actsGeoPlaneSvc = ActsGeoGen3PlaneSvc("ActsGeoPlaneSvc") -actsGeoPlaneSvc.DetElementName = "Tracker" -actsGeoPlaneSvc.LayerPatternExpr = r"layer\d" -actsGeoPlaneSvc.OutputLevel = VERBOSE - -ApplicationMgr( - TopAlg=[], - ExtSvc=[geoSvc, actsGeoPlaneSvc, EventDataSvc()], - EvtMax=1, - EvtSel="NONE", -) - From d2399b4580a3bb2017fc0a22b5601744df50ffa9 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 16 Apr 2026 22:08:39 +0200 Subject: [PATCH 47/69] Remove unnecessary file --- env.sh | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 env.sh diff --git a/env.sh b/env.sh deleted file mode 100644 index 8ad2c664..00000000 --- a/env.sh +++ /dev/null @@ -1,10 +0,0 @@ -#source /cvmfs/sw-nightlies.hsf.org/key4hep/setup.sh -r 2025-12-04 #Outdated. Boost 1.88 -source /cvmfs/sw-nightlies.hsf.org/key4hep/setup.sh -r 2026-02-08 -# LUXE compact -source /data/dust/user/wangyufe/luxegeo/install/bin/thisluxegeo.sh -export LD_LIBRARY_PATH=/data/dust/user/wangyufe/luxegeo/install/lib:$LD_LIBRARY_PATH -export DD4hep_LIBRARY_PATH=/data/dust/user/wangyufe/luxegeo/install/lib64:$DD4hep_LIBRARY_PATH - -export LD_LIBRARY_PATH=/data/dust/user/wangyufe/playground/k4ActsTracking/install/lib:$LD_LIBRARY_PATH -export GAUDI_PLUGIN_PATH=/data/dust/user/wangyufe/playground/k4ActsTracking/install/lib:$GAUDI_PLUGIN_PATH -export PYTHONPATH=/data/dust/user/wangyufe/playground/k4ActsTracking/install/python:$PYTHONPATH From 9af776546f58edf7b980871c47715d6f1e18ecbf Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Thu, 16 Apr 2026 22:32:44 +0200 Subject: [PATCH 48/69] Update tests to load geometries that should actually work --- test/CMakeLists.txt | 28 +++++++++++------- test/options/{geosvc.py => load_geometry.py} | 31 +++++++------------- 2 files changed, 28 insertions(+), 31 deletions(-) rename test/options/{geosvc.py => load_geometry.py} (58%) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 285dfd0f..77281806 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,18 +19,26 @@ limitations under the License. function(set_test_env _testname) - #set_property(TEST ${_testname} APPEND PROPERTY ENVIRONMENT "ROOT_INCLUDE_PATH=$<$:$/../include>:$<$:$/../include>:$ENV{ROOT_INCLUDE_PATH}") set_property(TEST ${_testname} APPEND PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${PROJECT_BINARY_DIR}:${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}:$<$:$>:$<$:$>:$<$:$>:$ENV{LD_LIBRARY_PATH}") set_property(TEST ${_testname} APPEND PROPERTY ENVIRONMENT "PYTHONPATH=${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}/${GAUDI_GENCONF_DIR}:$ENV{PYTHONPATH}") endfunction() -add_test(NAME LoadODD - WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} - COMMAND k4run options/geosvc.py) -set_test_env(LoadODD) +# add_geometry_load_test( ) +# +# Add a test that loads the passed compact file and converts the geometry into +# an ACTS geometry before dumping it to an .obj file +function(add_geometry_load_test _compact_file) + get_filename_component(_name "${_compact_file}" NAME_WE) + add_test(NAME load_geo_${_name} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND k4run ${CMAKE_CURRENT_SOURCE_DIR}/options/load_geometry.py + --compactFile "${_compact_file}") + set_test_env(load_geo_${_name}) +endfunction() -add_test(NAME CheckODDObjFile - WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} - COMMAND test -s MyObjFile.mtl) -set_test_env(CheckODDObjFile) -set_tests_properties(CheckODDObjFile PROPERTIES DEPENDS LoadODD) +add_geometry_load_test($ENV{k4geo_DIR}/MuColl/MAIA/compact/MAIA_v0/MAIA_v0.xml) +add_geometry_load_test($ENV{k4geo_DIR}/MuColl/MuSIC/compact/MuSIC_v2/MuSIC_v2.xml) +add_geometry_load_test($ENV{k4geo_DIR}/FCCee/ILD_FCCee/compact/ILD_FCCee_v01/ILD_FCCee_v01.xml) +add_geometry_load_test($ENV{k4geo_DIR}/FCCee/ILD_FCCee/compact/ILD_FCCee_v02/ILD_FCCee_v02.xml) +add_geometry_load_test($ENV{k4geo_DIR}/FCCee/CLD/compact/CLD_o2_v07/CLD_o2_v07.xml) +add_geometry_load_test($ENV{k4geo_DIR}/FCCee/CLD/compact/CLD_o2_v08/CLD_o2_v08.xml) diff --git a/test/options/geosvc.py b/test/options/load_geometry.py similarity index 58% rename from test/options/geosvc.py rename to test/options/load_geometry.py index d799dcbd..e3bdce18 100644 --- a/test/options/geosvc.py +++ b/test/options/load_geometry.py @@ -16,39 +16,28 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import os -from Gaudi.Configuration import INFO +import pathlib +from Gaudi.Configuration import INFO from Configurables import ActsGeoSvc, ApplicationMgr, GeoSvc +from k4FWCore.parseArgs import parser -algList = [] +parser.add_argument("--compactFile", help="The compact file of the geometry to load") -try: - odd_base = os.environ["OPENDATADETECTOR_DATA"] -except KeyError: - # For older releases OPENDATADETCTOR_DATA has not been defined so we try to - # retrieve it off the LD_LIBRARY_PATH - ld_lib_paths = os.environ["LD_LIBRARY_PATH"].split(":") - odd_paths = [p for p in ld_lib_paths if "opendatadetector" in p] - if odd_paths: - odd_base = f"{odd_paths[0].rsplit('/', 1)[0]}/share/OpenDataDetector" - else: - raise +args = parser.parse_known_args()[0] dd4hep_geo = GeoSvc("GeoSvc") -dd4hep_geo.detectors = [f"{odd_base}/xml/OpenDataDetector.xml"] +dd4hep_geo.detectors = [args.compactFile] dd4hep_geo.EnableGeant4Geo = False acts_geo = ActsGeoSvc("ActsGeoSvc") -acts_geo.GeoSvcName = dd4hep_geo.name() -acts_geo.debugGeometry = True -acts_geo.outputFileName = "MyObjFile" +acts_geo.DumpVisualization = True +acts_geo.ObjVisFileName = f"{pathlib.Path(args.compactFile).stem}-acts-geo.obj" ApplicationMgr( - TopAlg=algList, + TopAlg=[], EvtSel="NONE", - EvtMax=2, - # order dependent... + EvtMax=1, ExtSvc=[dd4hep_geo, acts_geo], OutputLevel=INFO, ) From ba83b0674de0c8be91b3dd11bc8372cac991ada2 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 19 Dec 2025 16:02:08 +0100 Subject: [PATCH 49/69] Replace old Geo svc with new implementation --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 3 +++ k4ActsTracking/src/components/ActsGeoSvc.h | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index 2fabdba7..398b88f5 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -27,12 +27,15 @@ #include #include #include +#include #include +#include #include #include #include #include #include +#include #include #include #include diff --git a/k4ActsTracking/src/components/ActsGeoSvc.h b/k4ActsTracking/src/components/ActsGeoSvc.h index f0d112e5..5b0f5fe9 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.h +++ b/k4ActsTracking/src/components/ActsGeoSvc.h @@ -21,15 +21,15 @@ #include "k4ActsTracking/IActsGeoSvc.h" -#include #include +#include +#include + #include #include -#include "GaudiKernel/Service.h" - #include #include #include From 957d42cb9da678fa073f4af469af73712b196ba8 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Fri, 17 Apr 2026 17:50:56 +0200 Subject: [PATCH 50/69] Make sure tests pick up our version --- test/CMakeLists.txt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 77281806..2b9c97de 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,10 +17,21 @@ See the License for the specific language governing permissions and limitations under the License. ]] +set(test_environment "\ +LD_LIBRARY_PATH=\ +${PROJECT_BINARY_DIR}:\ +${PROJECT_BINARY_DIR}/${PROJECT_NAME}:\ +$ENV{LD_LIBRARY_PATH}; +PYTHONPATH=\ +${PROJECT_BINARY_DIR}/${PROJECT_NAME}/genConfDir:\ +$ENV{PYTHONPATH};\ +GAUDI_PLUGIN_PATH=\ +${PROJECT_BINARY_DIR}/${PROJECT_NAME}:\ +$ENV{GAUDI_PLUGIN_PATH}" +) function(set_test_env _testname) - set_property(TEST ${_testname} APPEND PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${PROJECT_BINARY_DIR}:${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}:$<$:$>:$<$:$>:$<$:$>:$ENV{LD_LIBRARY_PATH}") - set_property(TEST ${_testname} APPEND PROPERTY ENVIRONMENT "PYTHONPATH=${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}/${GAUDI_GENCONF_DIR}:$ENV{PYTHONPATH}") + set_tests_properties(${_testname} PROPERTIES ENVIRONMENT "${test_environment}") endfunction() # add_geometry_load_test( ) From 491991adf8de8629135c1c885fcf7475b2a68db7 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 20 Apr 2026 11:43:30 +0200 Subject: [PATCH 51/69] Lift grouping into helper function --- .../DD4hepBlueprintConstruction.cpp | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 6f73dbe0..8c740c93 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -33,12 +33,14 @@ #include #include +#include using Acts::Experimental::ContainerBlueprintNode; using Acts::Experimental::CylinderContainerBlueprintNode; using Acts::Experimental::LayerBlueprintNode; using AxisDefinition = ActsPlugins::DD4hep::BlueprintBuilder::AxisDefinition; +using LayerGrouper = Acts::Experimental::SensorLayerAssembler::LayerGrouper; using namespace Acts::UnitLiterals; using enum Acts::AxisDirection; @@ -72,6 +74,21 @@ namespace Blueprints { return layer; } + template > + LayerGrouper makeLayerGrouper( + std::regex groupRgx, std::string labelBase, + TransformF transformMatch = [](const std::string& match) -> std::string { return match; }) { + return [=](const auto& e) { + std::smatch match; + const std::string elemName = e.name(); + if (std::regex_match(elemName, match, groupRgx)) { + const auto matchgroup = match[1].str(); + return fmt::format("{}_{}", labelBase, transformMatch(match[1].str())); + } + throw std::invalid_argument(fmt::format("Could not match regex for grouping layers. DetElem name: {}", elemName)); + }; + } + /// Make the Acts volumes for a VertexBarrel detector where the layers are /// grouped into double layers such that these double layers end up in one /// volume in the Acts geometry. @@ -101,15 +118,8 @@ namespace Blueprints { const auto vtxBarrelDetElem = builder.findDetElementByName(containerName); const auto vtxBarrelLayerElems = builder.findDetElementByNamePattern(vtxBarrelDetElem.value(), layerRgx); - const auto doubleLayerName = [&](const auto& e) { - std::smatch match; - const std::string elemName = e.name(); - std::regex_match(elemName, match, layerRgx); - const auto layer = std::stoi(match[1].str()); - // We divide the layer number by 2 and let integer division automatially - // sort that into the correct double layer - return fmt::format("doubleLayer_{}", layer / 2); - }; + const auto doubleLayerName = + makeLayerGrouper(layerRgx, "doubleLayer", [](const auto& m) { return std::stoi(m) / 2; }); auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {1_mm, 1_mm}); return builder.layersFromSensors() @@ -191,8 +201,8 @@ namespace Blueprints { .barrelFilter = std::regex{"layer\\d"}, .endcapContainer = "OuterTrackerEndcap", .endcapAxes = "YXZ", - .endcapPosFilter = std::regex{"layer_pos\\d"}, - .endcapNegFilter = std::regex{"layer_neg\\d"}, + .endcapPosFilter = std::regex{"layer_pos(\\d)"}, + .endcapNegFilter = std::regex{"layer_neg(\\d)"}, }; const auto InnerTrackerSpec = TrackerSpec{ @@ -201,8 +211,8 @@ namespace Blueprints { .barrelFilter = std::regex{"layer\\d"}, .endcapContainer = "InnerTrackerEndcap", .endcapAxes = "YXZ", - .endcapPosFilter = std::regex{"layer_pos\\d"}, - .endcapNegFilter = std::regex{"layer_neg\\d"}, + .endcapPosFilter = std::regex{"layer_pos(\\d)"}, + .endcapNegFilter = std::regex{"layer_neg(\\d)"}, }; /// Make the Acts volumes for a regular tracker consisting of a barrel and two From b6cd23b7715e8d2913d3fcaed4c1915def448731 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 20 Apr 2026 11:43:46 +0200 Subject: [PATCH 52/69] Make tests more chatty --- test/options/load_geometry.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/options/load_geometry.py b/test/options/load_geometry.py index e3bdce18..20636b1c 100644 --- a/test/options/load_geometry.py +++ b/test/options/load_geometry.py @@ -18,7 +18,7 @@ # import pathlib -from Gaudi.Configuration import INFO +from Gaudi.Configuration import INFO, VERBOSE from Configurables import ActsGeoSvc, ApplicationMgr, GeoSvc from k4FWCore.parseArgs import parser @@ -31,6 +31,7 @@ dd4hep_geo.EnableGeant4Geo = False acts_geo = ActsGeoSvc("ActsGeoSvc") +acts_geo.OutputLevel = VERBOSE acts_geo.DumpVisualization = True acts_geo.ObjVisFileName = f"{pathlib.Path(args.compactFile).stem}-acts-geo.obj" From e1e6a8581b22dbe8b916a0b86c86dd0c2738c728 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 20 Apr 2026 13:34:57 +0200 Subject: [PATCH 53/69] Make unmodified CLD geometries convert --- .../DD4hepBlueprintConstruction.cpp | 314 +++++++++++++++++- 1 file changed, 307 insertions(+), 7 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 8c740c93..3c403b9c 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -182,6 +183,71 @@ namespace Blueprints { return vertex; } + /// Attach the Endcaps to the VertexBarrel after constructing them to create + /// the full Vertex detector node. + /// + /// This version uses a DD4hep detector geometry where the individual layers + /// have not been put into dedicated DetElements. Instead this conversion will + /// pick up all DetElements of the sensors and group them internally. + /// + /// This accepts an existing VertexBarrel blueprint node and stacks the + /// endcaps onto it along the z-axis. + /// + /// @param builder The Blueprint builder that drives the construction + /// @param vtxBarrel The vertex barrel bluprint node + /// @param containerName The detector name in which all the sensitive elements + /// are placed + /// + /// @param posLayerPattern The expression for selecting layers from the + /// DetElement with the @containerName name for the + /// positive endcap + /// @param negLayerPattern The expression for selecting layers from the + /// DetElement with the @containerName name for the + /// negative endcap + std::shared_ptr completeVertexWithUngroupedEndcaps( + ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& vtxBarrel, + const std::string& containerName = "VertexEndcap", + const std::regex& posLayerPattern = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, + const std::regex& negLayerPattern = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}) { + auto vertex = std::make_shared("Vertex", AxisZ); + vertex->addChild(vtxBarrel); + + // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure + // TODO: This looks like it's double endcap layers, so we have to group them accordingly to avoid + auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {0.5_mm, 0.5_mm}).set(AxisR, {5_mm, 5_mm}); + + const auto endcapDetElem = builder.findDetElementByName(containerName); + + const auto posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), posLayerPattern); + const auto posLayerGrouper = + makeLayerGrouper(posLayerPattern, "doubleLayer_pos", [](const auto& m) { return std::stoi(m) / 2; }); + builder.layersFromSensors() + .endcap() + .setSensorAxes("XZY") + .setContainerName(containerName) + .groupBy(posLayerGrouper) + .setSensors(std::move(posEndcapDetElems)) + .setEnvelope(vtxEndcapEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*vertex); + + const auto negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), negLayerPattern); + const auto negLayerGrouper = + makeLayerGrouper(negLayerPattern, "doubleLayer_neg", [](const auto& m) { return std::stoi(m) / 2; }); + + builder.layersFromSensors() + .endcap() + .setSensorAxes("XZY") + .setContainerName(containerName) + .setSensors(std::move(negEndcapDetElems)) + .groupBy(negLayerGrouper) + .setEnvelope(vtxEndcapEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*vertex); + + return vertex; + } + /// A simple struct to contain the configuration for building a regular /// detector where the barrel and the endcaps can be cleanly stacked along the /// z-axis @@ -205,6 +271,16 @@ namespace Blueprints { .endcapNegFilter = std::regex{"layer_neg(\\d)"}, }; + const auto UngroupedOuterTrackerSpec = TrackerSpec{ + .barrelContainer = "OuterTrackerBarrel", + .barrelAxes = "XYZ", + .barrelFilter = std::regex{"layer\\d"}, + .endcapContainer = "OuterTrackerEndcap", + .endcapAxes = "YXZ", + .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, + .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, + }; + const auto InnerTrackerSpec = TrackerSpec{ .barrelContainer = "InnerTrackerBarrel", .barrelAxes = "XYZ", @@ -215,6 +291,16 @@ namespace Blueprints { .endcapNegFilter = std::regex{"layer_neg(\\d)"}, }; + const auto UngroupedInnerTrackerSpec = TrackerSpec{ + .barrelContainer = "InnerTrackerBarrel", + .barrelAxes = "XYZ", + .barrelFilter = std::regex{"layer\\d"}, + .endcapContainer = "InnerTrackerEndcap", + .endcapAxes = "YXZ", + .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, + .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, + }; + /// Make the Acts volumes for a regular tracker consisting of a barrel and two /// endcaps that can be cleanly stacked along the z-axis without nesting. /// @@ -263,6 +349,66 @@ namespace Blueprints { return tracker; } + /// Make the Acts volumes for a regular tracker consisting of a barrel and two + /// endcaps that can be cleanly stacked along the z-axis without nesting. In + /// this case the sensors are not placed into DetElements and have to be + /// grouped first. + /// + /// This is the simple case where all endcap layers fit within the z-extent of + /// the barrel, i.e. no endcap layer protrudes into the radial envelope of the + /// barrel layers. The barrel and both endcaps are stacked along z inside a + /// single container node. + /// + /// @param builder The Blueprint builder that drives the construction + /// @param spec The configuration spec defining the barrel and endcap + /// container names, sensor axes, and layer filters + /// @param trackerName The name of the resulting top-level tracker node + /// + /// @returns The tracker blueprint node + std::shared_ptr makeRegularTrackerUngroupedEndcap( + ActsPlugins::DD4hep::BlueprintBuilder& builder, const TrackerSpec& spec, const std::string& trackerName) { + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + auto tracker = std::make_shared(trackerName, AxisZ); + + // Barrel can just be done normally + builder.layers() + .barrel() + .setSensorAxes(spec.barrelAxes) + .setLayerFilter(spec.barrelFilter) + .setContainer(spec.barrelContainer) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*tracker); + + const auto endcapDetElem = builder.findDetElementByName(spec.endcapContainer); + + const auto posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosFilter); + const auto posLayerGrouper = makeLayerGrouper(spec.endcapPosFilter, "layer_pos"); + builder.layersFromSensors() + .endcap() + .setSensors(std::move(posEndcapDetElems)) + .groupBy(posLayerGrouper) + .setSensorAxes(spec.endcapAxes) + .setContainerName(spec.endcapContainer) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*tracker); + + const auto negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegFilter); + const auto negLayerGrouper = makeLayerGrouper(spec.endcapNegFilter, "layer_neg"); + builder.layersFromSensors() + .endcap() + .setSensors(std::move(negEndcapDetElems)) + .groupBy(negLayerGrouper) + .setSensorAxes(spec.endcapAxes) + .setContainerName(spec.endcapContainer) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*tracker); + + return tracker; + } + /// A simple struct to hold configuration to build a tracker that is nested /// such that a simple stacking in z does not work. struct NestedInnerTrackerSpec { @@ -286,6 +432,19 @@ namespace Blueprints { ///< negative endcap layers }; + const auto UngroupedNestedInnerTrackerSpec = NestedInnerTrackerSpec{ + .barrelContainer = "InnerTrackerBarrel", + .barrelAxes = "XYZ", + .barrelInnerFilter = std::regex{"layer[01]"}, + .barrelOuterFilter = std::regex{"layer2"}, + .endcapContainer = "InnerTrackerEndcap", + .endcapAxes = "YXZ", + .endcapPosInnerFilter = std::regex{"layer(0)_module\\d+_sensor\\d+_pos"}, + .endcapPosOuterFilter = std::regex{"layer([1-6])_module\\d+_sensor\\d+_pos"}, + .endcapNegInnerFilter = std::regex{"layer(0)_module\\d+_sensor\\d+_neg"}, + .endcapNegOuterFilter = std::regex{"layer([1-6])_module\\d+_sensor\\d+_neg"}, + }; + /// Make a nested inner tracker that encloses the vertex. /// /// Nesting in this case means that at least one of the endcap layers @@ -402,6 +561,145 @@ namespace Blueprints { return innerTracker; } + /// Make a nested inner tracker that encloses the vertex. + /// + /// This version uses a DD4hep detector geometry where the individual layers + /// have not been put into dedicated DetElements. Instead this conversion will + /// pick up all DetElements of the sensors and group them internally. + /// + /// Nesting in this case means that at least one of the endcap layers + /// protrudes into the cylinder described by the barrel layers. This makes it + /// necessary to stack the volumes surrounding the layers in the correct order + /// in r and z to avoid overlapping volumes. + /// + /// For this specific case the tracker can only be nested "once" this means + /// that it looks something like the following. + /// + /// b b + /// a a + /// r endcap(Pos|Neg)OuterFilter r + /// r ⌄ ⌄ ⌄ ⌄ ⌄ ⌄ r + /// r | | | ───────────────────── | | | < e + /// e | | | ───────────────────── | | | < l + /// l > | | | | | ───────── | | | | | O + /// I > | | | | | ───────── | | | | | u + /// n | | | | | | | | | | t + /// n | | | | | VTX | | | | | e + /// e | | | | | | | | | | r + /// r > | | | | | ───────── | | | | | F + /// F > | | | | | ───────── | | | | | i + /// i | | | ───────────────────── | | | < l + /// l | | | ───────────────────── | | | < t + /// t e + /// e ^ ^ ^ ^ r + /// r endcap(Pos|Neg)InnerFilter + /// + /// The labels correspond to the members of the NestedInnerTrackerSpec. + /// + /// @param builder The Blueprint builder that drives the construction + /// @param vertex The vertex detector blueprint node + /// @param spec The spec for defining how the nesting is done specifically + /// for this detector + /// + /// @returns The inner tracker blueprint node + std::shared_ptr makeNestedInnerTrackerUngroupedEndcaps( + ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& vertex, + const NestedInnerTrackerSpec& spec = UngroupedNestedInnerTrackerSpec) { + // We have to create the inner tracker in several steps, because the inner + // most endcap layer protrudes into the envelope that is created by the + // outermost barrel layer. That creates an overlap in z while stacking. + // Hence, we build it in steps grouping the innermost two layers of the + // barrel and the innermost layer of the endcap into an "inner" inner + // tracker (stacking them along z), we then stack the last barrel layer + // along r, before stacking the remaining endcap layers along z. + // Additionally, we have to first put the whole vertex detector inside the + // two innermost InnerTrackerBarrel layers because the outermost vertex + // layer extends further in r, than the innermost border of the InnerTracker + // endcaps. Hence, we also need to stack them in the correct order. + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + auto innerInnerBarrel = builder.layers() + .barrel() + .setSensorAxes(spec.barrelAxes) + .setLayerFilter(spec.barrelInnerFilter) + .setContainer(spec.barrelContainer) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .onLayer(Blueprints::unsetXYCoG) + .build(); + innerInnerBarrel->addChild(vertex); + + auto innerInnerTracker = std::make_shared("InnerInnerTracker", AxisZ); + innerInnerTracker->addChild(innerInnerBarrel); + + auto endcapDetElem = builder.findDetElementByName(spec.endcapContainer); + auto posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosInnerFilter); + auto posLayerGrouper = makeLayerGrouper(spec.endcapPosInnerFilter, "layer_pos"); + builder.layersFromSensors() + .endcap() + .setSensorAxes(spec.endcapAxes) + .setContainerName(spec.endcapContainer) + .setSensors(std::move(posEndcapDetElems)) + .groupBy(posLayerGrouper) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerInnerTracker); + + auto negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegInnerFilter); + auto negLayerGrouper = makeLayerGrouper(spec.endcapNegInnerFilter, "layer_neg"); + + builder.layersFromSensors() + .endcap() + .setSensorAxes(spec.endcapAxes) + .setContainerName(spec.endcapContainer) + .setSensors(std::move(negEndcapDetElems)) + .groupBy(negLayerGrouper) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerInnerTracker); + + auto innerTracker = std::make_shared("InnerTracker", AxisZ); + innerTracker->addCylinderContainer("InnerTrackerBarrel", AxisR, [&](auto& innerBarrel) { + innerBarrel.addChild(innerInnerTracker); + builder.layers() + .barrel() + .setSensorAxes(spec.barrelAxes) + .setContainer(spec.barrelContainer) + .setLayerFilter(spec.barrelOuterFilter) + .setEnvelope(envelope) + .onLayer(Blueprints::unsetXYCoG) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(innerBarrel); + }); + + // Then add the (rest of the) two endcaps + posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosOuterFilter); + posLayerGrouper = makeLayerGrouper(spec.endcapPosOuterFilter, "layer_pos"); + builder.layersFromSensors() + .endcap() + .setSensorAxes(spec.endcapAxes) + .setContainerName(spec.endcapContainer) + .setSensors(std::move(posEndcapDetElems)) + .groupBy(posLayerGrouper) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerTracker); + + negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegOuterFilter); + negLayerGrouper = makeLayerGrouper(spec.endcapNegOuterFilter, "layer_neg"); + + builder.layersFromSensors() + .endcap() + .setSensorAxes(spec.endcapAxes) + .setContainerName(spec.endcapContainer) + .setSensors(std::move(negEndcapDetElems)) + .groupBy(negLayerGrouper) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(*innerTracker); + + return innerTracker; + } + } // namespace Blueprints namespace MuColl { @@ -444,10 +742,11 @@ namespace FCCee { Blueprints::addCylindricalBeampipe(outer); auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); - auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vtxBarrel)); + auto vertex = Blueprints::completeVertexWithUngroupedEndcaps(builder, std::move(vtxBarrel)); outer.addChild(vertex); - auto innerTracker = Blueprints::makeRegularTracker(builder, Blueprints::InnerTrackerSpec, "InnerTracker"); + auto innerTracker = + Blueprints::makeRegularTrackerUngroupedEndcap(builder, Blueprints::UngroupedInnerTrackerSpec, "InnerTracker"); outer.addChild(innerTracker); } } // namespace ILD_FCCee_v01 @@ -459,9 +758,9 @@ namespace FCCee { Blueprints::addCylindricalBeampipe(outer); auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); - auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vtxBarrel)); + auto vertex = Blueprints::completeVertexWithUngroupedEndcaps(builder, std::move(vtxBarrel)); - auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex)); + auto innerTracker = Blueprints::makeNestedInnerTrackerUngroupedEndcaps(builder, std::move(vertex)); outer.addChild(innerTracker); } } // namespace ILD_FCCee_v02 @@ -472,12 +771,13 @@ namespace FCCee { auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); - auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vtxBarrel)); + auto vertex = Blueprints::completeVertexWithUngroupedEndcaps(builder, std::move(vtxBarrel)); - auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex)); + auto innerTracker = Blueprints::makeNestedInnerTrackerUngroupedEndcaps(builder, std::move(vertex)); outer.addChild(innerTracker); - auto outerTracker = Blueprints::makeRegularTracker(builder, Blueprints::OuterTrackerSpec, "OuterTracker"); + auto outerTracker = + Blueprints::makeRegularTrackerUngroupedEndcap(builder, Blueprints::UngroupedOuterTrackerSpec, "OuterTracker"); outer.addChild(outerTracker); } } // namespace CLD_o2_v07 From face23fa36d7215c0fa9e03fb83e190d7b54dec7 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 20 Apr 2026 16:29:27 +0200 Subject: [PATCH 54/69] Generalize endcap attachment function --- .../DD4hepBlueprintConstruction.cpp | 190 +++++++++--------- 1 file changed, 95 insertions(+), 95 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 3c403b9c..024a565f 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -135,132 +135,132 @@ namespace Blueprints { .build(); } - /// Attach the Endcaps to the VertexBarrel after constructing them to create - /// the full Vertex detector node. - /// - /// This accepts an existing VertexBarrel blueprint node and stacks the - /// endcaps onto it along the z-axis. + /// A simple struct to contain the configuration for building a regular + /// detector where the barrel and the endcaps can be cleanly stacked along the + /// z-axis + struct TrackerSpec { + std::string barrelContainer; ///< Name of the DetElement containing the barrel + AxisDefinition barrelAxes; ///< The axes directions for the barrel sensors + std::regex barrelFilter; ///< The layer pattern to filter out barrel layers + std::string endcapContainer; ///< Name of the DetElement containing the endcaps + AxisDefinition endcapAxes; ///< The axes directions for the endcap sensors + std::regex endcapPosFilter; ///< The layer pattern to filter out positive endcap layers + std::regex endcapNegFilter; ///< The layer pattern to filter out negative endcap layers + }; + + // Vertex endcap specs reuse TrackerSpec — barrel fields are ignored since the + // vertex barrel is always built separately via makeDoubleLayerVertexBarrel. + const auto GroupedVertexSpec = TrackerSpec{ + .barrelContainer = {}, + .barrelAxes = "XYZ", + .barrelFilter = {}, + .endcapContainer = "VertexEndcap", + .endcapAxes = "XZY", + .endcapPosFilter = std::regex{"layer_pos\\d+"}, + .endcapNegFilter = std::regex{"layer_neg\\d+"}, + }; + + const auto UngroupedVertexSpec = TrackerSpec{ + .barrelContainer = {}, + .barrelAxes = "XYZ", + .barrelFilter = {}, + .endcapContainer = "VertexEndcap", + .endcapAxes = "XZY", + .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, + .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, + }; + + /// Attach endcaps to an existing barrel node to form a complete cylindrical + /// detector node stacked along the z-axis. /// - /// @param builder The Blueprint builder that drives the construction - /// @param vtxBarrel The vertex barrel bluprint node - /// @param containerName The detector name in which all the sensitive elements - /// are placed + /// The layers must already be organised into dedicated DetElements so that + /// @c builder.layers() can pick them up directly via the filter patterns in + /// @p spec. /// - /// @param posLayerPattern The expression for selecting layers from the - /// DetElement with the @containerName name for the - /// positive endcap - /// @param negLayerPattern The expression for selecting layers from the - /// DetElement with the @containerName name for the - /// negative endcap - std::shared_ptr completeVertexWithEndcaps( - ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& vtxBarrel, - const std::string& containerName = "VertexEndcap", - const std::regex& posLayerPattern = std::regex{"layer_pos\\d+"}, - const std::regex& negLayerPattern = std::regex{"layer_neg\\d+"}) { - auto vertex = std::make_shared("Vertex", AxisZ); - vertex->addChild(vtxBarrel); + /// @param builder The Blueprint builder that drives the construction + /// @param barrel The barrel blueprint node to attach the endcaps to + /// @param spec Endcap configuration (container name, axes, pos/neg filters); + /// barrel fields of the spec are ignored + std::shared_ptr attachEndcaps(ActsPlugins::DD4hep::BlueprintBuilder& builder, + std::shared_ptr&& barrel, + const TrackerSpec& spec = GroupedVertexSpec) { + auto node = std::make_shared("Vertex", AxisZ); + node->addChild(barrel); // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure - auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); builder.layers() .endcap() - .setSensorAxes("XZY") - .setContainer(containerName) - .setLayerFilter(posLayerPattern) - .setEnvelope(vtxEndcapEnvelope) + .setSensorAxes(spec.endcapAxes) + .setContainer(spec.endcapContainer) + .setLayerFilter(spec.endcapPosFilter) + .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*vertex); + .addTo(*node); builder.layers() .endcap() - .setSensorAxes("XZY") - .setContainer(containerName) - .setLayerFilter(negLayerPattern) - .setEnvelope(vtxEndcapEnvelope) + .setSensorAxes(spec.endcapAxes) + .setContainer(spec.endcapContainer) + .setLayerFilter(spec.endcapNegFilter) + .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*vertex); + .addTo(*node); - return vertex; + return node; } - /// Attach the Endcaps to the VertexBarrel after constructing them to create - /// the full Vertex detector node. + /// Attach endcaps to an existing barrel node to form a complete cylindrical + /// detector node stacked along the z-axis. /// - /// This version uses a DD4hep detector geometry where the individual layers - /// have not been put into dedicated DetElements. Instead this conversion will - /// pick up all DetElements of the sensors and group them internally. - /// - /// This accepts an existing VertexBarrel blueprint node and stacks the - /// endcaps onto it along the z-axis. + /// Use this variant when the individual sensors have not been placed into + /// dedicated layer DetElements. The function collects all matching sensor + /// DetElements and groups them into layers internally via @c layersFromSensors. /// - /// @param builder The Blueprint builder that drives the construction - /// @param vtxBarrel The vertex barrel bluprint node - /// @param containerName The detector name in which all the sensitive elements - /// are placed - /// - /// @param posLayerPattern The expression for selecting layers from the - /// DetElement with the @containerName name for the - /// positive endcap - /// @param negLayerPattern The expression for selecting layers from the - /// DetElement with the @containerName name for the - /// negative endcap - std::shared_ptr completeVertexWithUngroupedEndcaps( - ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& vtxBarrel, - const std::string& containerName = "VertexEndcap", - const std::regex& posLayerPattern = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, - const std::regex& negLayerPattern = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}) { - auto vertex = std::make_shared("Vertex", AxisZ); - vertex->addChild(vtxBarrel); - - // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure - // TODO: This looks like it's double endcap layers, so we have to group them accordingly to avoid - auto vtxEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {0.5_mm, 0.5_mm}).set(AxisR, {5_mm, 5_mm}); + /// @param builder The Blueprint builder that drives the construction + /// @param vtxBarrel The barrel blueprint node to attach the endcaps to + /// @param spec Endcap configuration (container name, axes, pos/neg filters); + /// barrel fields of the spec are ignored + std::shared_ptr attachUngroupedEndcaps( + ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& barrel, + const TrackerSpec& spec = UngroupedVertexSpec) { + auto node = std::make_shared("Vertex", AxisZ); + node->addChild(barrel); + + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {0.5_mm, 0.5_mm}).set(AxisR, {5_mm, 5_mm}); - const auto endcapDetElem = builder.findDetElementByName(containerName); + const auto endcapDetElem = builder.findDetElementByName(spec.endcapContainer); - const auto posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), posLayerPattern); + const auto posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosFilter); const auto posLayerGrouper = - makeLayerGrouper(posLayerPattern, "doubleLayer_pos", [](const auto& m) { return std::stoi(m) / 2; }); + makeLayerGrouper(spec.endcapPosFilter, "doubleLayer_pos", [](const auto& m) { return std::stoi(m) / 2; }); builder.layersFromSensors() .endcap() - .setSensorAxes("XZY") - .setContainerName(containerName) + .setSensorAxes(spec.endcapAxes) + .setContainerName(spec.endcapContainer) .groupBy(posLayerGrouper) .setSensors(std::move(posEndcapDetElems)) - .setEnvelope(vtxEndcapEnvelope) + .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*vertex); + .addTo(*node); - const auto negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), negLayerPattern); + const auto negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegFilter); const auto negLayerGrouper = - makeLayerGrouper(negLayerPattern, "doubleLayer_neg", [](const auto& m) { return std::stoi(m) / 2; }); + makeLayerGrouper(spec.endcapNegFilter, "doubleLayer_neg", [](const auto& m) { return std::stoi(m) / 2; }); builder.layersFromSensors() .endcap() - .setSensorAxes("XZY") - .setContainerName(containerName) + .setSensorAxes(spec.endcapAxes) + .setContainerName(spec.endcapContainer) .setSensors(std::move(negEndcapDetElems)) .groupBy(negLayerGrouper) - .setEnvelope(vtxEndcapEnvelope) + .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*vertex); + .addTo(*node); - return vertex; + return node; } - /// A simple struct to contain the configuration for building a regular - /// detector where the barrel and the endcaps can be cleanly stacked along the - /// z-axis - struct TrackerSpec { - std::string barrelContainer; ///< Name of the DetElement containing the barrel - AxisDefinition barrelAxes; ///< The axes directions for the barrel sensors - std::regex barrelFilter; ///< The layer pattern to filter out barrel layers - std::string endcapContainer; ///< Name of the DetElement containing the endcaps - AxisDefinition endcapAxes; ///< The axes directions for the endcap sensors - std::regex endcapPosFilter; ///< The layer pattern to filter out positive endcap layers - std::regex endcapNegFilter; ///< The layer pattern to filter out negative endcap layers - }; - const auto OuterTrackerSpec = TrackerSpec{ .barrelContainer = "OuterTrackerBarrel", .barrelAxes = "XYZ", @@ -722,7 +722,7 @@ namespace MuColl { .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .onLayer(Blueprints::unsetXYCoG) .build(); - auto vertex = Blueprints::completeVertexWithEndcaps(builder, std::move(vertexBarrel)); + auto vertex = Blueprints::attachEndcaps(builder, std::move(vertexBarrel)); auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex)); outer.addChild(innerTracker); @@ -742,7 +742,7 @@ namespace FCCee { Blueprints::addCylindricalBeampipe(outer); auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); - auto vertex = Blueprints::completeVertexWithUngroupedEndcaps(builder, std::move(vtxBarrel)); + auto vertex = Blueprints::attachUngroupedEndcaps(builder, std::move(vtxBarrel)); outer.addChild(vertex); auto innerTracker = @@ -758,7 +758,7 @@ namespace FCCee { Blueprints::addCylindricalBeampipe(outer); auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); - auto vertex = Blueprints::completeVertexWithUngroupedEndcaps(builder, std::move(vtxBarrel)); + auto vertex = Blueprints::attachUngroupedEndcaps(builder, std::move(vtxBarrel)); auto innerTracker = Blueprints::makeNestedInnerTrackerUngroupedEndcaps(builder, std::move(vertex)); outer.addChild(innerTracker); @@ -771,7 +771,7 @@ namespace FCCee { auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); - auto vertex = Blueprints::completeVertexWithUngroupedEndcaps(builder, std::move(vtxBarrel)); + auto vertex = Blueprints::attachUngroupedEndcaps(builder, std::move(vtxBarrel)); auto innerTracker = Blueprints::makeNestedInnerTrackerUngroupedEndcaps(builder, std::move(vertex)); outer.addChild(innerTracker); From eaac365d29c3c4ec31f552fda994a11fa8cd04c8 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 20 Apr 2026 20:52:45 +0200 Subject: [PATCH 55/69] Remove unused member --- k4ActsTracking/src/components/ActsGeoSvc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.h b/k4ActsTracking/src/components/ActsGeoSvc.h index 5b0f5fe9..53a743e5 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.h +++ b/k4ActsTracking/src/components/ActsGeoSvc.h @@ -71,7 +71,6 @@ class ActsGeoSvc : public extends { using BlueprintPopulationFunc = void(const std::string&, Acts::Experimental::Blueprint&, BlueprintBuilder&); - dd4hep::Detector* m_dd4hepGeo{nullptr}; SmartIF m_geoSvc; std::shared_ptr m_trackingGeo{nullptr}; std::shared_ptr m_magneticField{nullptr}; From 583c90b33713c8ac898f76bfc86a5a3ea357ffa7 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 20 Apr 2026 20:52:57 +0200 Subject: [PATCH 56/69] Explicitly check CellID to surface map for mismatch Make sure that the resulting mapping will work as expected --- k4ActsTracking/src/components/ActsGeoSvc.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/k4ActsTracking/src/components/ActsGeoSvc.cpp b/k4ActsTracking/src/components/ActsGeoSvc.cpp index 398b88f5..0b1f7bce 100644 --- a/k4ActsTracking/src/components/ActsGeoSvc.cpp +++ b/k4ActsTracking/src/components/ActsGeoSvc.cpp @@ -142,6 +142,12 @@ StatusCode ActsGeoSvc::initialize() { info() << fmt::format("Visited {} Surfaces and inserted {} pairs of CellID -> Acts::Surface* into the map.", nSurfaces, m_cellIDToSurface.size()) << endmsg; + if (nSurfaces != m_cellIDToSurface.size()) { + error() << fmt::format("{} Surfaces in the Tracking geometry but only {} distinct CellIDs found.", nSurfaces, + m_cellIDToSurface.size()); + return StatusCode::FAILURE; + } + if (m_dumpVisualization.value()) { info() << "Creating visualiztion" << endmsg; // Adjust the scale here to make it easier to import in blender From acb88bbfc1892123fe26d06e53dd59067976799b Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 20 Apr 2026 21:03:33 +0200 Subject: [PATCH 57/69] Properly stack ILD_FCCee_v01 InnerTrackerEndcaps have to be stacked along z after the stacking on the VertexBarrel along r --- .../DD4hepBlueprintConstruction.cpp | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 024a565f..0709ed9d 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -170,6 +170,31 @@ namespace Blueprints { .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, }; + /// Make a barrel blueprint node for a generic cylindrical detector. + /// + /// Uses the pre-grouped layer DetElements directly (via @c layers()), so + /// the barrel layers must already be organised into dedicated DetElements + /// matching @p spec.barrelFilter inside @p spec.barrelContainer. + /// + /// @param builder The Blueprint builder that drives the construction + /// @param spec Configuration spec (barrelContainer, barrelAxes, + /// barrelFilter used; endcap fields ignored) + /// + /// @returns The barrel blueprint node + std::shared_ptr makeBarrel(ActsPlugins::DD4hep::BlueprintBuilder& builder, + const TrackerSpec& spec) { + auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {1_mm, 1_mm}); + return builder.layers() + .barrel() + .setSensorAxes(spec.barrelAxes) + .setLayerFilter(spec.barrelFilter) + .setContainer(spec.barrelContainer) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .onLayer(unsetXYCoG) + .build(); + } + /// Attach endcaps to an existing barrel node to form a complete cylindrical /// detector node stacked along the z-axis. /// @@ -217,14 +242,15 @@ namespace Blueprints { /// dedicated layer DetElements. The function collects all matching sensor /// DetElements and groups them into layers internally via @c layersFromSensors. /// - /// @param builder The Blueprint builder that drives the construction - /// @param vtxBarrel The barrel blueprint node to attach the endcaps to - /// @param spec Endcap configuration (container name, axes, pos/neg filters); - /// barrel fields of the spec are ignored + /// @param builder The Blueprint builder that drives the construction + /// @param barrel The barrel blueprint node to attach the endcaps to + /// @param spec Endcap configuration (container name, axes, pos/neg filters); + /// barrel fields of the spec are ignored + /// @param containerName Name of the resulting top-level cylinder container node std::shared_ptr attachUngroupedEndcaps( ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& barrel, - const TrackerSpec& spec = UngroupedVertexSpec) { - auto node = std::make_shared("Vertex", AxisZ); + const TrackerSpec& spec = UngroupedVertexSpec, const std::string& containerName = "Vertex") { + auto node = std::make_shared(containerName, AxisZ); node->addChild(barrel); auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {0.5_mm, 0.5_mm}).set(AxisR, {5_mm, 5_mm}); @@ -686,7 +712,6 @@ namespace Blueprints { negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegOuterFilter); negLayerGrouper = makeLayerGrouper(spec.endcapNegOuterFilter, "layer_neg"); - builder.layersFromSensors() .endcap() .setSensorAxes(spec.endcapAxes) @@ -743,11 +768,14 @@ namespace FCCee { auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); auto vertex = Blueprints::attachUngroupedEndcaps(builder, std::move(vtxBarrel)); - outer.addChild(vertex); - auto innerTracker = - Blueprints::makeRegularTrackerUngroupedEndcap(builder, Blueprints::UngroupedInnerTrackerSpec, "InnerTracker"); - outer.addChild(innerTracker); + auto innerTrackerBarrel = Blueprints::makeBarrel(builder, Blueprints::UngroupedInnerTrackerSpec); + innerTrackerBarrel->addChild(vertex); + + auto innerTrackerEndcap = Blueprints::attachUngroupedEndcaps( + builder, std::move(innerTrackerBarrel), Blueprints::UngroupedInnerTrackerSpec, "InnerTrackerEndcap"); + + outer.addChild(innerTrackerEndcap); } } // namespace ILD_FCCee_v01 From d3300325300fa5973c83d17d612dbfe173a7f8b4 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 20 Apr 2026 21:08:15 +0200 Subject: [PATCH 58/69] Make grouped layers unique without Acts help Functionality has not landed in Acts yet and we can work around it for now --- .../DD4hepBlueprintConstruction.cpp | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 0709ed9d..b248de71 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -119,8 +119,8 @@ namespace Blueprints { const auto vtxBarrelDetElem = builder.findDetElementByName(containerName); const auto vtxBarrelLayerElems = builder.findDetElementByNamePattern(vtxBarrelDetElem.value(), layerRgx); - const auto doubleLayerName = - makeLayerGrouper(layerRgx, "doubleLayer", [](const auto& m) { return std::stoi(m) / 2; }); + const auto doubleLayerName = makeLayerGrouper(layerRgx, fmt::format("{}|doubleLayer", containerName), + [](const auto& m) { return std::stoi(m) / 2; }); auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {1_mm, 1_mm}); return builder.layersFromSensors() @@ -259,7 +259,8 @@ namespace Blueprints { const auto posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosFilter); const auto posLayerGrouper = - makeLayerGrouper(spec.endcapPosFilter, "doubleLayer_pos", [](const auto& m) { return std::stoi(m) / 2; }); + makeLayerGrouper(spec.endcapPosFilter, fmt::format("{}|doubleLayer_pos", spec.endcapContainer), + [](const auto& m) { return std::stoi(m) / 2; }); builder.layersFromSensors() .endcap() .setSensorAxes(spec.endcapAxes) @@ -272,7 +273,8 @@ namespace Blueprints { const auto negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegFilter); const auto negLayerGrouper = - makeLayerGrouper(spec.endcapNegFilter, "doubleLayer_neg", [](const auto& m) { return std::stoi(m) / 2; }); + makeLayerGrouper(spec.endcapNegFilter, fmt::format("{}|doubleLayer_neg", spec.endcapContainer), + [](const auto& m) { return std::stoi(m) / 2; }); builder.layersFromSensors() .endcap() @@ -409,7 +411,8 @@ namespace Blueprints { const auto endcapDetElem = builder.findDetElementByName(spec.endcapContainer); const auto posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosFilter); - const auto posLayerGrouper = makeLayerGrouper(spec.endcapPosFilter, "layer_pos"); + const auto posLayerGrouper = + makeLayerGrouper(spec.endcapPosFilter, fmt::format("{}|layer_pos", spec.endcapContainer)); builder.layersFromSensors() .endcap() .setSensors(std::move(posEndcapDetElems)) @@ -421,7 +424,8 @@ namespace Blueprints { .addTo(*tracker); const auto negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegFilter); - const auto negLayerGrouper = makeLayerGrouper(spec.endcapNegFilter, "layer_neg"); + const auto negLayerGrouper = + makeLayerGrouper(spec.endcapNegFilter, fmt::format("{}|layer_neg", spec.endcapContainer)); builder.layersFromSensors() .endcap() .setSensors(std::move(negEndcapDetElems)) @@ -659,7 +663,8 @@ namespace Blueprints { auto endcapDetElem = builder.findDetElementByName(spec.endcapContainer); auto posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosInnerFilter); - auto posLayerGrouper = makeLayerGrouper(spec.endcapPosInnerFilter, "layer_pos"); + auto posLayerGrouper = + makeLayerGrouper(spec.endcapPosInnerFilter, fmt::format("{}|layer_pos", spec.endcapContainer)); builder.layersFromSensors() .endcap() .setSensorAxes(spec.endcapAxes) @@ -671,7 +676,8 @@ namespace Blueprints { .addTo(*innerInnerTracker); auto negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegInnerFilter); - auto negLayerGrouper = makeLayerGrouper(spec.endcapNegInnerFilter, "layer_neg"); + auto negLayerGrouper = + makeLayerGrouper(spec.endcapNegInnerFilter, fmt::format("{}|layer_neg", spec.endcapContainer)); builder.layersFromSensors() .endcap() @@ -699,7 +705,7 @@ namespace Blueprints { // Then add the (rest of the) two endcaps posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosOuterFilter); - posLayerGrouper = makeLayerGrouper(spec.endcapPosOuterFilter, "layer_pos"); + posLayerGrouper = makeLayerGrouper(spec.endcapPosOuterFilter, fmt::format("{}|layer_pos", spec.endcapContainer)); builder.layersFromSensors() .endcap() .setSensorAxes(spec.endcapAxes) @@ -711,7 +717,7 @@ namespace Blueprints { .addTo(*innerTracker); negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegOuterFilter); - negLayerGrouper = makeLayerGrouper(spec.endcapNegOuterFilter, "layer_neg"); + negLayerGrouper = makeLayerGrouper(spec.endcapNegOuterFilter, fmt::format("{}|layer_neg", spec.endcapContainer)); builder.layersFromSensors() .endcap() .setSensorAxes(spec.endcapAxes) From 5054d33b03acce8d9515517f3858f8108f25fc02 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 Apr 2026 09:04:27 +0200 Subject: [PATCH 59/69] Add SET to ILD_FCCee_v01 and generalize double layer barrel creation --- .../DD4hepBlueprintConstruction.cpp | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index b248de71..097f4f0b 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -90,34 +90,31 @@ namespace Blueprints { }; } - /// Make the Acts volumes for a VertexBarrel detector where the layers are - /// grouped into double layers such that these double layers end up in one - /// volume in the Acts geometry. + /// Make the Acts volumes for a barrel detector where sensors are grouped into + /// double layers, each double layer ending up in one volume in the Acts geometry. /// - /// This might be necessary in case the spacing between double layers is too - /// small to have cylinder shells that do not overlap + /// Use this when sensors are not placed into dedicated layer DetElements and + /// the spacing between adjacent layers is too small for non-overlapping + /// cylinder shells if each layer were its own volume. /// /// @param builder The Blueprint builder that drives the construction - /// @param containerName The detector name in which all the sensitive elements - /// are placed - /// @param layerRgx The match expression to filter out the sensitive - /// elements. @note that these should not be the - /// "top-level" layer DetElements, but rather the - /// ladders. @note This needs to contain exactly one - /// matching group which has to be convertible to int as - /// that is what will be used for grouping them into - /// double layers + /// @param containerName The name of the DetElement containing the sensors + /// @param layerRgx Regex to select sensor DetElements. Must not match + /// top-level layer DetElements but the individual sensors + /// (e.g. ladders). Must contain exactly one capture group + /// whose value is convertible to int — adjacent pairs + /// (floor(n/2)) are merged into a single double layer. /// - /// @returns The vertex barrel blueprint node - std::shared_ptr makeDoubleLayerVertexBarrel(ActsPlugins::DD4hep::BlueprintBuilder& builder, - const std::string& containerName = "VertexBarrel", - const std::regex& layerRgx = std::regex{ - "VertexBarrel_layer(\\d)_ladder\\d+"}) { + /// @returns The barrel blueprint node + std::shared_ptr makeDoubleLayerBarrel(ActsPlugins::DD4hep::BlueprintBuilder& builder, + const std::string& containerName = "VertexBarrel", + const std::regex& layerRgx = std::regex{ + "VertexBarrel_layer(\\d)_ladder\\d+"}) { // Vertex Barrel has a double layer gap of only 1 mm. This makes it // (almost) impossible to fit them into mutually exclusive cylinder shell // volumes. Hence, we make each double layer an Acts layer / volume. - const auto vtxBarrelDetElem = builder.findDetElementByName(containerName); - const auto vtxBarrelLayerElems = builder.findDetElementByNamePattern(vtxBarrelDetElem.value(), layerRgx); + const auto barrelDetElem = builder.findDetElementByName(containerName); + const auto barrelLayerElems = builder.findDetElementByNamePattern(barrelDetElem.value(), layerRgx); const auto doubleLayerName = makeLayerGrouper(layerRgx, fmt::format("{}|doubleLayer", containerName), [](const auto& m) { return std::stoi(m) / 2; }); @@ -128,7 +125,7 @@ namespace Blueprints { .setEnvelope(barrelEnvelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .setSensorAxes("ZYX") - .setSensors(std::move(vtxBarrelLayerElems)) + .setSensors(std::move(barrelLayerElems)) .groupBy(doubleLayerName) .setContainerName(containerName) .onLayer(unsetXYCoG) @@ -772,7 +769,7 @@ namespace FCCee { Blueprints::addCylindricalBeampipe(outer); - auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); + auto vtxBarrel = Blueprints::makeDoubleLayerBarrel(builder); auto vertex = Blueprints::attachUngroupedEndcaps(builder, std::move(vtxBarrel)); auto innerTrackerBarrel = Blueprints::makeBarrel(builder, Blueprints::UngroupedInnerTrackerSpec); @@ -780,8 +777,10 @@ namespace FCCee { auto innerTrackerEndcap = Blueprints::attachUngroupedEndcaps( builder, std::move(innerTrackerBarrel), Blueprints::UngroupedInnerTrackerSpec, "InnerTrackerEndcap"); - outer.addChild(innerTrackerEndcap); + + auto set = Blueprints::makeDoubleLayerBarrel(builder, "SET", std::regex{"set_ladder_(\\d)_\\d_\\d+"}); + outer.addChild(set); } } // namespace ILD_FCCee_v01 @@ -791,7 +790,7 @@ namespace FCCee { auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); - auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); + auto vtxBarrel = Blueprints::makeDoubleLayerBarrel(builder); auto vertex = Blueprints::attachUngroupedEndcaps(builder, std::move(vtxBarrel)); auto innerTracker = Blueprints::makeNestedInnerTrackerUngroupedEndcaps(builder, std::move(vertex)); @@ -804,7 +803,7 @@ namespace FCCee { ActsPlugins::DD4hep::BlueprintBuilder& builder) { auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); - auto vtxBarrel = Blueprints::makeDoubleLayerVertexBarrel(builder); + auto vtxBarrel = Blueprints::makeDoubleLayerBarrel(builder); auto vertex = Blueprints::attachUngroupedEndcaps(builder, std::move(vtxBarrel)); auto innerTracker = Blueprints::makeNestedInnerTrackerUngroupedEndcaps(builder, std::move(vertex)); From a78b9556a0fea2132a2cd0a5b1aa9aa62b6a2a58 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 Apr 2026 10:59:20 +0200 Subject: [PATCH 60/69] Re-use existing options file for tests --- ...sActsGEo.py => test_visualize_acts_geo.py} | 29 ++++++++---- test/CMakeLists.txt | 6 ++- test/options/load_geometry.py | 44 ------------------- 3 files changed, 24 insertions(+), 55 deletions(-) rename k4ActsTracking/examples/{visActsGEo.py => test_visualize_acts_geo.py} (66%) delete mode 100644 test/options/load_geometry.py diff --git a/k4ActsTracking/examples/visActsGEo.py b/k4ActsTracking/examples/test_visualize_acts_geo.py similarity index 66% rename from k4ActsTracking/examples/visActsGEo.py rename to k4ActsTracking/examples/test_visualize_acts_geo.py index 2fde615f..18ba6c63 100644 --- a/k4ActsTracking/examples/visActsGEo.py +++ b/k4ActsTracking/examples/test_visualize_acts_geo.py @@ -18,35 +18,46 @@ # limitations under the License. # -from Gaudi.Configuration import VERBOSE, DEBUG +import pathlib + +from Gaudi.Configuration import DEBUG, VERBOSE from Configurables import ActsGeoSvc, GeoSvc, ActsTestPropagator, EventDataSvc from k4FWCore import ApplicationMgr, IOSvc from k4FWCore.parseArgs import parser parser.add_argument("--compactFile", help="Compact file") +parser.add_argument( + "--test-propagation", + help="Test propagation through the geometry using an ACTS particle gun", + action="store_true", + default=False, +) args = parser.parse_known_args()[0] iosvc = IOSvc() -iosvc.Output = "steps.root" +if args.test_propagation: + iosvc.Output = "steps.root" geoSvc = GeoSvc() geoSvc.detectors = [args.compactFile] actsGeoSvc = ActsGeoSvc("ActsGeoSvc") actsGeoSvc.DumpVisualization = True -actsGeoSvc.ObjVisFileName = "full_mucoll_old_vertex_endcap.obj" -actsGeoSvc.OutputLevel = DEBUG +actsGeoSvc.ObjVisFileName = f"{pathlib.Path(args.compactFile).stem}-acts-geo.obj" +actsGeoSvc.OutputLevel = VERBOSE -propTest = ActsTestPropagator("TestPropagator") -propTest.OutputLevel = DEBUG -propTest.NumTracks = 20000 +alg_list = [] +if args.test_propagation: + propTest = ActsTestPropagator("TestPropagator") + propTest.OutputLevel = DEBUG + propTest.NumTracks = 100 + alg_list.append(propTest) ApplicationMgr( - TopAlg=[], - # TopAlg=[], + TopAlg=alg_list, ExtSvc=[geoSvc, actsGeoSvc, EventDataSvc()], EvtMax=1, EvtSel="NONE", diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2b9c97de..6898adc9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -42,8 +42,10 @@ function(add_geometry_load_test _compact_file) get_filename_component(_name "${_compact_file}" NAME_WE) add_test(NAME load_geo_${_name} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND k4run ${CMAKE_CURRENT_SOURCE_DIR}/options/load_geometry.py - --compactFile "${_compact_file}") + COMMAND k4run ${PROJECT_SOURCE_DIR}/k4ActsTracking/examples/test_visualize_acts_geo.py + --compactFile "${_compact_file}" + --test-propagation + ) set_test_env(load_geo_${_name}) endfunction() diff --git a/test/options/load_geometry.py b/test/options/load_geometry.py deleted file mode 100644 index 20636b1c..00000000 --- a/test/options/load_geometry.py +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (c) 2014-2024 Key4hep-Project. -# -# This file is part of Key4hep. -# See https://key4hep.github.io/key4hep-doc/ for further info. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import pathlib - -from Gaudi.Configuration import INFO, VERBOSE -from Configurables import ActsGeoSvc, ApplicationMgr, GeoSvc -from k4FWCore.parseArgs import parser - -parser.add_argument("--compactFile", help="The compact file of the geometry to load") - -args = parser.parse_known_args()[0] - -dd4hep_geo = GeoSvc("GeoSvc") -dd4hep_geo.detectors = [args.compactFile] -dd4hep_geo.EnableGeant4Geo = False - -acts_geo = ActsGeoSvc("ActsGeoSvc") -acts_geo.OutputLevel = VERBOSE -acts_geo.DumpVisualization = True -acts_geo.ObjVisFileName = f"{pathlib.Path(args.compactFile).stem}-acts-geo.obj" - -ApplicationMgr( - TopAlg=[], - EvtSel="NONE", - EvtMax=1, - ExtSvc=[dd4hep_geo, acts_geo], - OutputLevel=INFO, -) From 46e74ea3c8c8af6dde979b3467c256b3f3921a36 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 Apr 2026 11:19:24 +0200 Subject: [PATCH 61/69] Refactor blueprint construction to reduce code duplication Done via Claude (Opus 4.7 for plan, Sonnet 4.6 for execution) --- .../DD4hepBlueprintConstruction.cpp | 491 +++++------------- 1 file changed, 128 insertions(+), 363 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 097f4f0b..c834dcc7 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -47,6 +47,14 @@ using namespace Acts::UnitLiterals; using enum Acts::AxisDirection; namespace Blueprints { + /// Commonly used envelopes for blueprint construction below + const auto kTrackerEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); + const auto kBarrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {1_mm, 1_mm}); + const auto kTightBarrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); + const auto kVertexEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); + const auto kUngroupedVertexEndcapEnvelope = + Acts::ExtentEnvelope{}.set(AxisZ, {0.5_mm, 0.5_mm}).set(AxisR, {5_mm, 5_mm}); + /// Add a cylindrical beampipe to the passed node using the measures passed as arguments. /// /// We use this to enclose our actual beampipe because that is not a sipmle @@ -90,6 +98,38 @@ namespace Blueprints { }; } + void addGroupedEndcapSide(ActsPlugins::DD4hep::BlueprintBuilder& builder, Acts::Experimental::BlueprintNode& parent, + const std::string& container, const std::regex& filter, AxisDefinition axes, + const Acts::ExtentEnvelope& envelope) { + builder.layers() + .endcap() + .setSensorAxes(std::move(axes)) + .setContainer(container) + .setLayerFilter(filter) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(parent); + } + + void addUngroupedEndcapSide( + ActsPlugins::DD4hep::BlueprintBuilder& builder, Acts::Experimental::BlueprintNode& parent, + const std::string& container, const std::regex& filter, AxisDefinition axes, const std::string& labelPrefix, + const Acts::ExtentEnvelope& envelope, + std::function keyXform = [](const std::string& m) { return m; }) { + const auto detElem = builder.findDetElementByName(container); + auto sensors = builder.findDetElementByNamePattern(detElem.value(), filter); + auto grouper = makeLayerGrouper(filter, labelPrefix, std::move(keyXform)); + builder.layersFromSensors() + .endcap() + .setSensorAxes(std::move(axes)) + .setContainerName(container) + .setSensors(std::move(sensors)) + .groupBy(std::move(grouper)) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .addTo(parent); + } + /// Make the Acts volumes for a barrel detector where sensors are grouped into /// double layers, each double layer ending up in one volume in the Acts geometry. /// @@ -119,10 +159,9 @@ namespace Blueprints { const auto doubleLayerName = makeLayerGrouper(layerRgx, fmt::format("{}|doubleLayer", containerName), [](const auto& m) { return std::stoi(m) / 2; }); - auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {1_mm, 1_mm}); return builder.layersFromSensors() .barrel() - .setEnvelope(barrelEnvelope) + .setEnvelope(kBarrelEnvelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .setSensorAxes("ZYX") .setSensors(std::move(barrelLayerElems)) @@ -136,6 +175,7 @@ namespace Blueprints { /// detector where the barrel and the endcaps can be cleanly stacked along the /// z-axis struct TrackerSpec { + enum class Layout { Grouped, Ungrouped }; std::string barrelContainer; ///< Name of the DetElement containing the barrel AxisDefinition barrelAxes; ///< The axes directions for the barrel sensors std::regex barrelFilter; ///< The layer pattern to filter out barrel layers @@ -143,6 +183,7 @@ namespace Blueprints { AxisDefinition endcapAxes; ///< The axes directions for the endcap sensors std::regex endcapPosFilter; ///< The layer pattern to filter out positive endcap layers std::regex endcapNegFilter; ///< The layer pattern to filter out negative endcap layers + Layout layout = Layout::Grouped; }; // Vertex endcap specs reuse TrackerSpec — barrel fields are ignored since the @@ -165,6 +206,7 @@ namespace Blueprints { .endcapAxes = "XZY", .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, + .layout = TrackerSpec::Layout::Ungrouped, }; /// Make a barrel blueprint node for a generic cylindrical detector. @@ -180,13 +222,12 @@ namespace Blueprints { /// @returns The barrel blueprint node std::shared_ptr makeBarrel(ActsPlugins::DD4hep::BlueprintBuilder& builder, const TrackerSpec& spec) { - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {1_mm, 1_mm}); return builder.layers() .barrel() .setSensorAxes(spec.barrelAxes) .setLayerFilter(spec.barrelFilter) .setContainer(spec.barrelContainer) - .setEnvelope(envelope) + .setEnvelope(kBarrelEnvelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .onLayer(unsetXYCoG) .build(); @@ -195,94 +236,38 @@ namespace Blueprints { /// Attach endcaps to an existing barrel node to form a complete cylindrical /// detector node stacked along the z-axis. /// - /// The layers must already be organised into dedicated DetElements so that - /// @c builder.layers() can pick them up directly via the filter patterns in - /// @p spec. - /// - /// @param builder The Blueprint builder that drives the construction - /// @param barrel The barrel blueprint node to attach the endcaps to - /// @param spec Endcap configuration (container name, axes, pos/neg filters); - /// barrel fields of the spec are ignored - std::shared_ptr attachEndcaps(ActsPlugins::DD4hep::BlueprintBuilder& builder, - std::shared_ptr&& barrel, - const TrackerSpec& spec = GroupedVertexSpec) { - auto node = std::make_shared("Vertex", AxisZ); - node->addChild(barrel); - - // We use an Endcap envelope with smaller z-padding to accomodate for the double layer structure - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {1_mm, 1_mm}).set(AxisR, {5_mm, 5_mm}); - builder.layers() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainer(spec.endcapContainer) - .setLayerFilter(spec.endcapPosFilter) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*node); - - builder.layers() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainer(spec.endcapContainer) - .setLayerFilter(spec.endcapNegFilter) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*node); - - return node; - } - - /// Attach endcaps to an existing barrel node to form a complete cylindrical - /// detector node stacked along the z-axis. - /// - /// Use this variant when the individual sensors have not been placed into - /// dedicated layer DetElements. The function collects all matching sensor - /// DetElements and groups them into layers internally via @c layersFromSensors. + /// Dispatches on @p spec.layout: Grouped uses pre-existing layer DetElements + /// directly; Ungrouped collects sensor DetElements and groups them internally. /// /// @param builder The Blueprint builder that drives the construction /// @param barrel The barrel blueprint node to attach the endcaps to - /// @param spec Endcap configuration (container name, axes, pos/neg filters); - /// barrel fields of the spec are ignored + /// @param spec Endcap configuration (container name, axes, pos/neg filters, + /// layout); barrel fields of the spec are ignored /// @param containerName Name of the resulting top-level cylinder container node - std::shared_ptr attachUngroupedEndcaps( + /// @param keyXform Transform applied to capture group 1 of the filter regex + /// to derive the layer-group key (Ungrouped path only) + std::shared_ptr attachEndcaps( ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& barrel, - const TrackerSpec& spec = UngroupedVertexSpec, const std::string& containerName = "Vertex") { - auto node = std::make_shared(containerName, AxisZ); + const TrackerSpec& spec = GroupedVertexSpec, const std::string& containerName = "Vertex", + std::function keyXform = [](const std::string& m) { + return std::to_string(std::stoi(m) / 2); + }) { + auto node = std::make_shared(containerName, AxisZ); node->addChild(barrel); - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {0.5_mm, 0.5_mm}).set(AxisR, {5_mm, 5_mm}); - - const auto endcapDetElem = builder.findDetElementByName(spec.endcapContainer); - - const auto posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosFilter); - const auto posLayerGrouper = - makeLayerGrouper(spec.endcapPosFilter, fmt::format("{}|doubleLayer_pos", spec.endcapContainer), - [](const auto& m) { return std::stoi(m) / 2; }); - builder.layersFromSensors() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainerName(spec.endcapContainer) - .groupBy(posLayerGrouper) - .setSensors(std::move(posEndcapDetElems)) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*node); - - const auto negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegFilter); - const auto negLayerGrouper = - makeLayerGrouper(spec.endcapNegFilter, fmt::format("{}|doubleLayer_neg", spec.endcapContainer), - [](const auto& m) { return std::stoi(m) / 2; }); - - builder.layersFromSensors() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainerName(spec.endcapContainer) - .setSensors(std::move(negEndcapDetElems)) - .groupBy(negLayerGrouper) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*node); - + if (spec.layout == TrackerSpec::Layout::Grouped) { + addGroupedEndcapSide(builder, *node, spec.endcapContainer, spec.endcapPosFilter, spec.endcapAxes, + kVertexEndcapEnvelope); + addGroupedEndcapSide(builder, *node, spec.endcapContainer, spec.endcapNegFilter, spec.endcapAxes, + kVertexEndcapEnvelope); + } else { + addUngroupedEndcapSide(builder, *node, spec.endcapContainer, spec.endcapPosFilter, spec.endcapAxes, + fmt::format("{}|doubleLayer_pos", spec.endcapContainer), kUngroupedVertexEndcapEnvelope, + keyXform); + addUngroupedEndcapSide(builder, *node, spec.endcapContainer, spec.endcapNegFilter, spec.endcapAxes, + fmt::format("{}|doubleLayer_neg", spec.endcapContainer), kUngroupedVertexEndcapEnvelope, + std::move(keyXform)); + } return node; } @@ -304,16 +289,7 @@ namespace Blueprints { .endcapAxes = "YXZ", .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, - }; - - const auto InnerTrackerSpec = TrackerSpec{ - .barrelContainer = "InnerTrackerBarrel", - .barrelAxes = "XYZ", - .barrelFilter = std::regex{"layer\\d"}, - .endcapContainer = "InnerTrackerEndcap", - .endcapAxes = "YXZ", - .endcapPosFilter = std::regex{"layer_pos(\\d)"}, - .endcapNegFilter = std::regex{"layer_neg(\\d)"}, + .layout = TrackerSpec::Layout::Ungrouped, }; const auto UngroupedInnerTrackerSpec = TrackerSpec{ @@ -324,6 +300,7 @@ namespace Blueprints { .endcapAxes = "YXZ", .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, + .layout = TrackerSpec::Layout::Ungrouped, }; /// Make the Acts volumes for a regular tracker consisting of a barrel and two @@ -332,107 +309,40 @@ namespace Blueprints { /// This is the simple case where all endcap layers fit within the z-extent of /// the barrel, i.e. no endcap layer protrudes into the radial envelope of the /// barrel layers. The barrel and both endcaps are stacked along z inside a - /// single container node. + /// single container node. Dispatches on @p spec.layout for grouped vs. + /// ungrouped endcap sensor DetElements. /// /// @param builder The Blueprint builder that drives the construction /// @param spec The configuration spec defining the barrel and endcap - /// container names, sensor axes, and layer filters + /// container names, sensor axes, layer filters, and layout /// @param trackerName The name of the resulting top-level tracker node /// /// @returns The tracker blueprint node std::shared_ptr makeRegularTracker(ActsPlugins::DD4hep::BlueprintBuilder& builder, const TrackerSpec& spec, const std::string& trackerName) { - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - auto tracker = std::make_shared(trackerName, AxisZ); - - builder.layers() - .barrel() - .setSensorAxes(spec.barrelAxes) - .setLayerFilter(spec.barrelFilter) - .setContainer(spec.barrelContainer) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*tracker); - builder.layers() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainer(spec.endcapContainer) - .setLayerFilter(spec.endcapNegFilter) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*tracker); - builder.layers() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainer(spec.endcapContainer) - .setLayerFilter(spec.endcapPosFilter) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*tracker); - - return tracker; - } - - /// Make the Acts volumes for a regular tracker consisting of a barrel and two - /// endcaps that can be cleanly stacked along the z-axis without nesting. In - /// this case the sensors are not placed into DetElements and have to be - /// grouped first. - /// - /// This is the simple case where all endcap layers fit within the z-extent of - /// the barrel, i.e. no endcap layer protrudes into the radial envelope of the - /// barrel layers. The barrel and both endcaps are stacked along z inside a - /// single container node. - /// - /// @param builder The Blueprint builder that drives the construction - /// @param spec The configuration spec defining the barrel and endcap - /// container names, sensor axes, and layer filters - /// @param trackerName The name of the resulting top-level tracker node - /// - /// @returns The tracker blueprint node - std::shared_ptr makeRegularTrackerUngroupedEndcap( - ActsPlugins::DD4hep::BlueprintBuilder& builder, const TrackerSpec& spec, const std::string& trackerName) { - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - auto tracker = std::make_shared(trackerName, AxisZ); + auto tracker = std::make_shared(trackerName, AxisZ); - // Barrel can just be done normally builder.layers() .barrel() .setSensorAxes(spec.barrelAxes) .setLayerFilter(spec.barrelFilter) .setContainer(spec.barrelContainer) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*tracker); - - const auto endcapDetElem = builder.findDetElementByName(spec.endcapContainer); - - const auto posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosFilter); - const auto posLayerGrouper = - makeLayerGrouper(spec.endcapPosFilter, fmt::format("{}|layer_pos", spec.endcapContainer)); - builder.layersFromSensors() - .endcap() - .setSensors(std::move(posEndcapDetElems)) - .groupBy(posLayerGrouper) - .setSensorAxes(spec.endcapAxes) - .setContainerName(spec.endcapContainer) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*tracker); - - const auto negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegFilter); - const auto negLayerGrouper = - makeLayerGrouper(spec.endcapNegFilter, fmt::format("{}|layer_neg", spec.endcapContainer)); - builder.layersFromSensors() - .endcap() - .setSensors(std::move(negEndcapDetElems)) - .groupBy(negLayerGrouper) - .setSensorAxes(spec.endcapAxes) - .setContainerName(spec.endcapContainer) - .setEnvelope(envelope) + .setEnvelope(kTrackerEnvelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .addTo(*tracker); + if (spec.layout == TrackerSpec::Layout::Grouped) { + addGroupedEndcapSide(builder, *tracker, spec.endcapContainer, spec.endcapNegFilter, spec.endcapAxes, + kTrackerEnvelope); + addGroupedEndcapSide(builder, *tracker, spec.endcapContainer, spec.endcapPosFilter, spec.endcapAxes, + kTrackerEnvelope); + } else { + addUngroupedEndcapSide(builder, *tracker, spec.endcapContainer, spec.endcapNegFilter, spec.endcapAxes, + fmt::format("{}|layer_neg", spec.endcapContainer), kTrackerEnvelope); + addUngroupedEndcapSide(builder, *tracker, spec.endcapContainer, spec.endcapPosFilter, spec.endcapAxes, + fmt::format("{}|layer_pos", spec.endcapContainer), kTrackerEnvelope); + } return tracker; } @@ -457,6 +367,8 @@ namespace Blueprints { ///< the barrel radial envelope std::regex endcapNegOuterFilter = std::regex{"layer_neg[1-6]"}; ///< The layer pattern to filter the outer ///< negative endcap layers + enum class Layout { Grouped, Ungrouped }; + Layout layout = Layout::Grouped; }; const auto UngroupedNestedInnerTrackerSpec = NestedInnerTrackerSpec{ @@ -470,6 +382,7 @@ namespace Blueprints { .endcapPosOuterFilter = std::regex{"layer([1-6])_module\\d+_sensor\\d+_pos"}, .endcapNegInnerFilter = std::regex{"layer(0)_module\\d+_sensor\\d+_neg"}, .endcapNegOuterFilter = std::regex{"layer([1-6])_module\\d+_sensor\\d+_neg"}, + .layout = NestedInnerTrackerSpec::Layout::Ungrouped, }; /// Make a nested inner tracker that encloses the vertex. @@ -502,6 +415,7 @@ namespace Blueprints { /// r endcap(Pos|Neg)InnerFilter /// /// The labels correspond to the members of the NestedInnerTrackerSpec. + /// Dispatches on @p spec.layout for grouped vs. ungrouped endcap sensors. /// /// @param builder The Blueprint builder that drives the construction /// @param vertex The vertex detector blueprint node @@ -523,133 +437,12 @@ namespace Blueprints { // two innermost InnerTrackerBarrel layers because the outermost vertex // layer extends further in r, than the innermost border of the InnerTracker // endcaps. Hence, we also need to stack them in the correct order. - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); - auto innerInnerBarrel = builder.layers() - .barrel() - .setSensorAxes(spec.barrelAxes) - .setLayerFilter(spec.barrelInnerFilter) - .setContainer(spec.barrelContainer) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .onLayer(Blueprints::unsetXYCoG) - .build(); - innerInnerBarrel->addChild(vertex); - - auto innerInnerTracker = std::make_shared("InnerInnerTracker", AxisZ); - innerInnerTracker->addChild(innerInnerBarrel); - builder.layers() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainer(spec.endcapContainer) - .setLayerFilter(spec.endcapPosInnerFilter) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerInnerTracker); - builder.layers() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainer(spec.endcapContainer) - .setLayerFilter(spec.endcapNegInnerFilter) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerInnerTracker); - - auto innerTracker = std::make_shared("InnerTracker", AxisZ); - innerTracker->addCylinderContainer("InnerTrackerBarrel", AxisR, [&](auto& innerBarrel) { - innerBarrel.addChild(innerInnerTracker); - builder.layers() - .barrel() - .setSensorAxes(spec.barrelAxes) - .setContainer(spec.barrelContainer) - .setLayerFilter(spec.barrelOuterFilter) - .setEnvelope(envelope) - .onLayer(Blueprints::unsetXYCoG) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(innerBarrel); - }); - // Then add the (rest of the) two endcaps - builder.layers() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainer(spec.endcapContainer) - .setLayerFilter(spec.endcapPosOuterFilter) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerTracker); - builder.layers() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainer(spec.endcapContainer) - .setLayerFilter(spec.endcapNegOuterFilter) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerTracker); - - return innerTracker; - } - - /// Make a nested inner tracker that encloses the vertex. - /// - /// This version uses a DD4hep detector geometry where the individual layers - /// have not been put into dedicated DetElements. Instead this conversion will - /// pick up all DetElements of the sensors and group them internally. - /// - /// Nesting in this case means that at least one of the endcap layers - /// protrudes into the cylinder described by the barrel layers. This makes it - /// necessary to stack the volumes surrounding the layers in the correct order - /// in r and z to avoid overlapping volumes. - /// - /// For this specific case the tracker can only be nested "once" this means - /// that it looks something like the following. - /// - /// b b - /// a a - /// r endcap(Pos|Neg)OuterFilter r - /// r ⌄ ⌄ ⌄ ⌄ ⌄ ⌄ r - /// r | | | ───────────────────── | | | < e - /// e | | | ───────────────────── | | | < l - /// l > | | | | | ───────── | | | | | O - /// I > | | | | | ───────── | | | | | u - /// n | | | | | | | | | | t - /// n | | | | | VTX | | | | | e - /// e | | | | | | | | | | r - /// r > | | | | | ───────── | | | | | F - /// F > | | | | | ───────── | | | | | i - /// i | | | ───────────────────── | | | < l - /// l | | | ───────────────────── | | | < t - /// t e - /// e ^ ^ ^ ^ r - /// r endcap(Pos|Neg)InnerFilter - /// - /// The labels correspond to the members of the NestedInnerTrackerSpec. - /// - /// @param builder The Blueprint builder that drives the construction - /// @param vertex The vertex detector blueprint node - /// @param spec The spec for defining how the nesting is done specifically - /// for this detector - /// - /// @returns The inner tracker blueprint node - std::shared_ptr makeNestedInnerTrackerUngroupedEndcaps( - ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& vertex, - const NestedInnerTrackerSpec& spec = UngroupedNestedInnerTrackerSpec) { - // We have to create the inner tracker in several steps, because the inner - // most endcap layer protrudes into the envelope that is created by the - // outermost barrel layer. That creates an overlap in z while stacking. - // Hence, we build it in steps grouping the innermost two layers of the - // barrel and the innermost layer of the endcap into an "inner" inner - // tracker (stacking them along z), we then stack the last barrel layer - // along r, before stacking the remaining endcap layers along z. - // Additionally, we have to first put the whole vertex detector inside the - // two innermost InnerTrackerBarrel layers because the outermost vertex - // layer extends further in r, than the innermost border of the InnerTracker - // endcaps. Hence, we also need to stack them in the correct order. - auto envelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {5_mm, 5_mm}); auto innerInnerBarrel = builder.layers() .barrel() .setSensorAxes(spec.barrelAxes) .setLayerFilter(spec.barrelInnerFilter) .setContainer(spec.barrelContainer) - .setEnvelope(envelope) + .setEnvelope(kTrackerEnvelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .onLayer(Blueprints::unsetXYCoG) .build(); @@ -658,33 +451,17 @@ namespace Blueprints { auto innerInnerTracker = std::make_shared("InnerInnerTracker", AxisZ); innerInnerTracker->addChild(innerInnerBarrel); - auto endcapDetElem = builder.findDetElementByName(spec.endcapContainer); - auto posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosInnerFilter); - auto posLayerGrouper = - makeLayerGrouper(spec.endcapPosInnerFilter, fmt::format("{}|layer_pos", spec.endcapContainer)); - builder.layersFromSensors() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainerName(spec.endcapContainer) - .setSensors(std::move(posEndcapDetElems)) - .groupBy(posLayerGrouper) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerInnerTracker); - - auto negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegInnerFilter); - auto negLayerGrouper = - makeLayerGrouper(spec.endcapNegInnerFilter, fmt::format("{}|layer_neg", spec.endcapContainer)); - - builder.layersFromSensors() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainerName(spec.endcapContainer) - .setSensors(std::move(negEndcapDetElems)) - .groupBy(negLayerGrouper) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerInnerTracker); + if (spec.layout == NestedInnerTrackerSpec::Layout::Grouped) { + addGroupedEndcapSide(builder, *innerInnerTracker, spec.endcapContainer, spec.endcapPosInnerFilter, + spec.endcapAxes, kTrackerEnvelope); + addGroupedEndcapSide(builder, *innerInnerTracker, spec.endcapContainer, spec.endcapNegInnerFilter, + spec.endcapAxes, kTrackerEnvelope); + } else { + addUngroupedEndcapSide(builder, *innerInnerTracker, spec.endcapContainer, spec.endcapPosInnerFilter, + spec.endcapAxes, fmt::format("{}|layer_pos", spec.endcapContainer), kTrackerEnvelope); + addUngroupedEndcapSide(builder, *innerInnerTracker, spec.endcapContainer, spec.endcapNegInnerFilter, + spec.endcapAxes, fmt::format("{}|layer_neg", spec.endcapContainer), kTrackerEnvelope); + } auto innerTracker = std::make_shared("InnerTracker", AxisZ); innerTracker->addCylinderContainer("InnerTrackerBarrel", AxisR, [&](auto& innerBarrel) { @@ -694,36 +471,23 @@ namespace Blueprints { .setSensorAxes(spec.barrelAxes) .setContainer(spec.barrelContainer) .setLayerFilter(spec.barrelOuterFilter) - .setEnvelope(envelope) + .setEnvelope(kTrackerEnvelope) .onLayer(Blueprints::unsetXYCoG) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .addTo(innerBarrel); }); - // Then add the (rest of the) two endcaps - posEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapPosOuterFilter); - posLayerGrouper = makeLayerGrouper(spec.endcapPosOuterFilter, fmt::format("{}|layer_pos", spec.endcapContainer)); - builder.layersFromSensors() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainerName(spec.endcapContainer) - .setSensors(std::move(posEndcapDetElems)) - .groupBy(posLayerGrouper) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerTracker); - - negEndcapDetElems = builder.findDetElementByNamePattern(endcapDetElem.value(), spec.endcapNegOuterFilter); - negLayerGrouper = makeLayerGrouper(spec.endcapNegOuterFilter, fmt::format("{}|layer_neg", spec.endcapContainer)); - builder.layersFromSensors() - .endcap() - .setSensorAxes(spec.endcapAxes) - .setContainerName(spec.endcapContainer) - .setSensors(std::move(negEndcapDetElems)) - .groupBy(negLayerGrouper) - .setEnvelope(envelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*innerTracker); + if (spec.layout == NestedInnerTrackerSpec::Layout::Grouped) { + addGroupedEndcapSide(builder, *innerTracker, spec.endcapContainer, spec.endcapPosOuterFilter, spec.endcapAxes, + kTrackerEnvelope); + addGroupedEndcapSide(builder, *innerTracker, spec.endcapContainer, spec.endcapNegOuterFilter, spec.endcapAxes, + kTrackerEnvelope); + } else { + addUngroupedEndcapSide(builder, *innerTracker, spec.endcapContainer, spec.endcapPosOuterFilter, spec.endcapAxes, + fmt::format("{}|layer_pos", spec.endcapContainer), kTrackerEnvelope); + addUngroupedEndcapSide(builder, *innerTracker, spec.endcapContainer, spec.endcapNegOuterFilter, spec.endcapAxes, + fmt::format("{}|layer_neg", spec.endcapContainer), kTrackerEnvelope); + } return innerTracker; } @@ -740,13 +504,12 @@ namespace MuColl { // NOTE: Need to set rather small padding here for the R-direction, because // the innermost two layers are a double layer for which the cylindrical // volumes are overlapping otherwise - auto barrelEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {5_mm, 5_mm}).set(AxisR, {0.4_mm, 0.4_mm}); - auto vertexBarrel = builder.layers() + auto vertexBarrel = builder.layers() .barrel() .setSensorAxes("ZYX") .setLayerFilter("layer_\\d") .setContainer("VertexBarrel") - .setEnvelope(barrelEnvelope) + .setEnvelope(Blueprints::kTightBarrelEnvelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .onLayer(Blueprints::unsetXYCoG) .build(); @@ -770,13 +533,13 @@ namespace FCCee { Blueprints::addCylindricalBeampipe(outer); auto vtxBarrel = Blueprints::makeDoubleLayerBarrel(builder); - auto vertex = Blueprints::attachUngroupedEndcaps(builder, std::move(vtxBarrel)); + auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), Blueprints::UngroupedVertexSpec); auto innerTrackerBarrel = Blueprints::makeBarrel(builder, Blueprints::UngroupedInnerTrackerSpec); innerTrackerBarrel->addChild(vertex); - auto innerTrackerEndcap = Blueprints::attachUngroupedEndcaps( - builder, std::move(innerTrackerBarrel), Blueprints::UngroupedInnerTrackerSpec, "InnerTrackerEndcap"); + auto innerTrackerEndcap = Blueprints::attachEndcaps(builder, std::move(innerTrackerBarrel), + Blueprints::UngroupedInnerTrackerSpec, "InnerTrackerEndcap"); outer.addChild(innerTrackerEndcap); auto set = Blueprints::makeDoubleLayerBarrel(builder, "SET", std::regex{"set_ladder_(\\d)_\\d_\\d+"}); @@ -791,9 +554,10 @@ namespace FCCee { Blueprints::addCylindricalBeampipe(outer); auto vtxBarrel = Blueprints::makeDoubleLayerBarrel(builder); - auto vertex = Blueprints::attachUngroupedEndcaps(builder, std::move(vtxBarrel)); + auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), Blueprints::UngroupedVertexSpec); - auto innerTracker = Blueprints::makeNestedInnerTrackerUngroupedEndcaps(builder, std::move(vertex)); + auto innerTracker = + Blueprints::makeNestedInnerTracker(builder, std::move(vertex), Blueprints::UngroupedNestedInnerTrackerSpec); outer.addChild(innerTracker); } } // namespace ILD_FCCee_v02 @@ -804,13 +568,14 @@ namespace FCCee { auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); auto vtxBarrel = Blueprints::makeDoubleLayerBarrel(builder); - auto vertex = Blueprints::attachUngroupedEndcaps(builder, std::move(vtxBarrel)); + auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), Blueprints::UngroupedVertexSpec); - auto innerTracker = Blueprints::makeNestedInnerTrackerUngroupedEndcaps(builder, std::move(vertex)); + auto innerTracker = + Blueprints::makeNestedInnerTracker(builder, std::move(vertex), Blueprints::UngroupedNestedInnerTrackerSpec); outer.addChild(innerTracker); auto outerTracker = - Blueprints::makeRegularTrackerUngroupedEndcap(builder, Blueprints::UngroupedOuterTrackerSpec, "OuterTracker"); + Blueprints::makeRegularTracker(builder, Blueprints::UngroupedOuterTrackerSpec, "OuterTracker"); outer.addChild(outerTracker); } } // namespace CLD_o2_v07 From 74473a21e1e2cb1f5e34589f091e0730e33b743c Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 Apr 2026 11:24:27 +0200 Subject: [PATCH 62/69] Add docstrings to functions --- .../DD4hepBlueprintConstruction.cpp | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index c834dcc7..663a22e1 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -83,6 +83,18 @@ namespace Blueprints { return layer; } + /// Build a LayerGrouper that assigns sensor DetElements to named layer groups. + /// + /// The returned callable matches each DetElement name against @p groupRgx. + /// Capture group 1 is extracted, optionally transformed by @p transformMatch, + /// and appended to @p labelBase to form the group key + /// (e.g. `"VertexBarrel|doubleLayer_0"`). Throws if the name does not match. + /// + /// @param groupRgx Regex with exactly one capture group selecting the + /// layer index or identifier within the element name + /// @param labelBase Prefix for the resulting group label + /// @param transformMatch Optional transform applied to capture group 1 before + /// appending to @p labelBase (default: identity) template > LayerGrouper makeLayerGrouper( std::regex groupRgx, std::string labelBase, @@ -98,6 +110,18 @@ namespace Blueprints { }; } + /// Add one endcap side to @p parent using pre-grouped layer DetElements. + /// + /// Delegates to @c builder.layers().endcap(), which requires the layers to + /// already be organised into dedicated DetElements matched by @p filter + /// inside @p container. + /// + /// @param builder Blueprint builder driving the construction + /// @param parent Node to attach the endcap side to + /// @param container Name of the DetElement that contains the endcap layers + /// @param filter Regex selecting the relevant layer DetElements + /// @param axes Sensor coordinate axes for this endcap side + /// @param envelope Extent envelope applied to the resulting volume void addGroupedEndcapSide(ActsPlugins::DD4hep::BlueprintBuilder& builder, Acts::Experimental::BlueprintNode& parent, const std::string& container, const std::regex& filter, AxisDefinition axes, const Acts::ExtentEnvelope& envelope) { @@ -111,6 +135,23 @@ namespace Blueprints { .addTo(parent); } + /// Add one endcap side to @p parent by collecting sensor DetElements and + /// grouping them into layers via @c builder.layersFromSensors(). + /// + /// Use this when sensors have not been placed into dedicated layer + /// DetElements. The grouper is built with @p labelPrefix as the label base + /// and @p keyXform to convert capture group 1 of @p filter to a group key. + /// + /// @param builder Blueprint builder driving the construction + /// @param parent Node to attach the endcap side to + /// @param container Name of the DetElement that contains the sensors + /// @param filter Regex selecting sensor DetElements; capture group 1 + /// is used as the layer discriminator + /// @param axes Sensor coordinate axes for this endcap side + /// @param labelPrefix Prefix passed to @c makeLayerGrouper as the label base + /// @param envelope Extent envelope applied to the resulting volume + /// @param keyXform Transform applied to capture group 1 to derive the + /// group key (default: identity) void addUngroupedEndcapSide( ActsPlugins::DD4hep::BlueprintBuilder& builder, Acts::Experimental::BlueprintNode& parent, const std::string& container, const std::regex& filter, AxisDefinition axes, const std::string& labelPrefix, From 4053b4dd969390e3e19056d750194f879ddf3740 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 Apr 2026 11:43:37 +0200 Subject: [PATCH 63/69] Add general introductory documentation and re-org file contents --- .../DD4hepBlueprintConstruction.cpp | 251 ++++++++++-------- 1 file changed, 142 insertions(+), 109 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 663a22e1..5dcc94fa 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -46,6 +46,37 @@ using LayerGrouper = Acts::Experimental::SensorLayerAssembler> LayerGrouper makeLayerGrouper( std::regex groupRgx, std::string labelBase, @@ -212,44 +354,6 @@ namespace Blueprints { .build(); } - /// A simple struct to contain the configuration for building a regular - /// detector where the barrel and the endcaps can be cleanly stacked along the - /// z-axis - struct TrackerSpec { - enum class Layout { Grouped, Ungrouped }; - std::string barrelContainer; ///< Name of the DetElement containing the barrel - AxisDefinition barrelAxes; ///< The axes directions for the barrel sensors - std::regex barrelFilter; ///< The layer pattern to filter out barrel layers - std::string endcapContainer; ///< Name of the DetElement containing the endcaps - AxisDefinition endcapAxes; ///< The axes directions for the endcap sensors - std::regex endcapPosFilter; ///< The layer pattern to filter out positive endcap layers - std::regex endcapNegFilter; ///< The layer pattern to filter out negative endcap layers - Layout layout = Layout::Grouped; - }; - - // Vertex endcap specs reuse TrackerSpec — barrel fields are ignored since the - // vertex barrel is always built separately via makeDoubleLayerVertexBarrel. - const auto GroupedVertexSpec = TrackerSpec{ - .barrelContainer = {}, - .barrelAxes = "XYZ", - .barrelFilter = {}, - .endcapContainer = "VertexEndcap", - .endcapAxes = "XZY", - .endcapPosFilter = std::regex{"layer_pos\\d+"}, - .endcapNegFilter = std::regex{"layer_neg\\d+"}, - }; - - const auto UngroupedVertexSpec = TrackerSpec{ - .barrelContainer = {}, - .barrelAxes = "XYZ", - .barrelFilter = {}, - .endcapContainer = "VertexEndcap", - .endcapAxes = "XZY", - .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, - .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, - .layout = TrackerSpec::Layout::Ungrouped, - }; - /// Make a barrel blueprint node for a generic cylindrical detector. /// /// Uses the pre-grouped layer DetElements directly (via @c layers()), so @@ -312,38 +416,6 @@ namespace Blueprints { return node; } - const auto OuterTrackerSpec = TrackerSpec{ - .barrelContainer = "OuterTrackerBarrel", - .barrelAxes = "XYZ", - .barrelFilter = std::regex{"layer\\d"}, - .endcapContainer = "OuterTrackerEndcap", - .endcapAxes = "YXZ", - .endcapPosFilter = std::regex{"layer_pos(\\d)"}, - .endcapNegFilter = std::regex{"layer_neg(\\d)"}, - }; - - const auto UngroupedOuterTrackerSpec = TrackerSpec{ - .barrelContainer = "OuterTrackerBarrel", - .barrelAxes = "XYZ", - .barrelFilter = std::regex{"layer\\d"}, - .endcapContainer = "OuterTrackerEndcap", - .endcapAxes = "YXZ", - .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, - .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, - .layout = TrackerSpec::Layout::Ungrouped, - }; - - const auto UngroupedInnerTrackerSpec = TrackerSpec{ - .barrelContainer = "InnerTrackerBarrel", - .barrelAxes = "XYZ", - .barrelFilter = std::regex{"layer\\d"}, - .endcapContainer = "InnerTrackerEndcap", - .endcapAxes = "YXZ", - .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, - .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, - .layout = TrackerSpec::Layout::Ungrouped, - }; - /// Make the Acts volumes for a regular tracker consisting of a barrel and two /// endcaps that can be cleanly stacked along the z-axis without nesting. /// @@ -387,45 +459,6 @@ namespace Blueprints { return tracker; } - /// A simple struct to hold configuration to build a tracker that is nested - /// such that a simple stacking in z does not work. - struct NestedInnerTrackerSpec { - std::string barrelContainer{"InnerTrackerBarrel"}; ///< Name of the DetElement containing the barrel - AxisDefinition barrelAxes{"XYZ"}; ///< The axes directions for the barrel sensors - std::regex barrelInnerFilter = std::regex{"layer[01]"}; ///< The layer pattern to filter the inner barrel layers - ///< that enclose the vertex detector - std::regex barrelOuterFilter = std::regex{"layer2"}; ///< The layer pattern to filter the outer barrel - ///< layer(s) stacked around the inner barrel - std::string endcapContainer{"InnerTrackerEndcap"}; ///< Name of the DetElement containing the endcaps - AxisDefinition endcapAxes{"YXZ"}; ///< The axes directions for the endcap sensors - std::regex endcapPosInnerFilter = std::regex{"layer_pos0"}; ///< The layer pattern to filter the innermost - ///< positive endcap layers that protrude into - ///< the barrel radial envelope - std::regex endcapPosOuterFilter = std::regex{"layer_pos[1-6]"}; ///< The layer pattern to filter the outer - ///< positive endcap layers - std::regex endcapNegInnerFilter = std::regex{"layer_neg0"}; ///< The layer pattern to filter the innermost - ///< negative endcap layers that protrude into - ///< the barrel radial envelope - std::regex endcapNegOuterFilter = std::regex{"layer_neg[1-6]"}; ///< The layer pattern to filter the outer - ///< negative endcap layers - enum class Layout { Grouped, Ungrouped }; - Layout layout = Layout::Grouped; - }; - - const auto UngroupedNestedInnerTrackerSpec = NestedInnerTrackerSpec{ - .barrelContainer = "InnerTrackerBarrel", - .barrelAxes = "XYZ", - .barrelInnerFilter = std::regex{"layer[01]"}, - .barrelOuterFilter = std::regex{"layer2"}, - .endcapContainer = "InnerTrackerEndcap", - .endcapAxes = "YXZ", - .endcapPosInnerFilter = std::regex{"layer(0)_module\\d+_sensor\\d+_pos"}, - .endcapPosOuterFilter = std::regex{"layer([1-6])_module\\d+_sensor\\d+_pos"}, - .endcapNegInnerFilter = std::regex{"layer(0)_module\\d+_sensor\\d+_neg"}, - .endcapNegOuterFilter = std::regex{"layer([1-6])_module\\d+_sensor\\d+_neg"}, - .layout = NestedInnerTrackerSpec::Layout::Ungrouped, - }; - /// Make a nested inner tracker that encloses the vertex. /// /// Nesting in this case means that at least one of the endcap layers From d722ab7634c49c198c6a4d05d4a67b0d6591a674 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 Apr 2026 11:58:12 +0200 Subject: [PATCH 64/69] Make sure to create distinct steps files --- k4ActsTracking/examples/test_visualize_acts_geo.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/k4ActsTracking/examples/test_visualize_acts_geo.py b/k4ActsTracking/examples/test_visualize_acts_geo.py index 18ba6c63..d4d35f2a 100644 --- a/k4ActsTracking/examples/test_visualize_acts_geo.py +++ b/k4ActsTracking/examples/test_visualize_acts_geo.py @@ -38,7 +38,7 @@ iosvc = IOSvc() if args.test_propagation: - iosvc.Output = "steps.root" + iosvc.Output = f"{pathlib.Path(args.compactFile).stem}-steps.root" geoSvc = GeoSvc() geoSvc.detectors = [args.compactFile] @@ -50,11 +50,11 @@ alg_list = [] -if args.test_propagation: - propTest = ActsTestPropagator("TestPropagator") - propTest.OutputLevel = DEBUG - propTest.NumTracks = 100 - alg_list.append(propTest) +# if args.test_propagation: +# propTest = ActsTestPropagator("TestPropagator") +# propTest.OutputLevel = DEBUG +# propTest.NumTracks = 100 +# alg_list.append(propTest) ApplicationMgr( TopAlg=alg_list, From 634b8941a62b939645cac7d06aa4ca55675d1028 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 Apr 2026 13:52:07 +0200 Subject: [PATCH 65/69] Generalize barrel creation to follow similar structure as the rest --- .../DD4hepBlueprintConstruction.cpp | 159 +++++++++--------- 1 file changed, 81 insertions(+), 78 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 5dcc94fa..9f35b35c 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -91,37 +91,40 @@ namespace Blueprints { /// z-axis struct TrackerSpec { enum class Layout { Grouped, Ungrouped }; - std::string barrelContainer; ///< Name of the DetElement containing the barrel - AxisDefinition barrelAxes; ///< The axes directions for the barrel sensors - std::regex barrelFilter; ///< The layer pattern to filter out barrel layers - std::string endcapContainer; ///< Name of the DetElement containing the endcaps - AxisDefinition endcapAxes; ///< The axes directions for the endcap sensors - std::regex endcapPosFilter; ///< The layer pattern to filter out positive endcap layers - std::regex endcapNegFilter; ///< The layer pattern to filter out negative endcap layers - Layout layout = Layout::Grouped; + std::string barrelContainer; ///< Name of the DetElement containing the barrel + AxisDefinition barrelAxes; ///< The axes directions for the barrel sensors + std::regex barrelFilter; ///< The layer pattern to filter out barrel layers + std::string endcapContainer; ///< Name of the DetElement containing the endcaps + AxisDefinition endcapAxes; ///< The axes directions for the endcap sensors + std::regex endcapPosFilter; ///< The layer pattern to filter out positive endcap layers + std::regex endcapNegFilter; ///< The layer pattern to filter out negative endcap layers + Layout endcapLayout = Layout::Grouped; ///< The layout for endcap construction + Layout barrelLayout = Layout::Grouped; ///< The layout for barrel construction }; // Vertex endcap specs reuse TrackerSpec — barrel fields are ignored since the - // vertex barrel is always built separately via makeDoubleLayerVertexBarrel. - const auto GroupedVertexSpec = TrackerSpec{ - .barrelContainer = {}, + // vertex barrel is always built separately via makeDoubleLayerBarrel. + const auto DoubleBarrelLayerVertexSpec = TrackerSpec{ + .barrelContainer = "VertexBarrel", .barrelAxes = "XYZ", - .barrelFilter = {}, + .barrelFilter = std::regex{"layer(\\d+)"}, .endcapContainer = "VertexEndcap", .endcapAxes = "XZY", .endcapPosFilter = std::regex{"layer_pos\\d+"}, .endcapNegFilter = std::regex{"layer_neg\\d+"}, + .endcapLayout = TrackerSpec::Layout::Grouped, + .barrelLayout = TrackerSpec::Layout::Ungrouped, // need to group double layers ourselves }; - const auto UngroupedVertexSpec = TrackerSpec{ - .barrelContainer = {}, + const auto UngroupedDoubleBarrelLayerVertexSpec = TrackerSpec{ + .barrelContainer = "VertexBarrel", .barrelAxes = "XYZ", .barrelFilter = {}, .endcapContainer = "VertexEndcap", .endcapAxes = "XZY", .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, - .layout = TrackerSpec::Layout::Ungrouped, + .endcapLayout = TrackerSpec::Layout::Ungrouped, }; const auto OuterTrackerSpec = TrackerSpec{ @@ -142,7 +145,7 @@ namespace Blueprints { .endcapAxes = "YXZ", .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, - .layout = TrackerSpec::Layout::Ungrouped, + .endcapLayout = TrackerSpec::Layout::Ungrouped, }; const auto UngroupedInnerTrackerSpec = TrackerSpec{ @@ -153,7 +156,19 @@ namespace Blueprints { .endcapAxes = "YXZ", .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, - .layout = TrackerSpec::Layout::Ungrouped, + .endcapLayout = TrackerSpec::Layout::Ungrouped, + }; + + const auto SETSpec = TrackerSpec{ + .barrelContainer = "SET", + .barrelAxes = "XYZ", + .barrelFilter = std::regex{"set_ladder_(\\d)_\\d_\\d+"}, + .endcapContainer = "", + .endcapAxes = "XYZ", + .endcapPosFilter = {}, + .endcapNegFilter = {}, + .endcapLayout = TrackerSpec::Layout::Grouped, + .barrelLayout = TrackerSpec::Layout::Ungrouped, }; /// A simple struct to hold configuration to build a tracker that is nested @@ -223,6 +238,11 @@ namespace Blueprints { return layer; } + /// Transform functions that are commonly used for the @c makeLayerGrouper + /// below + const std::string& identityKey(const std::string& m) { return m; } + int doubleLayerKey(const std::string& m) { return std::stoi(m) / 2; } + /// Build a LayerGrouper that assigns sensor DetElements to named layer groups. /// /// The returned callable matches each DetElement name against @p groupRgx. @@ -237,10 +257,8 @@ namespace Blueprints { /// appending to @p labelBase (default: identity) /// /// @returns a closure (lambda) object that can be passed to groupBy - template > - LayerGrouper makeLayerGrouper( - std::regex groupRgx, std::string labelBase, - TransformF transformMatch = [](const std::string& match) -> std::string { return match; }) { + template + LayerGrouper makeLayerGrouper(std::regex groupRgx, std::string labelBase, TransformF transformMatch = identityKey) { return [=](const auto& e) { std::smatch match; const std::string elemName = e.name(); @@ -313,47 +331,6 @@ namespace Blueprints { .addTo(parent); } - /// Make the Acts volumes for a barrel detector where sensors are grouped into - /// double layers, each double layer ending up in one volume in the Acts geometry. - /// - /// Use this when sensors are not placed into dedicated layer DetElements and - /// the spacing between adjacent layers is too small for non-overlapping - /// cylinder shells if each layer were its own volume. - /// - /// @param builder The Blueprint builder that drives the construction - /// @param containerName The name of the DetElement containing the sensors - /// @param layerRgx Regex to select sensor DetElements. Must not match - /// top-level layer DetElements but the individual sensors - /// (e.g. ladders). Must contain exactly one capture group - /// whose value is convertible to int — adjacent pairs - /// (floor(n/2)) are merged into a single double layer. - /// - /// @returns The barrel blueprint node - std::shared_ptr makeDoubleLayerBarrel(ActsPlugins::DD4hep::BlueprintBuilder& builder, - const std::string& containerName = "VertexBarrel", - const std::regex& layerRgx = std::regex{ - "VertexBarrel_layer(\\d)_ladder\\d+"}) { - // Vertex Barrel has a double layer gap of only 1 mm. This makes it - // (almost) impossible to fit them into mutually exclusive cylinder shell - // volumes. Hence, we make each double layer an Acts layer / volume. - const auto barrelDetElem = builder.findDetElementByName(containerName); - const auto barrelLayerElems = builder.findDetElementByNamePattern(barrelDetElem.value(), layerRgx); - - const auto doubleLayerName = makeLayerGrouper(layerRgx, fmt::format("{}|doubleLayer", containerName), - [](const auto& m) { return std::stoi(m) / 2; }); - - return builder.layersFromSensors() - .barrel() - .setEnvelope(kBarrelEnvelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .setSensorAxes("ZYX") - .setSensors(std::move(barrelLayerElems)) - .groupBy(doubleLayerName) - .setContainerName(containerName) - .onLayer(unsetXYCoG) - .build(); - } - /// Make a barrel blueprint node for a generic cylindrical detector. /// /// Uses the pre-grouped layer DetElements directly (via @c layers()), so @@ -365,15 +342,34 @@ namespace Blueprints { /// barrelFilter used; endcap fields ignored) /// /// @returns The barrel blueprint node + template std::shared_ptr makeBarrel(ActsPlugins::DD4hep::BlueprintBuilder& builder, - const TrackerSpec& spec) { - return builder.layers() + const TrackerSpec& spec, MatchTransformF keyXfrom = identityKey) { + if (spec.barrelLayout == TrackerSpec::Layout::Grouped) { + return builder.layers() + .barrel() + .setSensorAxes(spec.barrelAxes) + .setLayerFilter(spec.barrelFilter) + .setContainer(spec.barrelContainer) + .setEnvelope(kBarrelEnvelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .onLayer(unsetXYCoG) + .build(); + } + + const auto barrelDetElem = builder.findDetElementByName(spec.barrelContainer); + const auto barrelLayerElems = builder.findDetElementByNamePattern(barrelDetElem.value(), spec.barrelFilter); + const auto doubleLayerName = + makeLayerGrouper(spec.barrelFilter, fmt::format("{}|doubleLayer", spec.barrelContainer), keyXfrom); + + return builder.layersFromSensors() .barrel() - .setSensorAxes(spec.barrelAxes) - .setLayerFilter(spec.barrelFilter) - .setContainer(spec.barrelContainer) .setEnvelope(kBarrelEnvelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .setSensorAxes(spec.barrelAxes) + .setSensors(std::move(barrelLayerElems)) + .groupBy(doubleLayerName) + .setContainerName(spec.barrelContainer) .onLayer(unsetXYCoG) .build(); } @@ -393,14 +389,14 @@ namespace Blueprints { /// to derive the layer-group key (Ungrouped path only) std::shared_ptr attachEndcaps( ActsPlugins::DD4hep::BlueprintBuilder& builder, std::shared_ptr&& barrel, - const TrackerSpec& spec = GroupedVertexSpec, const std::string& containerName = "Vertex", + const TrackerSpec& spec, const std::string& containerName, std::function keyXform = [](const std::string& m) { return std::to_string(std::stoi(m) / 2); }) { auto node = std::make_shared(containerName, AxisZ); node->addChild(barrel); - if (spec.layout == TrackerSpec::Layout::Grouped) { + if (spec.endcapLayout == TrackerSpec::Layout::Grouped) { addGroupedEndcapSide(builder, *node, spec.endcapContainer, spec.endcapPosFilter, spec.endcapAxes, kVertexEndcapEnvelope); addGroupedEndcapSide(builder, *node, spec.endcapContainer, spec.endcapNegFilter, spec.endcapAxes, @@ -445,7 +441,7 @@ namespace Blueprints { .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .addTo(*tracker); - if (spec.layout == TrackerSpec::Layout::Grouped) { + if (spec.endcapLayout == TrackerSpec::Layout::Grouped) { addGroupedEndcapSide(builder, *tracker, spec.endcapContainer, spec.endcapNegFilter, spec.endcapAxes, kTrackerEnvelope); addGroupedEndcapSide(builder, *tracker, spec.endcapContainer, spec.endcapPosFilter, spec.endcapAxes, @@ -587,7 +583,8 @@ namespace MuColl { .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .onLayer(Blueprints::unsetXYCoG) .build(); - auto vertex = Blueprints::attachEndcaps(builder, std::move(vertexBarrel)); + auto vertex = Blueprints::attachEndcaps(builder, std::move(vertexBarrel), Blueprints::DoubleBarrelLayerVertexSpec, + "Vertex"); auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex)); outer.addChild(innerTracker); @@ -606,8 +603,10 @@ namespace FCCee { Blueprints::addCylindricalBeampipe(outer); - auto vtxBarrel = Blueprints::makeDoubleLayerBarrel(builder); - auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), Blueprints::UngroupedVertexSpec); + auto vtxBarrel = + Blueprints::makeBarrel(builder, Blueprints::UngroupedDoubleBarrelLayerVertexSpec, Blueprints::doubleLayerKey); + auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), + Blueprints::UngroupedDoubleBarrelLayerVertexSpec, "Vertex"); auto innerTrackerBarrel = Blueprints::makeBarrel(builder, Blueprints::UngroupedInnerTrackerSpec); innerTrackerBarrel->addChild(vertex); @@ -616,7 +615,7 @@ namespace FCCee { Blueprints::UngroupedInnerTrackerSpec, "InnerTrackerEndcap"); outer.addChild(innerTrackerEndcap); - auto set = Blueprints::makeDoubleLayerBarrel(builder, "SET", std::regex{"set_ladder_(\\d)_\\d_\\d+"}); + auto set = Blueprints::makeBarrel(builder, Blueprints::SETSpec, Blueprints::doubleLayerKey); outer.addChild(set); } } // namespace ILD_FCCee_v01 @@ -627,8 +626,10 @@ namespace FCCee { auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); - auto vtxBarrel = Blueprints::makeDoubleLayerBarrel(builder); - auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), Blueprints::UngroupedVertexSpec); + auto vtxBarrel = + Blueprints::makeBarrel(builder, Blueprints::UngroupedDoubleBarrelLayerVertexSpec, Blueprints::doubleLayerKey); + auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), + Blueprints::UngroupedDoubleBarrelLayerVertexSpec, "Vertex"); auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex), Blueprints::UngroupedNestedInnerTrackerSpec); @@ -641,8 +642,10 @@ namespace FCCee { ActsPlugins::DD4hep::BlueprintBuilder& builder) { auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); - auto vtxBarrel = Blueprints::makeDoubleLayerBarrel(builder); - auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), Blueprints::UngroupedVertexSpec); + auto vtxBarrel = + Blueprints::makeBarrel(builder, Blueprints::UngroupedDoubleBarrelLayerVertexSpec, Blueprints::doubleLayerKey); + auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), + Blueprints::UngroupedDoubleBarrelLayerVertexSpec, "Vertex"); auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex), Blueprints::UngroupedNestedInnerTrackerSpec); From 7ea65dd22471ff5b92f63828708fa948d1455b18 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 Apr 2026 14:18:13 +0200 Subject: [PATCH 66/69] Extract more functionality into helper functions --- .../DD4hepBlueprintConstruction.cpp | 223 +++++++++--------- 1 file changed, 113 insertions(+), 110 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 9f35b35c..5ea44d15 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -86,11 +86,15 @@ namespace Blueprints { const auto kUngroupedVertexEndcapEnvelope = Acts::ExtentEnvelope{}.set(AxisZ, {0.5_mm, 0.5_mm}).set(AxisR, {5_mm, 5_mm}); + /// Enum denoting whether a (subdetector) geometry has grouping (layer) + /// DetElements or whether the grouping into layers has to be done during the + /// conversoin. + enum class Layout { Grouped, Ungrouped }; + /// A simple struct to contain the configuration for building a regular /// detector where the barrel and the endcaps can be cleanly stacked along the /// z-axis struct TrackerSpec { - enum class Layout { Grouped, Ungrouped }; std::string barrelContainer; ///< Name of the DetElement containing the barrel AxisDefinition barrelAxes; ///< The axes directions for the barrel sensors std::regex barrelFilter; ///< The layer pattern to filter out barrel layers @@ -112,8 +116,8 @@ namespace Blueprints { .endcapAxes = "XZY", .endcapPosFilter = std::regex{"layer_pos\\d+"}, .endcapNegFilter = std::regex{"layer_neg\\d+"}, - .endcapLayout = TrackerSpec::Layout::Grouped, - .barrelLayout = TrackerSpec::Layout::Ungrouped, // need to group double layers ourselves + .endcapLayout = Layout::Grouped, + .barrelLayout = Layout::Ungrouped, // need to group double layers ourselves }; const auto UngroupedDoubleBarrelLayerVertexSpec = TrackerSpec{ @@ -124,7 +128,7 @@ namespace Blueprints { .endcapAxes = "XZY", .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, - .endcapLayout = TrackerSpec::Layout::Ungrouped, + .endcapLayout = Layout::Ungrouped, }; const auto OuterTrackerSpec = TrackerSpec{ @@ -145,7 +149,7 @@ namespace Blueprints { .endcapAxes = "YXZ", .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, - .endcapLayout = TrackerSpec::Layout::Ungrouped, + .endcapLayout = Layout::Ungrouped, }; const auto UngroupedInnerTrackerSpec = TrackerSpec{ @@ -156,7 +160,7 @@ namespace Blueprints { .endcapAxes = "YXZ", .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, - .endcapLayout = TrackerSpec::Layout::Ungrouped, + .endcapLayout = Layout::Ungrouped, }; const auto SETSpec = TrackerSpec{ @@ -167,8 +171,8 @@ namespace Blueprints { .endcapAxes = "XYZ", .endcapPosFilter = {}, .endcapNegFilter = {}, - .endcapLayout = TrackerSpec::Layout::Grouped, - .barrelLayout = TrackerSpec::Layout::Ungrouped, + .endcapLayout = Layout::Grouped, + .barrelLayout = Layout::Ungrouped, }; /// A simple struct to hold configuration to build a tracker that is nested @@ -192,7 +196,6 @@ namespace Blueprints { ///< the barrel radial envelope std::regex endcapNegOuterFilter = std::regex{"layer_neg[1-6]"}; ///< The layer pattern to filter the outer ///< negative endcap layers - enum class Layout { Grouped, Ungrouped }; Layout layout = Layout::Grouped; }; @@ -207,7 +210,7 @@ namespace Blueprints { .endcapPosOuterFilter = std::regex{"layer([1-6])_module\\d+_sensor\\d+_pos"}, .endcapNegInnerFilter = std::regex{"layer(0)_module\\d+_sensor\\d+_neg"}, .endcapNegOuterFilter = std::regex{"layer([1-6])_module\\d+_sensor\\d+_neg"}, - .layout = NestedInnerTrackerSpec::Layout::Ungrouped, + .layout = Layout::Ungrouped, }; /// Add a cylindrical beampipe to the passed node using the measures passed as arguments. @@ -331,40 +334,89 @@ namespace Blueprints { .addTo(parent); } + /// Add both endcap sides (pos and neg) to @p parent, dispatching on @p layout. + /// + /// @param builder Blueprint builder driving the construction + /// @param parent Node to attach the endcap sides to + /// @param container Name of the DetElement containing the endcap layers/sensors + /// @param posFilter Regex selecting the positive-side layers or sensors + /// @param negFilter Regex selecting the negative-side layers or sensors + /// @param axes Sensor coordinate axes for the endcap sides + /// @param layout Grouped or Ungrouped dispatch + /// @param envelope Extent envelope applied to the resulting volumes + /// @param posLabel Label prefix for the positive side (Ungrouped path only) + /// @param negLabel Label prefix for the negative side (Ungrouped path only) + /// @param keyXform Transform applied to capture group 1 (Ungrouped path only) + template + void addBothEndcapSides(ActsPlugins::DD4hep::BlueprintBuilder& builder, Acts::Experimental::BlueprintNode& parent, + const std::string& container, const std::regex& posFilter, const std::regex& negFilter, + AxisDefinition axes, Layout layout, const Acts::ExtentEnvelope& envelope, + const std::string& posLabel = "", const std::string& negLabel = "", + MatchTransformF keyXform = identityKey) { + if (layout == Layout::Grouped) { + addGroupedEndcapSide(builder, parent, container, posFilter, axes, envelope); + addGroupedEndcapSide(builder, parent, container, negFilter, axes, envelope); + } else { + addUngroupedEndcapSide(builder, parent, container, posFilter, axes, posLabel, envelope, keyXform); + addUngroupedEndcapSide(builder, parent, container, negFilter, axes, negLabel, envelope, keyXform); + } + } + + /// Build a grouped barrel blueprint node from a raw builder chain. + /// + /// Owns the single repeated builder chain for grouped barrel construction. + /// All five grouped barrel call sites delegate here. + /// + /// @param builder Blueprint builder driving the construction + /// @param container Name of the DetElement containing the barrel layers + /// @param filter Regex selecting the layer DetElements + /// @param axes Sensor coordinate axes for the barrel + /// @param envelope Extent envelope applied to the resulting volume + /// + /// @returns The barrel blueprint node + std::shared_ptr makeGroupedBarrel(ActsPlugins::DD4hep::BlueprintBuilder& builder, + const std::string& container, const std::regex& filter, + AxisDefinition axes, const Acts::ExtentEnvelope& envelope) { + return builder.layers() + .barrel() + .setSensorAxes(std::move(axes)) + .setLayerFilter(filter) + .setContainer(container) + .setEnvelope(envelope) + .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) + .onLayer(unsetXYCoG) + .build(); + } + /// Make a barrel blueprint node for a generic cylindrical detector. /// /// Uses the pre-grouped layer DetElements directly (via @c layers()), so /// the barrel layers must already be organised into dedicated DetElements /// matching @p spec.barrelFilter inside @p spec.barrelContainer. /// - /// @param builder The Blueprint builder that drives the construction - /// @param spec Configuration spec (barrelContainer, barrelAxes, - /// barrelFilter used; endcap fields ignored) + /// @param builder The Blueprint builder that drives the construction + /// @param spec Configuration spec (barrelContainer, barrelAxes, + /// barrelFilter used; endcap fields ignored) + /// @param envelope Extent envelope applied to the resulting volume /// /// @returns The barrel blueprint node template std::shared_ptr makeBarrel(ActsPlugins::DD4hep::BlueprintBuilder& builder, - const TrackerSpec& spec, MatchTransformF keyXfrom = identityKey) { - if (spec.barrelLayout == TrackerSpec::Layout::Grouped) { - return builder.layers() - .barrel() - .setSensorAxes(spec.barrelAxes) - .setLayerFilter(spec.barrelFilter) - .setContainer(spec.barrelContainer) - .setEnvelope(kBarrelEnvelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .onLayer(unsetXYCoG) - .build(); + const TrackerSpec& spec, + const Acts::ExtentEnvelope& envelope = kBarrelEnvelope, + MatchTransformF keyXform = identityKey) { + if (spec.barrelLayout == Layout::Grouped) { + return makeGroupedBarrel(builder, spec.barrelContainer, spec.barrelFilter, spec.barrelAxes, envelope); } const auto barrelDetElem = builder.findDetElementByName(spec.barrelContainer); const auto barrelLayerElems = builder.findDetElementByNamePattern(barrelDetElem.value(), spec.barrelFilter); const auto doubleLayerName = - makeLayerGrouper(spec.barrelFilter, fmt::format("{}|doubleLayer", spec.barrelContainer), keyXfrom); + makeLayerGrouper(spec.barrelFilter, fmt::format("{}|doubleLayer", spec.barrelContainer), keyXform); return builder.layersFromSensors() .barrel() - .setEnvelope(kBarrelEnvelope) + .setEnvelope(envelope) .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) .setSensorAxes(spec.barrelAxes) .setSensors(std::move(barrelLayerElems)) @@ -396,7 +448,7 @@ namespace Blueprints { auto node = std::make_shared(containerName, AxisZ); node->addChild(barrel); - if (spec.endcapLayout == TrackerSpec::Layout::Grouped) { + if (spec.endcapLayout == Layout::Grouped) { addGroupedEndcapSide(builder, *node, spec.endcapContainer, spec.endcapPosFilter, spec.endcapAxes, kVertexEndcapEnvelope); addGroupedEndcapSide(builder, *node, spec.endcapContainer, spec.endcapNegFilter, spec.endcapAxes, @@ -431,27 +483,11 @@ namespace Blueprints { const TrackerSpec& spec, const std::string& trackerName) { auto tracker = std::make_shared(trackerName, AxisZ); - - builder.layers() - .barrel() - .setSensorAxes(spec.barrelAxes) - .setLayerFilter(spec.barrelFilter) - .setContainer(spec.barrelContainer) - .setEnvelope(kTrackerEnvelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(*tracker); - - if (spec.endcapLayout == TrackerSpec::Layout::Grouped) { - addGroupedEndcapSide(builder, *tracker, spec.endcapContainer, spec.endcapNegFilter, spec.endcapAxes, - kTrackerEnvelope); - addGroupedEndcapSide(builder, *tracker, spec.endcapContainer, spec.endcapPosFilter, spec.endcapAxes, - kTrackerEnvelope); - } else { - addUngroupedEndcapSide(builder, *tracker, spec.endcapContainer, spec.endcapNegFilter, spec.endcapAxes, - fmt::format("{}|layer_neg", spec.endcapContainer), kTrackerEnvelope); - addUngroupedEndcapSide(builder, *tracker, spec.endcapContainer, spec.endcapPosFilter, spec.endcapAxes, - fmt::format("{}|layer_pos", spec.endcapContainer), kTrackerEnvelope); - } + tracker->addChild(makeBarrel(builder, spec, kTrackerEnvelope)); + addBothEndcapSides(builder, *tracker, spec.endcapContainer, spec.endcapPosFilter, spec.endcapNegFilter, + spec.endcapAxes, spec.endcapLayout, kTrackerEnvelope, + fmt::format("{}|layer_pos", spec.endcapContainer), + fmt::format("{}|layer_neg", spec.endcapContainer)); return tracker; } @@ -507,57 +543,30 @@ namespace Blueprints { // two innermost InnerTrackerBarrel layers because the outermost vertex // layer extends further in r, than the innermost border of the InnerTracker // endcaps. Hence, we also need to stack them in the correct order. - auto innerInnerBarrel = builder.layers() - .barrel() - .setSensorAxes(spec.barrelAxes) - .setLayerFilter(spec.barrelInnerFilter) - .setContainer(spec.barrelContainer) - .setEnvelope(kTrackerEnvelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .onLayer(Blueprints::unsetXYCoG) - .build(); + auto innerInnerBarrel = + makeGroupedBarrel(builder, spec.barrelContainer, spec.barrelInnerFilter, spec.barrelAxes, kTrackerEnvelope); innerInnerBarrel->addChild(vertex); auto innerInnerTracker = std::make_shared("InnerInnerTracker", AxisZ); innerInnerTracker->addChild(innerInnerBarrel); - if (spec.layout == NestedInnerTrackerSpec::Layout::Grouped) { - addGroupedEndcapSide(builder, *innerInnerTracker, spec.endcapContainer, spec.endcapPosInnerFilter, - spec.endcapAxes, kTrackerEnvelope); - addGroupedEndcapSide(builder, *innerInnerTracker, spec.endcapContainer, spec.endcapNegInnerFilter, - spec.endcapAxes, kTrackerEnvelope); - } else { - addUngroupedEndcapSide(builder, *innerInnerTracker, spec.endcapContainer, spec.endcapPosInnerFilter, - spec.endcapAxes, fmt::format("{}|layer_pos", spec.endcapContainer), kTrackerEnvelope); - addUngroupedEndcapSide(builder, *innerInnerTracker, spec.endcapContainer, spec.endcapNegInnerFilter, - spec.endcapAxes, fmt::format("{}|layer_neg", spec.endcapContainer), kTrackerEnvelope); - } + addBothEndcapSides(builder, *innerInnerTracker, spec.endcapContainer, spec.endcapPosInnerFilter, + spec.endcapNegInnerFilter, spec.endcapAxes, spec.layout, kTrackerEnvelope, + fmt::format("{}|layer_pos", spec.endcapContainer), + fmt::format("{}|layer_neg", spec.endcapContainer)); auto innerTracker = std::make_shared("InnerTracker", AxisZ); innerTracker->addCylinderContainer("InnerTrackerBarrel", AxisR, [&](auto& innerBarrel) { innerBarrel.addChild(innerInnerTracker); - builder.layers() - .barrel() - .setSensorAxes(spec.barrelAxes) - .setContainer(spec.barrelContainer) - .setLayerFilter(spec.barrelOuterFilter) - .setEnvelope(kTrackerEnvelope) - .onLayer(Blueprints::unsetXYCoG) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .addTo(innerBarrel); + auto outerBarrel = + makeGroupedBarrel(builder, spec.barrelContainer, spec.barrelOuterFilter, spec.barrelAxes, kTrackerEnvelope); + innerBarrel.addChild(outerBarrel); }); - if (spec.layout == NestedInnerTrackerSpec::Layout::Grouped) { - addGroupedEndcapSide(builder, *innerTracker, spec.endcapContainer, spec.endcapPosOuterFilter, spec.endcapAxes, - kTrackerEnvelope); - addGroupedEndcapSide(builder, *innerTracker, spec.endcapContainer, spec.endcapNegOuterFilter, spec.endcapAxes, - kTrackerEnvelope); - } else { - addUngroupedEndcapSide(builder, *innerTracker, spec.endcapContainer, spec.endcapPosOuterFilter, spec.endcapAxes, - fmt::format("{}|layer_pos", spec.endcapContainer), kTrackerEnvelope); - addUngroupedEndcapSide(builder, *innerTracker, spec.endcapContainer, spec.endcapNegOuterFilter, spec.endcapAxes, - fmt::format("{}|layer_neg", spec.endcapContainer), kTrackerEnvelope); - } + addBothEndcapSides(builder, *innerTracker, spec.endcapContainer, spec.endcapPosOuterFilter, + spec.endcapNegOuterFilter, spec.endcapAxes, spec.layout, kTrackerEnvelope, + fmt::format("{}|layer_pos", spec.endcapContainer), + fmt::format("{}|layer_neg", spec.endcapContainer)); return innerTracker; } @@ -574,15 +583,8 @@ namespace MuColl { // NOTE: Need to set rather small padding here for the R-direction, because // the innermost two layers are a double layer for which the cylindrical // volumes are overlapping otherwise - auto vertexBarrel = builder.layers() - .barrel() - .setSensorAxes("ZYX") - .setLayerFilter("layer_\\d") - .setContainer("VertexBarrel") - .setEnvelope(Blueprints::kTightBarrelEnvelope) - .setAttachmentStrategy(Acts::VolumeAttachmentStrategy::First) - .onLayer(Blueprints::unsetXYCoG) - .build(); + auto vertexBarrel = Blueprints::makeGroupedBarrel(builder, "VertexBarrel", std::regex{"layer_\\d"}, "ZYX", + Blueprints::kTightBarrelEnvelope); auto vertex = Blueprints::attachEndcaps(builder, std::move(vertexBarrel), Blueprints::DoubleBarrelLayerVertexSpec, "Vertex"); @@ -603,10 +605,10 @@ namespace FCCee { Blueprints::addCylindricalBeampipe(outer); - auto vtxBarrel = - Blueprints::makeBarrel(builder, Blueprints::UngroupedDoubleBarrelLayerVertexSpec, Blueprints::doubleLayerKey); - auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), - Blueprints::UngroupedDoubleBarrelLayerVertexSpec, "Vertex"); + auto vtxBarrel = Blueprints::makeBarrel(builder, Blueprints::UngroupedDoubleBarrelLayerVertexSpec, + Blueprints::kBarrelEnvelope, Blueprints::doubleLayerKey); + auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), + Blueprints::UngroupedDoubleBarrelLayerVertexSpec, "Vertex"); auto innerTrackerBarrel = Blueprints::makeBarrel(builder, Blueprints::UngroupedInnerTrackerSpec); innerTrackerBarrel->addChild(vertex); @@ -615,7 +617,8 @@ namespace FCCee { Blueprints::UngroupedInnerTrackerSpec, "InnerTrackerEndcap"); outer.addChild(innerTrackerEndcap); - auto set = Blueprints::makeBarrel(builder, Blueprints::SETSpec, Blueprints::doubleLayerKey); + auto set = + Blueprints::makeBarrel(builder, Blueprints::SETSpec, Blueprints::kBarrelEnvelope, Blueprints::doubleLayerKey); outer.addChild(set); } } // namespace ILD_FCCee_v01 @@ -626,10 +629,10 @@ namespace FCCee { auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); - auto vtxBarrel = - Blueprints::makeBarrel(builder, Blueprints::UngroupedDoubleBarrelLayerVertexSpec, Blueprints::doubleLayerKey); - auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), - Blueprints::UngroupedDoubleBarrelLayerVertexSpec, "Vertex"); + auto vtxBarrel = Blueprints::makeBarrel(builder, Blueprints::UngroupedDoubleBarrelLayerVertexSpec, + Blueprints::kBarrelEnvelope, Blueprints::doubleLayerKey); + auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), + Blueprints::UngroupedDoubleBarrelLayerVertexSpec, "Vertex"); auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex), Blueprints::UngroupedNestedInnerTrackerSpec); @@ -642,10 +645,10 @@ namespace FCCee { ActsPlugins::DD4hep::BlueprintBuilder& builder) { auto& outer = root.addCylinderContainer(detName, AxisR); Blueprints::addCylindricalBeampipe(outer); - auto vtxBarrel = - Blueprints::makeBarrel(builder, Blueprints::UngroupedDoubleBarrelLayerVertexSpec, Blueprints::doubleLayerKey); - auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), - Blueprints::UngroupedDoubleBarrelLayerVertexSpec, "Vertex"); + auto vtxBarrel = Blueprints::makeBarrel(builder, Blueprints::UngroupedDoubleBarrelLayerVertexSpec, + Blueprints::kBarrelEnvelope, Blueprints::doubleLayerKey); + auto vertex = Blueprints::attachEndcaps(builder, std::move(vtxBarrel), + Blueprints::UngroupedDoubleBarrelLayerVertexSpec, "Vertex"); auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex), Blueprints::UngroupedNestedInnerTrackerSpec); From 76690b90581f3dc9292b7105bc3eb5c031c0e292 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 Apr 2026 14:27:42 +0200 Subject: [PATCH 67/69] Fix a few details that have been lost in refactoring --- .../src/components/DD4hepBlueprintConstruction.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index 5ea44d15..b974c2ae 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -106,29 +106,26 @@ namespace Blueprints { Layout barrelLayout = Layout::Grouped; ///< The layout for barrel construction }; - // Vertex endcap specs reuse TrackerSpec — barrel fields are ignored since the - // vertex barrel is always built separately via makeDoubleLayerBarrel. const auto DoubleBarrelLayerVertexSpec = TrackerSpec{ .barrelContainer = "VertexBarrel", - .barrelAxes = "XYZ", - .barrelFilter = std::regex{"layer(\\d+)"}, + .barrelAxes = "ZYX", + .barrelFilter = std::regex{"layer_\\d+"}, .endcapContainer = "VertexEndcap", .endcapAxes = "XZY", .endcapPosFilter = std::regex{"layer_pos\\d+"}, .endcapNegFilter = std::regex{"layer_neg\\d+"}, - .endcapLayout = Layout::Grouped, - .barrelLayout = Layout::Ungrouped, // need to group double layers ourselves }; const auto UngroupedDoubleBarrelLayerVertexSpec = TrackerSpec{ .barrelContainer = "VertexBarrel", - .barrelAxes = "XYZ", - .barrelFilter = {}, + .barrelAxes = "ZYX", + .barrelFilter = std::regex{"VertexBarrel_layer(\\d)_ladder\\d+"}, .endcapContainer = "VertexEndcap", .endcapAxes = "XZY", .endcapPosFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_pos"}, .endcapNegFilter = std::regex{"layer(\\d+)_module\\d+_sensor\\d+_neg"}, .endcapLayout = Layout::Ungrouped, + .barrelLayout = Layout::Ungrouped, }; const auto OuterTrackerSpec = TrackerSpec{ From 1fb7c0ace14064aeba1096a8e20e8022338a2696 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 Apr 2026 14:29:30 +0200 Subject: [PATCH 68/69] Make sure to run propagation checks --- k4ActsTracking/examples/test_visualize_acts_geo.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/k4ActsTracking/examples/test_visualize_acts_geo.py b/k4ActsTracking/examples/test_visualize_acts_geo.py index d4d35f2a..75853227 100644 --- a/k4ActsTracking/examples/test_visualize_acts_geo.py +++ b/k4ActsTracking/examples/test_visualize_acts_geo.py @@ -50,11 +50,11 @@ alg_list = [] -# if args.test_propagation: -# propTest = ActsTestPropagator("TestPropagator") -# propTest.OutputLevel = DEBUG -# propTest.NumTracks = 100 -# alg_list.append(propTest) +if args.test_propagation: + propTest = ActsTestPropagator("TestPropagator") + propTest.OutputLevel = DEBUG + propTest.NumTracks = 100 + alg_list.append(propTest) ApplicationMgr( TopAlg=alg_list, From f939537de460f543ac00449fe66f2c8a966c8394 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 21 Apr 2026 14:38:49 +0200 Subject: [PATCH 69/69] Add TODOs for ILD_FCCee models --- .../src/components/DD4hepBlueprintConstruction.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp index b974c2ae..30865ac9 100644 --- a/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp +++ b/k4ActsTracking/src/components/DD4hepBlueprintConstruction.cpp @@ -614,6 +614,10 @@ namespace FCCee { Blueprints::UngroupedInnerTrackerSpec, "InnerTrackerEndcap"); outer.addChild(innerTrackerEndcap); + // TODO: this is not yet properly working only part of the SET show up in + // the exporte .obj geometry. This usually indicates some issues with the + // AxisDirection, but that would mean that there are different + // AxisDirections in play for the SET geometry auto set = Blueprints::makeBarrel(builder, Blueprints::SETSpec, Blueprints::kBarrelEnvelope, Blueprints::doubleLayerKey); outer.addChild(set); @@ -634,6 +638,8 @@ namespace FCCee { auto innerTracker = Blueprints::makeNestedInnerTracker(builder, std::move(vertex), Blueprints::UngroupedNestedInnerTrackerSpec); outer.addChild(innerTracker); + + // TODO: Add SET (see V01 for caveats) } } // namespace ILD_FCCee_v02